Skip to content

Commit 6b87017

Browse files
Merge branch 'SDKS-8407_baseline' into init_function_for_side_effects
2 parents 34c4bce + fb616ca commit 6b87017

14 files changed

+98
-193
lines changed

src/storages/AbstractSegmentsCacheSync.ts renamed to src/storages/AbstractMySegmentsCacheSync.ts

Lines changed: 13 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
/* eslint-disable @typescript-eslint/no-unused-vars */
2-
/* eslint-disable no-unused-vars */
31
import { IMySegmentsResponse } from '../dtos/types';
42
import { MySegmentsData } from '../sync/polling/types';
53
import { ISegmentsCacheSync } from './types';
@@ -8,18 +6,11 @@ import { ISegmentsCacheSync } from './types';
86
* This class provides a skeletal implementation of the ISegmentsCacheSync interface
97
* to minimize the effort required to implement this interface.
108
*/
11-
export abstract class AbstractSegmentsCacheSync implements ISegmentsCacheSync {
12-
/**
13-
* For server-side synchronizer: add `segmentKeys` list of keys to `name` segment.
14-
* For client-side synchronizer: add `name` segment to the cache. `segmentKeys` is undefined.
15-
*/
16-
abstract addToSegment(name: string, segmentKeys?: string[]): boolean
9+
export abstract class AbstractMySegmentsCacheSync implements ISegmentsCacheSync {
1710

18-
/**
19-
* For server-side synchronizer: remove `segmentKeys` list of keys from `name` segment.
20-
* For client-side synchronizer: remove `name` segment from the cache. `segmentKeys` is undefined.
21-
*/
22-
abstract removeFromSegment(name: string, segmentKeys?: string[]): boolean
11+
protected abstract addSegment(name: string): boolean
12+
protected abstract removeSegment(name: string): boolean
13+
protected abstract setChangeNumber(changeNumber?: number): boolean | void
2314

2415
/**
2516
* For server-side synchronizer: check if `key` is in `name` segment.
@@ -34,11 +25,10 @@ export abstract class AbstractSegmentsCacheSync implements ISegmentsCacheSync {
3425
this.resetSegments({});
3526
}
3627

37-
/**
38-
* For server-side synchronizer: add the given list of segments to the cache, with an empty list of keys. The segments that already exist are not modified.
39-
* For client-side synchronizer: the method is not used.
40-
*/
41-
registerSegments(names: string[]): boolean { return false; }
28+
29+
// No-op. Not used in client-side.
30+
registerSegments(): boolean { return false; }
31+
update() { return false; }
4232

4333
/**
4434
* For server-side synchronizer: get the list of segments to fetch changes.
@@ -52,31 +42,26 @@ export abstract class AbstractSegmentsCacheSync implements ISegmentsCacheSync {
5242
*/
5343
abstract getKeysCount(): number
5444

55-
/**
56-
* For server-side synchronizer: change number of `name` segment.
57-
* For client-side synchronizer: change number of mySegments.
58-
*/
59-
abstract setChangeNumber(name?: string, changeNumber?: number): boolean | void
6045
abstract getChangeNumber(name: string): number
6146

6247
/**
6348
* For server-side synchronizer: the method is not used.
6449
* For client-side synchronizer: it resets or updates the cache.
6550
*/
6651
resetSegments(segmentsData: MySegmentsData | IMySegmentsResponse): boolean {
67-
this.setChangeNumber(undefined, segmentsData.cn);
52+
this.setChangeNumber(segmentsData.cn);
6853

6954
const { added, removed } = segmentsData as MySegmentsData;
7055

7156
if (added && removed) {
7257
let isDiff = false;
7358

7459
added.forEach(segment => {
75-
isDiff = this.addToSegment(segment) || isDiff;
60+
isDiff = this.addSegment(segment) || isDiff;
7661
});
7762

7863
removed.forEach(segment => {
79-
isDiff = this.removeFromSegment(segment) || isDiff;
64+
isDiff = this.removeSegment(segment) || isDiff;
8065
});
8166

8267
return isDiff;
@@ -97,11 +82,11 @@ export abstract class AbstractSegmentsCacheSync implements ISegmentsCacheSync {
9782

9883
// Slowest path => add and/or remove segments
9984
for (let removeIndex = index; removeIndex < storedSegmentKeys.length; removeIndex++) {
100-
this.removeFromSegment(storedSegmentKeys[removeIndex]);
85+
this.removeSegment(storedSegmentKeys[removeIndex]);
10186
}
10287

10388
for (let addIndex = index; addIndex < names.length; addIndex++) {
104-
this.addToSegment(names[addIndex]);
89+
this.addSegment(names[addIndex]);
10590
}
10691

10792
return true;

src/storages/inLocalStorage/MySegmentsCacheInLocal.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { ILogger } from '../../logger/types';
22
import { isNaNNumber } from '../../utils/lang';
3-
import { AbstractSegmentsCacheSync } from '../AbstractSegmentsCacheSync';
3+
import { AbstractMySegmentsCacheSync } from '../AbstractMySegmentsCacheSync';
44
import type { MySegmentsKeyBuilder } from '../KeyBuilderCS';
55
import { LOG_PREFIX, DEFINED } from './constants';
66

7-
export class MySegmentsCacheInLocal extends AbstractSegmentsCacheSync {
7+
export class MySegmentsCacheInLocal extends AbstractMySegmentsCacheSync {
88

99
private readonly keys: MySegmentsKeyBuilder;
1010
private readonly log: ILogger;
@@ -16,7 +16,7 @@ export class MySegmentsCacheInLocal extends AbstractSegmentsCacheSync {
1616
// There is not need to flush segments cache like splits cache, since resetSegments receives the up-to-date list of active segments
1717
}
1818

19-
addToSegment(name: string): boolean {
19+
protected addSegment(name: string): boolean {
2020
const segmentKey = this.keys.buildSegmentNameKey(name);
2121

2222
try {
@@ -29,7 +29,7 @@ export class MySegmentsCacheInLocal extends AbstractSegmentsCacheSync {
2929
}
3030
}
3131

32-
removeFromSegment(name: string): boolean {
32+
protected removeSegment(name: string): boolean {
3333
const segmentKey = this.keys.buildSegmentNameKey(name);
3434

3535
try {
@@ -81,7 +81,7 @@ export class MySegmentsCacheInLocal extends AbstractSegmentsCacheSync {
8181
return 1;
8282
}
8383

84-
setChangeNumber(name?: string, changeNumber?: number) {
84+
protected setChangeNumber(changeNumber?: number) {
8585
try {
8686
if (changeNumber) localStorage.setItem(this.keys.buildTillKey(), changeNumber + '');
8787
else localStorage.removeItem(this.keys.buildTillKey());

src/storages/inLocalStorage/__tests__/MySegmentsCacheInLocal.spec.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ test('SEGMENT CACHE / in LocalStorage', () => {
2222
});
2323

2424
caches.forEach(cache => {
25-
cache.removeFromSegment('mocked-segment');
25+
// @ts-expect-error
26+
cache.resetSegments({
27+
added: [],
28+
removed: ['mocked-segment']
29+
});
2630

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

src/storages/inMemory/MySegmentsCacheInMemory.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
1-
import { AbstractSegmentsCacheSync } from '../AbstractSegmentsCacheSync';
1+
import { AbstractMySegmentsCacheSync } from '../AbstractMySegmentsCacheSync';
22

33
/**
44
* Default MySegmentsCacheInMemory implementation that stores MySegments in memory.
55
* Supported by all JS runtimes.
66
*/
7-
export class MySegmentsCacheInMemory extends AbstractSegmentsCacheSync {
7+
export class MySegmentsCacheInMemory extends AbstractMySegmentsCacheSync {
88

99
private segmentCache: Record<string, boolean> = {};
1010
private cn?: number;
1111

12-
addToSegment(name: string): boolean {
12+
protected addSegment(name: string): boolean {
1313
if (this.segmentCache[name]) return false;
1414

1515
this.segmentCache[name] = true;
1616

1717
return true;
1818
}
1919

20-
removeFromSegment(name: string): boolean {
20+
protected removeSegment(name: string): boolean {
2121
if (!this.segmentCache[name]) return false;
2222

2323
delete this.segmentCache[name];
@@ -30,7 +30,7 @@ export class MySegmentsCacheInMemory extends AbstractSegmentsCacheSync {
3030
}
3131

3232

33-
setChangeNumber(name?: string, changeNumber?: number) {
33+
protected setChangeNumber(changeNumber?: number) {
3434
this.cn = changeNumber;
3535
}
3636

src/storages/inMemory/SegmentsCacheInMemory.ts

Lines changed: 12 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,25 @@
1-
import { AbstractSegmentsCacheSync } from '../AbstractSegmentsCacheSync';
21
import { ISet, _Set } from '../../utils/lang/sets';
32
import { isIntegerNumber } from '../../utils/lang';
3+
import { ISegmentsCacheSync } from '../types';
44

55
/**
6-
* Default ISplitsCacheSync implementation that stores split definitions in memory.
7-
* Supported by all JS runtimes.
6+
* Default ISplitsCacheSync implementation for server-side that stores segments definitions in memory.
87
*/
9-
export class SegmentsCacheInMemory extends AbstractSegmentsCacheSync {
8+
export class SegmentsCacheInMemory implements ISegmentsCacheSync {
109

1110
private segmentCache: Record<string, ISet<string>> = {};
1211
private segmentChangeNumber: Record<string, number> = {};
1312

14-
addToSegment(name: string, segmentKeys: string[]): boolean {
15-
const values = this.segmentCache[name];
16-
const keySet = values ? values : new _Set<string>();
13+
update(name: string, addedKeys: string[], removedKeys: string[], changeNumber: number) {
14+
const keySet = this.segmentCache[name] || new _Set<string>();
1715

18-
segmentKeys.forEach(k => keySet.add(k));
16+
addedKeys.forEach(k => keySet.add(k));
17+
removedKeys.forEach(k => keySet.delete(k));
1918

2019
this.segmentCache[name] = keySet;
20+
this.segmentChangeNumber[name] = changeNumber;
2121

22-
return true;
23-
}
24-
25-
removeFromSegment(name: string, segmentKeys: string[]): boolean {
26-
const values = this.segmentCache[name];
27-
const keySet = values ? values : new _Set<string>();
28-
29-
segmentKeys.forEach(k => keySet.delete(k));
30-
31-
this.segmentCache[name] = keySet;
32-
33-
return true;
22+
return addedKeys.length > 0 || removedKeys.length > 0;
3423
}
3524

3625
isInSegment(name: string, key: string): boolean {
@@ -74,16 +63,13 @@ export class SegmentsCacheInMemory extends AbstractSegmentsCacheSync {
7463
}, 0);
7564
}
7665

77-
setChangeNumber(name: string, changeNumber: number) {
78-
this.segmentChangeNumber[name] = changeNumber;
79-
80-
return true;
81-
}
82-
8366
getChangeNumber(name: string) {
8467
const value = this.segmentChangeNumber[name];
8568

8669
return isIntegerNumber(value) ? value : -1;
8770
}
8871

72+
// No-op. Not used in server-side
73+
resetSegments() { return false; }
74+
8975
}

src/storages/inMemory/SplitsCacheInMemory.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { ISet, _Set } from '../../utils/lang/sets';
55

66
/**
77
* Default ISplitsCacheSync implementation that stores split definitions in memory.
8-
* Supported by all JS runtimes.
98
*/
109
export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
1110

src/storages/inMemory/__tests__/SegmentsCacheInMemory.spec.ts

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,18 @@ import { SegmentsCacheInMemory } from '../SegmentsCacheInMemory';
22

33
describe('SEGMENTS CACHE IN MEMORY', () => {
44

5-
test('isInSegment, set/getChangeNumber, add/removeFromSegment, getKeysCount', () => {
5+
test('isInSegment, getChangeNumber, update, getKeysCount', () => {
66
const cache = new SegmentsCacheInMemory();
77

8-
cache.addToSegment('mocked-segment', [
9-
'a', 'b', 'c'
10-
]);
11-
12-
cache.setChangeNumber('mocked-segment', 1);
13-
14-
cache.removeFromSegment('mocked-segment', ['d']);
8+
cache.update('mocked-segment', [ 'a', 'b', 'c'], [], 1);
9+
cache.update('mocked-segment', [], ['d'], 1);
1510

1611
expect(cache.getChangeNumber('mocked-segment') === 1).toBe(true);
1712

18-
cache.addToSegment('mocked-segment', ['d', 'e']);
13+
cache.update('mocked-segment', [ 'd', 'e'], [], 2);
14+
cache.update('mocked-segment', [], ['a', 'c'], 2);
1915

20-
cache.removeFromSegment('mocked-segment', ['a', 'c']);
21-
22-
expect(cache.getChangeNumber('mocked-segment') === 1).toBe(true);
16+
expect(cache.getChangeNumber('mocked-segment') === 2).toBe(true);
2317

2418
expect(cache.isInSegment('mocked-segment', 'a')).toBe(false);
2519
expect(cache.isInSegment('mocked-segment', 'b')).toBe(true); // b
@@ -29,7 +23,7 @@ describe('SEGMENTS CACHE IN MEMORY', () => {
2923

3024
// getKeysCount
3125
expect(cache.getKeysCount()).toBe(3);
32-
cache.addToSegment('mocked-segment-2', ['a', 'b', 'c', 'd', 'e']);
26+
cache.update('mocked-segment-2', ['a', 'b', 'c', 'd', 'e'], [], 2);
3327
expect(cache.getKeysCount()).toBe(8);
3428
cache.clear();
3529
expect(cache.getKeysCount()).toBe(0);

src/storages/inRedis/SegmentsCacheInRedis.ts

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,24 +17,21 @@ export class SegmentsCacheInRedis implements ISegmentsCacheAsync {
1717
this.keys = keys;
1818
}
1919

20-
addToSegment(name: string, segmentKeys: string[]) {
20+
/**
21+
* Update the given segment `name` with the lists of `addedKeys`, `removedKeys` and `changeNumber`.
22+
* The returned promise is resolved if the operation success, with `true` if the segment was updated (i.e., some key was added or removed),
23+
* or rejected if it fails (e.g., Redis operation fails).
24+
*/
25+
update(name: string, addedKeys: string[], removedKeys: string[], changeNumber: number) {
2126
const segmentKey = this.keys.buildSegmentNameKey(name);
2227

23-
if (segmentKeys.length) {
24-
return this.redis.sadd(segmentKey, segmentKeys).then(() => true);
25-
} else {
26-
return Promise.resolve(true);
27-
}
28-
}
29-
30-
removeFromSegment(name: string, segmentKeys: string[]) {
31-
const segmentKey = this.keys.buildSegmentNameKey(name);
32-
33-
if (segmentKeys.length) {
34-
return this.redis.srem(segmentKey, segmentKeys).then(() => true);
35-
} else {
36-
return Promise.resolve(true);
37-
}
28+
return Promise.all([
29+
addedKeys.length && this.redis.sadd(segmentKey, addedKeys),
30+
removedKeys.length && this.redis.srem(segmentKey, removedKeys),
31+
this.redis.set(this.keys.buildSegmentTillKey(name), changeNumber + '')
32+
]).then(() => {
33+
return addedKeys.length > 0 || removedKeys.length > 0;
34+
});
3835
}
3936

4037
isInSegment(name: string, key: string) {
@@ -43,12 +40,6 @@ export class SegmentsCacheInRedis implements ISegmentsCacheAsync {
4340
).then(matches => matches !== 0);
4441
}
4542

46-
setChangeNumber(name: string, changeNumber: number) {
47-
return this.redis.set(
48-
this.keys.buildSegmentTillKey(name), changeNumber + ''
49-
).then(status => status === 'OK');
50-
}
51-
5243
getChangeNumber(name: string) {
5344
return this.redis.get(this.keys.buildSegmentTillKey(name)).then((value: string | null) => {
5445
const i = parseInt(value as string, 10);

src/storages/inRedis/__tests__/SegmentsCacheInRedis.spec.ts

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,21 @@ const keys = new KeyBuilderSS(prefix, metadata);
99

1010
describe('SEGMENTS CACHE IN REDIS', () => {
1111

12-
test('isInSegment, set/getChangeNumber, add/removeFromSegment', async () => {
12+
test('isInSegment, getChangeNumber, update', async () => {
1313
const connection = new RedisAdapter(loggerMock);
1414
const cache = new SegmentsCacheInRedis(loggerMock, keys, connection);
1515

16-
await cache.addToSegment('mocked-segment', ['a', 'b', 'c']);
17-
18-
await cache.setChangeNumber('mocked-segment', 1);
19-
20-
await cache.removeFromSegment('mocked-segment', ['d']);
16+
await cache.update('mocked-segment', ['a', 'b', 'c'], ['d'], 1);
2117

2218
expect(await cache.getChangeNumber('mocked-segment') === 1).toBe(true);
2319

2420
expect(await cache.getChangeNumber('inexistent-segment')).toBe(-1); // -1 if the segment doesn't exist
2521

26-
await cache.addToSegment('mocked-segment', ['d', 'e']);
22+
await cache.update('mocked-segment', ['d', 'e'], [], 2);
2723

28-
await cache.removeFromSegment('mocked-segment', ['a', 'c']);
24+
await cache.update('mocked-segment', [], ['a', 'c'], 2);
2925

30-
expect(await cache.getChangeNumber('mocked-segment') === 1).toBe(true);
26+
expect(await cache.getChangeNumber('mocked-segment') === 2).toBe(true);
3127

3228
expect(await cache.isInSegment('mocked-segment', 'a')).toBe(false);
3329
expect(await cache.isInSegment('mocked-segment', 'b')).toBe(true);

0 commit comments

Comments
 (0)