mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-08-28 04:37:37 +00:00
🔗 fix: Preserve Stream State Across Reconnects to Prevent Reorder Buffer Desync (#11842)
When all subscribers left a stream, both RedisEventTransport and InMemoryEventTransport deleted the entire stream state, destroying the allSubscribersLeftCallbacks and abortCallbacks registered by GenerationJobManager.createJob(). On the next subscribe/unsubscribe cycle, the callback that resets hasSubscriber was gone, causing syncReorderBuffer to be skipped on subsequent reconnects. This led to the reorder buffer expecting seq 0 while the publisher was at seq 300+, triggering a 500ms force-flush timeout and "skipping N missing messages" warnings. Fix: preserve stream state (callbacks, abort handlers) when the last subscriber leaves instead of deleting it. State is fully cleaned up by cleanup() when the job completes, aborts, or is collected by periodic orphan cleanup.
This commit is contained in:
parent
5824298125
commit
252a5cc7ca
3 changed files with 463 additions and 4 deletions
|
|
@ -58,9 +58,11 @@ export class InMemoryEventTransport implements IEventTransport {
|
|||
// Check if all subscribers left - cleanup and notify
|
||||
if (currentState.emitter.listenerCount('chunk') === 0) {
|
||||
currentState.allSubscribersLeftCallback?.();
|
||||
// Auto-cleanup the stream entry when no subscribers remain
|
||||
/* Remove all EventEmitter listeners but preserve stream state
|
||||
* (including allSubscribersLeftCallback) for reconnection.
|
||||
* State is fully cleaned up by cleanup() when the job completes.
|
||||
*/
|
||||
currentState.emitter.removeAllListeners();
|
||||
this.streams.delete(streamId);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -425,8 +425,15 @@ export class RedisEventTransport implements IEventTransport {
|
|||
logger.error(`[RedisEventTransport] Error in allSubscribersLeft callback:`, err);
|
||||
}
|
||||
}
|
||||
|
||||
this.streams.delete(streamId);
|
||||
/**
|
||||
* Preserve stream state (callbacks, abort handlers) for reconnection.
|
||||
* Previously this deleted the entire state, which lost the
|
||||
* allSubscribersLeftCallbacks and abortCallbacks registered by
|
||||
* GenerationJobManager.createJob(). On the next subscribe() call,
|
||||
* fresh state was created without those callbacks, causing
|
||||
* hasSubscriber to never reset and syncReorderBuffer to be skipped.
|
||||
* State is fully cleaned up by cleanup() when the job completes.
|
||||
*/
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue