Suppress duplicate relay websocket close errors
All checks were successful
deploy / deploy (push) Successful in 33s
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:
parent
a7a73336a5
commit
8641c60ab7
4 changed files with 18 additions and 0 deletions
|
|
@ -188,6 +188,7 @@ export async function startSolverRelayWs({
|
||||||
|
|
||||||
activeSocket.addEventListener('error', (error) => {
|
activeSocket.addEventListener('error', (error) => {
|
||||||
if (activeSocket !== socket) return;
|
if (activeSocket !== socket) return;
|
||||||
|
if (closingSockets.has(activeSocket)) return;
|
||||||
connected = false;
|
connected = false;
|
||||||
lastDisconnectedAt = new Date().toISOString();
|
lastDisconnectedAt = new Date().toISOString();
|
||||||
rejectAllPending(new Error('Socket error'));
|
rejectAllPending(new Error('Socket error'));
|
||||||
|
|
|
||||||
|
|
@ -158,6 +158,7 @@ export async function startNearIntentsWs({
|
||||||
|
|
||||||
ws.addEventListener('error', (err) => {
|
ws.addEventListener('error', (err) => {
|
||||||
if (activeSocket !== ws) return;
|
if (activeSocket !== ws) return;
|
||||||
|
if (closingSockets.has(ws)) return;
|
||||||
connected = false;
|
connected = false;
|
||||||
lastDisconnectedAt = new Date().toISOString();
|
lastDisconnectedAt = new Date().toISOString();
|
||||||
logger?.error('socket_error', {
|
logger?.error('socket_error', {
|
||||||
|
|
|
||||||
|
|
@ -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 () => {
|
test('near intents websocket close is reentrant-safe when close emits an error', async () => {
|
||||||
const mock = installMockWebSocket();
|
const mock = installMockWebSocket();
|
||||||
|
const errors = [];
|
||||||
const producer = { sendJson: async () => {} };
|
const producer = { sendJson: async () => {} };
|
||||||
|
const logger = {
|
||||||
|
error: (event) => errors.push(event),
|
||||||
|
info: () => {},
|
||||||
|
warn: () => {},
|
||||||
|
};
|
||||||
const client = await startNearIntentsWs({
|
const client = await startNearIntentsWs({
|
||||||
apiKey: 'api-key',
|
apiKey: 'api-key',
|
||||||
wsUrl: 'wss://relay.example/ws',
|
wsUrl: 'wss://relay.example/ws',
|
||||||
|
|
@ -113,6 +119,7 @@ test('near intents websocket close is reentrant-safe when close emits an error',
|
||||||
producer,
|
producer,
|
||||||
rawTopic: 'raw.near_intents.quote',
|
rawTopic: 'raw.near_intents.quote',
|
||||||
normalizedTopic: 'norm.swap_demand',
|
normalizedTopic: 'norm.swap_demand',
|
||||||
|
logger,
|
||||||
reconnectDelayMs: 1,
|
reconnectDelayMs: 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -127,6 +134,7 @@ test('near intents websocket close is reentrant-safe when close emits an error',
|
||||||
await delay(10);
|
await delay(10);
|
||||||
|
|
||||||
assert.equal(mock.instances[0].closeCalls, 1);
|
assert.equal(mock.instances[0].closeCalls, 1);
|
||||||
|
assert.equal(errors.filter((event) => event === 'socket_error').length, 1);
|
||||||
assert.equal(client.getState().connected, false);
|
assert.equal(client.getState().connected, false);
|
||||||
assert.ok(client.getState().reconnect_count >= 2);
|
assert.ok(client.getState().reconnect_count >= 2);
|
||||||
assert.ok(mock.instances.length >= 2);
|
assert.ok(mock.instances.length >= 2);
|
||||||
|
|
|
||||||
|
|
@ -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 () => {
|
test('solver relay websocket close is reentrant-safe when close emits an error', async () => {
|
||||||
const mock = installMockWebSocket();
|
const mock = installMockWebSocket();
|
||||||
|
const errors = [];
|
||||||
|
const logger = {
|
||||||
|
error: (event) => errors.push(event),
|
||||||
|
info: () => {},
|
||||||
|
warn: () => {},
|
||||||
|
};
|
||||||
const client = await startSolverRelayWs({
|
const client = await startSolverRelayWs({
|
||||||
apiKey: 'api-key',
|
apiKey: 'api-key',
|
||||||
wsUrl: 'wss://relay.example/ws',
|
wsUrl: 'wss://relay.example/ws',
|
||||||
|
logger,
|
||||||
reconnectDelayMs: 1,
|
reconnectDelayMs: 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -137,6 +144,7 @@ test('solver relay websocket close is reentrant-safe when close emits an error',
|
||||||
await delay(10);
|
await delay(10);
|
||||||
|
|
||||||
assert.equal(mock.instances[0].closeCalls, 1);
|
assert.equal(mock.instances[0].closeCalls, 1);
|
||||||
|
assert.equal(errors.filter((event) => event === 'socket_error').length, 1);
|
||||||
assert.equal(client.getState().connected, false);
|
assert.equal(client.getState().connected, false);
|
||||||
assert.ok(client.getState().reconnect_count >= 2);
|
assert.ok(client.getState().reconnect_count >= 2);
|
||||||
assert.ok(mock.instances.length >= 2);
|
assert.ok(mock.instances.length >= 2);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue