From 0a5ea062ebeca29715e8abc08701b239cd749138 Mon Sep 17 00:00:00 2001 From: Nate Anderson Date: Fri, 11 Aug 2023 15:16:12 -0700 Subject: [PATCH] feat: add a configuration for lambda (#708) * feat: add a configuration for lambda Make the number of data clients a cache client creates configurable. 6 is likely too many for lambdas as the extra latency for the first 6 calls isn't worth it in a short-lived environment. Create a new configuration, InRegion.Lambda, for use inside lambdas. It only creates 1 data client instead of 6. * PR fixes Move the lambda config out of InRegion. Get rid of the v1 config since it isn't set in stone yet. We will make one in the future. Make the numClients unique in the various tests. Add a test showing numClients can be overridden. --- .../client-sdk-nodejs/src/cache-client.ts | 13 +++------ .../src/config/configurations.ts | 29 +++++++++++++++++++ .../config/transport/grpc-configuration.ts | 19 ++++++++++++ .../config/transport/transport-strategy.ts | 21 ++++++++++++++ .../test/unit/config/configuration.test.ts | 3 ++ .../transport/transport-strategy.test.ts | 16 ++++++++++ 6 files changed, 92 insertions(+), 9 deletions(-) diff --git a/packages/client-sdk-nodejs/src/cache-client.ts b/packages/client-sdk-nodejs/src/cache-client.ts index 1604a4258..58b3004e7 100644 --- a/packages/client-sdk-nodejs/src/cache-client.ts +++ b/packages/client-sdk-nodejs/src/cache-client.ts @@ -34,15 +34,10 @@ export class CacheClient extends AbstractCacheClient implements ICacheClient { credentialProvider: props.credentialProvider, }); - // For high load, we get better performance with multiple clients. Here we - // are setting a default, hard-coded value for the number of clients to use, - // because we haven't yet designed the API for users to use to configure - // tunables: - // https://github.com/momentohq/dev-eco-issue-tracker/issues/85 - // The choice of 6 as the initial value is a rough guess at a reasonable - // default for the short-term, based on load testing results captured in: - // https://github.com/momentohq/oncall-tracker/issues/186 - const numClients = 6; + const numClients = props.configuration + .getTransportStrategy() + .getGrpcConfig() + .getNumClients(); const dataClients = range(numClients).map(() => new DataClient(props)); super(controlClient, dataClients); diff --git a/packages/client-sdk-nodejs/src/config/configurations.ts b/packages/client-sdk-nodejs/src/config/configurations.ts index 40a571b74..19954845a 100644 --- a/packages/client-sdk-nodejs/src/config/configurations.ts +++ b/packages/client-sdk-nodejs/src/config/configurations.ts @@ -73,6 +73,35 @@ export class Laptop extends CacheConfiguration { } } +export class Lambda extends CacheConfiguration { + /** + * Provides the latest recommended configuration for a lambda environment. NOTE: this configuration may + * change in future releases to take advantage of improvements we identify for default configurations. + * @param {MomentoLoggerFactory} [loggerFactory=defaultLoggerFactory] + * @returns {CacheConfiguration} + */ + static latest( + loggerFactory: MomentoLoggerFactory = defaultLoggerFactory + ): CacheConfiguration { + const deadlineMillis = 1100; + const grpcConfig: GrpcConfiguration = new StaticGrpcConfiguration({ + deadlineMillis: deadlineMillis, + maxSessionMemoryMb: defaultMaxSessionMemoryMb, + numClients: 1, + }); + const transportStrategy: TransportStrategy = new StaticTransportStrategy({ + grpcConfiguration: grpcConfig, + maxIdleMillis: defaultMaxIdleMillis, + }); + return new Lambda({ + loggerFactory: loggerFactory, + retryStrategy: defaultRetryStrategy(loggerFactory), + transportStrategy: transportStrategy, + middlewares: defaultMiddlewares, + }); + } +} + class InRegionDefault extends CacheConfiguration { /** * Provides the latest recommended configuration for a typical in-region environment. NOTE: this configuration may diff --git a/packages/client-sdk-nodejs/src/config/transport/grpc-configuration.ts b/packages/client-sdk-nodejs/src/config/transport/grpc-configuration.ts index 8abf4a68f..499f15b55 100644 --- a/packages/client-sdk-nodejs/src/config/transport/grpc-configuration.ts +++ b/packages/client-sdk-nodejs/src/config/transport/grpc-configuration.ts @@ -9,6 +9,12 @@ export interface GrpcConfigurationProps { * more than this amount will return a ResourceExhausted error. */ maxSessionMemoryMb: number; + + /** + * The number of internal clients a cache client will create to communicate with Momento. More of them allows + * more concurrent requests, at the cost of more open connections and the latency of setting up each client. + */ + numClients?: number; } /** @@ -42,4 +48,17 @@ export interface GrpcConfiguration { * @returns {GrpcConfiguration} a new GrpcConfiguration with the specified maximum memory */ withMaxSessionMemoryMb(maxSessionMemoryMb: number): GrpcConfiguration; + + /** + * @returns {number} the number of internal clients a cache client will create to communicate with Momento. More of + * them will allow for more concurrent requests. + */ + getNumClients(): number; + + /** + * Copy constructor for overriding the number of clients to create + * @param {number} numClients the number of internal clients to create + * @returns {GrpcConfiguration} a new GrpcConfiguration with the specified number of clients + */ + withNumClients(numClients: number): GrpcConfiguration; } diff --git a/packages/client-sdk-nodejs/src/config/transport/transport-strategy.ts b/packages/client-sdk-nodejs/src/config/transport/transport-strategy.ts index 290acd344..83c254433 100644 --- a/packages/client-sdk-nodejs/src/config/transport/transport-strategy.ts +++ b/packages/client-sdk-nodejs/src/config/transport/transport-strategy.ts @@ -61,9 +61,16 @@ export interface TransportStrategyProps { export class StaticGrpcConfiguration implements GrpcConfiguration { private readonly deadlineMillis: number; private readonly maxSessionMemoryMb: number; + private readonly numClients: number; constructor(props: GrpcConfigurationProps) { this.deadlineMillis = props.deadlineMillis; this.maxSessionMemoryMb = props.maxSessionMemoryMb; + if (props.numClients !== undefined && props.numClients !== null) { + this.numClients = props.numClients; + } else { + // This is the previously hardcoded value and a safe default for most environments. + this.numClients = 6; + } } getDeadlineMillis(): number { @@ -78,6 +85,7 @@ export class StaticGrpcConfiguration implements GrpcConfiguration { return new StaticGrpcConfiguration({ deadlineMillis: deadlineMillis, maxSessionMemoryMb: this.maxSessionMemoryMb, + numClients: this.numClients, }); } @@ -85,6 +93,19 @@ export class StaticGrpcConfiguration implements GrpcConfiguration { return new StaticGrpcConfiguration({ deadlineMillis: this.deadlineMillis, maxSessionMemoryMb: maxSessionMemoryMb, + numClients: this.numClients, + }); + } + + getNumClients(): number { + return this.numClients; + } + + withNumClients(numClients: number): GrpcConfiguration { + return new StaticGrpcConfiguration({ + deadlineMillis: this.deadlineMillis, + maxSessionMemoryMb: this.maxSessionMemoryMb, + numClients: numClients, }); } } diff --git a/packages/client-sdk-nodejs/test/unit/config/configuration.test.ts b/packages/client-sdk-nodejs/test/unit/config/configuration.test.ts index feafbb079..9ffe3d563 100644 --- a/packages/client-sdk-nodejs/test/unit/config/configuration.test.ts +++ b/packages/client-sdk-nodejs/test/unit/config/configuration.test.ts @@ -16,6 +16,7 @@ describe('configuration.ts', () => { const testGrpcConfiguration = new StaticGrpcConfiguration({ deadlineMillis: 90210, maxSessionMemoryMb: 90211, + numClients: 2, }); const testMaxIdleMillis = 90212; const testTransportStrategy = new StaticTransportStrategy({ @@ -52,6 +53,7 @@ describe('configuration.ts', () => { const newGrpcConfiguration = new StaticGrpcConfiguration({ deadlineMillis: 5000, maxSessionMemoryMb: 5001, + numClients: 3, }); const newMaxIdleMillis = 5002; const newTransportStrategy = new StaticTransportStrategy({ @@ -77,6 +79,7 @@ describe('configuration.ts', () => { grpcConfiguration: new StaticGrpcConfiguration({ deadlineMillis: newClientTimeoutMillis, maxSessionMemoryMb: testGrpcConfiguration.getMaxSessionMemoryMb(), + numClients: testGrpcConfiguration.getNumClients(), }), maxIdleMillis: testMaxIdleMillis, }); diff --git a/packages/client-sdk-nodejs/test/unit/config/transport/transport-strategy.test.ts b/packages/client-sdk-nodejs/test/unit/config/transport/transport-strategy.test.ts index 6f6f380ce..bb5100a2f 100644 --- a/packages/client-sdk-nodejs/test/unit/config/transport/transport-strategy.test.ts +++ b/packages/client-sdk-nodejs/test/unit/config/transport/transport-strategy.test.ts @@ -6,9 +6,11 @@ import { describe('StaticGrpcConfiguration', () => { const testDeadlineMillis = 90210; const testMaxSessionMemoryMb = 90211; + const testNumClients = 4; const testGrpcConfiguration = new StaticGrpcConfiguration({ deadlineMillis: testDeadlineMillis, maxSessionMemoryMb: testMaxSessionMemoryMb, + numClients: testNumClients, }); it('should support overriding deadline millis', () => { @@ -34,14 +36,26 @@ describe('StaticGrpcConfiguration', () => { newMaxSessionMemory ); }); + + it('should support overriding num clients', () => { + const newNumClients = 9; + const configWithNewDeadline = + testGrpcConfiguration.withNumClients(newNumClients); + expect(configWithNewDeadline.getNumClients()).toEqual(newNumClients); + expect(configWithNewDeadline.getMaxSessionMemoryMb()).toEqual( + testMaxSessionMemoryMb + ); + }); }); describe('StaticTransportStrategy', () => { const testDeadlineMillis = 90210; const testMaxSessionMemoryMb = 90211; + const testNumClients = 5; const testGrpcConfiguration = new StaticGrpcConfiguration({ deadlineMillis: testDeadlineMillis, maxSessionMemoryMb: testMaxSessionMemoryMb, + numClients: testNumClients, }); const testMaxIdleMillis = 90212; @@ -56,6 +70,7 @@ describe('StaticTransportStrategy', () => { const newGrpcConfig = new StaticGrpcConfiguration({ deadlineMillis: newDeadlineMillis, maxSessionMemoryMb: newMaxSessionMemoryMb, + numClients: testNumClients, }); const strategyWithNewGrpcConfig = testTransportStrategy.withGrpcConfig(newGrpcConfig); @@ -82,6 +97,7 @@ describe('StaticTransportStrategy', () => { const expectedGrpcConfig = new StaticGrpcConfiguration({ deadlineMillis: newClientTimeout, maxSessionMemoryMb: testMaxSessionMemoryMb, + numClients: testNumClients, }); const strategyWithNewClientTimeout = testTransportStrategy.withClientTimeoutMillis(newClientTimeout);