refactor: prevent race conditions on init

This commit is contained in:
Carlos Valente
2024-12-29 20:30:10 +01:00
committed by Carlos Valente
parent f65ec0da3c
commit 0dfef732bf
4 changed files with 29 additions and 15 deletions
+5 -2
View File
@@ -1,4 +1,4 @@
import { LogOrigin, Playback, SimpleDirection, SimplePlayback } from 'ontime-types';
import { LogOrigin, Playback, runtimeStorePlaceholder, SimpleDirection, SimplePlayback } from 'ontime-types';
import 'dotenv/config';
import express from 'express';
@@ -200,7 +200,7 @@ export const startServer = async (
clock: state.clock,
timer: state.timer,
onAir: state.timer.playback !== Playback.Stop,
message: messageService.getState(),
message: { ...runtimeStorePlaceholder.message },
runtime: state.runtime,
eventNow: state.eventNow,
currentBlock: {
@@ -224,6 +224,9 @@ export const startServer = async (
const persistedCustomFields = getDataProvider().getCustomFields();
initRundown(persistedRundown, persistedCustomFields);
// initialise message service
messageService.init(eventStore.set, eventStore.get);
// load restore point if it exists
const maybeRestorePoint = await restoreService.load();
@@ -2,12 +2,21 @@ import { MessageState, runtimeStorePlaceholder } from 'ontime-types';
import { DeepPartial } from 'ts-essentials';
import { throttle } from '../../utils/throttle.js';
import { eventStore, type PublishFn } from '../../stores/EventStore.js';
import type { StoreGetter, PublishFn } from '../../stores/EventStore.js';
/**
* Create a throttled version of the set function
*/
const throttledSet: PublishFn = throttle(eventStore.set, 100);
let throttledSet: PublishFn = () => undefined;
let storeGet: StoreGetter = (_key: string) => undefined;
/**
* Allows providing store interfaces
*/
export function init(storeSetter: PublishFn, storeGetter: StoreGetter) {
throttledSet = throttle(storeSetter, 100);
storeGet = storeGetter;
}
/**
* Exposes function to reset the internal state
@@ -22,7 +31,7 @@ export function clear() {
* Exposes the internal state of the message service
*/
export function getState(): MessageState {
return eventStore.get('message');
return storeGet('message');
}
/**
@@ -2,31 +2,32 @@ import * as messageService from '../MessageService.js';
describe('MessageService', () => {
beforeEach(() => {
// at runtime, the store is instantiated before the message service
const store = {};
const storeSetter = (key, value) => (store[key] = value);
const storeGetter = (key) => store[key];
messageService.init(storeSetter, storeGetter);
messageService.clear();
});
it('should patch the message state', () => {
const message = {
const newState = messageService.patch({
timer: { text: 'new text', visible: true },
external: 'external',
};
});
const newState = messageService.patch(message);
expect(newState).toEqual({
expect(newState).toMatchObject({
timer: { text: 'new text', visible: true, blackout: false, blink: false, secondarySource: null },
external: 'external',
});
});
it('should not affect other properties when patching', () => {
const initialMessage = {
const newState = messageService.patch({
timer: { text: 'initial text', visible: true },
};
});
const newState = messageService.patch(initialMessage);
expect(newState).toEqual({
expect(newState).toMatchObject({
timer: { text: 'initial text', visible: true, blackout: false, blink: false, secondarySource: null },
external: '',
});
+1
View File
@@ -3,6 +3,7 @@ import { RuntimeStore } from 'ontime-types';
import { socket } from '../adapters/WebsocketAdapter.js';
export type PublishFn = <T extends keyof RuntimeStore>(key: T, value: RuntimeStore[T]) => void;
export type StoreGetter = <T extends keyof RuntimeStore>(key: T) => Partial<RuntimeStore>[T];
let store: Partial<RuntimeStore> = {};