Skip to content

Commit e895c5b

Browse files
authored
fix(client): Respect retry attempts when server goes away after connecting in single connection mode (#59)
Closes #55
1 parent a16b921 commit e895c5b

File tree

2 files changed

+84
-36
lines changed

2 files changed

+84
-36
lines changed

src/__tests__/client.ts

+76-34
Original file line numberDiff line numberDiff line change
@@ -155,40 +155,6 @@ it('should report error to sink if server goes away', async () => {
155155
);
156156
});
157157

158-
it('should respect retry attempts when server goes away after connecting', async () => {
159-
const { fetch, waitForRequest, dispose } = createTFetch();
160-
161-
const client = createClient({
162-
fetchFn: fetch,
163-
url: 'http://localhost',
164-
retryAttempts: 2,
165-
retry: () => Promise.resolve(),
166-
});
167-
168-
const sub = tsubscribe(client, {
169-
query: `subscription { ping(key: "${Math.random()}") }`,
170-
});
171-
172-
// start
173-
await waitForRequest();
174-
await dispose();
175-
176-
// 1st retry
177-
await waitForRequest();
178-
await dispose();
179-
180-
// 2nd retry
181-
await waitForRequest();
182-
await dispose();
183-
184-
// no more retries
185-
await expect(
186-
Promise.race([waitForRequest(), sub.waitForError()]),
187-
).resolves.toMatchInlineSnapshot(
188-
`[NetworkError: Connection closed while having active streams]`,
189-
);
190-
});
191-
192158
it('should report error to sink if server goes away during generator emission', async () => {
193159
const { fetch, dispose } = createTFetch();
194160

@@ -384,6 +350,47 @@ describe('single connection mode', () => {
384350
expect(stream.signal.aborted).toBeTruthy();
385351
});
386352
});
353+
354+
it('should respect retry attempts when server goes away after connecting', async () => {
355+
const { fetch, waitForRequest, dispose } = createTFetch();
356+
357+
const client = createClient({
358+
singleConnection: true,
359+
fetchFn: fetch,
360+
url: 'http://localhost',
361+
retryAttempts: 2,
362+
retry: () => Promise.resolve(),
363+
});
364+
365+
const sub = tsubscribe(client, {
366+
query: `subscription { ping(key: "${Math.random()}") }`,
367+
});
368+
369+
// start
370+
await waitForRequest(); // reservation
371+
await waitForRequest(); // connect
372+
await waitForRequest(); // operation
373+
await dispose();
374+
375+
// 1st retry
376+
await waitForRequest(); // reservation
377+
await waitForRequest(); // connect
378+
await waitForRequest(); // operation
379+
await dispose();
380+
381+
// 2nd retry
382+
await waitForRequest(); // reservation
383+
await waitForRequest(); // connect
384+
await waitForRequest(); // operation
385+
await dispose();
386+
387+
// no more retries
388+
await expect(
389+
Promise.race([waitForRequest(), sub.waitForError()]),
390+
).resolves.toMatchInlineSnapshot(
391+
`[NetworkError: Connection closed while having active streams]`,
392+
);
393+
});
387394
});
388395

389396
describe('distinct connections mode', () => {
@@ -468,6 +475,41 @@ describe('distinct connections mode', () => {
468475
expect(stream1.signal.aborted).toBeTruthy();
469476
expect(stream2.signal.aborted).toBeTruthy();
470477
});
478+
479+
it('should respect retry attempts when server goes away after connecting', async () => {
480+
const { fetch, waitForRequest, dispose } = createTFetch();
481+
482+
const client = createClient({
483+
singleConnection: false,
484+
fetchFn: fetch,
485+
url: 'http://localhost',
486+
retryAttempts: 2,
487+
retry: () => Promise.resolve(),
488+
});
489+
490+
const sub = tsubscribe(client, {
491+
query: `subscription { ping(key: "${Math.random()}") }`,
492+
});
493+
494+
// start
495+
await waitForRequest();
496+
await dispose();
497+
498+
// 1st retry
499+
await waitForRequest();
500+
await dispose();
501+
502+
// 2nd retry
503+
await waitForRequest();
504+
await dispose();
505+
506+
// no more retries
507+
await expect(
508+
Promise.race([waitForRequest(), sub.waitForError()]),
509+
).resolves.toMatchInlineSnapshot(
510+
`[NetworkError: Connection closed while having active streams]`,
511+
);
512+
});
471513
});
472514

473515
describe('retries', () => {

src/client.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -376,8 +376,6 @@ export function createClient<SingleConnection extends boolean = false>(
376376
fetchFn,
377377
onMessage,
378378
});
379-
retryingErr = null; // future connects are not retries
380-
retries = 0; // reset the retries on connect
381379

382380
connected.waitForThrow().catch(() => (conn = undefined));
383381

@@ -577,6 +575,14 @@ export function createClient<SingleConnection extends boolean = false>(
577575
signal: control.signal,
578576
operationId,
579577
})) {
578+
// only after receiving results are future connects not considered retries.
579+
// this is because a client might successfully connect, but the server
580+
// ends up terminating the connection afterwards before streaming anything.
581+
// of course, if the client completes the subscription, this loop will
582+
// break and therefore stop the stream (it wont reconnect)
583+
retryingErr = null;
584+
retries = 0;
585+
580586
// eslint-disable-next-line @typescript-eslint/no-explicit-any
581587
sink.next(result as any);
582588
}

0 commit comments

Comments
 (0)