cache clearing through cluster messages
parent
60f0a13959
commit
1401786254
21
bin/www.js
21
bin/www.js
|
|
@ -7,20 +7,37 @@ import * as configFunctions from '../helpers/functions.config.js';
|
||||||
import exitHook from 'exit-hook';
|
import exitHook from 'exit-hook';
|
||||||
import ntfyPublish from '@cityssm/ntfy-publish';
|
import ntfyPublish from '@cityssm/ntfy-publish';
|
||||||
import Debug from 'debug';
|
import Debug from 'debug';
|
||||||
const debug = Debug('lot-occupancy-system:www');
|
const debug = Debug(`lot-occupancy-system:www:${process.pid}`);
|
||||||
const directoryName = dirname(fileURLToPath(import.meta.url));
|
const directoryName = dirname(fileURLToPath(import.meta.url));
|
||||||
const processCount = Math.min(configFunctions.getProperty('application.maximumProcesses'), os.cpus().length);
|
const processCount = Math.min(configFunctions.getProperty('application.maximumProcesses'), os.cpus().length);
|
||||||
|
process.title =
|
||||||
|
configFunctions.getProperty('application.applicationName') + ' (Primary)';
|
||||||
debug(`Primary pid: ${process.pid}`);
|
debug(`Primary pid: ${process.pid}`);
|
||||||
|
debug(`Primary title: ${process.title}`);
|
||||||
debug(`Launching ${processCount} processes`);
|
debug(`Launching ${processCount} processes`);
|
||||||
const clusterSettings = {
|
const clusterSettings = {
|
||||||
exec: directoryName + '/wwwProcess.js'
|
exec: directoryName + '/wwwProcess.js'
|
||||||
};
|
};
|
||||||
cluster.setupPrimary(clusterSettings);
|
cluster.setupPrimary(clusterSettings);
|
||||||
|
const activeWorkers = new Map();
|
||||||
for (let index = 0; index < processCount; index += 1) {
|
for (let index = 0; index < processCount; index += 1) {
|
||||||
cluster.fork();
|
const worker = cluster.fork();
|
||||||
|
activeWorkers.set(worker.process.pid, worker);
|
||||||
}
|
}
|
||||||
|
cluster.on('message', (worker, message) => {
|
||||||
|
if (message?.messageType === 'clearCache') {
|
||||||
|
for (const [pid, worker] of activeWorkers.entries()) {
|
||||||
|
if (worker === undefined || pid === message.pid) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
debug('Relaying message to workers');
|
||||||
|
worker.send(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
cluster.on('exit', (worker, code, signal) => {
|
cluster.on('exit', (worker, code, signal) => {
|
||||||
debug(`Worker ${worker.process.pid.toString()} has been killed`);
|
debug(`Worker ${worker.process.pid.toString()} has been killed`);
|
||||||
|
activeWorkers.delete(worker.process.pid);
|
||||||
debug('Starting another worker');
|
debug('Starting another worker');
|
||||||
cluster.fork();
|
cluster.fork();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
28
bin/www.ts
28
bin/www.ts
|
|
@ -12,8 +12,10 @@ import exitHook from 'exit-hook'
|
||||||
import ntfyPublish from '@cityssm/ntfy-publish'
|
import ntfyPublish from '@cityssm/ntfy-publish'
|
||||||
import type * as ntfyTypes from '@cityssm/ntfy-publish/types'
|
import type * as ntfyTypes from '@cityssm/ntfy-publish/types'
|
||||||
|
|
||||||
|
import type { WorkerMessage } from '../types/applicationTypes'
|
||||||
|
|
||||||
import Debug from 'debug'
|
import Debug from 'debug'
|
||||||
const debug = Debug('lot-occupancy-system:www')
|
const debug = Debug(`lot-occupancy-system:www:${process.pid}`)
|
||||||
|
|
||||||
const directoryName = dirname(fileURLToPath(import.meta.url))
|
const directoryName = dirname(fileURLToPath(import.meta.url))
|
||||||
|
|
||||||
|
|
@ -22,7 +24,11 @@ const processCount = Math.min(
|
||||||
os.cpus().length
|
os.cpus().length
|
||||||
)
|
)
|
||||||
|
|
||||||
|
process.title =
|
||||||
|
configFunctions.getProperty('application.applicationName') + ' (Primary)'
|
||||||
|
|
||||||
debug(`Primary pid: ${process.pid}`)
|
debug(`Primary pid: ${process.pid}`)
|
||||||
|
debug(`Primary title: ${process.title}`)
|
||||||
debug(`Launching ${processCount} processes`)
|
debug(`Launching ${processCount} processes`)
|
||||||
|
|
||||||
const clusterSettings = {
|
const clusterSettings = {
|
||||||
|
|
@ -31,12 +37,30 @@ const clusterSettings = {
|
||||||
|
|
||||||
cluster.setupPrimary(clusterSettings)
|
cluster.setupPrimary(clusterSettings)
|
||||||
|
|
||||||
|
const activeWorkers = new Map<number, any>()
|
||||||
|
|
||||||
for (let index = 0; index < processCount; index += 1) {
|
for (let index = 0; index < processCount; index += 1) {
|
||||||
cluster.fork()
|
const worker = cluster.fork()
|
||||||
|
activeWorkers.set(worker.process.pid!, worker)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cluster.on('message', (worker, message: WorkerMessage) => {
|
||||||
|
if (message?.messageType === 'clearCache') {
|
||||||
|
for (const [pid, worker] of activeWorkers.entries()) {
|
||||||
|
if (worker === undefined || pid === message.pid) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
debug('Relaying message to workers')
|
||||||
|
worker.send(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
cluster.on('exit', (worker, code, signal) => {
|
cluster.on('exit', (worker, code, signal) => {
|
||||||
debug(`Worker ${worker.process.pid!.toString()} has been killed`)
|
debug(`Worker ${worker.process.pid!.toString()} has been killed`)
|
||||||
|
activeWorkers.delete(worker.process.pid!)
|
||||||
|
|
||||||
debug('Starting another worker')
|
debug('Starting another worker')
|
||||||
cluster.fork()
|
cluster.fork()
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ function onListening(server) {
|
||||||
debug('HTTP Listening on ' + bind);
|
debug('HTTP Listening on ' + bind);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
process.title = configFunctions.getProperty('application.applicationName') + ' (Worker)';
|
||||||
const httpPort = configFunctions.getProperty('application.httpPort');
|
const httpPort = configFunctions.getProperty('application.httpPort');
|
||||||
const httpServer = http.createServer(app);
|
const httpServer = http.createServer(app);
|
||||||
httpServer.listen(httpPort);
|
httpServer.listen(httpPort);
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,8 @@ function onListening(server: http.Server): void {
|
||||||
* Initialize HTTP
|
* Initialize HTTP
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
process.title = configFunctions.getProperty('application.applicationName') + ' (Worker)'
|
||||||
|
|
||||||
const httpPort = configFunctions.getProperty('application.httpPort')
|
const httpPort = configFunctions.getProperty('application.httpPort')
|
||||||
|
|
||||||
const httpServer = http.createServer(app)
|
const httpServer = http.createServer(app)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
[Home](https://cityssm.github.io/lot-occupancy-system/)
|
||||||
|
•
|
||||||
|
[Help](https://cityssm.github.io/lot-occupancy-system/docs/)
|
||||||
|
|
||||||
|
# Cemetery Management System Workflow - Newly Deceased
|
||||||
|
|
||||||
|
_The following workflow describes a process that can be used when the Lot Occupancy System is used
|
||||||
|
as a Cemetery Management System._
|
||||||
|
|
||||||
|
## Step 1: Search for a Related Preneed Occupancy Record
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
If the deceased purchased preneed services, find them now.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
It is important to note what services have been paid for,
|
||||||
|
and who is entitled to the services.
|
||||||
|
|
||||||
|
## Step 2: Create the New Interment or Cremation Occupancy Record
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
If a preneed occupancy record exists, you can save time by copying the preneed record as a new record.
|
||||||
|
|
||||||
|
If no preneed occupancy record exists, a new occupancy record should be created.
|
||||||
|
|
||||||
|
## Step 3: Create a Work Order Associated with the Occupancy Record
|
||||||
|
|
||||||
|
Ensure the necessary milestones are included.
|
||||||
|
|
||||||
|
## Step 4: Complete the Work Order
|
||||||
|
|
@ -18,4 +18,4 @@ export declare function getWorkOrderTypeById(workOrderTypeId: number): Promise<r
|
||||||
export declare function getWorkOrderMilestoneTypes(): Promise<recordTypes.WorkOrderMilestoneType[]>;
|
export declare function getWorkOrderMilestoneTypes(): Promise<recordTypes.WorkOrderMilestoneType[]>;
|
||||||
export declare function getWorkOrderMilestoneTypeById(workOrderMilestoneTypeId: number): Promise<recordTypes.WorkOrderMilestoneType | undefined>;
|
export declare function getWorkOrderMilestoneTypeById(workOrderMilestoneTypeId: number): Promise<recordTypes.WorkOrderMilestoneType | undefined>;
|
||||||
export declare function getWorkOrderMilestoneTypeByWorkOrderMilestoneType(workOrderMilestoneTypeString: string): Promise<recordTypes.WorkOrderMilestoneType | undefined>;
|
export declare function getWorkOrderMilestoneTypeByWorkOrderMilestoneType(workOrderMilestoneTypeString: string): Promise<recordTypes.WorkOrderMilestoneType | undefined>;
|
||||||
export declare function clearCacheByTableName(tableName: string): void;
|
export declare function clearCacheByTableName(tableName: string, relayMessage?: boolean): void;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import cluster from 'node:cluster';
|
||||||
import * as configFunctions from './functions.config.js';
|
import * as configFunctions from './functions.config.js';
|
||||||
import { getLotOccupantTypes as getLotOccupantTypesFromDatabase } from './lotOccupancyDB/getLotOccupantTypes.js';
|
import { getLotOccupantTypes as getLotOccupantTypesFromDatabase } from './lotOccupancyDB/getLotOccupantTypes.js';
|
||||||
import { getLotStatuses as getLotStatusesFromDatabase } from './lotOccupancyDB/getLotStatuses.js';
|
import { getLotStatuses as getLotStatusesFromDatabase } from './lotOccupancyDB/getLotStatuses.js';
|
||||||
|
|
@ -6,9 +7,8 @@ import { getOccupancyTypes as getOccupancyTypesFromDatabase } from './lotOccupan
|
||||||
import { getOccupancyTypeFields as getOccupancyTypeFieldsFromDatabase } from './lotOccupancyDB/getOccupancyTypeFields.js';
|
import { getOccupancyTypeFields as getOccupancyTypeFieldsFromDatabase } from './lotOccupancyDB/getOccupancyTypeFields.js';
|
||||||
import { getWorkOrderTypes as getWorkOrderTypesFromDatabase } from './lotOccupancyDB/getWorkOrderTypes.js';
|
import { getWorkOrderTypes as getWorkOrderTypesFromDatabase } from './lotOccupancyDB/getWorkOrderTypes.js';
|
||||||
import { getWorkOrderMilestoneTypes as getWorkOrderMilestoneTypesFromDatabase } from './lotOccupancyDB/getWorkOrderMilestoneTypes.js';
|
import { getWorkOrderMilestoneTypes as getWorkOrderMilestoneTypesFromDatabase } from './lotOccupancyDB/getWorkOrderMilestoneTypes.js';
|
||||||
import { getConfigTableMaxTimeMillis } from './lotOccupancyDB/getConfigTableMaxTimeMillis.js';
|
import Debug from 'debug';
|
||||||
import { setIntervalAsync, clearIntervalAsync } from 'set-interval-async';
|
const debug = Debug(`lot-occupancy-system:functions.cache:${process.pid}`);
|
||||||
import { asyncExitHook } from 'exit-hook';
|
|
||||||
let lotOccupantTypes;
|
let lotOccupantTypes;
|
||||||
export async function getLotOccupantTypes() {
|
export async function getLotOccupantTypes() {
|
||||||
if (lotOccupantTypes === undefined) {
|
if (lotOccupantTypes === undefined) {
|
||||||
|
|
@ -163,7 +163,7 @@ export async function getWorkOrderMilestoneTypeByWorkOrderMilestoneType(workOrde
|
||||||
function clearWorkOrderMilestoneTypesCache() {
|
function clearWorkOrderMilestoneTypesCache() {
|
||||||
workOrderMilestoneTypes = undefined;
|
workOrderMilestoneTypes = undefined;
|
||||||
}
|
}
|
||||||
export function clearCacheByTableName(tableName) {
|
export function clearCacheByTableName(tableName, relayMessage = true) {
|
||||||
switch (tableName) {
|
switch (tableName) {
|
||||||
case 'LotOccupantTypes': {
|
case 'LotOccupantTypes': {
|
||||||
clearLotOccupantTypesCache();
|
clearLotOccupantTypesCache();
|
||||||
|
|
@ -193,26 +193,23 @@ export function clearCacheByTableName(tableName) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
try {
|
||||||
function clearAllCaches() {
|
if (relayMessage && cluster.isWorker) {
|
||||||
clearLotOccupantTypesCache();
|
const workerMessage = {
|
||||||
clearLotStatusesCache();
|
messageType: 'clearCache',
|
||||||
clearLotTypesCache();
|
tableName,
|
||||||
clearOccupancyTypesCache();
|
timeMillis: Date.now(),
|
||||||
clearWorkOrderMilestoneTypesCache();
|
pid: process.pid
|
||||||
clearWorkOrderTypesCache();
|
};
|
||||||
}
|
debug(`Sending clear cache from worker: ${tableName}`);
|
||||||
let configTimeMillis = 0;
|
process.send(workerMessage);
|
||||||
async function checkCacheIntegrity() {
|
|
||||||
const timeMillis = await getConfigTableMaxTimeMillis();
|
|
||||||
if (timeMillis > configTimeMillis) {
|
|
||||||
configTimeMillis = timeMillis;
|
|
||||||
clearAllCaches();
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
}
|
}
|
||||||
const cacheTimer = setIntervalAsync(checkCacheIntegrity, 10 * 60 * 1000);
|
process.on('message', (message) => {
|
||||||
asyncExitHook(async () => {
|
if (message.messageType === 'clearCache' && message.pid !== process.pid) {
|
||||||
await clearIntervalAsync(cacheTimer);
|
debug(`Clearing cache: ${message.tableName}`);
|
||||||
}, {
|
clearCacheByTableName(message.tableName, false);
|
||||||
minimumWait: 250
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
/* eslint-disable @typescript-eslint/indent */
|
/* eslint-disable @typescript-eslint/indent */
|
||||||
|
|
||||||
|
import cluster from 'node:cluster'
|
||||||
|
|
||||||
import * as configFunctions from './functions.config.js'
|
import * as configFunctions from './functions.config.js'
|
||||||
|
|
||||||
import { getLotOccupantTypes as getLotOccupantTypesFromDatabase } from './lotOccupancyDB/getLotOccupantTypes.js'
|
import { getLotOccupantTypes as getLotOccupantTypesFromDatabase } from './lotOccupancyDB/getLotOccupantTypes.js'
|
||||||
|
|
@ -15,12 +17,11 @@ import { getWorkOrderTypes as getWorkOrderTypesFromDatabase } from './lotOccupan
|
||||||
|
|
||||||
import { getWorkOrderMilestoneTypes as getWorkOrderMilestoneTypesFromDatabase } from './lotOccupancyDB/getWorkOrderMilestoneTypes.js'
|
import { getWorkOrderMilestoneTypes as getWorkOrderMilestoneTypesFromDatabase } from './lotOccupancyDB/getWorkOrderMilestoneTypes.js'
|
||||||
|
|
||||||
import { getConfigTableMaxTimeMillis } from './lotOccupancyDB/getConfigTableMaxTimeMillis.js'
|
|
||||||
|
|
||||||
import { setIntervalAsync, clearIntervalAsync } from 'set-interval-async'
|
|
||||||
import { asyncExitHook } from 'exit-hook'
|
|
||||||
|
|
||||||
import type * as recordTypes from '../types/recordTypes'
|
import type * as recordTypes from '../types/recordTypes'
|
||||||
|
import type { WorkerMessage } from '../types/applicationTypes'
|
||||||
|
|
||||||
|
import Debug from 'debug'
|
||||||
|
const debug = Debug(`lot-occupancy-system:functions.cache:${process.pid}`)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Lot Occupant Types
|
* Lot Occupant Types
|
||||||
|
|
@ -301,7 +302,10 @@ function clearWorkOrderMilestoneTypesCache(): void {
|
||||||
workOrderMilestoneTypes = undefined
|
workOrderMilestoneTypes = undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
export function clearCacheByTableName(tableName: string): void {
|
export function clearCacheByTableName(
|
||||||
|
tableName: string,
|
||||||
|
relayMessage = true
|
||||||
|
): void {
|
||||||
switch (tableName) {
|
switch (tableName) {
|
||||||
case 'LotOccupantTypes': {
|
case 'LotOccupantTypes': {
|
||||||
clearLotOccupantTypesCache()
|
clearLotOccupantTypesCache()
|
||||||
|
|
@ -336,39 +340,26 @@ export function clearCacheByTableName(tableName: string): void {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function clearAllCaches(): void {
|
try {
|
||||||
clearLotOccupantTypesCache()
|
if (relayMessage && cluster.isWorker) {
|
||||||
clearLotStatusesCache()
|
const workerMessage: WorkerMessage = {
|
||||||
clearLotTypesCache()
|
messageType: 'clearCache',
|
||||||
clearOccupancyTypesCache()
|
tableName,
|
||||||
clearWorkOrderMilestoneTypesCache()
|
timeMillis: Date.now(),
|
||||||
clearWorkOrderTypesCache()
|
pid: process.pid
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Config Time Millis
|
|
||||||
*/
|
|
||||||
|
|
||||||
let configTimeMillis = 0
|
|
||||||
|
|
||||||
async function checkCacheIntegrity(): Promise<void> {
|
|
||||||
const timeMillis = await getConfigTableMaxTimeMillis()
|
|
||||||
|
|
||||||
if (timeMillis > configTimeMillis) {
|
|
||||||
configTimeMillis = timeMillis
|
|
||||||
clearAllCaches()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug(`Sending clear cache from worker: ${tableName}`)
|
||||||
|
|
||||||
|
process.send!(workerMessage)
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
const cacheTimer = setIntervalAsync(checkCacheIntegrity, 10 * 60 * 1000)
|
process.on('message', (message: WorkerMessage) => {
|
||||||
|
if (message.messageType === 'clearCache' && message.pid !== process.pid) {
|
||||||
asyncExitHook(
|
debug(`Clearing cache: ${message.tableName}`)
|
||||||
async () => {
|
clearCacheByTableName(message.tableName, false)
|
||||||
await clearIntervalAsync(cacheTimer)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
minimumWait: 250
|
|
||||||
}
|
}
|
||||||
)
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
export declare function getConfigTableMaxTimeMillis(): Promise<number>;
|
|
||||||
export default getConfigTableMaxTimeMillis;
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
import { acquireConnection } from './pool.js';
|
|
||||||
export async function getConfigTableMaxTimeMillis() {
|
|
||||||
const database = await acquireConnection();
|
|
||||||
const result = database
|
|
||||||
.prepare(`select max(timeMillis) as timeMillis from (
|
|
||||||
select max(max(recordUpdate_timeMillis, ifnull(recordDelete_timeMillis,0))) as timeMillis
|
|
||||||
from LotOccupantTypes
|
|
||||||
UNION
|
|
||||||
select max(max(recordUpdate_timeMillis, ifnull(recordDelete_timeMillis,0))) as timeMillis
|
|
||||||
from LotStatuses
|
|
||||||
UNION
|
|
||||||
select max(max(recordUpdate_timeMillis, ifnull(recordDelete_timeMillis,0))) as timeMillis
|
|
||||||
from LotTypes
|
|
||||||
UNION
|
|
||||||
select max(max(recordUpdate_timeMillis, ifnull(recordDelete_timeMillis,0))) as timeMillis
|
|
||||||
from OccupancyTypes
|
|
||||||
UNION
|
|
||||||
select max(max(recordUpdate_timeMillis, ifnull(recordDelete_timeMillis,0))) as timeMillis
|
|
||||||
from OccupancyTypeFields
|
|
||||||
UNION
|
|
||||||
select max(max(recordUpdate_timeMillis, ifnull(recordDelete_timeMillis,0))) as timeMillis
|
|
||||||
from OccupancyTypePrints
|
|
||||||
UNION
|
|
||||||
select max(max(recordUpdate_timeMillis, ifnull(recordDelete_timeMillis,0))) as timeMillis
|
|
||||||
from WorkOrderTypes
|
|
||||||
UNION
|
|
||||||
select max(max(recordUpdate_timeMillis, ifnull(recordDelete_timeMillis,0))) as timeMillis
|
|
||||||
from WorkOrderMilestoneTypes
|
|
||||||
)`)
|
|
||||||
.get();
|
|
||||||
database.release();
|
|
||||||
return result?.timeMillis ?? 0;
|
|
||||||
}
|
|
||||||
export default getConfigTableMaxTimeMillis;
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
||||||
import { acquireConnection } from './pool.js'
|
|
||||||
|
|
||||||
export async function getConfigTableMaxTimeMillis(): Promise<number> {
|
|
||||||
const database = await acquireConnection()
|
|
||||||
|
|
||||||
const result = database
|
|
||||||
.prepare(
|
|
||||||
`select max(timeMillis) as timeMillis from (
|
|
||||||
select max(max(recordUpdate_timeMillis, ifnull(recordDelete_timeMillis,0))) as timeMillis
|
|
||||||
from LotOccupantTypes
|
|
||||||
UNION
|
|
||||||
select max(max(recordUpdate_timeMillis, ifnull(recordDelete_timeMillis,0))) as timeMillis
|
|
||||||
from LotStatuses
|
|
||||||
UNION
|
|
||||||
select max(max(recordUpdate_timeMillis, ifnull(recordDelete_timeMillis,0))) as timeMillis
|
|
||||||
from LotTypes
|
|
||||||
UNION
|
|
||||||
select max(max(recordUpdate_timeMillis, ifnull(recordDelete_timeMillis,0))) as timeMillis
|
|
||||||
from OccupancyTypes
|
|
||||||
UNION
|
|
||||||
select max(max(recordUpdate_timeMillis, ifnull(recordDelete_timeMillis,0))) as timeMillis
|
|
||||||
from OccupancyTypeFields
|
|
||||||
UNION
|
|
||||||
select max(max(recordUpdate_timeMillis, ifnull(recordDelete_timeMillis,0))) as timeMillis
|
|
||||||
from OccupancyTypePrints
|
|
||||||
UNION
|
|
||||||
select max(max(recordUpdate_timeMillis, ifnull(recordDelete_timeMillis,0))) as timeMillis
|
|
||||||
from WorkOrderTypes
|
|
||||||
UNION
|
|
||||||
select max(max(recordUpdate_timeMillis, ifnull(recordDelete_timeMillis,0))) as timeMillis
|
|
||||||
from WorkOrderMilestoneTypes
|
|
||||||
)`
|
|
||||||
)
|
|
||||||
.get()
|
|
||||||
|
|
||||||
database.release()
|
|
||||||
|
|
||||||
return result?.timeMillis ?? 0
|
|
||||||
}
|
|
||||||
|
|
||||||
export default getConfigTableMaxTimeMillis
|
|
||||||
|
|
@ -8,8 +8,8 @@
|
||||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "cross-env NODE_ENV=production node ./bin/www",
|
"start": "cross-env NODE_ENV=production node ./bin/www.js",
|
||||||
"dev:test": "cross-env NODE_ENV=dev DEBUG=lot-occupancy-system:*,dynamics-gp:* TEST_DATABASES=true nodemon ./bin/www.js",
|
"dev:test": "cross-env NODE_ENV=dev DEBUG=lot-occupancy-system:*,dynamics-gp:* TEST_DATABASES=true nodemon --inspect ./bin/www.js",
|
||||||
"dev:test:process": "cross-env NODE_ENV=dev DEBUG=lot-occupancy-system:* TEST_DATABASES=true nodemon ./bin/wwwProcess.js",
|
"dev:test:process": "cross-env NODE_ENV=dev DEBUG=lot-occupancy-system:* TEST_DATABASES=true nodemon ./bin/wwwProcess.js",
|
||||||
"dev:live": "cross-env NODE_ENV=dev DEBUG=lot-occupancy-system:* nodemon ./bin/www.js",
|
"dev:live": "cross-env NODE_ENV=dev DEBUG=lot-occupancy-system:* nodemon ./bin/www.js",
|
||||||
"cy:open": "cypress open --config-file cypress.config.js",
|
"cy:open": "cypress open --config-file cypress.config.js",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
@import '@cityssm/bulma-webapp-css/cityssm';
|
@import '@cityssm/bulma-webapp-css/cityssm';
|
||||||
@import 'bulma/sass/utilities/derived-variables';
|
@import 'bulma/sass/utilities/derived-variables';
|
||||||
@import 'bulma-calendar/src/sass/index';
|
@import 'bulma-calendar/src/sass';
|
||||||
@import '@cityssm/fa-glow/fa-glow';
|
@import '@cityssm/fa-glow/fa-glow';
|
||||||
|
|
||||||
$white: #fff;
|
$white: #fff;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
export interface WorkerMessage {
|
||||||
|
messageType: 'clearCache';
|
||||||
|
tableName: string;
|
||||||
|
timeMillis: number;
|
||||||
|
pid: number;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
export interface WorkerMessage {
|
||||||
|
messageType: 'clearCache'
|
||||||
|
tableName: string
|
||||||
|
timeMillis: number
|
||||||
|
pid: number
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue