fix(telemetry): join overlapping flush hints; contain adoption replay per event

Second review round, both pinned red-first:

- Overlapping turn-boundary flush hints now JOIN the outstanding flush
  promise (Promise.all) instead of displacing it: the SDK's
  concurrent-flush guard resolves an overlapping forceFlush()
  immediately, so retaining only the latest promise let shutdown()
  proceed while the first export was still in flight — the same silent
  drop the single-flush fix closed.
- Adoption replay contains failures per event, matching the firehose:
  one rejected record is withheld fail-closed while the rest of the
  historical log still hands off. Wrapping the whole loop let a single
  failure silently skip the remainder on an already-adopted session.
This commit is contained in:
kingwl
2026-07-25 03:48:32 +08:00
parent e6a8ff2621
commit d398eda432
4 changed files with 77 additions and 12 deletions
@@ -113,15 +113,19 @@ export class TelemetryCoordinator {
* @param session - the live session to adopt; a second adoption is a no-op.
*/
private adopt(session: Session): void {
this.contain(() => {
if (this.adopted.has(session)) return
this.adopted.add(session)
const cursor = handoffCursor.get(session) ?? -1
for (const event of session.events) {
if (this.adopted.has(session)) return
this.adopted.add(session)
const cursor = handoffCursor.get(session) ?? -1
// Containment is PER EVENT, matching the firehose: one rejected record
// is withheld fail-closed while the rest of the historical replay
// proceeds — wrapping the whole loop would let a single failure silently
// skip the remainder of the log on an already-adopted session.
for (const event of session.events) {
this.contain(() => {
if (event.seq <= cursor) this.track(session, event)
else this.capture(session, event)
}
})
})
}
}
/** Feed the chunk projection without handing off — the ≤cursor half of re-adoption. */