@luvio/environments 0.99.1 → 0.99.2-240.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/es/es2018/environments.js +79 -43
- package/dist/umd/es2018/environments.js +79 -43
- package/dist/umd/es5/environments.js +151 -63
- package/package.json +4 -3
|
@@ -345,18 +345,19 @@ function emitDurableEnvironmentAdapterEvent(eventData, observers) {
|
|
|
345
345
|
|
|
346
346
|
const AdapterContextSegment = 'ADAPTER-CONTEXT';
|
|
347
347
|
const ADAPTER_CONTEXT_ID_SUFFIX = '__NAMED_CONTEXT';
|
|
348
|
-
function reviveOrCreateContext(adapterId, durableStore, durableStoreErrorHandler, onContextLoaded) {
|
|
348
|
+
async function reviveOrCreateContext(adapterId, durableStore, durableStoreErrorHandler, contextStores, pendingContextStoreKeys, onContextLoaded) {
|
|
349
349
|
// initialize empty context store
|
|
350
|
-
|
|
350
|
+
contextStores[adapterId] = create(null);
|
|
351
351
|
const context = {
|
|
352
352
|
set(key, value) {
|
|
353
|
-
|
|
353
|
+
contextStores[adapterId][key] = value;
|
|
354
354
|
durableStore.setEntries({
|
|
355
|
-
[adapterId]: { data:
|
|
355
|
+
[adapterId]: { data: contextStores[adapterId] },
|
|
356
356
|
}, AdapterContextSegment);
|
|
357
|
+
pendingContextStoreKeys.add(adapterId);
|
|
357
358
|
},
|
|
358
359
|
get(key) {
|
|
359
|
-
return
|
|
360
|
+
return contextStores[adapterId][key];
|
|
360
361
|
},
|
|
361
362
|
};
|
|
362
363
|
const contextReturn = () => {
|
|
@@ -367,17 +368,17 @@ function reviveOrCreateContext(adapterId, durableStore, durableStoreErrorHandler
|
|
|
367
368
|
}
|
|
368
369
|
return context;
|
|
369
370
|
};
|
|
370
|
-
|
|
371
|
-
|
|
371
|
+
try {
|
|
372
|
+
const entries = await durableStore.getEntries([adapterId], AdapterContextSegment);
|
|
372
373
|
if (entries !== undefined && entries[adapterId] !== undefined) {
|
|
373
|
-
|
|
374
|
+
// if durable store has a saved context then load it in the store
|
|
375
|
+
contextStores[adapterId] = entries[adapterId].data;
|
|
374
376
|
}
|
|
375
|
-
|
|
376
|
-
|
|
377
|
+
}
|
|
378
|
+
catch (error) {
|
|
377
379
|
durableStoreErrorHandler(error);
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
});
|
|
380
|
+
}
|
|
381
|
+
return contextReturn();
|
|
381
382
|
}
|
|
382
383
|
function isUnfulfilledSnapshot(cachedSnapshotResult) {
|
|
383
384
|
if (cachedSnapshotResult === undefined) {
|
|
@@ -414,6 +415,11 @@ function makeDurable(environment, { durableStore, instrumentation }) {
|
|
|
414
415
|
isRevivingTTLOverrides = undefined;
|
|
415
416
|
});
|
|
416
417
|
});
|
|
418
|
+
// When a context store is mutated we write it to L2, which causes DS on change
|
|
419
|
+
// event. If this instance of makeDurable caused that L2 write we can ignore that
|
|
420
|
+
// on change event. This Set helps us do that.
|
|
421
|
+
const pendingContextStoreKeys = new Set();
|
|
422
|
+
const contextStores = create(null);
|
|
417
423
|
//instrumentation for durable store errors
|
|
418
424
|
const durableStoreErrorHandler = handleDurableStoreRejection(instrumentation);
|
|
419
425
|
let disposed = false;
|
|
@@ -423,38 +429,68 @@ function makeDurable(environment, { durableStore, instrumentation }) {
|
|
|
423
429
|
}
|
|
424
430
|
};
|
|
425
431
|
const unsubscribe = durableStore.registerOnChangedListener((changes) => {
|
|
426
|
-
const
|
|
432
|
+
const defaultSegmentKeys = [];
|
|
433
|
+
const adapterContextSegmentKeys = [];
|
|
434
|
+
for (let i = 0, len = changes.length; i < len; i++) {
|
|
435
|
+
const change = changes[i];
|
|
427
436
|
// we only care about changes to the data which is stored in the default
|
|
428
|
-
// segment
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
let allKeys = [];
|
|
435
|
-
for (let i = 0, len = applicableChanges.length; i < len; i++) {
|
|
436
|
-
const change = applicableChanges[i];
|
|
437
|
-
allKeys.push(...change.ids);
|
|
438
|
-
}
|
|
439
|
-
const keysLength = allKeys.length;
|
|
440
|
-
if (keysLength === 0) {
|
|
441
|
-
// if no keys changed then no need to broadcast
|
|
442
|
-
return;
|
|
443
|
-
}
|
|
444
|
-
for (let i = 0; i < keysLength; i++) {
|
|
445
|
-
const key = allKeys[i];
|
|
446
|
-
const canonical = environment.storeGetCanonicalKey(key);
|
|
447
|
-
if (canonical !== key) {
|
|
448
|
-
continue;
|
|
437
|
+
// segment or the adapter context
|
|
438
|
+
if (change.segment === DefaultDurableSegment) {
|
|
439
|
+
defaultSegmentKeys.push(...change.ids);
|
|
440
|
+
}
|
|
441
|
+
else if (change.segment === AdapterContextSegment) {
|
|
442
|
+
adapterContextSegmentKeys.push(...change.ids);
|
|
449
443
|
}
|
|
450
|
-
// TODO: W-8909393 If expiration is the only thing that changed we should not evict the data... so
|
|
451
|
-
// if we stored expiration and data at different keys (or same keys in different segments)
|
|
452
|
-
// then we could know if only the expiration has changed and we wouldn't need to evict
|
|
453
|
-
// and go through an entire broadcast/revive cycle for unchanged data
|
|
454
|
-
// call base environment storeEvict so this evict is not tracked for durable deletion
|
|
455
|
-
environment.storeEvict(key);
|
|
456
444
|
}
|
|
457
|
-
|
|
445
|
+
// process adapter context changes
|
|
446
|
+
const adapterContextKeysFromDifferentInstance = [];
|
|
447
|
+
for (const key of adapterContextSegmentKeys) {
|
|
448
|
+
if (pendingContextStoreKeys.has(key)) {
|
|
449
|
+
// if this instance caused the L2 write then remove from the
|
|
450
|
+
// "pending" Set and move on
|
|
451
|
+
pendingContextStoreKeys.delete(key);
|
|
452
|
+
}
|
|
453
|
+
else {
|
|
454
|
+
// else it came from another luvio instance and we need to
|
|
455
|
+
// read from L2
|
|
456
|
+
adapterContextKeysFromDifferentInstance.push(key);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
if (adapterContextKeysFromDifferentInstance.length > 0) {
|
|
460
|
+
// change handlers are sync, so kick off L2 read of the changed
|
|
461
|
+
// segment keys but we can't await it
|
|
462
|
+
durableStore
|
|
463
|
+
.getEntries(adapterContextKeysFromDifferentInstance, AdapterContextSegment)
|
|
464
|
+
.then((entries) => {
|
|
465
|
+
if (entries !== undefined) {
|
|
466
|
+
const entryKeys = keys(entries);
|
|
467
|
+
for (let i = 0, len = entryKeys.length; i < len; i++) {
|
|
468
|
+
const entryKey = entryKeys[i];
|
|
469
|
+
const entry = entries[entryKey];
|
|
470
|
+
contextStores[entryKey] = entry.data;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
})
|
|
474
|
+
.catch(durableStoreErrorHandler);
|
|
475
|
+
}
|
|
476
|
+
// process default segment changes
|
|
477
|
+
const defaultSegmentKeysLength = defaultSegmentKeys.length;
|
|
478
|
+
if (defaultSegmentKeysLength > 0) {
|
|
479
|
+
for (let i = 0; i < defaultSegmentKeysLength; i++) {
|
|
480
|
+
const key = defaultSegmentKeys[i];
|
|
481
|
+
const canonical = environment.storeGetCanonicalKey(key);
|
|
482
|
+
if (canonical !== key) {
|
|
483
|
+
continue;
|
|
484
|
+
}
|
|
485
|
+
// TODO: W-8909393 If expiration is the only thing that changed we should not evict the data... so
|
|
486
|
+
// if we stored expiration and data at different keys (or same keys in different segments)
|
|
487
|
+
// then we could know if only the expiration has changed and we wouldn't need to evict
|
|
488
|
+
// and go through an entire broadcast/revive cycle for unchanged data
|
|
489
|
+
// call base environment storeEvict so this evict is not tracked for durable deletion
|
|
490
|
+
environment.storeEvict(key);
|
|
491
|
+
}
|
|
492
|
+
environment.storeBroadcast(rebuildSnapshot, environment.snapshotAvailable);
|
|
493
|
+
}
|
|
458
494
|
});
|
|
459
495
|
const dispose = function () {
|
|
460
496
|
validateNotDisposed();
|
|
@@ -569,7 +605,7 @@ function makeDurable(environment, { durableStore, instrumentation }) {
|
|
|
569
605
|
validateNotDisposed();
|
|
570
606
|
const { contextId, onContextLoaded } = options;
|
|
571
607
|
let context = undefined;
|
|
572
|
-
const contextAsPromise = reviveOrCreateContext(`${contextId}${ADAPTER_CONTEXT_ID_SUFFIX}`, durableStore, durableStoreErrorHandler, onContextLoaded);
|
|
608
|
+
const contextAsPromise = reviveOrCreateContext(`${contextId}${ADAPTER_CONTEXT_ID_SUFFIX}`, durableStore, durableStoreErrorHandler, contextStores, pendingContextStoreKeys, onContextLoaded);
|
|
573
609
|
return (config, requestContext) => {
|
|
574
610
|
if (context === undefined) {
|
|
575
611
|
return contextAsPromise.then((revivedContext) => {
|
|
@@ -349,18 +349,19 @@
|
|
|
349
349
|
|
|
350
350
|
const AdapterContextSegment = 'ADAPTER-CONTEXT';
|
|
351
351
|
const ADAPTER_CONTEXT_ID_SUFFIX = '__NAMED_CONTEXT';
|
|
352
|
-
function reviveOrCreateContext(adapterId, durableStore, durableStoreErrorHandler, onContextLoaded) {
|
|
352
|
+
async function reviveOrCreateContext(adapterId, durableStore, durableStoreErrorHandler, contextStores, pendingContextStoreKeys, onContextLoaded) {
|
|
353
353
|
// initialize empty context store
|
|
354
|
-
|
|
354
|
+
contextStores[adapterId] = create(null);
|
|
355
355
|
const context = {
|
|
356
356
|
set(key, value) {
|
|
357
|
-
|
|
357
|
+
contextStores[adapterId][key] = value;
|
|
358
358
|
durableStore.setEntries({
|
|
359
|
-
[adapterId]: { data:
|
|
359
|
+
[adapterId]: { data: contextStores[adapterId] },
|
|
360
360
|
}, AdapterContextSegment);
|
|
361
|
+
pendingContextStoreKeys.add(adapterId);
|
|
361
362
|
},
|
|
362
363
|
get(key) {
|
|
363
|
-
return
|
|
364
|
+
return contextStores[adapterId][key];
|
|
364
365
|
},
|
|
365
366
|
};
|
|
366
367
|
const contextReturn = () => {
|
|
@@ -371,17 +372,17 @@
|
|
|
371
372
|
}
|
|
372
373
|
return context;
|
|
373
374
|
};
|
|
374
|
-
|
|
375
|
-
|
|
375
|
+
try {
|
|
376
|
+
const entries = await durableStore.getEntries([adapterId], AdapterContextSegment);
|
|
376
377
|
if (entries !== undefined && entries[adapterId] !== undefined) {
|
|
377
|
-
|
|
378
|
+
// if durable store has a saved context then load it in the store
|
|
379
|
+
contextStores[adapterId] = entries[adapterId].data;
|
|
378
380
|
}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
+
}
|
|
382
|
+
catch (error) {
|
|
381
383
|
durableStoreErrorHandler(error);
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
});
|
|
384
|
+
}
|
|
385
|
+
return contextReturn();
|
|
385
386
|
}
|
|
386
387
|
function isUnfulfilledSnapshot(cachedSnapshotResult) {
|
|
387
388
|
if (cachedSnapshotResult === undefined) {
|
|
@@ -418,6 +419,11 @@
|
|
|
418
419
|
isRevivingTTLOverrides = undefined;
|
|
419
420
|
});
|
|
420
421
|
});
|
|
422
|
+
// When a context store is mutated we write it to L2, which causes DS on change
|
|
423
|
+
// event. If this instance of makeDurable caused that L2 write we can ignore that
|
|
424
|
+
// on change event. This Set helps us do that.
|
|
425
|
+
const pendingContextStoreKeys = new Set();
|
|
426
|
+
const contextStores = create(null);
|
|
421
427
|
//instrumentation for durable store errors
|
|
422
428
|
const durableStoreErrorHandler = handleDurableStoreRejection(instrumentation);
|
|
423
429
|
let disposed = false;
|
|
@@ -427,38 +433,68 @@
|
|
|
427
433
|
}
|
|
428
434
|
};
|
|
429
435
|
const unsubscribe = durableStore.registerOnChangedListener((changes) => {
|
|
430
|
-
const
|
|
436
|
+
const defaultSegmentKeys = [];
|
|
437
|
+
const adapterContextSegmentKeys = [];
|
|
438
|
+
for (let i = 0, len = changes.length; i < len; i++) {
|
|
439
|
+
const change = changes[i];
|
|
431
440
|
// we only care about changes to the data which is stored in the default
|
|
432
|
-
// segment
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
let allKeys = [];
|
|
439
|
-
for (let i = 0, len = applicableChanges.length; i < len; i++) {
|
|
440
|
-
const change = applicableChanges[i];
|
|
441
|
-
allKeys.push(...change.ids);
|
|
442
|
-
}
|
|
443
|
-
const keysLength = allKeys.length;
|
|
444
|
-
if (keysLength === 0) {
|
|
445
|
-
// if no keys changed then no need to broadcast
|
|
446
|
-
return;
|
|
447
|
-
}
|
|
448
|
-
for (let i = 0; i < keysLength; i++) {
|
|
449
|
-
const key = allKeys[i];
|
|
450
|
-
const canonical = environment.storeGetCanonicalKey(key);
|
|
451
|
-
if (canonical !== key) {
|
|
452
|
-
continue;
|
|
441
|
+
// segment or the adapter context
|
|
442
|
+
if (change.segment === DefaultDurableSegment) {
|
|
443
|
+
defaultSegmentKeys.push(...change.ids);
|
|
444
|
+
}
|
|
445
|
+
else if (change.segment === AdapterContextSegment) {
|
|
446
|
+
adapterContextSegmentKeys.push(...change.ids);
|
|
453
447
|
}
|
|
454
|
-
// TODO: W-8909393 If expiration is the only thing that changed we should not evict the data... so
|
|
455
|
-
// if we stored expiration and data at different keys (or same keys in different segments)
|
|
456
|
-
// then we could know if only the expiration has changed and we wouldn't need to evict
|
|
457
|
-
// and go through an entire broadcast/revive cycle for unchanged data
|
|
458
|
-
// call base environment storeEvict so this evict is not tracked for durable deletion
|
|
459
|
-
environment.storeEvict(key);
|
|
460
448
|
}
|
|
461
|
-
|
|
449
|
+
// process adapter context changes
|
|
450
|
+
const adapterContextKeysFromDifferentInstance = [];
|
|
451
|
+
for (const key of adapterContextSegmentKeys) {
|
|
452
|
+
if (pendingContextStoreKeys.has(key)) {
|
|
453
|
+
// if this instance caused the L2 write then remove from the
|
|
454
|
+
// "pending" Set and move on
|
|
455
|
+
pendingContextStoreKeys.delete(key);
|
|
456
|
+
}
|
|
457
|
+
else {
|
|
458
|
+
// else it came from another luvio instance and we need to
|
|
459
|
+
// read from L2
|
|
460
|
+
adapterContextKeysFromDifferentInstance.push(key);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
if (adapterContextKeysFromDifferentInstance.length > 0) {
|
|
464
|
+
// change handlers are sync, so kick off L2 read of the changed
|
|
465
|
+
// segment keys but we can't await it
|
|
466
|
+
durableStore
|
|
467
|
+
.getEntries(adapterContextKeysFromDifferentInstance, AdapterContextSegment)
|
|
468
|
+
.then((entries) => {
|
|
469
|
+
if (entries !== undefined) {
|
|
470
|
+
const entryKeys = keys(entries);
|
|
471
|
+
for (let i = 0, len = entryKeys.length; i < len; i++) {
|
|
472
|
+
const entryKey = entryKeys[i];
|
|
473
|
+
const entry = entries[entryKey];
|
|
474
|
+
contextStores[entryKey] = entry.data;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
})
|
|
478
|
+
.catch(durableStoreErrorHandler);
|
|
479
|
+
}
|
|
480
|
+
// process default segment changes
|
|
481
|
+
const defaultSegmentKeysLength = defaultSegmentKeys.length;
|
|
482
|
+
if (defaultSegmentKeysLength > 0) {
|
|
483
|
+
for (let i = 0; i < defaultSegmentKeysLength; i++) {
|
|
484
|
+
const key = defaultSegmentKeys[i];
|
|
485
|
+
const canonical = environment.storeGetCanonicalKey(key);
|
|
486
|
+
if (canonical !== key) {
|
|
487
|
+
continue;
|
|
488
|
+
}
|
|
489
|
+
// TODO: W-8909393 If expiration is the only thing that changed we should not evict the data... so
|
|
490
|
+
// if we stored expiration and data at different keys (or same keys in different segments)
|
|
491
|
+
// then we could know if only the expiration has changed and we wouldn't need to evict
|
|
492
|
+
// and go through an entire broadcast/revive cycle for unchanged data
|
|
493
|
+
// call base environment storeEvict so this evict is not tracked for durable deletion
|
|
494
|
+
environment.storeEvict(key);
|
|
495
|
+
}
|
|
496
|
+
environment.storeBroadcast(rebuildSnapshot, environment.snapshotAvailable);
|
|
497
|
+
}
|
|
462
498
|
});
|
|
463
499
|
const dispose = function () {
|
|
464
500
|
validateNotDisposed();
|
|
@@ -573,7 +609,7 @@
|
|
|
573
609
|
validateNotDisposed();
|
|
574
610
|
const { contextId, onContextLoaded } = options;
|
|
575
611
|
let context = undefined;
|
|
576
|
-
const contextAsPromise = reviveOrCreateContext(`${contextId}${ADAPTER_CONTEXT_ID_SUFFIX}`, durableStore, durableStoreErrorHandler, onContextLoaded);
|
|
612
|
+
const contextAsPromise = reviveOrCreateContext(`${contextId}${ADAPTER_CONTEXT_ID_SUFFIX}`, durableStore, durableStoreErrorHandler, contextStores, pendingContextStoreKeys, onContextLoaded);
|
|
577
613
|
return (config, requestContext) => {
|
|
578
614
|
if (context === undefined) {
|
|
579
615
|
return contextAsPromise.then((revivedContext) => {
|
|
@@ -44,6 +44,44 @@
|
|
|
44
44
|
return __assign.apply(this, arguments);
|
|
45
45
|
};
|
|
46
46
|
|
|
47
|
+
function __awaiter(thisArg, _arguments, P, generator) {
|
|
48
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
49
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
50
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
51
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
52
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
53
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function __generator(thisArg, body) {
|
|
58
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
59
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
60
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
61
|
+
function step(op) {
|
|
62
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
63
|
+
while (_) try {
|
|
64
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
65
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
66
|
+
switch (op[0]) {
|
|
67
|
+
case 0: case 1: t = op; break;
|
|
68
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
69
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
70
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
71
|
+
default:
|
|
72
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
73
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
74
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
75
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
76
|
+
if (t[2]) _.ops.pop();
|
|
77
|
+
_.trys.pop(); continue;
|
|
78
|
+
}
|
|
79
|
+
op = body.call(thisArg, _);
|
|
80
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
81
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
47
85
|
function __spreadArray(to, from, pack) {
|
|
48
86
|
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
49
87
|
if (ar || !(i in from)) {
|
|
@@ -392,39 +430,53 @@
|
|
|
392
430
|
|
|
393
431
|
var AdapterContextSegment = 'ADAPTER-CONTEXT';
|
|
394
432
|
var ADAPTER_CONTEXT_ID_SUFFIX = '__NAMED_CONTEXT';
|
|
395
|
-
function reviveOrCreateContext(adapterId, durableStore, durableStoreErrorHandler, onContextLoaded) {
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
433
|
+
function reviveOrCreateContext(adapterId, durableStore, durableStoreErrorHandler, contextStores, pendingContextStoreKeys, onContextLoaded) {
|
|
434
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
435
|
+
var context, contextReturn, entries, error_1;
|
|
436
|
+
return __generator(this, function (_a) {
|
|
437
|
+
switch (_a.label) {
|
|
438
|
+
case 0:
|
|
439
|
+
// initialize empty context store
|
|
440
|
+
contextStores[adapterId] = create(null);
|
|
441
|
+
context = {
|
|
442
|
+
set: function (key, value) {
|
|
443
|
+
var _a;
|
|
444
|
+
contextStores[adapterId][key] = value;
|
|
445
|
+
durableStore.setEntries((_a = {},
|
|
446
|
+
_a[adapterId] = { data: contextStores[adapterId] },
|
|
447
|
+
_a), AdapterContextSegment);
|
|
448
|
+
pendingContextStoreKeys.add(adapterId);
|
|
449
|
+
},
|
|
450
|
+
get: function (key) {
|
|
451
|
+
return contextStores[adapterId][key];
|
|
452
|
+
},
|
|
453
|
+
};
|
|
454
|
+
contextReturn = function () {
|
|
455
|
+
if (onContextLoaded !== undefined) {
|
|
456
|
+
return onContextLoaded(context).then(function () {
|
|
457
|
+
return context;
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
return context;
|
|
461
|
+
};
|
|
462
|
+
_a.label = 1;
|
|
463
|
+
case 1:
|
|
464
|
+
_a.trys.push([1, 3, , 4]);
|
|
465
|
+
return [4 /*yield*/, durableStore.getEntries([adapterId], AdapterContextSegment)];
|
|
466
|
+
case 2:
|
|
467
|
+
entries = _a.sent();
|
|
468
|
+
if (entries !== undefined && entries[adapterId] !== undefined) {
|
|
469
|
+
// if durable store has a saved context then load it in the store
|
|
470
|
+
contextStores[adapterId] = entries[adapterId].data;
|
|
471
|
+
}
|
|
472
|
+
return [3 /*break*/, 4];
|
|
473
|
+
case 3:
|
|
474
|
+
error_1 = _a.sent();
|
|
475
|
+
durableStoreErrorHandler(error_1);
|
|
476
|
+
return [3 /*break*/, 4];
|
|
477
|
+
case 4: return [2 /*return*/, contextReturn()];
|
|
478
|
+
}
|
|
479
|
+
});
|
|
428
480
|
});
|
|
429
481
|
}
|
|
430
482
|
function isUnfulfilledSnapshot(cachedSnapshotResult) {
|
|
@@ -463,6 +515,11 @@
|
|
|
463
515
|
isRevivingTTLOverrides = undefined;
|
|
464
516
|
});
|
|
465
517
|
});
|
|
518
|
+
// When a context store is mutated we write it to L2, which causes DS on change
|
|
519
|
+
// event. If this instance of makeDurable caused that L2 write we can ignore that
|
|
520
|
+
// on change event. This Set helps us do that.
|
|
521
|
+
var pendingContextStoreKeys = new Set();
|
|
522
|
+
var contextStores = create(null);
|
|
466
523
|
//instrumentation for durable store errors
|
|
467
524
|
var durableStoreErrorHandler = handleDurableStoreRejection(instrumentation);
|
|
468
525
|
var disposed = false;
|
|
@@ -472,38 +529,69 @@
|
|
|
472
529
|
}
|
|
473
530
|
};
|
|
474
531
|
var unsubscribe = durableStore.registerOnChangedListener(function (changes) {
|
|
475
|
-
var
|
|
532
|
+
var defaultSegmentKeys = [];
|
|
533
|
+
var adapterContextSegmentKeys = [];
|
|
534
|
+
for (var i = 0, len = changes.length; i < len; i++) {
|
|
535
|
+
var change = changes[i];
|
|
476
536
|
// we only care about changes to the data which is stored in the default
|
|
477
|
-
// segment
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
var allKeys = [];
|
|
484
|
-
for (var i = 0, len = applicableChanges.length; i < len; i++) {
|
|
485
|
-
var change = applicableChanges[i];
|
|
486
|
-
allKeys.push.apply(allKeys, change.ids);
|
|
487
|
-
}
|
|
488
|
-
var keysLength = allKeys.length;
|
|
489
|
-
if (keysLength === 0) {
|
|
490
|
-
// if no keys changed then no need to broadcast
|
|
491
|
-
return;
|
|
492
|
-
}
|
|
493
|
-
for (var i = 0; i < keysLength; i++) {
|
|
494
|
-
var key = allKeys[i];
|
|
495
|
-
var canonical = environment.storeGetCanonicalKey(key);
|
|
496
|
-
if (canonical !== key) {
|
|
497
|
-
continue;
|
|
537
|
+
// segment or the adapter context
|
|
538
|
+
if (change.segment === DefaultDurableSegment) {
|
|
539
|
+
defaultSegmentKeys.push.apply(defaultSegmentKeys, change.ids);
|
|
540
|
+
}
|
|
541
|
+
else if (change.segment === AdapterContextSegment) {
|
|
542
|
+
adapterContextSegmentKeys.push.apply(adapterContextSegmentKeys, change.ids);
|
|
498
543
|
}
|
|
499
|
-
// TODO: W-8909393 If expiration is the only thing that changed we should not evict the data... so
|
|
500
|
-
// if we stored expiration and data at different keys (or same keys in different segments)
|
|
501
|
-
// then we could know if only the expiration has changed and we wouldn't need to evict
|
|
502
|
-
// and go through an entire broadcast/revive cycle for unchanged data
|
|
503
|
-
// call base environment storeEvict so this evict is not tracked for durable deletion
|
|
504
|
-
environment.storeEvict(key);
|
|
505
544
|
}
|
|
506
|
-
|
|
545
|
+
// process adapter context changes
|
|
546
|
+
var adapterContextKeysFromDifferentInstance = [];
|
|
547
|
+
for (var _i = 0, adapterContextSegmentKeys_1 = adapterContextSegmentKeys; _i < adapterContextSegmentKeys_1.length; _i++) {
|
|
548
|
+
var key = adapterContextSegmentKeys_1[_i];
|
|
549
|
+
if (pendingContextStoreKeys.has(key)) {
|
|
550
|
+
// if this instance caused the L2 write then remove from the
|
|
551
|
+
// "pending" Set and move on
|
|
552
|
+
pendingContextStoreKeys.delete(key);
|
|
553
|
+
}
|
|
554
|
+
else {
|
|
555
|
+
// else it came from another luvio instance and we need to
|
|
556
|
+
// read from L2
|
|
557
|
+
adapterContextKeysFromDifferentInstance.push(key);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
if (adapterContextKeysFromDifferentInstance.length > 0) {
|
|
561
|
+
// change handlers are sync, so kick off L2 read of the changed
|
|
562
|
+
// segment keys but we can't await it
|
|
563
|
+
durableStore
|
|
564
|
+
.getEntries(adapterContextKeysFromDifferentInstance, AdapterContextSegment)
|
|
565
|
+
.then(function (entries) {
|
|
566
|
+
if (entries !== undefined) {
|
|
567
|
+
var entryKeys = keys(entries);
|
|
568
|
+
for (var i = 0, len = entryKeys.length; i < len; i++) {
|
|
569
|
+
var entryKey = entryKeys[i];
|
|
570
|
+
var entry = entries[entryKey];
|
|
571
|
+
contextStores[entryKey] = entry.data;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
})
|
|
575
|
+
.catch(durableStoreErrorHandler);
|
|
576
|
+
}
|
|
577
|
+
// process default segment changes
|
|
578
|
+
var defaultSegmentKeysLength = defaultSegmentKeys.length;
|
|
579
|
+
if (defaultSegmentKeysLength > 0) {
|
|
580
|
+
for (var i = 0; i < defaultSegmentKeysLength; i++) {
|
|
581
|
+
var key = defaultSegmentKeys[i];
|
|
582
|
+
var canonical = environment.storeGetCanonicalKey(key);
|
|
583
|
+
if (canonical !== key) {
|
|
584
|
+
continue;
|
|
585
|
+
}
|
|
586
|
+
// TODO: W-8909393 If expiration is the only thing that changed we should not evict the data... so
|
|
587
|
+
// if we stored expiration and data at different keys (or same keys in different segments)
|
|
588
|
+
// then we could know if only the expiration has changed and we wouldn't need to evict
|
|
589
|
+
// and go through an entire broadcast/revive cycle for unchanged data
|
|
590
|
+
// call base environment storeEvict so this evict is not tracked for durable deletion
|
|
591
|
+
environment.storeEvict(key);
|
|
592
|
+
}
|
|
593
|
+
environment.storeBroadcast(rebuildSnapshot, environment.snapshotAvailable);
|
|
594
|
+
}
|
|
507
595
|
});
|
|
508
596
|
var dispose = function () {
|
|
509
597
|
validateNotDisposed();
|
|
@@ -616,7 +704,7 @@
|
|
|
616
704
|
validateNotDisposed();
|
|
617
705
|
var contextId = options.contextId, onContextLoaded = options.onContextLoaded;
|
|
618
706
|
var context = undefined;
|
|
619
|
-
var contextAsPromise = reviveOrCreateContext("".concat(contextId).concat(ADAPTER_CONTEXT_ID_SUFFIX), durableStore, durableStoreErrorHandler, onContextLoaded);
|
|
707
|
+
var contextAsPromise = reviveOrCreateContext("".concat(contextId).concat(ADAPTER_CONTEXT_ID_SUFFIX), durableStore, durableStoreErrorHandler, contextStores, pendingContextStoreKeys, onContextLoaded);
|
|
620
708
|
return function (config, requestContext) {
|
|
621
709
|
if (context === undefined) {
|
|
622
710
|
return contextAsPromise.then(function (revivedContext) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@luvio/environments",
|
|
3
|
-
"version": "0.99.
|
|
3
|
+
"version": "0.99.2-240.2",
|
|
4
4
|
"description": "Luvio Environments",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"watch": "yarn build --watch"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@luvio/engine": "0.99.
|
|
26
|
+
"@luvio/engine": "0.99.2-240.2"
|
|
27
27
|
},
|
|
28
28
|
"bundlesize": [
|
|
29
29
|
{
|
|
@@ -40,5 +40,6 @@
|
|
|
40
40
|
]
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
|
-
}
|
|
43
|
+
},
|
|
44
|
+
"gitHead": "952181dfaee7b94e41b375c3e9d98223c56bdbe2"
|
|
44
45
|
}
|