Skip to content

Commit

Permalink
Per-API Key Caches (#74)
Browse files Browse the repository at this point in the history
* cache racing start in place

* additional test

* clean up debug logs

* cleanup from self-review of PR

* clean up annotations

* test created

* independant storages

* changes from self-review of PR

* make suffix required in the persistant stores

* fix up tests

* test for local storage

* test for index

* fix test typescript error
  • Loading branch information
aarsilv authored Jun 7, 2024
1 parent 2f117f7 commit 025f80d
Show file tree
Hide file tree
Showing 28 changed files with 356 additions and 174 deletions.
21 changes: 0 additions & 21 deletions docs/js-client-sdk.chromestorageasyncstore._constructor_.md

This file was deleted.

15 changes: 0 additions & 15 deletions docs/js-client-sdk.chromestorageasyncstore.getentries.md

This file was deleted.

15 changes: 0 additions & 15 deletions docs/js-client-sdk.chromestorageasyncstore.isexpired.md

This file was deleted.

15 changes: 0 additions & 15 deletions docs/js-client-sdk.chromestorageasyncstore.isinitialized.md

This file was deleted.

28 changes: 0 additions & 28 deletions docs/js-client-sdk.chromestorageasyncstore.md

This file was deleted.

22 changes: 0 additions & 22 deletions docs/js-client-sdk.chromestorageasyncstore.setentries.md

This file was deleted.

20 changes: 20 additions & 0 deletions docs/js-client-sdk.chromestorageengine._constructor_.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [@eppo/js-client-sdk](./js-client-sdk.md) &gt; [ChromeStorageEngine](./js-client-sdk.chromestorageengine.md) &gt; [(constructor)](./js-client-sdk.chromestorageengine._constructor_.md)

## ChromeStorageEngine.(constructor)

Constructs a new instance of the `ChromeStorageEngine` class

**Signature:**

```typescript
constructor(storageArea: chrome.storage.StorageArea);
```

## Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| storageArea | chrome.storage.StorageArea | |

11 changes: 11 additions & 0 deletions docs/js-client-sdk.chromestorageengine.getcontentsjsonstring.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [@eppo/js-client-sdk](./js-client-sdk.md) &gt; [ChromeStorageEngine](./js-client-sdk.chromestorageengine.md) &gt; [getContentsJsonString](./js-client-sdk.chromestorageengine.getcontentsjsonstring.md)

## ChromeStorageEngine.getContentsJsonString property

**Signature:**

```typescript
getContentsJsonString: () => Promise<string | null>;
```
11 changes: 11 additions & 0 deletions docs/js-client-sdk.chromestorageengine.getmetajsonstring.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [@eppo/js-client-sdk](./js-client-sdk.md) &gt; [ChromeStorageEngine](./js-client-sdk.chromestorageengine.md) &gt; [getMetaJsonString](./js-client-sdk.chromestorageengine.getmetajsonstring.md)

## ChromeStorageEngine.getMetaJsonString property

**Signature:**

```typescript
getMetaJsonString: () => Promise<string | null>;
```
32 changes: 32 additions & 0 deletions docs/js-client-sdk.chromestorageengine.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [@eppo/js-client-sdk](./js-client-sdk.md) &gt; [ChromeStorageEngine](./js-client-sdk.chromestorageengine.md)

## ChromeStorageEngine class

Chrome storage implementation of a string-valued store for storing a configuration and its metadata.

This serializes the entire configuration object into a string and then stores it to a single key within the object for another single top-level key. Same with metadata about the store (e.g., when it was last updated).

**Signature:**

```typescript
export declare class ChromeStorageEngine implements IStringStorageEngine
```
**Implements:** IStringStorageEngine
## Constructors
| Constructor | Modifiers | Description |
| --- | --- | --- |
| [(constructor)(storageArea)](./js-client-sdk.chromestorageengine._constructor_.md) | | Constructs a new instance of the <code>ChromeStorageEngine</code> class |
## Properties
| Property | Modifiers | Type | Description |
| --- | --- | --- | --- |
| [getContentsJsonString](./js-client-sdk.chromestorageengine.getcontentsjsonstring.md) | | () =&gt; Promise&lt;string \| null&gt; | |
| [getMetaJsonString](./js-client-sdk.chromestorageengine.getmetajsonstring.md) | | () =&gt; Promise&lt;string \| null&gt; | |
| [setContentsJsonString](./js-client-sdk.chromestorageengine.setcontentsjsonstring.md) | | (configurationJsonString: string) =&gt; Promise&lt;void&gt; | |
| [setMetaJsonString](./js-client-sdk.chromestorageengine.setmetajsonstring.md) | | (metaJsonString: string) =&gt; Promise&lt;void&gt; | |
11 changes: 11 additions & 0 deletions docs/js-client-sdk.chromestorageengine.setcontentsjsonstring.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [@eppo/js-client-sdk](./js-client-sdk.md) &gt; [ChromeStorageEngine](./js-client-sdk.chromestorageengine.md) &gt; [setContentsJsonString](./js-client-sdk.chromestorageengine.setcontentsjsonstring.md)

## ChromeStorageEngine.setContentsJsonString property

**Signature:**

```typescript
setContentsJsonString: (configurationJsonString: string) => Promise<void>;
```
11 changes: 11 additions & 0 deletions docs/js-client-sdk.chromestorageengine.setmetajsonstring.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [@eppo/js-client-sdk](./js-client-sdk.md) &gt; [ChromeStorageEngine](./js-client-sdk.chromestorageengine.md) &gt; [setMetaJsonString](./js-client-sdk.chromestorageengine.setmetajsonstring.md)

## ChromeStorageEngine.setMetaJsonString property

**Signature:**

```typescript
setMetaJsonString: (metaJsonString: string) => Promise<void>;
```
5 changes: 5 additions & 0 deletions docs/js-client-sdk.eppojsclient.getboolassignment.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@

## EppoJSClient.getBoolAssignment() method

> Warning: This API is now obsolete.
>
> Use getBooleanAssignment instead
>
**Signature:**

```typescript
Expand Down
13 changes: 13 additions & 0 deletions docs/js-client-sdk.iclientconfig.maxcacheageseconds.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [@eppo/js-client-sdk](./js-client-sdk.md) &gt; [IClientConfig](./js-client-sdk.iclientconfig.md) &gt; [maxCacheAgeSeconds](./js-client-sdk.iclientconfig.maxcacheageseconds.md)

## IClientConfig.maxCacheAgeSeconds property

Maximum age, in seconds, previously cached values are considered valid until new values will be fetched (default: 0)

**Signature:**

```typescript
maxCacheAgeSeconds?: number;
```
3 changes: 3 additions & 0 deletions docs/js-client-sdk.iclientconfig.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export interface IClientConfig
| [apiKey](./js-client-sdk.iclientconfig.apikey.md) | | string | Eppo API key |
| [assignmentLogger](./js-client-sdk.iclientconfig.assignmentlogger.md) | | IAssignmentLogger | Pass a logging implementation to send variation assignments to your data warehouse. |
| [baseUrl?](./js-client-sdk.iclientconfig.baseurl.md) | | string | _(Optional)_ Base URL of the Eppo API. Clients should use the default setting in most cases. |
| [maxCacheAgeSeconds?](./js-client-sdk.iclientconfig.maxcacheageseconds.md) | | number | _(Optional)_ Maximum age, in seconds, previously cached values are considered valid until new values will be fetched (default: 0) |
| [numInitialRequestRetries?](./js-client-sdk.iclientconfig.numinitialrequestretries.md) | | number | _(Optional)_ Number of additional times the initial configuration request will be attempted if it fails. This is the request typically synchronously waited (via await) for completion. A small wait will be done between requests. (Default: 1) |
| [numPollRequestRetries?](./js-client-sdk.iclientconfig.numpollrequestretries.md) | | number | _(Optional)_ Number of additional times polling for updated configurations will be attempted before giving up. Polling is done after a successful initial request. Subsequent attempts are done using an exponential backoff. (Default: 7) |
| [persistentStore?](./js-client-sdk.iclientconfig.persistentstore.md) | | IAsyncStore&lt;Flag&gt; | _(Optional)_ A custom class to use for storing flag configurations. This is useful for cases where you want to use a different storage mechanism than the default storage provided by the SDK. |
Expand All @@ -27,4 +28,6 @@ export interface IClientConfig
| [requestTimeoutMs?](./js-client-sdk.iclientconfig.requesttimeoutms.md) | | number | _(Optional)_ \* Timeout in milliseconds for the HTTPS request for the experiment configuration. (Default: 5000) |
| [skipInitialRequest?](./js-client-sdk.iclientconfig.skipinitialrequest.md) | | boolean | _(Optional)_ Skip the request for new configurations during initialization. (default: false) |
| [throwOnFailedInitialization?](./js-client-sdk.iclientconfig.throwonfailedinitialization.md) | | boolean | _(Optional)_ Throw an error if unable to fetch an initial configuration during initialization. (default: true) |
| [updateOnFetch?](./js-client-sdk.iclientconfig.updateonfetch.md) | | ServingStoreUpdateStrategy | _(Optional)_ Sets how the configuration is updated after a successful fetch - always: immediately start using the new configuration - expired: immediately start using the new configuration only if the current one has expired - empty: only use the new configuration if the current one is both expired and uninitialized/empty |
| [useExpiredCache?](./js-client-sdk.iclientconfig.useexpiredcache.md) | | boolean | _(Optional)_ Whether initialization will be considered successfully complete if expired cache values are loaded. If false, initialization will always wait for a fetch if cached values are expired. (default: false) |

13 changes: 13 additions & 0 deletions docs/js-client-sdk.iclientconfig.updateonfetch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [@eppo/js-client-sdk](./js-client-sdk.md) &gt; [IClientConfig](./js-client-sdk.iclientconfig.md) &gt; [updateOnFetch](./js-client-sdk.iclientconfig.updateonfetch.md)

## IClientConfig.updateOnFetch property

Sets how the configuration is updated after a successful fetch - always: immediately start using the new configuration - expired: immediately start using the new configuration only if the current one has expired - empty: only use the new configuration if the current one is both expired and uninitialized/empty

**Signature:**

```typescript
updateOnFetch?: ServingStoreUpdateStrategy;
```
13 changes: 13 additions & 0 deletions docs/js-client-sdk.iclientconfig.useexpiredcache.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [@eppo/js-client-sdk](./js-client-sdk.md) &gt; [IClientConfig](./js-client-sdk.iclientconfig.md) &gt; [useExpiredCache](./js-client-sdk.iclientconfig.useexpiredcache.md)

## IClientConfig.useExpiredCache property

Whether initialization will be considered successfully complete if expired cache values are loaded. If false, initialization will always wait for a fetch if cached values are expired. (default: false)

**Signature:**

```typescript
useExpiredCache?: boolean;
```
2 changes: 1 addition & 1 deletion docs/js-client-sdk.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

| Class | Description |
| --- | --- |
| [ChromeStorageAsyncStore](./js-client-sdk.chromestorageasyncstore.md) | |
| [ChromeStorageEngine](./js-client-sdk.chromestorageengine.md) | <p>Chrome storage implementation of a string-valued store for storing a configuration and its metadata.</p><p>This serializes the entire configuration object into a string and then stores it to a single key within the object for another single top-level key. Same with metadata about the store (e.g., when it was last updated).</p> |
| [EppoJSClient](./js-client-sdk.eppojsclient.md) | Client for assigning experiment variations. |

## Functions
Expand Down
5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@eppo/js-client-sdk",
"version": "3.1.0",
"version": "3.1.1",
"description": "Eppo SDK for client-side JavaScript applications",
"main": "dist/index.js",
"files": [
Expand Down Expand Up @@ -57,7 +57,6 @@
"webpack-cli": "^4.10.0"
},
"dependencies": {
"@eppo/js-client-sdk-common": "3.1.0",
"md5": "^2.3.0"
"@eppo/js-client-sdk-common": "3.1.0"
}
}
24 changes: 16 additions & 8 deletions src/chrome-storage-engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,39 @@ import { IStringStorageEngine } from './string-valued.store';
* This serializes the entire configuration object into a string and then stores it to a single key
* within the object for another single top-level key.
* Same with metadata about the store (e.g., when it was last updated).
*
* Note: this behaves a bit differently than local storage as the chrome storage API gets and sets
* subsets of key-value pairs, so we have to dereference or re-specify the key.
*/
export class ChromeStorageEngine implements IStringStorageEngine {
private chromeStorageKey = 'eppo-sdk';
private readonly contentsKey;
private readonly metaKey;

public constructor(private storageArea: chrome.storage.StorageArea) {}
public constructor(private storageArea: chrome.storage.StorageArea, storageKeySuffix: string) {
const keySuffix = storageKeySuffix ? `-${storageKeySuffix}` : '';
this.contentsKey = CONFIGURATION_KEY + keySuffix;
this.metaKey = META_KEY + keySuffix;
}

public getContentsJsonString = async (): Promise<string | null> => {
const storage = await this.storageArea.get(this.chromeStorageKey);
return storage?.[CONFIGURATION_KEY] ?? null;
const storageSubset = await this.storageArea.get(this.contentsKey);
return storageSubset?.[this.contentsKey] ?? null;
};

public getMetaJsonString = async (): Promise<string | null> => {
const storage = await this.storageArea.get(this.chromeStorageKey);
return storage?.[META_KEY] ?? null;
const storageSubset = await this.storageArea.get(this.metaKey);
return storageSubset?.[this.metaKey] ?? null;
};

public setContentsJsonString = async (configurationJsonString: string): Promise<void> => {
await this.storageArea.set({
[CONFIGURATION_KEY]: configurationJsonString,
[this.contentsKey]: configurationJsonString,
});
};

public setMetaJsonString = async (metaJsonString: string): Promise<void> => {
await this.storageArea.set({
[META_KEY]: metaJsonString,
[this.metaKey]: metaJsonString,
});
};
}
Loading

0 comments on commit 025f80d

Please sign in to comment.