Skip to content

Commit

Permalink
fix(client): Debounce close by lazyCloseTimeout
Browse files Browse the repository at this point in the history
Closes #388
  • Loading branch information
enisdenjo committed Aug 19, 2022
1 parent 1a79552 commit c332837
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 8 deletions.
35 changes: 35 additions & 0 deletions src/__tests__/client.ts
Expand Up @@ -1393,6 +1393,41 @@ describe('lazy', () => {
await server.waitForClientClose();
});

it('should debounce close by lazyCloseTimeout', async () => {
const { url, ...server } = await startTServer();

const client = createClient({
url,
lazy: true, // default
lazyCloseTimeout: 10,
retryAttempts: 0,
onNonLazyError: noop,
});

// loop subscriptions and delay them by 5ms (while lazy close is 10ms)
for (let i = 0; i < 5; i++) {
await new Promise((resolve) => setTimeout(resolve, 5));

await new Promise<void>((resolve, reject) => {
client.subscribe(
{ query: '{ getValue }' },
{
next: () => {
// noop
},
error: reject,
complete: resolve,
},
);
});
}

// if the debounce is set up incorrectly, a leftover timeout might close the connection earlier
await server.waitForClientClose(() => {
fail("Client shouldn't have closed");
}, 5);
});

it('should report errors to the `onNonLazyError` callback', async (done) => {
const { url, ...server } = await startTServer();

Expand Down
19 changes: 11 additions & 8 deletions src/client.ts
Expand Up @@ -468,7 +468,7 @@ export function createClient<
connectionParams,
lazy = true,
onNonLazyError = console.error,
lazyCloseTimeout = 0,
lazyCloseTimeout: lazyCloseTimeoutMs = 0,
keepAlive = 0,
disablePong,
connectionAckWaitTimeout = 0,
Expand Down Expand Up @@ -600,6 +600,7 @@ export function createClient<
type Connected = [socket: WebSocket, throwOnClose: Promise<void>];
let connecting: Promise<Connected> | undefined,
locks = 0,
lazyCloseTimeout: ReturnType<typeof setTimeout>,
retrying = false,
retries = 0,
disposed = false;
Expand All @@ -610,6 +611,10 @@ export function createClient<
waitForReleaseOrThrowOnClose: Promise<void>,
]
> {
// clear the lazy close timeout immediatelly so that close gets debounced
// see: https://github.com/enisdenjo/graphql-ws/issues/388
clearTimeout(lazyCloseTimeout);

const [socket, throwOnClose] = await (connecting ??
(connecting = new Promise<Connected>((connected, denied) =>
(async () => {
Expand Down Expand Up @@ -787,14 +792,12 @@ export function createClient<
if (!locks) {
// and if no more locks are present, complete the connection
const complete = () => socket.close(1000, 'Normal Closure');
if (isFinite(lazyCloseTimeout) && lazyCloseTimeout > 0) {
if (isFinite(lazyCloseTimeoutMs) && lazyCloseTimeoutMs > 0) {
// if the keepalive is set, allow for the specified calmdown time and
// then complete. but only if no lock got created in the meantime and
// if the socket is still open
setTimeout(() => {
if (!locks && socket.readyState === WebSocketImpl.OPEN)
complete();
}, lazyCloseTimeout);
// then complete if the socket is still open.
lazyCloseTimeout = setTimeout(() => {
if (socket.readyState === WebSocketImpl.OPEN) complete();
}, lazyCloseTimeoutMs);
} else {
// otherwise complete immediately
complete();
Expand Down

0 comments on commit c332837

Please sign in to comment.