From 913ca75412d9852b90fa093b55cdc7b840440099 Mon Sep 17 00:00:00 2001 From: Johan Nyman Date: Thu, 3 Oct 2024 10:09:58 +0200 Subject: [PATCH 1/2] fix: trace and emit some helpful info in case the timeline resolver throws --- packages/timeline-state-resolver/package.json | 2 +- .../src/AsyncResolver.ts | 71 +++++++++++++++---- .../timeline-state-resolver/src/conductor.ts | 20 +++++- yarn.lock | 10 +-- 4 files changed, 81 insertions(+), 22 deletions(-) diff --git a/packages/timeline-state-resolver/package.json b/packages/timeline-state-resolver/package.json index e2bf57d91..b8cf30589 100644 --- a/packages/timeline-state-resolver/package.json +++ b/packages/timeline-state-resolver/package.json @@ -109,7 +109,7 @@ "p-timeout": "^3.2.0", "simple-oauth2": "^5.0.0", "sprintf-js": "^1.1.3", - "superfly-timeline": "^9.0.1", + "superfly-timeline": "9.0.2-nightly-fix-better-trace-resolving-20241003-064759-7723b89.0", "threadedclass": "^1.2.1", "timeline-state-resolver-types": "9.2.0-alpha.0", "tslib": "^2.6.2", diff --git a/packages/timeline-state-resolver/src/AsyncResolver.ts b/packages/timeline-state-resolver/src/AsyncResolver.ts index 244227200..f4533ccfe 100644 --- a/packages/timeline-state-resolver/src/AsyncResolver.ts +++ b/packages/timeline-state-resolver/src/AsyncResolver.ts @@ -4,32 +4,75 @@ import { ResolvedTimelineObject, ResolverCache, resolveTimeline, + ResolveError, + ResolveOptions, } from 'superfly-timeline' import { TimelineTriggerTimeResult } from './conductor' import { TSRTimeline, TSRTimelineContent, TSRTimelineObj } from 'timeline-state-resolver-types' +import { EventEmitter } from 'eventemitter3' -export class AsyncResolver { +export type AsyncResolverEvents = { + error: [string] +} + +// This is a debug flag, to trace some helpful information when resolving fails. +// If should be set to false in production. +const TRACE_RESOLVING = true + +export class AsyncResolver extends EventEmitter { private readonly onSetTimelineTriggerTime: (res: TimelineTriggerTimeResult) => void private cache: Partial = {} public constructor(onSetTimelineTriggerTime: (res: TimelineTriggerTimeResult) => void) { + super() this.onSetTimelineTriggerTime = onSetTimelineTriggerTime } public resolveTimeline(resolveTime: number, timeline: TSRTimeline, limitTime: number, useCache: boolean) { - const objectsFixed = this._fixNowObjects(timeline, resolveTime) - - const resolvedTimeline = resolveTimeline(timeline, { - limitCount: 999, - limitTime: limitTime, - time: resolveTime, - cache: useCache ? this.cache : undefined, - }) - - return { - resolvedTimeline, - objectsFixed, + try { + const objectsFixed = this._fixNowObjects(timeline, resolveTime) + + const resolvedTimeline = this._resolveTimeline(timeline, { + limitCount: 999, + limitTime: limitTime, + time: resolveTime, + cache: useCache ? this.cache : undefined, + traceResolving: TRACE_RESOLVING, + }) + + return { + resolvedTimeline, + objectsFixed, + } + } catch (e) { + if (e instanceof ResolveError) { + // Trace some helpful information related to the error: + this.emit('error', 'Error resolveTrace: ' + JSON.stringify(e.resolvedTimeline.statistics.resolveTrace)) + } + throw e + } + } + + private _resolveTimeline(timeline: TSRTimeline, options: ResolveOptions) { + try { + return resolveTimeline(timeline, options) + } catch (e) { + if (!options.traceResolving) { + // Try again, but now with tracing: + options.traceResolving = true + + // Note, we expect this to throw again: + const try2 = resolveTimeline(timeline, options) + + // Oh, this is weird, it worked on second try! Log the error: + this.emit( + 'error', + 'Error when resolving timeline, but on second try with tracing, it worked! Original error:' + e + ) + return try2 + } + throw e } } @@ -104,7 +147,7 @@ export class AsyncResolver { wouldLikeToIterateAgain = false dontIterateAgain = true - resolvedTimeline = resolveTimeline(Array.from(timeLineMap.values()), { + resolvedTimeline = this._resolveTimeline(Array.from(timeLineMap.values()), { time: now, }) diff --git a/packages/timeline-state-resolver/src/conductor.ts b/packages/timeline-state-resolver/src/conductor.ts index 008a13b73..fe878e28f 100644 --- a/packages/timeline-state-resolver/src/conductor.ts +++ b/packages/timeline-state-resolver/src/conductor.ts @@ -1,5 +1,11 @@ import * as _ from 'underscore' -import { getResolvedState, ResolvedTimeline, ResolvedTimelineObjectInstance, TimelineObject } from 'superfly-timeline' +import { + getResolvedState, + ResolvedTimeline, + ResolvedTimelineObjectInstance, + TimelineObject, + TimelineState, +} from 'superfly-timeline' import { EventEmitter } from 'eventemitter3' import { MemUsageReport, threadedClass, ThreadedClass, ThreadedClassConfig, ThreadedClassManager } from 'threadedclass' import PQueue from 'p-queue' @@ -260,6 +266,9 @@ export class Conductor extends EventEmitter { instanceName: 'resolver', } ) + await this._resolver.on('error', (e) => { + this.emit('error', 'AsyncResolver error: ' + e) + }) ThreadedClassManager.onEvent(this._resolver, 'thread_closed', () => { // This is called if a child crashes - we are using autoRestart, so we just log @@ -919,7 +928,14 @@ export class Conductor extends EventEmitter { _.each(timeline, (o) => applyRecursively(o, fixNow)) } - const tlState = getResolvedState(resolvedTimeline, resolveTime, 1) + let tlState: TimelineState + try { + tlState = getResolvedState(resolvedTimeline, resolveTime, 1) + } catch (e) { + // Trace some helpful information related to the error: + this.emit('error', 'Error resolveTrace: ' + JSON.stringify(resolvedTimeline.statistics.resolveTrace)) + throw e + } await pPrepareForHandleStates statTimeTimelineResolved = Date.now() diff --git a/yarn.lock b/yarn.lock index cd90d916c..b0732bb32 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11110,12 +11110,12 @@ asn1@evs-broadcast/node-asn1: languageName: node linkType: hard -"superfly-timeline@npm:^9.0.1": - version: 9.0.1 - resolution: "superfly-timeline@npm:9.0.1" +"superfly-timeline@npm:9.0.2-nightly-fix-better-trace-resolving-20241003-064759-7723b89.0": + version: 9.0.2-nightly-fix-better-trace-resolving-20241003-064759-7723b89.0 + resolution: "superfly-timeline@npm:9.0.2-nightly-fix-better-trace-resolving-20241003-064759-7723b89.0" dependencies: tslib: ^2.6.0 - checksum: 4267eed691fe9ce9f89bf17c8aed1a98206938dd6d850c64b083e4fd3a3dc5329801c76c757450c9520375bad100ce512cc6d6a3e4a997bdfa14a4e7d65f09f2 + checksum: 19e26155d151c88f63711bd980568932bd988e526c6cc49b3235310265ba2a3b7c2dbe9436b08147e810f6db08bf7a3ad8cde6e9c524d0d7a8b3e08c279665d0 languageName: node linkType: hard @@ -11415,7 +11415,7 @@ asn1@evs-broadcast/node-asn1: p-timeout: ^3.2.0 simple-oauth2: ^5.0.0 sprintf-js: ^1.1.3 - superfly-timeline: ^9.0.1 + superfly-timeline: 9.0.2-nightly-fix-better-trace-resolving-20241003-064759-7723b89.0 threadedclass: ^1.2.1 timeline-state-resolver-types: 9.2.0-alpha.0 tslib: ^2.6.2 From 5962b485111312a454f915c823a0f6ac720ed155 Mon Sep 17 00:00:00 2001 From: Johan Nyman Date: Thu, 10 Oct 2024 09:39:42 +0200 Subject: [PATCH 2/2] chore: timeline dep --- packages/timeline-state-resolver/package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/timeline-state-resolver/package.json b/packages/timeline-state-resolver/package.json index b8cf30589..121c23b76 100644 --- a/packages/timeline-state-resolver/package.json +++ b/packages/timeline-state-resolver/package.json @@ -109,7 +109,7 @@ "p-timeout": "^3.2.0", "simple-oauth2": "^5.0.0", "sprintf-js": "^1.1.3", - "superfly-timeline": "9.0.2-nightly-fix-better-trace-resolving-20241003-064759-7723b89.0", + "superfly-timeline": "9.0.2", "threadedclass": "^1.2.1", "timeline-state-resolver-types": "9.2.0-alpha.0", "tslib": "^2.6.2", diff --git a/yarn.lock b/yarn.lock index b0732bb32..09e6fe324 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11110,12 +11110,12 @@ asn1@evs-broadcast/node-asn1: languageName: node linkType: hard -"superfly-timeline@npm:9.0.2-nightly-fix-better-trace-resolving-20241003-064759-7723b89.0": - version: 9.0.2-nightly-fix-better-trace-resolving-20241003-064759-7723b89.0 - resolution: "superfly-timeline@npm:9.0.2-nightly-fix-better-trace-resolving-20241003-064759-7723b89.0" +"superfly-timeline@npm:9.0.2": + version: 9.0.2 + resolution: "superfly-timeline@npm:9.0.2" dependencies: tslib: ^2.6.0 - checksum: 19e26155d151c88f63711bd980568932bd988e526c6cc49b3235310265ba2a3b7c2dbe9436b08147e810f6db08bf7a3ad8cde6e9c524d0d7a8b3e08c279665d0 + checksum: d628d467d5384f5667bc10b877478c5b8b0a91774b5d5c5e9d9d3134b8f1b760225f2fbbb0f9ccd3e55f930c9f3719f81b9347b94ea853fbc0a18bc121d97665 languageName: node linkType: hard @@ -11415,7 +11415,7 @@ asn1@evs-broadcast/node-asn1: p-timeout: ^3.2.0 simple-oauth2: ^5.0.0 sprintf-js: ^1.1.3 - superfly-timeline: 9.0.2-nightly-fix-better-trace-resolving-20241003-064759-7723b89.0 + superfly-timeline: 9.0.2 threadedclass: ^1.2.1 timeline-state-resolver-types: 9.2.0-alpha.0 tslib: ^2.6.2