From 8e06eb68c2352b59e7298c1bc2543ffa150edd7f Mon Sep 17 00:00:00 2001 From: Balte de Wit Date: Thu, 5 May 2022 18:40:33 +0200 Subject: [PATCH] fix: fixed memory leak in datastore --- .../timeline-state-resolver/src/conductor.ts | 106 +++++++++--------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/packages/timeline-state-resolver/src/conductor.ts b/packages/timeline-state-resolver/src/conductor.ts index f053a5e33c..c8d3fec672 100644 --- a/packages/timeline-state-resolver/src/conductor.ts +++ b/packages/timeline-state-resolver/src/conductor.ts @@ -1093,16 +1093,19 @@ export class Conductor extends EventEmitter { setDepencencies(layer.content) }) - this._deviceStates[deviceId] = [ - // todo - this will be a memory leak - ...this._deviceStates[deviceId].filter((s) => s.time < time), + this._deviceStates[deviceId] = _.compact([ + this._deviceStates[deviceId].reverse().find((s) => s.time <= this.getCurrentTime()), + ...this._deviceStates[deviceId] + .reverse() + .filter((s) => s.time < time && s.time > this.getCurrentTime()) + .reverse(), { time, state, dependencies, mappings, }, - ] + ]) const filledState: typeof state = JSON.parse(JSON.stringify(state)) const fillState = (content: Record) => { @@ -1124,60 +1127,57 @@ export class Conductor extends EventEmitter { return this.getDevice(deviceId)?.device.handleState(filledState, mappings) } setDatastore(newStore: Record) { - const allKeys = new Set([...Object.keys(newStore), ...Object.keys(this._datastore)]) - - const changed: string[] = [] - for (const key of allKeys) { - if (this._datastore[key] !== newStore[key]) { - // it changed! let's sift through our dependencies to see if we need to do anything - Object.entries(this._deviceStates).forEach(([deviceId, states]) => { - if (states.find((state) => state.dependencies.find((deps) => deps === key))) { - changed.push(deviceId) + this._actionQueue + .add(() => { + const allKeys = new Set([...Object.keys(newStore), ...Object.keys(this._datastore)]) + + const changed: string[] = [] + for (const key of allKeys) { + if (this._datastore[key] !== newStore[key]) { + // it changed! let's sift through our dependencies to see if we need to do anything + Object.entries(this._deviceStates).forEach(([deviceId, states]) => { + if (states.find((state) => state.dependencies.find((deps) => deps === key))) { + changed.push(deviceId) + } + }) } - }) - } - } + } - this._datastore = newStore + this._datastore = newStore + + for (const deviceId of changed) { + const toBeFilled = _.compact([ + this._deviceStates[deviceId].reverse().find((s) => s.time <= this.getCurrentTime()), // one state before now + ...this._deviceStates[deviceId].filter((s) => s.time > this.getCurrentTime()), // all states after now + ]) + + for (const s of toBeFilled) { + const filledState: typeof s.state = JSON.parse(JSON.stringify(s.state)) + const fillState = (content: Record) => { + Object.entries(content).forEach(([index, val]) => { + if (typeof val === 'object') { + if ('_datastoreKey' in val) { + content[index] = this._datastore[val._datastoreKey] ?? val.default + } else { + // todo - can we do a tighter check + fillState(val) + } + } + }) + } + Object.values(filledState.layers).forEach((layer) => { + fillState(layer.content) + }) - for (const deviceId of changed) { - let hasOneBefore = false - const toBeFilled = this._deviceStates[deviceId] - .reverse() - .filter((s) => { - if (s.time > this.getCurrentTime()) { - return true - } else if (hasOneBefore === false) { - hasOneBefore = true - return true + this.getDevice(deviceId) + ?.device.handleState(filledState, s.mappings) + .catch((e) => this.emit('error', 'resolveTimeline' + e + '\nStack: ' + (e as Error).stack)) } - return false - }) - .reverse() - - for (const s of toBeFilled) { - const filledState: typeof s.state = JSON.parse(JSON.stringify(s.state)) - const fillState = (content: Record) => { - Object.entries(content).forEach(([index, val]) => { - if (typeof val === 'object') { - if ('_datastoreKey' in val) { - content[index] = this._datastore[val._datastoreKey] ?? val.default - } else { - // todo - can we do a tighter check - fillState(val) - } - } - }) } - Object.values(filledState.layers).forEach((layer) => { - fillState(layer.content) - }) - - this.getDevice(deviceId) - ?.device.handleState(filledState, s.mappings) - .catch((e) => this.emit('error', 'resolveTimeline' + e + '\nStack: ' + (e as Error).stack)) - } - } + }) + .catch((e) => { + this.emit('error', 'Caught error in setDatastore' + e) + }) } getTimelineSize(): number { if (this._timelineSize === undefined) {