Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add retry strategy to storage client #1396

Merged
merged 43 commits into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
be4836b
feat: add retry strategy to storage client
anitarua Jul 25, 2024
536d334
implement mechanism for 1s timeouts+retries up until 5s overall timeout
anitarua Jul 26, 2024
855d503
add some unit tests for storage retry strategy
anitarua Jul 31, 2024
d7c826f
remove debug logger from integration test setup
anitarua Jul 31, 2024
c410186
reset deadline only after last one has passed
anitarua Jul 31, 2024
c11c892
remove unnecessary comment
anitarua Aug 5, 2024
348e0b5
no need to create new date in if statement
anitarua Aug 5, 2024
b93b415
rename client timeout to request timeout
anitarua Aug 5, 2024
6eab380
move response data received timeout to grpc config level and add mill…
anitarua Aug 5, 2024
cbdfff3
add initial implementation of checking metadata for retries
anitarua Aug 7, 2024
a271ebe
revise retry metadata parsing
anitarua Aug 13, 2024
02b7d93
fix auth header bug and pass request metadata to interceptors
anitarua Aug 14, 2024
9c278c4
add jitter to retry interceptor
anitarua Aug 19, 2024
2dc26d0
fix: jitter created negative millis
anitarua Aug 20, 2024
b0be674
remove data client interceptor piping, restructure response data time…
anitarua Aug 20, 2024
b40157b
add factory method for storage retry strategy
anitarua Aug 20, 2024
1768881
determine which interceptor to use based on retry strategy
anitarua Aug 20, 2024
ff84ac5
client interceptor determination using retry strategy
anitarua Aug 20, 2024
135595e
remove stale comment
anitarua Aug 21, 2024
d495771
update jitter formula
anitarua Aug 21, 2024
04234c1
move jitter inside retry strategy
anitarua Aug 21, 2024
bc12f88
do not export internal interceptors
anitarua Aug 21, 2024
c4fc89f
rename to FixedTimeoutRetryStrategy
anitarua Aug 21, 2024
d76e945
fix unit test to account for jitter
anitarua Aug 21, 2024
352cf8f
simplify timeout interceptor
anitarua Aug 21, 2024
a0e7bd6
add optional property to retry strategy
anitarua Aug 21, 2024
cec9758
use expectWithMessage in failing test, interceptor should not cancel …
anitarua Aug 21, 2024
1ab9453
fix typo in jitter comment
anitarua Aug 22, 2024
e6cc11b
DRY up interceptor code, create helper function for creating new dead…
anitarua Aug 22, 2024
3cfd8c3
revert renamings
anitarua Aug 22, 2024
b68939a
chore: add basic storage example
cprice404 Aug 23, 2024
64b5f13
fix: missed an edge case in interceptor for resetting overall deadline
anitarua Aug 23, 2024
dbc84d5
no need to export addJitter
anitarua Aug 23, 2024
f75c11f
make sure to export FixedTimeoutRetryStrategy
anitarua Aug 23, 2024
8a2d1c8
we can ditch the unnecessary factory method
anitarua Aug 23, 2024
8451b10
add more info to logs, fix attempt number in retry interceptor lo
anitarua Aug 23, 2024
e01feb9
make requestMetadata a required property
anitarua Aug 23, 2024
5b69f57
fix storage examples file paths for npm target validate-examples
anitarua Aug 23, 2024
61981ec
chore: simplify header/retry interceptor construction, collapse timeo…
cprice404 Aug 26, 2024
017d87c
fix: accidentally checked in local ref for storage examples
cprice404 Aug 26, 2024
f80becc
fix: remove more local changes to basic storage example
cprice404 Aug 26, 2024
ac12515
chore: finish migrating client timeout logic into retry interceptor
cprice404 Aug 26, 2024
57a0e92
chore: remove accidental momento local in storage basic example
cprice404 Aug 27, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions examples/nodejs/storage/basic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import {
CreateStoreResponse,
CredentialProvider,
PreviewStorageClient,
StorageConfigurations,
StorageGetResponse,
StoragePutResponse,
} from '@gomomento/sdk';

async function main() {
const storageClient = new PreviewStorageClient({
configuration: StorageConfigurations.Laptop.latest(),
credentialProvider: CredentialProvider.fromEnvironmentVariable('MOMENTO_API_KEY'),
});

const storeName = 'my-store';
const createStoreResponse = await storageClient.createStore(storeName);
switch (createStoreResponse.type) {
case CreateStoreResponse.AlreadyExists:
console.log(`Store '${storeName}' already exists`);
break;
case CreateStoreResponse.Success:
console.log(`Store '${storeName}' created`);
break;
case CreateStoreResponse.Error:
throw new Error(
`An error occurred while attempting to create store '${storeName}': ${createStoreResponse.errorCode()}: ${createStoreResponse.toString()}`
);
}

const putResponse = await storageClient.putString(storeName, 'test-key', 'test-value');
switch (putResponse.type) {
case StoragePutResponse.Success:
console.log("Key 'test-key' stored successfully");
break;
case StoragePutResponse.Error:
throw new Error(
`An error occurred while attempting to store key 'test-key' in store '${storeName}': ${putResponse.errorCode()}: ${putResponse.toString()}`
);
}

const getResponse = await storageClient.get(storeName, 'test-key');
// simplified style; assume the value was found, and that it was a string
console.log(`string hit: ${getResponse.value()!.string()!}`);

// pattern-matching style; safer for production code
switch (getResponse.type) {
case StorageGetResponse.Found:
// if you know the value is a string:
console.log(`Retrieved value for key 'test-key': ${getResponse.value().string()!}`);
break;
case StorageGetResponse.NotFound:
console.log(`Key 'test-key' was not found in store '${storeName}'`);
break;
case StorageGetResponse.Error:
throw new Error(
`An error occurred while attempting to get key 'test-key' from store '${storeName}': ${getResponse.errorCode()}: ${getResponse.toString()}`
);
}
}

main().catch(e => {
console.error('An error occurred! ', e);
throw e;
});
84 changes: 46 additions & 38 deletions examples/nodejs/storage/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion examples/nodejs/storage/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
"scripts": {
"prebuild": "eslint . --ext .ts",
"build": "tsc",
"basic": "tsc && node dist/basic.js",
"doc-examples": "tsc && node dist/doc-examples-js-apis.js",
"validate-examples": "tsc && node dist/doc-examples-js-apis.js && node dist/cheat-sheet-main.js",
"validate-examples": "tsc && node dist/doc-example-files/doc-examples-js-apis.js && node dist/doc-example-files/cheat-sheet-main.js",
"test": "jest",
"lint": "eslint . --ext .ts",
"format": "eslint . --ext .ts --fix"
Expand Down
6 changes: 6 additions & 0 deletions packages/client-sdk-nodejs/src/auth-client-props.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import {CredentialProvider} from '.';
import {AuthClientConfiguration} from './config/auth-client-configuration';

export interface AuthClientProps {
/**
* controls how the client will get authentication information for connecting to the Momento service
*/
credentialProvider: CredentialProvider;

/**
* Controls the configuration settings for the auth client, such as logging configuration.
*/
configuration?: AuthClientConfiguration;

/**
* Configures whether the client should return a Momento Error object or throw an exception when an
* error occurs. By default, this is set to false, and the client will return a Momento Error object on errors. Set it
Expand Down
33 changes: 33 additions & 0 deletions packages/client-sdk-nodejs/src/config/auth-client-configuration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import {MomentoLoggerFactory} from '@gomomento/sdk-core';

export interface AuthClientConfigurationProps {
/**
* Configures logging verbosity and format
*/
loggerFactory: MomentoLoggerFactory;
}

/**
* Configuration options for Momento CacheClient.
*
* @export
* @interface Configuration
*/
export interface AuthConfiguration {
/**
* @returns {MomentoLoggerFactory} the current configuration options for logging verbosity and format
*/
getLoggerFactory(): MomentoLoggerFactory;
}

export class AuthClientConfiguration implements AuthConfiguration {
private readonly loggerFactory: MomentoLoggerFactory;

constructor(props: AuthClientConfigurationProps) {
this.loggerFactory = props.loggerFactory;
}

getLoggerFactory(): MomentoLoggerFactory {
return this.loggerFactory;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {AuthClientConfiguration} from './auth-client-configuration';
import {MomentoLoggerFactory} from '@gomomento/sdk-core';
import {DefaultMomentoLoggerFactory} from './logging/default-momento-logger';

const defaultLoggerFactory: MomentoLoggerFactory =
new DefaultMomentoLoggerFactory();

/**
* Laptop config provides defaults suitable for a medium-to-high-latency dev environment. Permissive timeouts, retries, and
* relaxed latency and throughput targets.
* @export
* @class Laptop
*/
export class Default extends AuthClientConfiguration {
/**
* Provides the latest recommended configuration for a laptop development 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
): AuthClientConfiguration {
return new AuthClientConfiguration({
loggerFactory: loggerFactory,
});
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import {StatusObject} from '@grpc/grpc-js';
import {Metadata, StatusObject} from '@grpc/grpc-js';
import {ClientMethodDefinition} from '@grpc/grpc-js/build/src/make-client';

export interface EligibleForRetryProps {
grpcStatus: StatusObject;
grpcRequest: ClientMethodDefinition<unknown, unknown>;
requestMetadata: Metadata;
}

export interface EligibilityStrategy {
Expand Down
Loading
Loading