Improve event stream resilience
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
export const MESSAGE_STREAM_GLOBAL_WS_PATH = '/api/global/event/ws';
|
||||
export const MESSAGE_STREAM_DIRECTORY_WS_PATH = '/api/event/ws';
|
||||
export const MESSAGE_STREAM_WS_HEARTBEAT_INTERVAL_MS = 15 * 1000;
|
||||
// Per-client pending outbound WS buffer, not a payload or stream-size limit.
|
||||
// Healthy clients stay near 0; this only trips when a client is far behind.
|
||||
export const MESSAGE_STREAM_WS_MAX_BUFFERED_BYTES = 4 * 1024 * 1024;
|
||||
|
||||
export function parseSseEventEnvelope(block) {
|
||||
if (!block || typeof block !== 'string') {
|
||||
@@ -64,8 +67,23 @@ export function sendMessageStreamWsFrame(socket, payload) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof socket.bufferedAmount === 'number' && socket.bufferedAmount > MESSAGE_STREAM_WS_MAX_BUFFERED_BYTES) {
|
||||
try {
|
||||
socket.close(1013, 'Message stream client is too slow');
|
||||
} catch {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
socket.send(JSON.stringify(payload));
|
||||
if (typeof socket.bufferedAmount === 'number' && socket.bufferedAmount > MESSAGE_STREAM_WS_MAX_BUFFERED_BYTES) {
|
||||
try {
|
||||
socket.close(1013, 'Message stream client is too slow');
|
||||
} catch {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
|
||||
@@ -3,6 +3,7 @@ import { describe, expect, it } from 'vitest';
|
||||
import {
|
||||
MESSAGE_STREAM_DIRECTORY_WS_PATH,
|
||||
MESSAGE_STREAM_GLOBAL_WS_PATH,
|
||||
MESSAGE_STREAM_WS_MAX_BUFFERED_BYTES,
|
||||
parseSseEventEnvelope,
|
||||
sendMessageStreamWsEvent,
|
||||
sendMessageStreamWsFrame,
|
||||
@@ -63,6 +64,30 @@ describe('event stream protocol helpers', () => {
|
||||
expect(rawPayload).toBe('{"type":"ready"}');
|
||||
});
|
||||
|
||||
it('closes slow websocket clients instead of adding more buffered data', () => {
|
||||
let closeCall = null;
|
||||
let sendCalls = 0;
|
||||
const socket = {
|
||||
readyState: 1,
|
||||
bufferedAmount: MESSAGE_STREAM_WS_MAX_BUFFERED_BYTES + 1,
|
||||
send() {
|
||||
sendCalls += 1;
|
||||
},
|
||||
close(code, reason) {
|
||||
closeCall = { code, reason };
|
||||
},
|
||||
};
|
||||
|
||||
const sent = sendMessageStreamWsFrame(socket, { type: 'ready' });
|
||||
|
||||
expect(sent).toBe(false);
|
||||
expect(sendCalls).toBe(0);
|
||||
expect(closeCall).toEqual({
|
||||
code: 1013,
|
||||
reason: 'Message stream client is too slow',
|
||||
});
|
||||
});
|
||||
|
||||
it('serializes event frames with routing metadata', () => {
|
||||
let rawPayload = null;
|
||||
const socket = {
|
||||
|
||||
Reference in New Issue
Block a user