Suppress duplicate relay websocket close errors
All checks were successful
deploy / deploy (push) Successful in 33s

Proof: npm test; npm run operator-dashboard:build; node --test test/near-intents-ws.test.mjs test/solver-relay-ws.test.mjs; PYTHONPATH=. python3 test/bootstrap_script_static_test.py; PYTHONPATH=. python3 test/render_release_manifest_test.py; PYTHONPATH=. python3 test/repo_deployments_test.py; PYTHONPATH=. python3 test/ntfy_manifest_test.py; kubectl kustomize deploy/k8s/base.

Assumptions: nested error events emitted during WebSocket close are the same close attempt, not independent evidence that should be logged twice.

Still fake: Notification emissions are limited to credited deposits, completed withdrawals, and completed trades with durable inventory movement; generic alert notification policy remains disabled.
This commit is contained in:
philipp 2026-04-16 14:36:18 +02:00
parent a7a73336a5
commit 8641c60ab7
4 changed files with 18 additions and 0 deletions

View file

@ -188,6 +188,7 @@ export async function startSolverRelayWs({
activeSocket.addEventListener('error', (error) => {
if (activeSocket !== socket) return;
if (closingSockets.has(activeSocket)) return;
connected = false;
lastDisconnectedAt = new Date().toISOString();
rejectAllPending(new Error('Socket error'));

View file

@ -158,6 +158,7 @@ export async function startNearIntentsWs({
ws.addEventListener('error', (err) => {
if (activeSocket !== ws) return;
if (closingSockets.has(ws)) return;
connected = false;
lastDisconnectedAt = new Date().toISOString();
logger?.error('socket_error', {

View file

@ -105,7 +105,13 @@ test('near intents ingest reconnects after websocket error before open without r
test('near intents websocket close is reentrant-safe when close emits an error', async () => {
const mock = installMockWebSocket();
const errors = [];
const producer = { sendJson: async () => {} };
const logger = {
error: (event) => errors.push(event),
info: () => {},
warn: () => {},
};
const client = await startNearIntentsWs({
apiKey: 'api-key',
wsUrl: 'wss://relay.example/ws',
@ -113,6 +119,7 @@ test('near intents websocket close is reentrant-safe when close emits an error',
producer,
rawTopic: 'raw.near_intents.quote',
normalizedTopic: 'norm.swap_demand',
logger,
reconnectDelayMs: 1,
});
@ -127,6 +134,7 @@ test('near intents websocket close is reentrant-safe when close emits an error',
await delay(10);
assert.equal(mock.instances[0].closeCalls, 1);
assert.equal(errors.filter((event) => event === 'socket_error').length, 1);
assert.equal(client.getState().connected, false);
assert.ok(client.getState().reconnect_count >= 2);
assert.ok(mock.instances.length >= 2);

View file

@ -120,9 +120,16 @@ test('solver relay reconnects after websocket error even when no close event is
test('solver relay websocket close is reentrant-safe when close emits an error', async () => {
const mock = installMockWebSocket();
const errors = [];
const logger = {
error: (event) => errors.push(event),
info: () => {},
warn: () => {},
};
const client = await startSolverRelayWs({
apiKey: 'api-key',
wsUrl: 'wss://relay.example/ws',
logger,
reconnectDelayMs: 1,
});
@ -137,6 +144,7 @@ test('solver relay websocket close is reentrant-safe when close emits an error',
await delay(10);
assert.equal(mock.instances[0].closeCalls, 1);
assert.equal(errors.filter((event) => event === 'socket_error').length, 1);
assert.equal(client.getState().connected, false);
assert.ok(client.getState().reconnect_count >= 2);
assert.ok(mock.instances.length >= 2);