Skip to content

Commit

Permalink
feat(client): Allow keeping the connection alive for some time before…
Browse files Browse the repository at this point in the history
… lazy closing (enisdenjo#69)

* docs: prefer "otherwise"

* feat: keepalive for the client

* docs: generate

* fix: wrong else

* style: comment in block

* test: close after keepalive

* docs: smaller clarification
  • Loading branch information
enisdenjo committed Nov 12, 2020
1 parent 43f9f2a commit 555c2c3
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 3 deletions.
13 changes: 13 additions & 0 deletions docs/interfaces/_client_.clientoptions.md
Expand Up @@ -16,6 +16,7 @@ Configuration used for the `create` client function.

* [connectionParams](_client_.clientoptions.md#connectionparams)
* [generateID](_client_.clientoptions.md#generateid)
* [keepAlive](_client_.clientoptions.md#keepalive)
* [lazy](_client_.clientoptions.md#lazy)
* [on](_client_.clientoptions.md#on)
* [retryAttempts](_client_.clientoptions.md#retryattempts)
Expand Down Expand Up @@ -49,6 +50,18 @@ Reference: https://stackoverflow.com/a/2117523/709884

___

### keepAlive

`Optional` **keepAlive**: undefined \| number

How long should the client wait before closing the socket after the last oparation has
completed. This is meant to be used in combination with `lazy`. You might want to have
a calmdown time before actually closing the connection. Kinda' like a lazy close "debounce".

**`default`** 0 // close immediately

___

### lazy

`Optional` **lazy**: undefined \| false \| true
Expand Down
27 changes: 24 additions & 3 deletions src/client.ts
Expand Up @@ -69,6 +69,14 @@ export interface ClientOptions {
* @default true
*/
lazy?: boolean;
/**
* How long should the client wait before closing the socket after the last oparation has
* completed. This is meant to be used in combination with `lazy`. You might want to have
* a calmdown time before actually closing the connection. Kinda' like a lazy close "debounce".
*
* @default 0 // close immediately
*/
keepAlive?: number;
/**
* How many times should the client try to reconnect on abnormal socket closure before it errors out?
*
Expand Down Expand Up @@ -126,6 +134,7 @@ export function createClient(options: ClientOptions): Client {
url,
connectionParams,
lazy = true,
keepAlive = 0,
retryAttempts = 5,
retryTimeout = 3 * 1000, // 3 seconds
on,
Expand Down Expand Up @@ -358,7 +367,19 @@ export function createClient(options: ClientOptions): Client {
cleanup?.();
state.locks--;
if (!state.locks) {
socket.close(1000, 'Normal Closure');
if (keepAlive > 0 && isFinite(keepAlive)) {
// if the keepalive is set, allow for the specified calmdown
// time and then close. but only if no lock got created in the
// meantime and if the socket is still open
setTimeout(() => {
if (!state.locks && socket.OPEN) {
socket.close(1000, 'Normal Closure');
}
}, keepAlive);
} else {
// otherwise close immediately
socket.close(1000, 'Normal Closure');
}
}
socket.removeEventListener('close', listener);
return resolve();
Expand Down Expand Up @@ -394,7 +415,7 @@ export function createClient(options: ClientOptions): Client {
return;
}

// otherwize, wait a bit and retry
// otherwise, wait a bit and retry
await new Promise((resolve) => setTimeout(resolve, retryTimeout));
}
}
Expand Down Expand Up @@ -513,7 +534,7 @@ export function createClient(options: ClientOptions): Client {
throw errOrCloseEvent;
}

// otherwize, wait a bit and retry
// otherwise, wait a bit and retry
await new Promise((resolve) => setTimeout(resolve, retryTimeout));
}
}
Expand Down
32 changes: 32 additions & 0 deletions src/tests/client.ts
Expand Up @@ -535,6 +535,38 @@ describe('lazy', () => {
sub2.dispose();
await server.waitForClientClose();
});

it('should disconnect after the keepAlive has passed after last unsubscribe', async () => {
const { url, ...server } = await startTServer();

const client = createClient({
url,
lazy: true, // default
keepAlive: 20,
retryAttempts: 0,
});

const sub = tsubscribe(client, {
query: 'subscription { ping }',
});
await server.waitForOperation();

// still is connected
await server.waitForClientClose(() => {
fail("Client shouldn't have closed");
}, 10);

// everyone unsubscribed
sub.dispose();

// still connected because of the keepAlive
await server.waitForClientClose(() => {
fail("Client shouldn't have closed");
}, 10);

// but will close eventually
await server.waitForClientClose();
});
});

describe('reconnecting', () => {
Expand Down

0 comments on commit 555c2c3

Please sign in to comment.