mirror of
https://github.com/cpvalente/ontime.git
synced 2026-09-10 08:39:34 +00:00
refactor: debounce writing to disk
This commit is contained in:
committed by
Carlos Valente
parent
e16ec3f388
commit
77779a4648
@@ -108,11 +108,6 @@ export function createTransaction(options: TransactionOptions): Transaction {
|
|||||||
function commit(shouldProcess: boolean = true) {
|
function commit(shouldProcess: boolean = true) {
|
||||||
// if the rundown is mutable we persist the changes
|
// if the rundown is mutable we persist the changes
|
||||||
if (options.mutableRundown) {
|
if (options.mutableRundown) {
|
||||||
// schedule a database update
|
|
||||||
setImmediate(async () => {
|
|
||||||
await getDataProvider().setRundown(cachedRundown.id, cachedRundown);
|
|
||||||
});
|
|
||||||
|
|
||||||
// update fields which are agnostic of whether the rundown is processed
|
// update fields which are agnostic of whether the rundown is processed
|
||||||
cachedRundown.revision = cachedRundown.revision + 1;
|
cachedRundown.revision = cachedRundown.revision + 1;
|
||||||
cachedRundown.title = rundown.title;
|
cachedRundown.title = rundown.title;
|
||||||
@@ -133,16 +128,17 @@ export function createTransaction(options: TransactionOptions): Transaction {
|
|||||||
cachedRundown.flatOrder = metadata.flatEntryOrder;
|
cachedRundown.flatOrder = metadata.flatEntryOrder;
|
||||||
rundownMetadata = metadata;
|
rundownMetadata = metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// persist after all mutations are applied
|
||||||
|
getDataProvider().setRundown(cachedRundown.id, cachedRundown);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the customFields are mutable we persist the changes
|
// if the customFields are mutable we persist the changes
|
||||||
if (options.mutableCustomFields) {
|
if (options.mutableCustomFields) {
|
||||||
// schedule a database update
|
|
||||||
setImmediate(async () => {
|
|
||||||
await getDataProvider().setCustomFields(projectCustomFields);
|
|
||||||
});
|
|
||||||
|
|
||||||
projectCustomFields = customFields;
|
projectCustomFields = customFields;
|
||||||
|
|
||||||
|
// persist after reassignment
|
||||||
|
getDataProvider().setCustomFields(projectCustomFields);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -552,10 +548,8 @@ export const rundownMutation = {
|
|||||||
/**
|
/**
|
||||||
* Exposes a way to update a rundown which is not active
|
* Exposes a way to update a rundown which is not active
|
||||||
*/
|
*/
|
||||||
export function updateBackgroundRundown(rundownId: string, rundown: Rundown) {
|
export async function updateBackgroundRundown(rundownId: string, rundown: Rundown) {
|
||||||
setImmediate(async () => {
|
await getDataProvider().setRundown(rundownId, rundown);
|
||||||
await getDataProvider().setRundown(rundownId, rundown);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -652,9 +646,7 @@ export function init(initialRundown: Readonly<Rundown>, initialCustomFields: Rea
|
|||||||
rundownMetadata = metadata;
|
rundownMetadata = metadata;
|
||||||
|
|
||||||
// defer writing to the database
|
// defer writing to the database
|
||||||
setImmediate(async () => {
|
getDataProvider().setRundown(cachedRundown.id, cachedRundown);
|
||||||
await getDataProvider().setRundown(cachedRundown.id, cachedRundown);
|
|
||||||
});
|
|
||||||
|
|
||||||
return { rundown, rundownMetadata, customFields, revision: rundown.revision };
|
return { rundown, rundownMetadata, customFields, revision: rundown.revision };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -486,7 +486,7 @@ export async function editCustomField(
|
|||||||
if (rundownId !== rundown.id) {
|
if (rundownId !== rundown.id) {
|
||||||
const backgroundRundown = structuredClone(projectRundowns[rundownId]);
|
const backgroundRundown = structuredClone(projectRundowns[rundownId]);
|
||||||
customFieldMutation.renameUsages(backgroundRundown, oldKey, newKey);
|
customFieldMutation.renameUsages(backgroundRundown, oldKey, newKey);
|
||||||
updateBackgroundRundown(rundownId, backgroundRundown);
|
await updateBackgroundRundown(rundownId, backgroundRundown);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -526,7 +526,7 @@ export async function deleteCustomField(key: CustomFieldKey, projectRundowns: Pr
|
|||||||
if (rundownId !== rundown.id) {
|
if (rundownId !== rundown.id) {
|
||||||
const backgroundRundown = structuredClone(projectRundowns[rundownId]);
|
const backgroundRundown = structuredClone(projectRundowns[rundownId]);
|
||||||
customFieldMutation.removeUsages(backgroundRundown, key);
|
customFieldMutation.removeUsages(backgroundRundown, key);
|
||||||
updateBackgroundRundown(rundownId, backgroundRundown);
|
await updateBackgroundRundown(rundownId, backgroundRundown);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import { integrationRouter } from './api-integration/integration.router.js';
|
|||||||
|
|
||||||
// Import adapters
|
// Import adapters
|
||||||
import { socket } from './adapters/WebsocketAdapter.js';
|
import { socket } from './adapters/WebsocketAdapter.js';
|
||||||
import { getDataProvider } from './classes/data-provider/DataProvider.js';
|
import { getDataProvider, flushPendingWrites } from './classes/data-provider/DataProvider.js';
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
import { logger } from './classes/Logger.js';
|
import { logger } from './classes/Logger.js';
|
||||||
@@ -266,6 +266,10 @@ export const startIntegrations = async () => {
|
|||||||
export const shutdown = async (exitCode = 0) => {
|
export const shutdown = async (exitCode = 0) => {
|
||||||
consoleHighlight(`Ontime shutting down with code ${exitCode}`);
|
consoleHighlight(`Ontime shutting down with code ${exitCode}`);
|
||||||
|
|
||||||
|
await flushPendingWrites().catch((_error) => {
|
||||||
|
/** nothing do to here */
|
||||||
|
});
|
||||||
|
|
||||||
// clear the restore file if it was a normal exit
|
// clear the restore file if it was a normal exit
|
||||||
// 0 means it was a SIGNAL
|
// 0 means it was a SIGNAL
|
||||||
// 1 means crash -> keep the file
|
// 1 means crash -> keep the file
|
||||||
|
|||||||
@@ -180,10 +180,58 @@ async function mergeIntoData(newData: Partial<DatabaseModel>): ReadonlyPromise<D
|
|||||||
return db.data;
|
return db.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Module-level state for debounced writes
|
||||||
|
let pendingWrite: NodeJS.Timeout | null = null;
|
||||||
|
let activeWrite: Promise<void> | null = null;
|
||||||
|
const writeDelayMs = 3000; // 3 seconds
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles persisting data to file
|
* Handles persisting data to file with trailing-edge debounce
|
||||||
|
* Multiple rapid calls will be coalesced into a single write
|
||||||
*/
|
*/
|
||||||
async function persist() {
|
async function persist() {
|
||||||
if (isTest) return;
|
if (isTest) return;
|
||||||
|
|
||||||
|
// Cancel any pending write and reschedule
|
||||||
|
if (pendingWrite) {
|
||||||
|
clearTimeout(pendingWrite);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schedule new write after quiet period
|
||||||
|
pendingWrite = setTimeout(async () => {
|
||||||
|
pendingWrite = null;
|
||||||
|
|
||||||
|
// Wait for any in-progress write to finish first
|
||||||
|
if (activeWrite) {
|
||||||
|
await activeWrite;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
activeWrite = db.write();
|
||||||
|
await activeWrite;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to persist database:', error);
|
||||||
|
} finally {
|
||||||
|
activeWrite = null;
|
||||||
|
}
|
||||||
|
}, writeDelayMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force immediate write of any pending changes
|
||||||
|
*/
|
||||||
|
export async function flushPendingWrites() {
|
||||||
|
if (isTest) return;
|
||||||
|
|
||||||
|
if (pendingWrite) {
|
||||||
|
clearTimeout(pendingWrite);
|
||||||
|
pendingWrite = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for any in-progress write to finish
|
||||||
|
if (activeWrite) {
|
||||||
|
await activeWrite;
|
||||||
|
}
|
||||||
|
|
||||||
await db.write();
|
await db.write();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user