@luvio/environments 0.52.0 → 0.55.0
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/DurableTTLStore.d.ts +25 -0
- package/dist/es/es2018/environments.js +227 -26
- package/dist/es/es2018/main.d.ts +2 -0
- package/dist/es/es2018/makeDurable/cachepolicy.d.ts +2 -0
- package/dist/es/es2018/makeDurable/flush.d.ts +4 -0
- package/dist/es/es2018/makeDurable/refresh.d.ts +2 -2
- package/dist/es/es2018/makeDurable/revive.d.ts +14 -3
- package/dist/es/es2018/makeDurable/utils.d.ts +2 -0
- package/dist/es/es2018/makeDurable.d.ts +5 -0
- package/dist/umd/es2018/DurableTTLStore.d.ts +25 -0
- package/dist/umd/es2018/environments.js +229 -29
- package/dist/umd/es2018/main.d.ts +2 -0
- package/dist/umd/es2018/makeDurable/cachepolicy.d.ts +2 -0
- package/dist/umd/es2018/makeDurable/flush.d.ts +4 -0
- package/dist/umd/es2018/makeDurable/refresh.d.ts +2 -2
- package/dist/umd/es2018/makeDurable/revive.d.ts +14 -3
- package/dist/umd/es2018/makeDurable/utils.d.ts +2 -0
- package/dist/umd/es2018/makeDurable.d.ts +5 -0
- package/dist/umd/es5/DurableTTLStore.d.ts +25 -0
- package/dist/umd/es5/environments.js +235 -29
- package/dist/umd/es5/main.d.ts +2 -0
- package/dist/umd/es5/makeDurable/cachepolicy.d.ts +2 -0
- package/dist/umd/es5/makeDurable/flush.d.ts +4 -0
- package/dist/umd/es5/makeDurable/refresh.d.ts +2 -2
- package/dist/umd/es5/makeDurable/revive.d.ts +14 -3
- package/dist/umd/es5/makeDurable/utils.d.ts +2 -0
- package/dist/umd/es5/makeDurable.d.ts +5 -0
- package/package.json +2 -2
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { DurableStore } from './DurableStore';
|
|
2
|
+
export declare const TTL_DURABLE_SEGMENT = "TTL_DURABLE_SEGMENT";
|
|
3
|
+
export interface DefaultDurableTTLOverride extends DurableTTLOverride {
|
|
4
|
+
namespace: 'TTL_DEFAULT_KEY';
|
|
5
|
+
representationName: 'TTL_DEFAULT_KEY';
|
|
6
|
+
}
|
|
7
|
+
export interface DurableTTLOverride {
|
|
8
|
+
namespace: string;
|
|
9
|
+
representationName: string;
|
|
10
|
+
ttl: number;
|
|
11
|
+
}
|
|
12
|
+
export interface TTLOverridesMap {
|
|
13
|
+
defaultTTL: DefaultDurableTTLOverride | undefined;
|
|
14
|
+
overrides: DurableTTLOverride[];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Class to set and get the TTL override values in the Durable Store
|
|
18
|
+
*/
|
|
19
|
+
export declare class DurableTTLStore {
|
|
20
|
+
private durableStore;
|
|
21
|
+
constructor(durableStore: DurableStore);
|
|
22
|
+
setDefaultDurableTTLOverrides(ttl: number): Promise<void>;
|
|
23
|
+
setDurableTTLOverride(namespace: string, representationName: string, ttl: number): Promise<void>;
|
|
24
|
+
getDurableTTLOverrides(): Promise<TTLOverridesMap>;
|
|
25
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { StoreResolveResultState, buildStaleWhileRevalidateImplementation as buildStaleWhileRevalidateImplementation$1 } from '@luvio/engine';
|
|
2
|
+
|
|
1
3
|
function isDeprecatedDurableStoreEntry(durableRecord) {
|
|
2
4
|
if (durableRecord.expiration !== undefined) {
|
|
3
5
|
return true;
|
|
@@ -16,6 +18,50 @@ const { keys, create, assign, freeze } = Object;
|
|
|
16
18
|
const { hasOwnProperty } = Object.prototype;
|
|
17
19
|
const { isArray } = Array;
|
|
18
20
|
|
|
21
|
+
// cache policy implementations
|
|
22
|
+
function buildTTLStrategy(staleDuration = 0) {
|
|
23
|
+
return (timestamp, metadata, valueIsError) => {
|
|
24
|
+
if (metadata !== undefined) {
|
|
25
|
+
const { expirationTimestamp } = metadata;
|
|
26
|
+
if (timestamp > expirationTimestamp) {
|
|
27
|
+
if (timestamp <= expirationTimestamp + staleDuration && valueIsError !== true) {
|
|
28
|
+
return StoreResolveResultState.Stale;
|
|
29
|
+
}
|
|
30
|
+
return StoreResolveResultState.NotPresent;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (valueIsError === true) {
|
|
34
|
+
return StoreResolveResultState.Error;
|
|
35
|
+
}
|
|
36
|
+
return StoreResolveResultState.Found;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function appendTTLStrategy(storeLookup, ttlStrategy) {
|
|
40
|
+
return (sel, refresh) => storeLookup(sel, refresh, ttlStrategy);
|
|
41
|
+
}
|
|
42
|
+
function buildStaleWhileRevalidateImplementation(validateNotDisposed, resolveSnapshot, staleDuration) {
|
|
43
|
+
return function (args) {
|
|
44
|
+
validateNotDisposed();
|
|
45
|
+
const { buildInMemorySnapshot, buildNetworkSnapshot, buildSnapshotContext, dispatchResourceRequest, storeLookup, } = args;
|
|
46
|
+
const snapshot = buildInMemorySnapshot(buildSnapshotContext, appendTTLStrategy(storeLookup, buildTTLStrategy(staleDuration)));
|
|
47
|
+
if (snapshot !== undefined) {
|
|
48
|
+
if (snapshot.state === 'Fulfilled' || snapshot.state === 'Error') {
|
|
49
|
+
return snapshot;
|
|
50
|
+
}
|
|
51
|
+
if (snapshot.state === 'Stale') {
|
|
52
|
+
// offline environment is already doing this; uncomment once we get rid of offline environment
|
|
53
|
+
// buildNetworkSnapshot(buildSnapshotContext, dispatchResourceRequest);
|
|
54
|
+
return snapshot;
|
|
55
|
+
}
|
|
56
|
+
return resolveSnapshot(snapshot, {
|
|
57
|
+
resolve: () => buildNetworkSnapshot(buildSnapshotContext, dispatchResourceRequest),
|
|
58
|
+
config: undefined,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
return buildNetworkSnapshot(buildSnapshotContext, dispatchResourceRequest);
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
19
65
|
//Durable store error instrumentation key
|
|
20
66
|
const DURABLE_STORE_ERROR = 'durable-store-error';
|
|
21
67
|
/**
|
|
@@ -213,8 +259,7 @@ function buildDurableStoreAwareRefresh(refresh, snapshot, environment, pendingWr
|
|
|
213
259
|
}
|
|
214
260
|
promiseResolve(resolvedSnapshot);
|
|
215
261
|
});
|
|
216
|
-
|
|
217
|
-
resolve(config)
|
|
262
|
+
refresh()
|
|
218
263
|
.then((refreshedSnapshot) => {
|
|
219
264
|
// if error, just return it, because there won't be a L2 flush/broadcast/rebuild
|
|
220
265
|
// (non-cached errors don't broadcast, and subscribing to an error snapshot
|
|
@@ -256,11 +301,23 @@ function buildDurableStoreAwareRefresh(refresh, snapshot, environment, pendingWr
|
|
|
256
301
|
|
|
257
302
|
function isStoreEntryError(storeRecord) {
|
|
258
303
|
return storeRecord.__type === 'error';
|
|
259
|
-
}
|
|
304
|
+
}
|
|
305
|
+
|
|
260
306
|
function isStoreEntryExpiredAndError(storeRecord, expirationTimestamp, now) {
|
|
261
307
|
return isStoreEntryError(storeRecord) && expirationTimestamp < now;
|
|
262
308
|
}
|
|
263
|
-
|
|
309
|
+
/**
|
|
310
|
+
* Takes a set of entries from DurableStore and publishes them via the passed in funcs.
|
|
311
|
+
* This respects expiration and checks for valid DurableStore data shapes. This should
|
|
312
|
+
* be used over manually parsing DurableStoreEntries
|
|
313
|
+
*
|
|
314
|
+
* @param durableRecords The DurableStoreEntries to parse
|
|
315
|
+
* @param publish A function to call with the data of each DurableStoreEntry
|
|
316
|
+
* @param publishMetadata A function to call with the metadata of each DurableStoreEntry
|
|
317
|
+
* @param pendingWriter the PendingWriter (this is going away soon)
|
|
318
|
+
* @returns
|
|
319
|
+
*/
|
|
320
|
+
function publishDurableStoreEntries(durableRecords, publish, publishMetadata, pendingWriter) {
|
|
264
321
|
const revivedKeys = create(null);
|
|
265
322
|
let hadUnexpectedShape = false;
|
|
266
323
|
if (durableRecords === undefined) {
|
|
@@ -288,13 +345,13 @@ function reviveDurableEntriesToStore(durableRecords, environment, pendingWriter)
|
|
|
288
345
|
}
|
|
289
346
|
if (metadata !== undefined) {
|
|
290
347
|
const { expirationTimestamp, staleTimestamp } = metadata;
|
|
291
|
-
if (expirationTimestamp === undefined
|
|
348
|
+
if (expirationTimestamp === undefined) {
|
|
292
349
|
// if unexpected expiration data skip reviving
|
|
293
350
|
hadUnexpectedShape = true;
|
|
294
351
|
continue;
|
|
295
352
|
}
|
|
296
353
|
// if past stale TTL then don't revive
|
|
297
|
-
if (staleTimestamp < now) {
|
|
354
|
+
if (staleTimestamp !== undefined && staleTimestamp < now) {
|
|
298
355
|
continue;
|
|
299
356
|
}
|
|
300
357
|
// We don't want to revive a cached value if it's an error and it's
|
|
@@ -309,8 +366,7 @@ function reviveDurableEntriesToStore(durableRecords, environment, pendingWriter)
|
|
|
309
366
|
if (isStoreEntryExpiredAndError(data, expirationTimestamp, now)) {
|
|
310
367
|
continue;
|
|
311
368
|
}
|
|
312
|
-
|
|
313
|
-
environment.publishStoreMetadata(key, metadata);
|
|
369
|
+
publishMetadata(key, metadata);
|
|
314
370
|
}
|
|
315
371
|
if (isStoreEntryError(data)) {
|
|
316
372
|
// freeze errors on way into L1
|
|
@@ -320,8 +376,7 @@ function reviveDurableEntriesToStore(durableRecords, environment, pendingWriter)
|
|
|
320
376
|
// Note: this won't affect ingest (which ingests to L1 temporarily then
|
|
321
377
|
// flushes to L2) because that path is synchronous and atomic.
|
|
322
378
|
pendingWriter.removePendingWrite(key);
|
|
323
|
-
|
|
324
|
-
environment.storePublish(key, data);
|
|
379
|
+
publish(key, data);
|
|
325
380
|
revivedKeys[key] = true;
|
|
326
381
|
}
|
|
327
382
|
return { revivedKeys, hadUnexpectedShape };
|
|
@@ -333,13 +388,13 @@ const snapshotRefreshMap = new WeakSet();
|
|
|
333
388
|
* will refresh the snapshot from network, and then run the results from network
|
|
334
389
|
* through L2 ingestion, returning the subsequent revived snapshot.
|
|
335
390
|
*/
|
|
336
|
-
function reviveSnapshot(
|
|
391
|
+
function reviveSnapshot(baseEnvironment, durableStore, pendingWriter, unavailableSnapshot, refresh, durableStoreErrorHandler, baseSnapshot) {
|
|
337
392
|
const { recordId, select, seenRecords, state } = unavailableSnapshot;
|
|
338
393
|
// on rebuilds return the network snapshot on L2 miss
|
|
339
394
|
const refreshSnapshot = baseSnapshot !== undefined
|
|
340
395
|
? () => {
|
|
341
396
|
if (baseSnapshot.state !== 'Pending') {
|
|
342
|
-
return refresh
|
|
397
|
+
return refresh();
|
|
343
398
|
}
|
|
344
399
|
// A Pending snapshot could be in the store awaiting either a result
|
|
345
400
|
// from the network or L2. If another ingest brings in an overlapping key
|
|
@@ -352,17 +407,17 @@ function reviveSnapshot(environment, durableStore, pendingWriter, unavailableSna
|
|
|
352
407
|
}
|
|
353
408
|
// track the refresh operation to ensure no other refreshes kick off while in flight
|
|
354
409
|
snapshotRefreshMap.add(baseSnapshot);
|
|
355
|
-
return refresh
|
|
410
|
+
return refresh().finally(() => {
|
|
356
411
|
snapshotRefreshMap.delete(baseSnapshot);
|
|
357
412
|
});
|
|
358
413
|
}
|
|
359
|
-
: buildDurableStoreAwareRefresh(refresh, unavailableSnapshot,
|
|
414
|
+
: buildDurableStoreAwareRefresh(refresh, unavailableSnapshot, baseEnvironment, pendingWriter);
|
|
360
415
|
// L2 can only revive Unfulfilled snapshots that have a selector since they have the
|
|
361
416
|
// info needed to revive (like missingLinks) and rebuild. Otherwise refresh.
|
|
362
417
|
if (state !== 'Unfulfilled' || select === undefined) {
|
|
363
418
|
return refreshSnapshot();
|
|
364
419
|
}
|
|
365
|
-
// in case L1
|
|
420
|
+
// in case L1 store changes/deallocs a record while we are doing the async read
|
|
366
421
|
// we attempt to read all keys from L2 - so combine recordId with any seenRecords
|
|
367
422
|
const keysToReviveSet = assign({ [recordId]: true }, seenRecords);
|
|
368
423
|
const keysToRevive = keys(keysToReviveSet);
|
|
@@ -371,9 +426,9 @@ function reviveSnapshot(environment, durableStore, pendingWriter, unavailableSna
|
|
|
371
426
|
const paginationKey = paginationKeys[i];
|
|
372
427
|
keysToRevive.push(paginationKey);
|
|
373
428
|
}
|
|
374
|
-
const canonicalKeys = keysToRevive.map((x) =>
|
|
429
|
+
const canonicalKeys = keysToRevive.map((x) => baseEnvironment.storeGetCanonicalKey(x));
|
|
375
430
|
return durableStore.getEntries(canonicalKeys, DefaultDurableSegment).then((durableRecords) => {
|
|
376
|
-
const { revivedKeys, hadUnexpectedShape } =
|
|
431
|
+
const { revivedKeys, hadUnexpectedShape } = publishDurableStoreEntries(durableRecords, baseEnvironment.storePublish.bind(baseEnvironment), baseEnvironment.publishStoreMetadata.bind(baseEnvironment), pendingWriter);
|
|
377
432
|
// if the data coming back from DS had an unexpected shape then refresh
|
|
378
433
|
// data from network
|
|
379
434
|
if (hadUnexpectedShape === true) {
|
|
@@ -386,8 +441,8 @@ function reviveSnapshot(environment, durableStore, pendingWriter, unavailableSna
|
|
|
386
441
|
// attempt to lookup (or rebuild if baseSnapshot is provided) the snapshot
|
|
387
442
|
// now that we have revived the missingLinks
|
|
388
443
|
const snapshot = baseSnapshot === undefined
|
|
389
|
-
?
|
|
390
|
-
:
|
|
444
|
+
? baseEnvironment.storeLookup(unavailableSnapshot.select, baseEnvironment.createSnapshot, unavailableSnapshot.refresh)
|
|
445
|
+
: baseEnvironment.rebuildSnapshot(baseSnapshot, baseEnvironment.getStoreRecords(), baseEnvironment.getStoreMetadataMap(), baseEnvironment.getStoreRedirectKeys(), () => { });
|
|
391
446
|
// if snapshot is pending then some other in-flight refresh will broadcast
|
|
392
447
|
// later
|
|
393
448
|
if (snapshot.state === 'Pending') {
|
|
@@ -407,13 +462,13 @@ function reviveSnapshot(environment, durableStore, pendingWriter, unavailableSna
|
|
|
407
462
|
for (let i = 0, len = newKeys.length; i < len; i++) {
|
|
408
463
|
const newSnapshotSeenKey = newKeys[i];
|
|
409
464
|
if (alreadyRequestedOrRevivedSet[newSnapshotSeenKey] !== true) {
|
|
410
|
-
return reviveSnapshot(
|
|
465
|
+
return reviveSnapshot(baseEnvironment, durableStore, pendingWriter, snapshot, refresh, durableStoreErrorHandler, baseSnapshot);
|
|
411
466
|
}
|
|
412
467
|
}
|
|
413
468
|
// otherwise it's an L2 cache miss, so refresh
|
|
414
469
|
return refreshSnapshot();
|
|
415
470
|
}
|
|
416
|
-
if (
|
|
471
|
+
if (baseEnvironment.snapshotAvailable(snapshot)) {
|
|
417
472
|
return snapshot;
|
|
418
473
|
}
|
|
419
474
|
// all other scenarios we just refresh
|
|
@@ -425,6 +480,83 @@ function reviveSnapshot(environment, durableStore, pendingWriter, unavailableSna
|
|
|
425
480
|
});
|
|
426
481
|
}
|
|
427
482
|
|
|
483
|
+
const TTL_DURABLE_SEGMENT = 'TTL_DURABLE_SEGMENT';
|
|
484
|
+
const TTL_DEFAULT_KEY = 'TTL_DEFAULT_KEY';
|
|
485
|
+
function buildDurableTTLOverrideStoreKey(namespace, representationName) {
|
|
486
|
+
return `${namespace}::${representationName}`;
|
|
487
|
+
}
|
|
488
|
+
function isEntryDurableTTLOverride(entry) {
|
|
489
|
+
if (typeof entry === 'object' && entry !== undefined && entry !== null) {
|
|
490
|
+
const data = entry.data;
|
|
491
|
+
if (data !== undefined) {
|
|
492
|
+
return (data.namespace !== undefined &&
|
|
493
|
+
data.representationName !== undefined &&
|
|
494
|
+
data.ttl !== undefined);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
return false;
|
|
498
|
+
}
|
|
499
|
+
function isDefaultDurableTTLOverride(override) {
|
|
500
|
+
return (override.namespace === TTL_DEFAULT_KEY && override.representationName === TTL_DEFAULT_KEY);
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Class to set and get the TTL override values in the Durable Store
|
|
504
|
+
*/
|
|
505
|
+
class DurableTTLStore {
|
|
506
|
+
constructor(durableStore) {
|
|
507
|
+
this.durableStore = durableStore;
|
|
508
|
+
}
|
|
509
|
+
setDefaultDurableTTLOverrides(ttl) {
|
|
510
|
+
return this.durableStore.setEntries({
|
|
511
|
+
[buildDurableTTLOverrideStoreKey(TTL_DEFAULT_KEY, TTL_DEFAULT_KEY)]: {
|
|
512
|
+
data: {
|
|
513
|
+
namespace: TTL_DEFAULT_KEY,
|
|
514
|
+
representationName: TTL_DEFAULT_KEY,
|
|
515
|
+
ttl,
|
|
516
|
+
},
|
|
517
|
+
},
|
|
518
|
+
}, TTL_DURABLE_SEGMENT);
|
|
519
|
+
}
|
|
520
|
+
setDurableTTLOverride(namespace, representationName, ttl) {
|
|
521
|
+
return this.durableStore.setEntries({
|
|
522
|
+
[buildDurableTTLOverrideStoreKey(namespace, representationName)]: {
|
|
523
|
+
data: { namespace, representationName, ttl },
|
|
524
|
+
},
|
|
525
|
+
}, TTL_DURABLE_SEGMENT);
|
|
526
|
+
}
|
|
527
|
+
getDurableTTLOverrides() {
|
|
528
|
+
return this.durableStore
|
|
529
|
+
.getAllEntries(TTL_DURABLE_SEGMENT)
|
|
530
|
+
.then((entries) => {
|
|
531
|
+
const overrides = [];
|
|
532
|
+
let defaultTTL = undefined;
|
|
533
|
+
if (entries === undefined) {
|
|
534
|
+
return {
|
|
535
|
+
defaultTTL,
|
|
536
|
+
overrides,
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
const keys$1 = keys(entries);
|
|
540
|
+
for (let i = 0, len = keys$1.length; i < len; i++) {
|
|
541
|
+
const key = keys$1[i];
|
|
542
|
+
const entry = entries[key];
|
|
543
|
+
if (entry !== undefined && isEntryDurableTTLOverride(entry)) {
|
|
544
|
+
if (isDefaultDurableTTLOverride(entry.data)) {
|
|
545
|
+
defaultTTL = entry.data;
|
|
546
|
+
}
|
|
547
|
+
else {
|
|
548
|
+
overrides.push(entry.data);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
return {
|
|
553
|
+
defaultTTL,
|
|
554
|
+
overrides,
|
|
555
|
+
};
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
428
560
|
const AdapterContextSegment = 'ADAPTER-CONTEXT';
|
|
429
561
|
const ADAPTER_CONTEXT_ID_SUFFIX = '__NAMED_CONTEXT';
|
|
430
562
|
function reviveOrCreateContext(adapterId, durableStore, durableStoreErrorHandler, onContextLoaded) {
|
|
@@ -470,6 +602,7 @@ function reviveOrCreateContext(adapterId, durableStore, durableStoreErrorHandler
|
|
|
470
602
|
* @param instrumentation An instrumentation function implementation
|
|
471
603
|
*/
|
|
472
604
|
function makeDurable(environment, { durableStore, instrumentation, pendingWriter = buildPendingWriter() }) {
|
|
605
|
+
const durableTTLStore = new DurableTTLStore(durableStore);
|
|
473
606
|
//instrumentation for durable store errors
|
|
474
607
|
const durableStoreErrorHandler = handleDurableStoreRejection(instrumentation);
|
|
475
608
|
let disposed = false;
|
|
@@ -530,6 +663,19 @@ function makeDurable(environment, { durableStore, instrumentation, pendingWriter
|
|
|
530
663
|
pendingWriter.addPendingWrite(recordId);
|
|
531
664
|
environment.publishStoreMetadata(recordId, storeMetadata);
|
|
532
665
|
};
|
|
666
|
+
const storeIngestError = function (key, errorSnapshot, storeMetadataParams, _storeOverride) {
|
|
667
|
+
validateNotDisposed();
|
|
668
|
+
// TODO [W-9680660]: this will get cleaned up in next PR to use staging store
|
|
669
|
+
const store = {
|
|
670
|
+
publish: function (key, data) {
|
|
671
|
+
storePublish(key, data);
|
|
672
|
+
},
|
|
673
|
+
publishMetadata: function (key, storeMetadata) {
|
|
674
|
+
publishStoreMetadata(key, storeMetadata);
|
|
675
|
+
},
|
|
676
|
+
};
|
|
677
|
+
environment.storeIngestError(key, errorSnapshot, storeMetadataParams, store);
|
|
678
|
+
};
|
|
533
679
|
const storeBroadcast = function (_rebuildSnapshot, _snapshotDataAvailable) {
|
|
534
680
|
// don't await the DS write - DS implementation will take care of R/W
|
|
535
681
|
// synchronization
|
|
@@ -562,7 +708,9 @@ function makeDurable(environment, { durableStore, instrumentation, pendingWriter
|
|
|
562
708
|
if (snapshot.state === 'Pending') {
|
|
563
709
|
return environment.resolvePendingSnapshot(snapshot);
|
|
564
710
|
}
|
|
565
|
-
|
|
711
|
+
const { resolve, config } = refresh;
|
|
712
|
+
const refreshFunc = () => resolve(config);
|
|
713
|
+
return reviveSnapshot(environment, durableStore, pendingWriter, snapshot, refreshFunc, durableStoreErrorHandler);
|
|
566
714
|
};
|
|
567
715
|
const rebuildSnapshot = function (snapshot, records, storeMetadataMap, redirects, onAsyncRebuild) {
|
|
568
716
|
validateNotDisposed();
|
|
@@ -578,9 +726,11 @@ function makeDurable(environment, { durableStore, instrumentation, pendingWriter
|
|
|
578
726
|
if (refresh === undefined) {
|
|
579
727
|
return rebuilt;
|
|
580
728
|
}
|
|
729
|
+
const { resolve, config } = refresh;
|
|
730
|
+
const refreshFunc = () => resolve(config);
|
|
581
731
|
// Do an L2 revive (which will refresh from network on L2 miss) and if
|
|
582
732
|
// it results in an available snapshot emit to subscriber using the callback.
|
|
583
|
-
reviveSnapshot(environment, durableStore, pendingWriter, rebuilt,
|
|
733
|
+
reviveSnapshot(environment, durableStore, pendingWriter, rebuilt, refreshFunc, durableStoreErrorHandler, snapshot).then((revivedSnapshot) => {
|
|
584
734
|
if (environment.snapshotAvailable(revivedSnapshot) === true) {
|
|
585
735
|
onAsyncRebuild(revivedSnapshot);
|
|
586
736
|
}
|
|
@@ -626,19 +776,67 @@ function makeDurable(environment, { durableStore, instrumentation, pendingWriter
|
|
|
626
776
|
return result;
|
|
627
777
|
});
|
|
628
778
|
};
|
|
779
|
+
const storeSetTTLOverride = function (namespace, representationName, ttl) {
|
|
780
|
+
validateNotDisposed();
|
|
781
|
+
durableTTLStore.setDurableTTLOverride(namespace, representationName, ttl);
|
|
782
|
+
environment.storeSetTTLOverride(namespace, representationName, ttl);
|
|
783
|
+
};
|
|
784
|
+
const storeSetDefaultTTLOverride = function (ttl) {
|
|
785
|
+
validateNotDisposed();
|
|
786
|
+
durableTTLStore.setDefaultDurableTTLOverrides(ttl);
|
|
787
|
+
environment.storeSetDefaultTTLOverride(ttl);
|
|
788
|
+
};
|
|
789
|
+
const getDurableTTLOverrides = function () {
|
|
790
|
+
validateNotDisposed();
|
|
791
|
+
return durableTTLStore.getDurableTTLOverrides();
|
|
792
|
+
};
|
|
793
|
+
const defaultCachePolicy = buildStaleWhileRevalidateImplementation(validateNotDisposed, resolveSnapshot, Number.MAX_SAFE_INTEGER);
|
|
794
|
+
function resolveCachePolicy(cachePolicy, defaultCachePolicy) {
|
|
795
|
+
if (cachePolicy === undefined) {
|
|
796
|
+
return defaultCachePolicy;
|
|
797
|
+
}
|
|
798
|
+
switch (cachePolicy.type) {
|
|
799
|
+
case 'stale-while-revalidate':
|
|
800
|
+
return buildStaleWhileRevalidateImplementation(validateNotDisposed, resolveSnapshot, cachePolicy.staleDuration);
|
|
801
|
+
default: {
|
|
802
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
803
|
+
throw new Error(`unrecognized cache policy: ${JSON.stringify(cachePolicy)}`);
|
|
804
|
+
}
|
|
805
|
+
return defaultCachePolicy;
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
const applyCachePolicy = function (cachePolicy, buildSnapshotContext, buildInMemorySnapshot, buildNetworkSnapshot) {
|
|
810
|
+
validateNotDisposed();
|
|
811
|
+
const cachePolicyImpl = resolveCachePolicy(cachePolicy, defaultCachePolicy);
|
|
812
|
+
const storeLookup = (sel, refresh, ttlStrategy) => environment.storeLookup(sel, environment.createSnapshot, refresh, ttlStrategy);
|
|
813
|
+
return cachePolicyImpl({
|
|
814
|
+
buildInMemorySnapshot,
|
|
815
|
+
buildNetworkSnapshot,
|
|
816
|
+
buildSnapshotContext,
|
|
817
|
+
storeLookup,
|
|
818
|
+
dispatchResourceRequest,
|
|
819
|
+
});
|
|
820
|
+
};
|
|
629
821
|
return create(environment, {
|
|
630
822
|
publishStoreMetadata: { value: publishStoreMetadata },
|
|
823
|
+
storeIngestError: { value: storeIngestError },
|
|
631
824
|
storeBroadcast: { value: storeBroadcast },
|
|
632
825
|
storeReset: { value: storeReset },
|
|
633
826
|
resolveSnapshot: { value: resolveSnapshot },
|
|
634
827
|
storeEvict: { value: storeEvict },
|
|
635
828
|
withContext: { value: withContext },
|
|
636
829
|
rebuildSnapshot: { value: rebuildSnapshot },
|
|
830
|
+
storeSetTTLOverride: { value: storeSetTTLOverride },
|
|
831
|
+
storeSetDefaultTTLOverride: { value: storeSetDefaultTTLOverride },
|
|
637
832
|
storePublish: { value: storePublish },
|
|
638
833
|
storeRedirect: { value: storeRedirect },
|
|
639
834
|
dispatchResourceRequest: { value: dispatchResourceRequest },
|
|
640
835
|
dispose: { value: dispose },
|
|
641
836
|
publishChangesToDurableStore: { value: publishChangesToDurableStore },
|
|
837
|
+
getDurableTTLOverrides: { value: getDurableTTLOverrides },
|
|
838
|
+
defaultCachePolicy: { value: defaultCachePolicy },
|
|
839
|
+
applyCachePolicy: { value: applyCachePolicy },
|
|
642
840
|
});
|
|
643
841
|
}
|
|
644
842
|
|
|
@@ -654,8 +852,8 @@ function makeDurable(environment, { durableStore, instrumentation, pendingWriter
|
|
|
654
852
|
* cause a network refresh to happen).
|
|
655
853
|
*/
|
|
656
854
|
function makeOffline(environment) {
|
|
657
|
-
const storeLookup = function (sel, createSnapshot, refresh) {
|
|
658
|
-
const snapshot = environment.storeLookup(sel, createSnapshot, refresh);
|
|
855
|
+
const storeLookup = function (sel, createSnapshot, refresh, ttlStrategy) {
|
|
856
|
+
const snapshot = environment.storeLookup(sel, createSnapshot, refresh, ttlStrategy);
|
|
659
857
|
// if the snapshot is stale we want to kick off a refresh
|
|
660
858
|
if (snapshot.state === 'Stale') {
|
|
661
859
|
if (refresh !== undefined) {
|
|
@@ -678,6 +876,9 @@ function makeOffline(environment) {
|
|
|
678
876
|
});
|
|
679
877
|
};
|
|
680
878
|
return create(environment, {
|
|
879
|
+
defaultCachePolicy: {
|
|
880
|
+
value: buildStaleWhileRevalidateImplementation$1(Number.MAX_SAFE_INTEGER),
|
|
881
|
+
},
|
|
681
882
|
storeLookup: { value: storeLookup },
|
|
682
883
|
snapshotAvailable: {
|
|
683
884
|
value: snapshotAvailable,
|
|
@@ -688,4 +889,4 @@ function makeOffline(environment) {
|
|
|
688
889
|
});
|
|
689
890
|
}
|
|
690
891
|
|
|
691
|
-
export { DefaultDurableSegment, DurableStoreOperationType, makeDurable, makeOffline };
|
|
892
|
+
export { DefaultDurableSegment, DurableStoreOperationType, makeDurable, makeOffline, publishDurableStoreEntries };
|
package/dist/es/es2018/main.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export { DurableStore, DurableStoreEntries, DurableStoreEntry, DurableStoreChange, OnDurableStoreChangedListener, DefaultDurableSegment, DurableStoreOperation, DurableStoreOperationType, } from './DurableStore';
|
|
2
|
+
export { DurableTTLOverride, DefaultDurableTTLOverride } from './DurableTTLStore';
|
|
2
3
|
export { makeDurable, DurableEnvironment } from './makeDurable';
|
|
4
|
+
export { publishDurableStoreEntries } from './makeDurable/revive';
|
|
3
5
|
export { makeOffline } from './makeOffline';
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { CachePolicyImplementationArgs, Environment, Snapshot } from '@luvio/engine';
|
|
2
|
+
export declare function buildStaleWhileRevalidateImplementation(validateNotDisposed: () => void, resolveSnapshot: Environment['resolveSnapshot'], staleDuration: number): <C, D>(args: CachePolicyImplementationArgs<C, D>) => import("@luvio/engine").ErrorSnapshot | import("@luvio/engine").FulfilledSnapshot<D, unknown> | import("@luvio/engine").UnfulfilledSnapshot<D, unknown> | import("@luvio/engine").StaleSnapshot<D, unknown> | import("@luvio/engine").PendingSnapshot<D, unknown> | Promise<Snapshot<D, unknown>>;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Store } from '@luvio/engine';
|
|
2
|
+
import { DurableStore } from '../DurableStore';
|
|
3
|
+
import { DurableStoreRejectionHandler } from './error';
|
|
4
|
+
export declare function flushStoreValuesToDurableStore(store: Store, durableStore: DurableStore, durableStoreErrorHandler: DurableStoreRejectionHandler): Promise<void>;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { Environment, Snapshot,
|
|
1
|
+
import { Environment, Snapshot, UnAvailableSnapshot } from '@luvio/engine';
|
|
2
2
|
import { PendingWriter } from './pendingWriter';
|
|
3
|
-
export declare function buildDurableStoreAwareRefresh<ResponseType>(refresh:
|
|
3
|
+
export declare function buildDurableStoreAwareRefresh<ResponseType, V = unknown>(refresh: () => Promise<Snapshot<ResponseType, V>>, snapshot: UnAvailableSnapshot<ResponseType, V>, environment: Environment, pendingWriter: PendingWriter): () => Promise<Snapshot<ResponseType, V>>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Environment,
|
|
1
|
+
import { Environment, Snapshot, UnAvailableSnapshot, DataSnapshot, StoreMetadata } from '@luvio/engine';
|
|
2
2
|
import { DurableStore, DurableStoreEntries } from '../DurableStore';
|
|
3
3
|
import { DurableStoreRejectionHandler } from './error';
|
|
4
4
|
import { PendingWriter } from './pendingWriter';
|
|
@@ -9,12 +9,23 @@ declare type ReviveResponse = {
|
|
|
9
9
|
revivedKeys: ObjectAsSet;
|
|
10
10
|
hadUnexpectedShape: boolean;
|
|
11
11
|
};
|
|
12
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Takes a set of entries from DurableStore and publishes them via the passed in funcs.
|
|
14
|
+
* This respects expiration and checks for valid DurableStore data shapes. This should
|
|
15
|
+
* be used over manually parsing DurableStoreEntries
|
|
16
|
+
*
|
|
17
|
+
* @param durableRecords The DurableStoreEntries to parse
|
|
18
|
+
* @param publish A function to call with the data of each DurableStoreEntry
|
|
19
|
+
* @param publishMetadata A function to call with the metadata of each DurableStoreEntry
|
|
20
|
+
* @param pendingWriter the PendingWriter (this is going away soon)
|
|
21
|
+
* @returns
|
|
22
|
+
*/
|
|
23
|
+
export declare function publishDurableStoreEntries(durableRecords: DurableStoreEntries<unknown> | undefined, publish: (key: string, record: unknown) => void, publishMetadata: (key: string, metadata: StoreMetadata) => void, pendingWriter: PendingWriter): ReviveResponse;
|
|
13
24
|
/**
|
|
14
25
|
* This method returns a Promise to a snapshot that is revived from L2 cache. If
|
|
15
26
|
* L2 does not have the entries necessary to fulfill the snapshot then this method
|
|
16
27
|
* will refresh the snapshot from network, and then run the results from network
|
|
17
28
|
* through L2 ingestion, returning the subsequent revived snapshot.
|
|
18
29
|
*/
|
|
19
|
-
export declare function reviveSnapshot<D>(
|
|
30
|
+
export declare function reviveSnapshot<D, V = unknown>(baseEnvironment: Environment, durableStore: DurableStore, pendingWriter: PendingWriter, unavailableSnapshot: UnAvailableSnapshot<D, V>, refresh: () => Promise<Snapshot<D, V>>, durableStoreErrorHandler: DurableStoreRejectionHandler, baseSnapshot?: DataSnapshot<D, V>): Promise<Snapshot<D, V>>;
|
|
20
31
|
export {};
|
|
@@ -2,6 +2,7 @@ import { Environment } from '@luvio/engine';
|
|
|
2
2
|
import { DurableStore } from './DurableStore';
|
|
3
3
|
import { InstrumentationFunction } from './makeDurable/error';
|
|
4
4
|
import { PendingWriter } from './makeDurable/pendingWriter';
|
|
5
|
+
import { TTLOverridesMap } from './DurableTTLStore';
|
|
5
6
|
export interface DurableEnvironment extends Environment {
|
|
6
7
|
/**
|
|
7
8
|
* Disposes the environment and detaches the durable store listener
|
|
@@ -11,6 +12,10 @@ export interface DurableEnvironment extends Environment {
|
|
|
11
12
|
* publishes the pending changes to the durable store
|
|
12
13
|
*/
|
|
13
14
|
publishChangesToDurableStore(): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* gets all the stored ttl overrides stored in the durable store
|
|
17
|
+
*/
|
|
18
|
+
getDurableTTLOverrides(): Promise<TTLOverridesMap>;
|
|
14
19
|
}
|
|
15
20
|
export declare const AdapterContextSegment = "ADAPTER-CONTEXT";
|
|
16
21
|
export declare const ADAPTER_CONTEXT_ID_SUFFIX = "__NAMED_CONTEXT";
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { DurableStore } from './DurableStore';
|
|
2
|
+
export declare const TTL_DURABLE_SEGMENT = "TTL_DURABLE_SEGMENT";
|
|
3
|
+
export interface DefaultDurableTTLOverride extends DurableTTLOverride {
|
|
4
|
+
namespace: 'TTL_DEFAULT_KEY';
|
|
5
|
+
representationName: 'TTL_DEFAULT_KEY';
|
|
6
|
+
}
|
|
7
|
+
export interface DurableTTLOverride {
|
|
8
|
+
namespace: string;
|
|
9
|
+
representationName: string;
|
|
10
|
+
ttl: number;
|
|
11
|
+
}
|
|
12
|
+
export interface TTLOverridesMap {
|
|
13
|
+
defaultTTL: DefaultDurableTTLOverride | undefined;
|
|
14
|
+
overrides: DurableTTLOverride[];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Class to set and get the TTL override values in the Durable Store
|
|
18
|
+
*/
|
|
19
|
+
export declare class DurableTTLStore {
|
|
20
|
+
private durableStore;
|
|
21
|
+
constructor(durableStore: DurableStore);
|
|
22
|
+
setDefaultDurableTTLOverrides(ttl: number): Promise<void>;
|
|
23
|
+
setDurableTTLOverride(namespace: string, representationName: string, ttl: number): Promise<void>;
|
|
24
|
+
getDurableTTLOverrides(): Promise<TTLOverridesMap>;
|
|
25
|
+
}
|