Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
30 changes: 25 additions & 5 deletions src/storages/KeyBuilderCS.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { startsWith } from '../utils/lang';
import { KeyBuilder } from './KeyBuilder';

export class KeyBuilderCS extends KeyBuilder {
export interface MySegmentsKeyBuilder {
buildSegmentNameKey(segmentName: string): string;
extractSegmentName(builtSegmentKeyName: string): string | undefined;
extractOldSegmentKey(builtSegmentKeyName: string): string | undefined;
}

export class KeyBuilderCS extends KeyBuilder implements MySegmentsKeyBuilder {

protected readonly regexSplitsCacheKey: RegExp;
protected readonly matchingKey: string;
Expand All @@ -26,10 +32,6 @@ export class KeyBuilderCS extends KeyBuilder {
return builtSegmentKeyName.substr(prefix.length);
}

// @BREAKING: The key used to start with the matching key instead of the prefix, this was changed on version 10.17.3
buildOldSegmentNameKey(segmentName: string) {
return `${this.matchingKey}.${this.prefix}.segment.${segmentName}`;
}
// @BREAKING: The key used to start with the matching key instead of the prefix, this was changed on version 10.17.3
extractOldSegmentKey(builtSegmentKeyName: string) {
const prefix = `${this.matchingKey}.${this.prefix}.segment.`;
Expand All @@ -46,3 +48,21 @@ export class KeyBuilderCS extends KeyBuilder {
return this.regexSplitsCacheKey.test(key);
}
}

export function myLargeSegmentsKeyBuilder(prefix: string, matchingKey: string): MySegmentsKeyBuilder {
return {
buildSegmentNameKey(segmentName: string) {
return `${prefix}.${matchingKey}.largeSegment.${segmentName}`;
},

extractSegmentName(builtSegmentKeyName: string) {
const p = `${prefix}.${matchingKey}.largeSegment.`;

if (startsWith(builtSegmentKeyName, p)) return builtSegmentKeyName.substr(p.length);
},

extractOldSegmentKey() {
return undefined;
}
};
}
6 changes: 3 additions & 3 deletions src/storages/inLocalStorage/MySegmentsCacheInLocal.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { ILogger } from '../../logger/types';
import { AbstractSegmentsCacheSync } from '../AbstractSegmentsCacheSync';
import { KeyBuilderCS } from '../KeyBuilderCS';
import type { MySegmentsKeyBuilder } from '../KeyBuilderCS';
import { LOG_PREFIX, DEFINED } from './constants';

export class MySegmentsCacheInLocal extends AbstractSegmentsCacheSync {

private readonly keys: KeyBuilderCS;
private readonly keys: MySegmentsKeyBuilder;
private readonly log: ILogger;

constructor(log: ILogger, keys: KeyBuilderCS) {
constructor(log: ILogger, keys: MySegmentsKeyBuilder) {
super();
this.log = log;
this.keys = keys;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,36 @@
import { MySegmentsCacheInLocal } from '../MySegmentsCacheInLocal';
import { KeyBuilderCS } from '../../KeyBuilderCS';
import { KeyBuilderCS, myLargeSegmentsKeyBuilder } from '../../KeyBuilderCS';
import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock';

test('SEGMENT CACHE / in LocalStorage', () => {
const keys = new KeyBuilderCS('SPLITIO', 'user');
const cache = new MySegmentsCacheInLocal(loggerMock, keys);

cache.clear();

cache.addToSegment('mocked-segment');
cache.addToSegment('mocked-segment-2');

expect(cache.isInSegment('mocked-segment')).toBe(true);
expect(cache.getRegisteredSegments()).toEqual(['mocked-segment', 'mocked-segment-2']);
expect(cache.getKeysCount()).toBe(1);

cache.removeFromSegment('mocked-segment');

expect(cache.isInSegment('mocked-segment')).toBe(false);
expect(cache.getRegisteredSegments()).toEqual(['mocked-segment-2']);
expect(cache.getKeysCount()).toBe(1);

const caches = [
new MySegmentsCacheInLocal(loggerMock, new KeyBuilderCS('SPLITIO', 'user')),
new MySegmentsCacheInLocal(loggerMock, myLargeSegmentsKeyBuilder('SPLITIO', 'user'))
];

caches.forEach(cache => {
cache.clear();

cache.addToSegment('mocked-segment');
cache.addToSegment('mocked-segment-2');

expect(cache.isInSegment('mocked-segment')).toBe(true);
expect(cache.getRegisteredSegments()).toEqual(['mocked-segment', 'mocked-segment-2']);
expect(cache.getKeysCount()).toBe(1);
});

caches.forEach(cache => {
cache.removeFromSegment('mocked-segment');

expect(cache.isInSegment('mocked-segment')).toBe(false);
expect(cache.getRegisteredSegments()).toEqual(['mocked-segment-2']);
expect(cache.getKeysCount()).toBe(1);
});

expect(localStorage.getItem('SPLITIO.user.segment.mocked-segment-2')).toBe('1');
expect(localStorage.getItem('SPLITIO.user.segment.mocked-segment')).toBe(null);
expect(localStorage.getItem('SPLITIO.user.largeSegment.mocked-segment-2')).toBe('1');
expect(localStorage.getItem('SPLITIO.user.largeSegment.mocked-segment')).toBe(null);
});

// @BREAKING: REMOVE when removing this backwards compatibility.
Expand All @@ -37,7 +47,7 @@ test('SEGMENT CACHE / in LocalStorage migration for mysegments keys', () => {
cache.clear(); // cleanup before starting.

// Not adding a full suite for LS keys now, testing here
expect(oldKey1).toBe(keys.buildOldSegmentNameKey('segment1'));
expect(oldKey1).toBe(`test_nico.${keys.prefix}.segment.segment1`);
expect('segment1').toBe(keys.extractOldSegmentKey(oldKey1));

// add two segments, one we don't want to send on reset, should only be cleared, other one will be migrated.
Expand Down
12 changes: 8 additions & 4 deletions src/storages/inLocalStorage/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ImpressionCountsCacheInMemory } from '../inMemory/ImpressionCountsCache
import { EventsCacheInMemory } from '../inMemory/EventsCacheInMemory';
import { IStorageFactoryParams, IStorageSync, IStorageSyncFactory } from '../types';
import { validatePrefix } from '../KeyBuilder';
import { KeyBuilderCS } from '../KeyBuilderCS';
import { KeyBuilderCS, myLargeSegmentsKeyBuilder } from '../KeyBuilderCS';
import { isLocalStorageAvailable } from '../../utils/env/isLocalStorageAvailable';
import { SplitsCacheInLocal } from './SplitsCacheInLocal';
import { MySegmentsCacheInLocal } from './MySegmentsCacheInLocal';
Expand Down Expand Up @@ -38,15 +38,17 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn

const { settings, settings: { log, scheduler: { impressionsQueueSize, eventsQueueSize, }, sync: { impressionsMode, __splitFiltersValidation } } } = params;
const matchingKey = getMatching(settings.core.key);
const keys = new KeyBuilderCS(prefix, matchingKey as string);
const keys = new KeyBuilderCS(prefix, matchingKey);
const expirationTimestamp = Date.now() - DEFAULT_CACHE_EXPIRATION_IN_MILLIS;

const splits = new SplitsCacheInLocal(settings, keys, expirationTimestamp);
const segments = new MySegmentsCacheInLocal(log, keys);
const largeSegments = new MySegmentsCacheInLocal(log, myLargeSegmentsKeyBuilder(prefix, matchingKey));

return {
splits,
segments,
largeSegments,
impressions: new ImpressionsCacheInMemory(impressionsQueueSize),
impressionCounts: impressionsMode !== DEBUG ? new ImpressionCountsCacheInMemory() : undefined,
events: new EventsCacheInMemory(eventsQueueSize),
Expand All @@ -56,6 +58,7 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn
destroy() {
this.splits = new SplitsCacheInMemory(__splitFiltersValidation);
this.segments = new MySegmentsCacheInMemory();
this.largeSegments = new MySegmentsCacheInMemory();
this.impressions.clear();
this.impressionCounts && this.impressionCounts.clear();
this.events.clear();
Expand All @@ -64,11 +67,11 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn

// When using shared instanciation with MEMORY we reuse everything but segments (they are customer per key).
shared(matchingKey: string) {
const childKeysBuilder = new KeyBuilderCS(prefix, matchingKey);

return {
splits: this.splits,
segments: new MySegmentsCacheInLocal(log, childKeysBuilder),
segments: new MySegmentsCacheInLocal(log, new KeyBuilderCS(prefix, matchingKey)),
largeSegments: new MySegmentsCacheInLocal(log, myLargeSegmentsKeyBuilder(prefix, matchingKey)),
impressions: this.impressions,
impressionCounts: this.impressionCounts,
events: this.events,
Expand All @@ -77,6 +80,7 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn
destroy() {
this.splits = new SplitsCacheInMemory(__splitFiltersValidation);
this.segments = new MySegmentsCacheInMemory();
this.largeSegments = new MySegmentsCacheInMemory();
}
};
},
Expand Down
5 changes: 5 additions & 0 deletions src/storages/inMemory/InMemoryStorageCS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorag

const splits = new SplitsCacheInMemory(__splitFiltersValidation);
const segments = new MySegmentsCacheInMemory();
const largeSegments = new MySegmentsCacheInMemory();

const storage = {
splits,
segments,
largeSegments,
impressions: new ImpressionsCacheInMemory(impressionsQueueSize),
impressionCounts: impressionsMode !== DEBUG ? new ImpressionCountsCacheInMemory() : undefined,
events: new EventsCacheInMemory(eventsQueueSize),
Expand All @@ -32,6 +34,7 @@ export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorag
destroy() {
this.splits.clear();
this.segments.clear();
this.largeSegments.clear();
this.impressions.clear();
this.impressionCounts && this.impressionCounts.clear();
this.events.clear();
Expand All @@ -43,6 +46,7 @@ export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorag
return {
splits: this.splits,
segments: new MySegmentsCacheInMemory(),
largeSegments: new MySegmentsCacheInMemory(),
impressions: this.impressions,
impressionCounts: this.impressionCounts,
events: this.events,
Expand All @@ -52,6 +56,7 @@ export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorag
destroy() {
this.splits = new SplitsCacheInMemory(__splitFiltersValidation);
this.segments.clear();
this.largeSegments.clear();
}
};
},
Expand Down
4 changes: 3 additions & 1 deletion src/storages/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,9 @@ export interface IStorageSync extends IStorageBase<
IEventsCacheSync,
ITelemetryCacheSync,
IUniqueKeysCacheSync
> { }
> {
largeSegments?: ISegmentsCacheSync,
}

export interface IStorageAsync extends IStorageBase<
ISplitsCacheAsync,
Expand Down