Skip to content

Commit

Permalink
Merge pull request #135 from EventStore/keep-alives
Browse files Browse the repository at this point in the history
Keep alives
  • Loading branch information
hayley-jean authored Feb 16, 2021
2 parents 3fd8618 + 63a53f0 commit a4aa66a
Show file tree
Hide file tree
Showing 6 changed files with 402 additions and 5 deletions.
61 changes: 59 additions & 2 deletions src/Client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,22 @@ import { discoverEndpoint } from "./discovery";
import { parseConnectionString } from "./parseConnectionString";

interface ClientOptions {
/**
* The amount of time (in milliseconds) to wait after which a keepalive ping is sent on the transport.
* Use -1 to disable.
* @default 10_000
*/
keepAliveInterval?: number;
/**
* The amount of time (in milliseconds) the sender of the keepalive ping waits for an acknowledgement.
* If it does not receive an acknowledgment within this time, it will close the connection.
* @default 10_000
*/
keepAliveTimeout?: number;
/**
* Whether or not to immediately throw an exception when an append fails.
* @default true
*/
throwOnAppendFailure?: boolean;
}

Expand Down Expand Up @@ -73,6 +89,9 @@ export class Client {
#connectionSettings: ConnectionTypeOptions;
#channelCredentials: ChannelCredentials;
#insecure: boolean;
#keepAliveInterval: number;
#keepAliveTimeout: number;

#defaultCredentials?: Credentials;

#channel?: Promise<Channel>;
Expand All @@ -87,7 +106,7 @@ export class Client {
*/
static connectionString(
connectionString: TemplateStringsArray | string,
...parts: string[]
...parts: Array<string | number | boolean>
): Client {
const string: string = Array.isArray(connectionString)
? connectionString.reduce<string>(
Expand Down Expand Up @@ -120,6 +139,9 @@ export class Client {
discoveryInterval: options.discoveryInterval,
gossipTimeout: options.gossipTimeout,
maxDiscoverAttempts: options.maxDiscoverAttempts,
throwOnAppendFailure: options.throwOnAppendFailure,
keepAliveInterval: options.keepAliveInterval,
keepAliveTimeout: options.keepAliveTimeout,
},
channelCredentials,
options.defaultCredentials
Expand All @@ -134,6 +156,9 @@ export class Client {
discoveryInterval: options.discoveryInterval,
gossipTimeout: options.gossipTimeout,
maxDiscoverAttempts: options.maxDiscoverAttempts,
throwOnAppendFailure: options.throwOnAppendFailure,
keepAliveInterval: options.keepAliveInterval,
keepAliveTimeout: options.keepAliveTimeout,
},
channelCredentials,
options.defaultCredentials
Expand All @@ -143,6 +168,9 @@ export class Client {
return new Client(
{
endpoint: options.hosts[0],
throwOnAppendFailure: options.throwOnAppendFailure,
keepAliveInterval: options.keepAliveInterval,
keepAliveTimeout: options.keepAliveTimeout,
},
channelCredentials,
options.defaultCredentials
Expand All @@ -167,12 +195,34 @@ export class Client {
constructor(
{
throwOnAppendFailure = true,
keepAliveInterval = 10_000,
keepAliveTimeout = 10_000,
...connectionSettings
}: ConnectionTypeOptions,
channelCredentials: ChannelCredentialOptions = { insecure: false },
defaultUserCredentials?: Credentials
) {
if (keepAliveInterval < -1) {
throw new Error(
`Invalid keepAliveInterval "${keepAliveInterval}". Please provide a positive integer, or -1 to disable.`
);
}

if (keepAliveTimeout < -1) {
throw new Error(
`Invalid keepAliveTimeout "${keepAliveTimeout}". Please provide a positive integer, or -1 to disable.`
);
}

if (keepAliveInterval > -1 && keepAliveInterval < 10_000) {
console.warn(
`Specified KeepAliveInterval of ${keepAliveInterval} is less than recommended 10_000 ms.`
);
}

this.#throwOnAppendFailure = throwOnAppendFailure;
this.#keepAliveInterval = keepAliveInterval;
this.#keepAliveTimeout = keepAliveTimeout;
this.#connectionSettings = connectionSettings;
this.#insecure = !!channelCredentials.insecure;
this.#defaultCredentials = defaultUserCredentials;
Expand Down Expand Up @@ -248,7 +298,14 @@ export class Client {
uri
);

return new Channel(uri, this.#channelCredentials, {});
return new Channel(uri, this.#channelCredentials, {
"grpc.keepalive_time_ms":
this.#keepAliveInterval < 0
? Number.MAX_VALUE
: this.#keepAliveInterval,
"grpc.keepalive_timeout_ms":
this.#keepAliveTimeout < 0 ? Number.MAX_VALUE : this.#keepAliveTimeout,
});
};

private resolveUri = async (): Promise<string> => {
Expand Down
11 changes: 10 additions & 1 deletion src/Client/parseConnectionString.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ const lowerToKey: Record<string, keyof ConnectionOptions> = {
tls: "tls",
tlsverifycert: "tlsVerifyCert",
throwonappendfailure: "throwOnAppendFailure",
keepaliveinterval: "keepAliveInterval",
keepalivetimeout: "keepAliveTimeout",
};

export interface ConnectionOptions {
Expand All @@ -23,6 +25,9 @@ export interface ConnectionOptions {
tls: boolean;
tlsVerifyCert: boolean;
throwOnAppendFailure: boolean;
keepAliveInterval: number;
keepAliveTimeout: number;

defaultCredentials?: Credentials;
hosts: EndPoint[];
}
Expand All @@ -35,6 +40,8 @@ const defaultConnectionOptions: ConnectionOptions = {
nodePreference: "random",
tls: true,
tlsVerifyCert: true,
keepAliveInterval: 10_000,
keepAliveTimeout: 10_000,
throwOnAppendFailure: true,
hosts: [],
};
Expand Down Expand Up @@ -252,7 +259,9 @@ const verifyKeyValuePair = (
}
case "maxDiscoverAttempts":
case "discoveryInterval":
case "gossipTimeout": {
case "gossipTimeout":
case "keepAliveInterval":
case "keepAliveTimeout": {
const parsedValue = parseInt(value);

if (Number.isNaN(parsedValue)) {
Expand Down
89 changes: 89 additions & 0 deletions src/__test__/connection/__snapshots__/keepAlive.test.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`keepAlive settings should throw if < -1 keepAliveInterval connectionString 1`] = `"Invalid keepAliveInterval \\"-2\\". Please provide a positive integer, or -1 to disable."`;

exports[`keepAlive settings should throw if < -1 keepAliveInterval constructor 1`] = `"Invalid keepAliveInterval \\"-2\\". Please provide a positive integer, or -1 to disable."`;

exports[`keepAlive settings should throw if < -1 keepAliveTimeout connectionString 1`] = `"Invalid keepAliveTimeout \\"-100\\". Please provide a positive integer, or -1 to disable."`;

exports[`keepAlive settings should throw if < -1 keepAliveTimeout constructor 1`] = `"Invalid keepAliveTimeout \\"-100\\". Please provide a positive integer, or -1 to disable."`;

exports[`keepAlive settings should warn if keepAliveInterval is less than 10_000ms (but more than -1) connectionString 1`] = `
Array [
Array [
"Specified KeepAliveInterval of 0 is less than recommended 10_000 ms.",
],
]
`;

exports[`keepAlive settings should warn if keepAliveInterval is less than 10_000ms (but more than -1) connectionString 2`] = `
Array [
Array [
"Specified KeepAliveInterval of 1 is less than recommended 10_000 ms.",
],
]
`;

exports[`keepAlive settings should warn if keepAliveInterval is less than 10_000ms (but more than -1) connectionString 3`] = `
Array [
Array [
"Specified KeepAliveInterval of 10 is less than recommended 10_000 ms.",
],
]
`;

exports[`keepAlive settings should warn if keepAliveInterval is less than 10_000ms (but more than -1) connectionString 4`] = `
Array [
Array [
"Specified KeepAliveInterval of 1000 is less than recommended 10_000 ms.",
],
]
`;

exports[`keepAlive settings should warn if keepAliveInterval is less than 10_000ms (but more than -1) connectionString 5`] = `
Array [
Array [
"Specified KeepAliveInterval of 9999 is less than recommended 10_000 ms.",
],
]
`;

exports[`keepAlive settings should warn if keepAliveInterval is less than 10_000ms (but more than -1) constructor 1`] = `
Array [
Array [
"Specified KeepAliveInterval of 0 is less than recommended 10_000 ms.",
],
]
`;

exports[`keepAlive settings should warn if keepAliveInterval is less than 10_000ms (but more than -1) constructor 2`] = `
Array [
Array [
"Specified KeepAliveInterval of 1 is less than recommended 10_000 ms.",
],
]
`;

exports[`keepAlive settings should warn if keepAliveInterval is less than 10_000ms (but more than -1) constructor 3`] = `
Array [
Array [
"Specified KeepAliveInterval of 10 is less than recommended 10_000 ms.",
],
]
`;

exports[`keepAlive settings should warn if keepAliveInterval is less than 10_000ms (but more than -1) constructor 4`] = `
Array [
Array [
"Specified KeepAliveInterval of 1000 is less than recommended 10_000 ms.",
],
]
`;

exports[`keepAlive settings should warn if keepAliveInterval is less than 10_000ms (but more than -1) constructor 5`] = `
Array [
Array [
"Specified KeepAliveInterval of 9999 is less than recommended 10_000 ms.",
],
]
`;
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ exports[`connection string parser Should throw on invalid strings esdb://host1;h

exports[`connection string parser Should throw on invalid strings esdb://localhost/&tlsVerifyCert=false 1`] = `"Unexpected \\"&\\" at position 17, expected ?key=value."`;

exports[`connection string parser Should throw on invalid strings esdb://localhost?keepAliveInterval=XXIV 1`] = `"Unexpected \\"XXIV\\" at position 35, expected Integer."`;

exports[`connection string parser Should throw on invalid strings esdb://localhost?keepAliveTimeout=please 1`] = `"Unexpected \\"please\\" at position 34, expected Integer."`;

exports[`connection string parser Should throw on invalid strings esdb://localhost?throwOnAppendFailure=sometimes 1`] = `"Unexpected \\"sometimes\\" at position 38, expected true or false."`;

exports[`connection string parser Should throw on invalid strings esdb://localhost?tlsVerifyCert=false&nodePreference=any 1`] = `"Unexpected \\"any\\" at position 52, expected follower or leader or random."`;
Expand Down
Loading

0 comments on commit a4aa66a

Please sign in to comment.