Custom fields caching (#790)

* refactor: process custom fields on cache generate
This commit is contained in:
Carlos Valente
2024-02-25 17:21:59 +01:00
committed by GitHub
parent da7ef59216
commit 429df21557
3 changed files with 341 additions and 54 deletions
@@ -8,11 +8,12 @@ import {
OntimeRundown,
OntimeRundownEntry,
} from 'ontime-types';
import { generateId, deleteAtIndex, insertAtIndex, reorderArray, swapEventData, getLinkedTimes } from 'ontime-utils';
import { generateId, deleteAtIndex, insertAtIndex, reorderArray, swapEventData } from 'ontime-utils';
import { DataProvider } from '../../classes/data-provider/DataProvider.js';
import { createPatch } from '../../utils/parser.js';
import { apply } from './delayUtils.js';
import { handleCustomField, handleLink } from './rundownCacheUtils.js';
type EventID = string;
type NormalisedRundown = Record<EventID, OntimeRundownEntry>;
@@ -42,7 +43,12 @@ let links: Record<EventID, EventID> = {};
* }
*/
const customFieldChangelog = {};
const assignedCustomFields: Record<CustomFieldLabel, EventID[]> = {};
/**
* Keep track of which custom fields are used.
* This will be handy for when we delete custom fields
*/
let assignedCustomFields: Record<CustomFieldLabel, EventID[]> = {};
export async function init(initialRundown: OntimeRundown, customFields: CustomFields) {
persistedRundown = structuredClone(initialRundown);
@@ -62,22 +68,12 @@ export async function setRundown(initialRundown: OntimeRundown) {
*/
export function generate(
initialRundown: OntimeRundown = persistedRundown,
customProperties: CustomFields = persistedCustomFields,
customFields: CustomFields = persistedCustomFields,
) {
// we decided to re-write this dataset for every change
// instead of maintaining logic to update it
function getLink(currentIndex: number): OntimeEvent | null {
// currently the link is the previous event
for (let i = currentIndex - 1; i >= 0; i--) {
const event = initialRundown[i];
if (isOntimeEvent(event)) {
return event;
}
}
return null;
}
assignedCustomFields = {};
rundown = {};
order = [];
links = {};
@@ -87,42 +83,25 @@ export function generate(
for (let i = 0; i < initialRundown.length; i++) {
const currentEvent = initialRundown[i];
let updatedEvent = { ...currentEvent };
const updatedEvent = { ...currentEvent };
// handle links
if (isOntimeEvent(updatedEvent)) {
if (updatedEvent.linkStart) {
const linkedEvent = getLink(i);
// link is always the previous event for now
if (linkedEvent) {
links[linkedEvent.id] = currentEvent.id;
// 1. handle links
handleLink(i, initialRundown, updatedEvent, links);
const timePatch = getLinkedTimes(updatedEvent, linkedEvent);
updatedEvent = { ...updatedEvent, ...timePatch };
} else {
updatedEvent.linkStart = null;
}
// update the persisted event
initialRundown[i] = updatedEvent;
}
if (updatedEvent.custom) {
for (const property in updatedEvent.custom) {
const isValid = property in customProperties;
if (!isValid) {
delete updatedEvent.custom[property];
return;
}
if (!Array.isArray(assignedCustomFields[property])) {
assignedCustomFields[property] = [];
}
assignedCustomFields[property].push(updatedEvent.id);
}
// update the persisted event
initialRundown[i] = updatedEvent;
}
// TODO: wait until the next thing?
// update the persisted event
initialRundown[i] = updatedEvent;
// 2. handle custom fields
handleCustomField(customFields, customFieldChangelog, updatedEvent, assignedCustomFields);
// update the persisted event
initialRundown[i] = updatedEvent;
}
// calculate delays
// !!! this must happen after handling the links
if (isOntimeDelay(updatedEvent)) {
accumulatedDelay += updatedEvent.duration;
} else if (isOntimeEvent(updatedEvent)) {
@@ -220,7 +199,7 @@ export function mutateCache<T extends object>(mutation: MutatingFn<T>) {
// TODO: should we trottle this?
// defer writing to the database
setImmediate(() => {
console.log('writing to database', persistedRundown.length)
console.log('writing to database', persistedRundown.length);
DataProvider.setRundown(persistedRundown);
});
@@ -360,6 +339,37 @@ export function swap({ persistedRundown, fromId, toId }: SwapArgs): MutatingRetu
return { newRundown };
}
/**
* Invalidates service cache if a custom field is used
* @param label
*/
function invalidateIfUsed(label: CustomFieldLabel) {
if (label in assignedCustomFields) {
isStale = true;
}
// if the field was in use, we mark the cache as stale
if (label in assignedCustomFields) {
isStale = true;
}
// ... and schedule a cache update
// schedule a non priority cache update
setImmediate(() => {
console.time('rundownCache__init');
generate();
console.timeEnd('rundownCache__init');
});
}
/**
* Scheduløes a non priority custom field persist
* @param persistedCustomFields
*/
function scheduleCustomFieldPersist(persistedCustomFields: CustomFields) {
setImmediate(() => {
DataProvider.setCustomFields(persistedCustomFields);
});
}
/**
* Sanitises and creates a custom field in the database
* @param field
@@ -378,9 +388,7 @@ export const createCustomField = async (field: CustomField) => {
// update object and persist
persistedCustomFields[label] = { label, type, colour };
setImmediate(() => {
DataProvider.setCustomFields(persistedCustomFields);
});
scheduleCustomFieldPersist(persistedCustomFields);
return persistedCustomFields;
};
@@ -407,9 +415,8 @@ export const editCustomField = async (label: string, newField: Partial<CustomFie
persistedCustomFields[label] = { ...existingField, ...newField };
setImmediate(() => {
DataProvider.setCustomFields(persistedCustomFields);
});
scheduleCustomFieldPersist(persistedCustomFields);
invalidateIfUsed(label);
return persistedCustomFields;
};
@@ -423,9 +430,8 @@ export const removeCustomField = async (label: string) => {
delete persistedCustomFields[label];
}
setImmediate(() => {
DataProvider.setCustomFields(persistedCustomFields);
});
scheduleCustomFieldPersist(persistedCustomFields);
invalidateIfUsed(label);
return persistedCustomFields;
};