Skip to content

Commit

Permalink
Cache Key V3 Fix (#434)
Browse files Browse the repository at this point in the history
  • Loading branch information
sroyal-statsig authored Jan 5, 2024
1 parent 9ea8bc1 commit 57cf02e
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 12 deletions.
33 changes: 21 additions & 12 deletions src/StatsigStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ type APIInitializeDataWithDeltas = APIInitializeData & {
};
};

type APIInitializeDataWithPrefetchedUsers = APIInitializeData & {
export type APIInitializeDataWithPrefetchedUsers = APIInitializeData & {
prefetched_user_values?: Record<string, APIInitializeData>;
};

Expand All @@ -87,7 +87,7 @@ type APIInitializeDataWithDeltasWithPrefetchedUsers =
prefetched_user_values?: Record<string, APIInitializeDataWithDeltas>;
};

type UserCacheValues = APIInitializeDataWithPrefetchedUsers & {
export type UserCacheValues = APIInitializeDataWithPrefetchedUsers & {
sticky_experiments: Record<string, APIDynamicConfig | undefined>;
evaluation_time?: number;
user_hash?: string;
Expand Down Expand Up @@ -453,6 +453,20 @@ export default class StatsigStore {
await this.writeValuesToStorage(copiedValues);
}

public getDeltasMergeFunction(
mergedValues: Record<string, UserCacheValues>,
): (user: UserCacheValues, key: UserCacheKey) => UserCacheValues {
return (deltas, key) => {
const baseValues =
mergedValues[key.v3] ??
mergedValues[key.v2] ??
mergedValues[key.v1] ??
this.getDefaultUserCacheValues();

return this.mergeUserCacheValues(baseValues, deltas);
};
}

public async saveInitDeltas(
user: StatsigUser | null,
response: Record<string, unknown>,
Expand All @@ -475,12 +489,7 @@ export default class StatsigStore {
mergedValues,
requestedUserCacheKey,
user,
(deltas, key) => {
const baseValues =
mergedValues[key] ?? this.getDefaultUserCacheValues();

return this.mergeUserCacheValues(baseValues, deltas);
},
this.getDeltasMergeFunction(mergedValues),
prefetchUsers,
);
let hasBadHash = false;
Expand Down Expand Up @@ -642,12 +651,12 @@ export default class StatsigStore {
/**
* Merges the provided init configs into the provided config map, according to the provided merge function
*/
private mergeInitializeResponseIntoUserMap(
public mergeInitializeResponseIntoUserMap(
data: APIInitializeDataWithPrefetchedUsers,
configMap: Record<string, UserCacheValues | undefined>,
requestedUserCacheKey: UserCacheKey,
user: StatsigUser | null,
mergeFn: (user: UserCacheValues, key: string) => UserCacheValues,
mergeFn: (user: UserCacheValues, key: UserCacheKey) => UserCacheValues,
prefetchUsers?: Record<string, StatsigUser>,
) {
if (data.prefetched_user_values) {
Expand All @@ -656,7 +665,7 @@ export default class StatsigStore {
const prefetched = data.prefetched_user_values[key];
const values = mergeFn(
this.convertAPIDataToCacheValues(prefetched, key),
key,
{ v1: key, v2: key, v3: key },
);
if (prefetchUsers) {
const userHash = djb2HashForObject(prefetchUsers[key]);
Expand All @@ -681,7 +690,7 @@ export default class StatsigStore {

configMap[requestedUserCacheKey.v3] = mergeFn(
requestedUserValues,
requestedUserCacheKey.v3,
requestedUserCacheKey,
);
}
}
Expand Down
76 changes: 76 additions & 0 deletions src/__tests__/DeltasMerge.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import StatsigClient from '../StatsigClient';
import {
APIInitializeDataWithPrefetchedUsers,
UserCacheValues,
} from '../StatsigStore';

describe('On merge deltas', () => {
test('V2 values merge correctly', async () => {
const statsig = new StatsigClient(
'client-key',
{},
{
overrideStableID: '123',
},
);
const data: APIInitializeDataWithPrefetchedUsers = {
feature_gates: {
a_gate: {
value: true,
rule_id: '123',
name: 'a_gate',
secondary_exposures: [],
},
},
dynamic_configs: {},
layer_configs: {},
time: 2,
};
const mergedValues: Record<string, UserCacheValues> = {
v2_key: {
feature_gates: {
a_gate: {
value: false,
rule_id: 'default',
name: 'a_gate',
secondary_exposures: [],
},
b_gate: {
value: true,
rule_id: 'default',
name: 'b_gate',
secondary_exposures: [],
},
},
dynamic_configs: {},
layer_configs: {},
time: 0,
sticky_experiments: {},
},
};
statsig
.getStore()
.mergeInitializeResponseIntoUserMap(
data,
mergedValues,
{ v1: 'v1_key', v2: 'v2_key', v3: 'v3_key' },
{},
statsig.getStore().getDeltasMergeFunction(mergedValues),
);
expect(mergedValues.v3_key.feature_gates).toEqual({
a_gate: {
value: true,
rule_id: '123',
name: 'a_gate',
secondary_exposures: [],
},
b_gate: {
value: true,
rule_id: 'default',
name: 'b_gate',
secondary_exposures: [],
},
});
expect(mergedValues.v3_key.time).toEqual(2);
});
});

0 comments on commit 57cf02e

Please sign in to comment.