Skip to content

Commit c08d5a8

Browse files
Merge pull request #342 from splitio/readiness_manager_update
Added lastUpdate property to ReadinessManager module
2 parents 39f2a92 + 45c4903 commit c08d5a8

File tree

8 files changed

+70
-60
lines changed

8 files changed

+70
-60
lines changed

CHANGES.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
1.16.1 (July 10, 2024)
1+
1.17.0 (September XX, 2024)
2+
- Added `isTimedout` and `lastUpdate` properties to IStatusInterface to keep track of the timestamp of the last SDK event, used on React and Redux SDKs.
23
- Updated some transitive dependencies for vulnerability fixes.
34

45
1.16.0 (June 13, 2024)

package-lock.json

Lines changed: 16 additions & 54 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/readiness/__tests__/readinessManager.spec.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,16 @@ import { IReadinessManager } from '../types';
44
import { SDK_READY, SDK_UPDATE, SDK_SPLITS_ARRIVED, SDK_SEGMENTS_ARRIVED, SDK_READY_FROM_CACHE, SDK_SPLITS_CACHE_LOADED, SDK_READY_TIMED_OUT } from '../constants';
55

66
const timeoutMs = 100;
7-
const statusFlagsCount = 5;
7+
const statusFlagsCount = 7;
88

99
function assertInitialStatus(readinessManager: IReadinessManager) {
1010
expect(readinessManager.isReady()).toBe(false);
1111
expect(readinessManager.isReadyFromCache()).toBe(false);
12+
expect(readinessManager.isTimedout()).toBe(false);
1213
expect(readinessManager.hasTimedout()).toBe(false);
1314
expect(readinessManager.isDestroyed()).toBe(false);
1415
expect(readinessManager.isOperational()).toBe(false);
16+
expect(readinessManager.lastUpdate()).toBe(0);
1517
}
1618

1719
test('READINESS MANAGER / Share splits but segments (without timeout enabled)', (done) => {
@@ -153,6 +155,7 @@ describe('READINESS MANAGER / Timeout ready event', () => {
153155
timeoutCounter = 0;
154156

155157
readinessManager.gate.on(SDK_READY_TIMED_OUT, () => {
158+
expect(readinessManager.isTimedout()).toBe(true);
156159
expect(readinessManager.hasTimedout()).toBe(true);
157160
if (!readinessManager.isReady()) timeoutCounter++;
158161
});
@@ -166,6 +169,8 @@ describe('READINESS MANAGER / Timeout ready event', () => {
166169
test('should be fired once', (done) => {
167170
readinessManager.gate.on(SDK_READY, () => {
168171
expect(readinessManager.isReady()).toBe(true);
172+
expect(readinessManager.isTimedout()).toBe(false);
173+
expect(readinessManager.hasTimedout()).toBe(true);
169174
expect(timeoutCounter).toBe(1);
170175
done();
171176
});
@@ -221,14 +226,24 @@ test('READINESS MANAGER / Destroy after it was ready but before timedout', () =>
221226
counter++;
222227
});
223228

229+
let lastUpdate = readinessManager.lastUpdate();
230+
expect(lastUpdate).toBe(0);
231+
224232
readinessManager.splits.emit(SDK_SPLITS_ARRIVED);
225233
readinessManager.segments.emit(SDK_SEGMENTS_ARRIVED); // ready state
226234

235+
expect(readinessManager.lastUpdate()).toBeGreaterThan(lastUpdate);
236+
lastUpdate = readinessManager.lastUpdate();
237+
227238
readinessManager.segments.emit(SDK_SEGMENTS_ARRIVED); // fires an update
239+
expect(readinessManager.lastUpdate()).toBeGreaterThan(lastUpdate);
240+
lastUpdate = readinessManager.lastUpdate();
228241

229242
expect(readinessManager.isDestroyed()).toBe(false);
230243
readinessManager.destroy(); // Destroy the gate, removing all the listeners and clearing the ready timeout.
231244
expect(readinessManager.isDestroyed()).toBe(true);
245+
expect(readinessManager.lastUpdate()).toBeGreaterThan(lastUpdate);
246+
232247
readinessManager.destroy(); // no-op
233248
readinessManager.destroy(); // no-op
234249

src/readiness/__tests__/sdkReadinessManager.spec.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ describe('SDK Readiness Manager - Event emitter', () => {
5050
});
5151

5252
expect(typeof sdkStatus.ready).toBe('function'); // The sdkStatus exposes a .ready() function.
53+
expect(typeof sdkStatus.__getStatus).toBe('function'); // The sdkStatus exposes a .__getStatus() function.
54+
expect(sdkStatus.__getStatus()).toEqual({
55+
isReady: false, isReadyFromCache: false, isTimedout: false, hasTimedout: false, isDestroyed: false, isOperational: false, lastUpdate: 0
56+
});
5357

5458
expect(typeof sdkStatus.Event).toBe('object'); // It also exposes the Event map,
5559
expect(sdkStatus.Event.SDK_READY).toBe(SDK_READY); // which contains the constants for the events, for backwards compatibility.

src/readiness/readinessManager.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,13 @@ export function readinessManagerFactory(
3939
const segments: ISegmentsEventEmitter = segmentsEventEmitterFactory(EventEmitter);
4040
const gate: IReadinessEventEmitter = new EventEmitter();
4141

42+
let lastUpdate = 0;
43+
function syncLastUpdate() {
44+
const dateNow = Date.now();
45+
// ensure lastUpdate is always increasing per event, is case Date.now() is mocked or its value is the same
46+
lastUpdate = dateNow > lastUpdate ? dateNow : lastUpdate + 1;
47+
}
48+
4249
// emit SDK_READY_FROM_CACHE
4350
let isReadyFromCache = false;
4451
if (splits.splitsCacheLoaded) isReadyFromCache = true; // ready from cache, but doesn't emit SDK_READY_FROM_CACHE
@@ -50,6 +57,7 @@ export function readinessManagerFactory(
5057
function timeout() {
5158
if (hasTimedout) return;
5259
hasTimedout = true;
60+
syncLastUpdate();
5361
gate.emit(SDK_READY_TIMED_OUT, 'Split SDK emitted SDK_READY_TIMED_OUT event.');
5462
}
5563

@@ -70,6 +78,7 @@ export function readinessManagerFactory(
7078
// Don't emit SDK_READY_FROM_CACHE if SDK_READY has been emitted
7179
if (!isReady) {
7280
try {
81+
syncLastUpdate();
7382
gate.emit(SDK_READY_FROM_CACHE);
7483
} catch (e) {
7584
// throws user callback exceptions in next tick
@@ -81,6 +90,7 @@ export function readinessManagerFactory(
8190
function checkIsReadyOrUpdate(diff: any) {
8291
if (isReady) {
8392
try {
93+
syncLastUpdate();
8494
gate.emit(SDK_UPDATE, diff);
8595
} catch (e) {
8696
// throws user callback exceptions in next tick
@@ -91,6 +101,7 @@ export function readinessManagerFactory(
91101
clearTimeout(readyTimeoutId);
92102
isReady = true;
93103
try {
104+
syncLastUpdate();
94105
gate.emit(SDK_READY);
95106
} catch (e) {
96107
// throws user callback exceptions in next tick
@@ -121,6 +132,7 @@ export function readinessManagerFactory(
121132

122133
destroy() {
123134
isDestroyed = true;
135+
syncLastUpdate();
124136

125137
segments.removeAllListeners();
126138
gate.removeAllListeners();
@@ -131,10 +143,12 @@ export function readinessManagerFactory(
131143
},
132144

133145
isReady() { return isReady; },
134-
hasTimedout() { return hasTimedout; },
135146
isReadyFromCache() { return isReadyFromCache; },
147+
isTimedout() { return hasTimedout && !isReady; },
148+
hasTimedout() { return hasTimedout; },
136149
isDestroyed() { return isDestroyed; },
137-
isOperational() { return (isReady || isReadyFromCache) && !isDestroyed; }
150+
isOperational() { return (isReady || isReadyFromCache) && !isDestroyed; },
151+
lastUpdate() { return lastUpdate; }
138152
};
139153

140154
}

src/readiness/sdkReadinessManager.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,14 +121,15 @@ export function sdkReadinessManagerFactory(
121121
return readyPromise;
122122
},
123123

124-
// Expose status for internal purposes only. Not considered part of the public API, and might be updated eventually.
125124
__getStatus() {
126125
return {
127126
isReady: readinessManager.isReady(),
128127
isReadyFromCache: readinessManager.isReadyFromCache(),
129-
isOperational: readinessManager.isOperational(),
128+
isTimedout: readinessManager.isTimedout(),
130129
hasTimedout: readinessManager.hasTimedout(),
131130
isDestroyed: readinessManager.isDestroyed(),
131+
isOperational: readinessManager.isOperational(),
132+
lastUpdate: readinessManager.lastUpdate(),
132133
};
133134
},
134135
}

src/readiness/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,11 @@ export interface IReadinessManager {
5050
/** Readiness status */
5151
isReady(): boolean,
5252
isReadyFromCache(): boolean,
53+
isTimedout(): boolean,
5354
hasTimedout(): boolean,
5455
isDestroyed(): boolean,
5556
isOperational(): boolean,
57+
lastUpdate(): number,
5658

5759
timeout(): void,
5860
setDestroyed(): void,

src/types.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,17 @@ export interface IStatusInterface extends IEventEmitter {
403403
* @returns {Promise<void>}
404404
*/
405405
ready(): Promise<void>
406+
407+
// Expose status for internal purposes only. Not considered part of the public API, and might be updated eventually.
408+
__getStatus(): {
409+
isReady: boolean;
410+
isReadyFromCache: boolean;
411+
isTimedout: boolean;
412+
hasTimedout: boolean;
413+
isDestroyed: boolean;
414+
isOperational: boolean;
415+
lastUpdate: number;
416+
}
406417
}
407418
/**
408419
* Common definitions between clients for different environments interface.

0 commit comments

Comments
 (0)