@salesforce/lds-runtime-bridge 1.233.0 → 1.235.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/ldsRuntimeBridge.js +330 -78
- package/package.json +1 -1
package/dist/ldsRuntimeBridge.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* *******************************************************************************************
|
|
13
13
|
*/
|
|
14
14
|
import { setDefaultLuvio } from 'force/ldsEngine';
|
|
15
|
-
import { StoreKeySet, serializeStructuredKey, Reader, deepFreeze, emitAdapterEvent, InMemoryStore, Environment, Luvio } from 'force/luvioEngine';
|
|
15
|
+
import { StoreKeySet, serializeStructuredKey, StringKeyInMemoryStore, Reader, deepFreeze, emitAdapterEvent, InMemoryStore, Environment, Luvio } from 'force/luvioEngine';
|
|
16
16
|
import { instrumentLuvio } from 'force/ldsInstrumentation';
|
|
17
17
|
import { keyBuilderRecord } from 'force/ldsAdaptersUiapi';
|
|
18
18
|
import '@salesforce/gate/lds.graphqlEvalExcludeStaleRecords';
|
|
@@ -131,7 +131,7 @@ function publishDurableStoreEntries(durableRecords, put, publishMetadata) {
|
|
|
131
131
|
* will refresh the snapshot from network, and then run the results from network
|
|
132
132
|
* through L2 ingestion, returning the subsequent revived snapshot.
|
|
133
133
|
*/
|
|
134
|
-
function reviveSnapshot(baseEnvironment, durableStore, unavailableSnapshot, durableStoreErrorHandler, buildL1Snapshot, reviveMetrics = { l2Trips: [] }) {
|
|
134
|
+
function reviveSnapshot(baseEnvironment, durableStore, unavailableSnapshot, durableStoreErrorHandler, buildL1Snapshot, revivingStore, reviveMetrics = { l2Trips: [] }) {
|
|
135
135
|
const { recordId, select, missingLinks, seenRecords, state } = unavailableSnapshot;
|
|
136
136
|
// L2 can only revive Unfulfilled snapshots that have a selector since they have the
|
|
137
137
|
// info needed to revive (like missingLinks) and rebuild. Otherwise return L1 snapshot.
|
|
@@ -141,10 +141,21 @@ function reviveSnapshot(baseEnvironment, durableStore, unavailableSnapshot, dura
|
|
|
141
141
|
metrics: reviveMetrics,
|
|
142
142
|
});
|
|
143
143
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
144
|
+
const keysToReviveSet = new StoreKeySet();
|
|
145
|
+
if (revivingStore) {
|
|
146
|
+
// Any stale keys since the last l2 read should be cleared and fetched again
|
|
147
|
+
for (const staleKey of revivingStore.staleEntries) {
|
|
148
|
+
keysToReviveSet.add(staleKey);
|
|
149
|
+
}
|
|
150
|
+
revivingStore.clearStale();
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
// when not using a reviving store:
|
|
154
|
+
// in case L1 store changes/deallocs a record while we are doing the async read
|
|
155
|
+
// we attempt to read all keys from L2 - so combine recordId with any seenRecords
|
|
156
|
+
keysToReviveSet.add(recordId);
|
|
157
|
+
keysToReviveSet.merge(seenRecords);
|
|
158
|
+
}
|
|
148
159
|
keysToReviveSet.merge(missingLinks);
|
|
149
160
|
const keysToRevive = keysToReviveSet.keysAsArray();
|
|
150
161
|
const canonicalKeys = keysToRevive.map((x) => serializeStructuredKey(baseEnvironment.storeGetCanonicalKey(x)));
|
|
@@ -194,7 +205,7 @@ function reviveSnapshot(baseEnvironment, durableStore, unavailableSnapshot, dura
|
|
|
194
205
|
for (let i = 0, len = newKeys.length; i < len; i++) {
|
|
195
206
|
const newSnapshotSeenKey = newKeys[i];
|
|
196
207
|
if (!alreadyRequestedOrRevivedSet.has(newSnapshotSeenKey)) {
|
|
197
|
-
return reviveSnapshot(baseEnvironment, durableStore, snapshot, durableStoreErrorHandler, buildL1Snapshot, reviveMetrics);
|
|
208
|
+
return reviveSnapshot(baseEnvironment, durableStore, snapshot, durableStoreErrorHandler, buildL1Snapshot, revivingStore, reviveMetrics);
|
|
198
209
|
}
|
|
199
210
|
}
|
|
200
211
|
}
|
|
@@ -283,8 +294,9 @@ class DurableTTLStore {
|
|
|
283
294
|
}
|
|
284
295
|
}
|
|
285
296
|
|
|
286
|
-
function flushInMemoryStoreValuesToDurableStore(store, durableStore, durableStoreErrorHandler, redirects, additionalDurableStoreOperations = []) {
|
|
297
|
+
function flushInMemoryStoreValuesToDurableStore(store, durableStore, durableStoreErrorHandler, redirects, additionalDurableStoreOperations = [], enableDurableMetadataRefresh = false) {
|
|
287
298
|
const durableRecords = create$2(null);
|
|
299
|
+
const refreshedDurableRecords = create$2(null);
|
|
288
300
|
const evictedRecords = create$2(null);
|
|
289
301
|
const { records, metadata: storeMetadata, visitedIds, refreshedIds, } = store.fallbackStringKeyInMemoryStore;
|
|
290
302
|
// TODO: W-8909393 Once metadata is stored in its own segment we need to
|
|
@@ -294,32 +306,36 @@ function flushInMemoryStoreValuesToDurableStore(store, durableStore, durableStor
|
|
|
294
306
|
for (let i = 0, len = keys$1.length; i < len; i += 1) {
|
|
295
307
|
const key = keys$1[i];
|
|
296
308
|
const record = records[key];
|
|
309
|
+
const wasVisited = visitedIds[key] !== undefined;
|
|
297
310
|
// this record has been evicted, evict from DS
|
|
298
|
-
if (record === undefined) {
|
|
311
|
+
if (wasVisited && record === undefined) {
|
|
299
312
|
evictedRecords[key] = true;
|
|
300
313
|
continue;
|
|
301
314
|
}
|
|
302
315
|
const metadata = storeMetadata[key];
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
durableRecords[key].metadata = {
|
|
308
|
-
...metadata,
|
|
309
|
-
metadataVersion: DURABLE_METADATA_VERSION,
|
|
310
|
-
};
|
|
311
|
-
}
|
|
316
|
+
const entries = wasVisited === true || enableDurableMetadataRefresh === false
|
|
317
|
+
? durableRecords
|
|
318
|
+
: refreshedDurableRecords;
|
|
319
|
+
setRecordTo(entries, key, record, metadata);
|
|
312
320
|
}
|
|
313
321
|
const durableStoreOperations = additionalDurableStoreOperations;
|
|
314
|
-
// publishes
|
|
315
322
|
const recordKeys = keys$2(durableRecords);
|
|
316
323
|
if (recordKeys.length > 0) {
|
|
324
|
+
// publishes with data
|
|
317
325
|
durableStoreOperations.push({
|
|
318
326
|
type: 'setEntries',
|
|
319
327
|
entries: durableRecords,
|
|
320
328
|
segment: DefaultDurableSegment,
|
|
321
329
|
});
|
|
322
330
|
}
|
|
331
|
+
if (keys$2(refreshedDurableRecords).length > 0) {
|
|
332
|
+
// publishes with only metadata updates
|
|
333
|
+
durableStoreOperations.push({
|
|
334
|
+
type: 'setMetadata',
|
|
335
|
+
entries: refreshedDurableRecords,
|
|
336
|
+
segment: DefaultDurableSegment,
|
|
337
|
+
});
|
|
338
|
+
}
|
|
323
339
|
// redirects
|
|
324
340
|
redirects.forEach((value, key) => {
|
|
325
341
|
durableStoreOperations.push({
|
|
@@ -346,6 +362,17 @@ function flushInMemoryStoreValuesToDurableStore(store, durableStore, durableStor
|
|
|
346
362
|
}
|
|
347
363
|
return Promise.resolve();
|
|
348
364
|
}
|
|
365
|
+
function setRecordTo(entries, key, record, metadata) {
|
|
366
|
+
entries[key] = {
|
|
367
|
+
data: record,
|
|
368
|
+
};
|
|
369
|
+
if (metadata !== undefined) {
|
|
370
|
+
entries[key].metadata = {
|
|
371
|
+
...metadata,
|
|
372
|
+
metadataVersion: DURABLE_METADATA_VERSION,
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
}
|
|
349
376
|
|
|
350
377
|
const DurableEnvironmentEventDiscriminator = 'durable';
|
|
351
378
|
function emitDurableEnvironmentAdapterEvent(eventData, observers) {
|
|
@@ -390,6 +417,50 @@ async function reviveRedirects(durableStore, env) {
|
|
|
390
417
|
}
|
|
391
418
|
}
|
|
392
419
|
|
|
420
|
+
function buildRevivingStagingStore(upstreamStore) {
|
|
421
|
+
const localStore = new StringKeyInMemoryStore();
|
|
422
|
+
const staleEntries = new Set();
|
|
423
|
+
function readEntry(key) {
|
|
424
|
+
if (typeof key !== 'string') {
|
|
425
|
+
return upstreamStore.readEntry(key);
|
|
426
|
+
}
|
|
427
|
+
let storeEntry = localStore.readEntry(key);
|
|
428
|
+
if (!storeEntry) {
|
|
429
|
+
// read from upstream store...
|
|
430
|
+
storeEntry = upstreamStore.readEntry(key);
|
|
431
|
+
// put it in our store to avoid it getting evicted prior to the next durable store read
|
|
432
|
+
localStore.put(key, storeEntry);
|
|
433
|
+
}
|
|
434
|
+
return storeEntry;
|
|
435
|
+
}
|
|
436
|
+
// Entries are marked stale by the durable store change listener. They are not
|
|
437
|
+
// immediately evicted so as to not result in a cache miss during a rebuild.
|
|
438
|
+
// The revive process will clear stale entries and read them from the durable store
|
|
439
|
+
// on the next revive loop.
|
|
440
|
+
function markStale(key) {
|
|
441
|
+
staleEntries.add(key);
|
|
442
|
+
}
|
|
443
|
+
// The revive loop clears stale entries right before reading from the durable store.
|
|
444
|
+
// Any stale entries will be revived to ensure they are present in L1 and match the
|
|
445
|
+
// latest data.
|
|
446
|
+
function clearStale() {
|
|
447
|
+
for (const key of staleEntries) {
|
|
448
|
+
localStore.dealloc(key);
|
|
449
|
+
}
|
|
450
|
+
staleEntries.clear();
|
|
451
|
+
}
|
|
452
|
+
// All functions other than `readEntry` pass through to the upstream store.
|
|
453
|
+
// A reviving store is only "active" during a call to `environment.storeLookup`, and will
|
|
454
|
+
// be used by the reader attempting to build an L1 snapshot. Immediately after the L1 rebuild
|
|
455
|
+
// the reviving store becomes inactive other than receiving change notifications.
|
|
456
|
+
return create$2(upstreamStore, {
|
|
457
|
+
readEntry: { value: readEntry },
|
|
458
|
+
markStale: { value: markStale },
|
|
459
|
+
clearStale: { value: clearStale },
|
|
460
|
+
staleEntries: { value: staleEntries },
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
|
|
393
464
|
const AdapterContextSegment = 'ADAPTER-CONTEXT';
|
|
394
465
|
const ADAPTER_CONTEXT_ID_SUFFIX = '__NAMED_CONTEXT';
|
|
395
466
|
async function reviveOrCreateContext(adapterId, durableStore, durableStoreErrorHandler, contextStores, pendingContextStoreKeys, onContextLoaded) {
|
|
@@ -445,14 +516,16 @@ function isUnfulfilledSnapshot(cachedSnapshotResult) {
|
|
|
445
516
|
* @param durableStore A DurableStore implementation
|
|
446
517
|
* @param instrumentation An instrumentation function implementation
|
|
447
518
|
*/
|
|
448
|
-
function makeDurable(environment, { durableStore, instrumentation }) {
|
|
449
|
-
let
|
|
519
|
+
function makeDurable(environment, { durableStore, instrumentation, useRevivingStore, enableDurableMetadataRefresh = false, }) {
|
|
520
|
+
let stagingStore = null;
|
|
450
521
|
const durableTTLStore = new DurableTTLStore(durableStore);
|
|
451
522
|
const mergeKeysPromiseMap = new Map();
|
|
452
523
|
// When a context store is mutated we write it to L2, which causes DS on change
|
|
453
524
|
// event. If this instance of makeDurable caused that L2 write we can ignore that
|
|
454
525
|
// on change event. This Set helps us do that.
|
|
455
526
|
const pendingContextStoreKeys = new Set();
|
|
527
|
+
// Reviving stores are tracked so that they can be notified of durable store change notifications.
|
|
528
|
+
const revivingStores = new Set();
|
|
456
529
|
// redirects that need to be flushed to the durable store
|
|
457
530
|
const pendingStoreRedirects = new Map();
|
|
458
531
|
const contextStores = create$2(null);
|
|
@@ -478,6 +551,7 @@ function makeDurable(environment, { durableStore, instrumentation }) {
|
|
|
478
551
|
const defaultSegmentKeys = [];
|
|
479
552
|
const adapterContextSegmentKeys = [];
|
|
480
553
|
const redirectSegmentKeys = [];
|
|
554
|
+
const metadataRefreshSegmentKeys = [];
|
|
481
555
|
const messagingSegmentKeys = [];
|
|
482
556
|
let shouldBroadcast = false;
|
|
483
557
|
for (let i = 0, len = changes.length; i < len; i++) {
|
|
@@ -485,7 +559,12 @@ function makeDurable(environment, { durableStore, instrumentation }) {
|
|
|
485
559
|
// we only care about changes to the data which is stored in the default
|
|
486
560
|
// segment or the adapter context
|
|
487
561
|
if (change.segment === DefaultDurableSegment) {
|
|
488
|
-
|
|
562
|
+
if (change.type === 'setMetadata') {
|
|
563
|
+
metadataRefreshSegmentKeys.push(...change.ids);
|
|
564
|
+
}
|
|
565
|
+
else {
|
|
566
|
+
defaultSegmentKeys.push(...change.ids);
|
|
567
|
+
}
|
|
489
568
|
}
|
|
490
569
|
else if (change.segment === AdapterContextSegment) {
|
|
491
570
|
adapterContextSegmentKeys.push(...change.ids);
|
|
@@ -549,9 +628,26 @@ function makeDurable(environment, { durableStore, instrumentation }) {
|
|
|
549
628
|
// and go through an entire broadcast/revive cycle for unchanged data
|
|
550
629
|
// call base environment storeEvict so this evict is not tracked for durable deletion
|
|
551
630
|
environment.storeEvict(key);
|
|
631
|
+
for (const revivingStore of revivingStores) {
|
|
632
|
+
revivingStore.markStale(key);
|
|
633
|
+
}
|
|
552
634
|
}
|
|
553
635
|
shouldBroadcast = true;
|
|
554
636
|
}
|
|
637
|
+
// process metadata only refreshes
|
|
638
|
+
if (metadataRefreshSegmentKeys.length > 0) {
|
|
639
|
+
const entries = await durableStore.getMetadata(metadataRefreshSegmentKeys, DefaultDurableSegment);
|
|
640
|
+
if (entries !== undefined) {
|
|
641
|
+
const entryKeys = keys$2(entries);
|
|
642
|
+
for (let i = 0, len = entryKeys.length; i < len; i++) {
|
|
643
|
+
const entryKey = entryKeys[i];
|
|
644
|
+
const { metadata } = entries[entryKey];
|
|
645
|
+
if (metadata !== undefined) {
|
|
646
|
+
environment.putStoreMetadata(entryKey, metadata, false);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
}
|
|
555
651
|
if (shouldBroadcast) {
|
|
556
652
|
await environment.storeBroadcast(rebuildSnapshot, environment.snapshotAvailable);
|
|
557
653
|
}
|
|
@@ -577,10 +673,10 @@ function makeDurable(environment, { durableStore, instrumentation }) {
|
|
|
577
673
|
};
|
|
578
674
|
const storePublish = function (key, data) {
|
|
579
675
|
validateNotDisposed();
|
|
580
|
-
if (
|
|
581
|
-
|
|
676
|
+
if (stagingStore === null) {
|
|
677
|
+
stagingStore = buildIngestStagingStore(environment);
|
|
582
678
|
}
|
|
583
|
-
|
|
679
|
+
stagingStore.publish(key, data);
|
|
584
680
|
// remove record from main luvio L1 cache while we are on the synchronous path
|
|
585
681
|
// because we do not want some other code attempting to use the
|
|
586
682
|
// in-memory values before the durable store onChanged handler
|
|
@@ -589,26 +685,26 @@ function makeDurable(environment, { durableStore, instrumentation }) {
|
|
|
589
685
|
};
|
|
590
686
|
const publishStoreMetadata = function (recordId, storeMetadata) {
|
|
591
687
|
validateNotDisposed();
|
|
592
|
-
if (
|
|
593
|
-
|
|
688
|
+
if (stagingStore === null) {
|
|
689
|
+
stagingStore = buildIngestStagingStore(environment);
|
|
594
690
|
}
|
|
595
|
-
|
|
691
|
+
stagingStore.publishMetadata(recordId, storeMetadata);
|
|
596
692
|
};
|
|
597
693
|
const storeIngest = function (key, ingest, response, luvio) {
|
|
598
694
|
validateNotDisposed();
|
|
599
695
|
// we don't ingest to the luvio L1 store from network directly, we ingest to
|
|
600
696
|
// L2 and let DurableStore on change event revive keys into luvio L1 store
|
|
601
|
-
if (
|
|
602
|
-
|
|
697
|
+
if (stagingStore === null) {
|
|
698
|
+
stagingStore = buildIngestStagingStore(environment);
|
|
603
699
|
}
|
|
604
|
-
environment.storeIngest(key, ingest, response, luvio,
|
|
700
|
+
environment.storeIngest(key, ingest, response, luvio, stagingStore);
|
|
605
701
|
};
|
|
606
702
|
const storeIngestError = function (key, errorSnapshot, storeMetadataParams, _storeOverride) {
|
|
607
703
|
validateNotDisposed();
|
|
608
|
-
if (
|
|
609
|
-
|
|
704
|
+
if (stagingStore === null) {
|
|
705
|
+
stagingStore = buildIngestStagingStore(environment);
|
|
610
706
|
}
|
|
611
|
-
environment.storeIngestError(key, errorSnapshot, storeMetadataParams,
|
|
707
|
+
environment.storeIngestError(key, errorSnapshot, storeMetadataParams, stagingStore);
|
|
612
708
|
};
|
|
613
709
|
const storeBroadcast = function (_rebuildSnapshot, _snapshotDataAvailable) {
|
|
614
710
|
validateNotDisposed();
|
|
@@ -619,19 +715,19 @@ function makeDurable(environment, { durableStore, instrumentation }) {
|
|
|
619
715
|
};
|
|
620
716
|
const publishChangesToDurableStore = function (additionalDurableStoreOperations) {
|
|
621
717
|
validateNotDisposed();
|
|
622
|
-
if (
|
|
718
|
+
if (stagingStore === null) {
|
|
623
719
|
return Promise.resolve();
|
|
624
720
|
}
|
|
625
|
-
const promise = flushInMemoryStoreValuesToDurableStore(
|
|
721
|
+
const promise = flushInMemoryStoreValuesToDurableStore(stagingStore, durableStore, durableStoreErrorHandler, new Map(pendingStoreRedirects), additionalDurableStoreOperations, enableDurableMetadataRefresh);
|
|
626
722
|
pendingStoreRedirects.clear();
|
|
627
|
-
|
|
723
|
+
stagingStore = null;
|
|
628
724
|
return promise;
|
|
629
725
|
};
|
|
630
726
|
const storeLookup = function (sel, createSnapshot, refresh, ttlStrategy) {
|
|
631
727
|
validateNotDisposed();
|
|
632
|
-
// if this lookup is right after an ingest there will be a staging store
|
|
633
|
-
if (
|
|
634
|
-
const reader = new Reader(
|
|
728
|
+
// if this lookup is right after an ingest or during a revive there will be a staging store
|
|
729
|
+
if (stagingStore !== null) {
|
|
730
|
+
const reader = new Reader(stagingStore, sel.variables, refresh, undefined, ttlStrategy);
|
|
635
731
|
return reader.read(sel);
|
|
636
732
|
}
|
|
637
733
|
// otherwise this is from buildCachedSnapshot and we should use the luvio
|
|
@@ -640,24 +736,24 @@ function makeDurable(environment, { durableStore, instrumentation }) {
|
|
|
640
736
|
};
|
|
641
737
|
const storeEvict = function (key) {
|
|
642
738
|
validateNotDisposed();
|
|
643
|
-
if (
|
|
644
|
-
|
|
739
|
+
if (stagingStore === null) {
|
|
740
|
+
stagingStore = buildIngestStagingStore(environment);
|
|
645
741
|
}
|
|
646
|
-
|
|
742
|
+
stagingStore.evict(key);
|
|
647
743
|
};
|
|
648
744
|
const getNode = function (key) {
|
|
649
745
|
validateNotDisposed();
|
|
650
|
-
if (
|
|
651
|
-
|
|
746
|
+
if (stagingStore === null) {
|
|
747
|
+
stagingStore = buildIngestStagingStore(environment);
|
|
652
748
|
}
|
|
653
|
-
return environment.getNode(key,
|
|
749
|
+
return environment.getNode(key, stagingStore);
|
|
654
750
|
};
|
|
655
751
|
const wrapNormalizedGraphNode = function (normalized) {
|
|
656
752
|
validateNotDisposed();
|
|
657
|
-
if (
|
|
658
|
-
|
|
753
|
+
if (stagingStore === null) {
|
|
754
|
+
stagingStore = buildIngestStagingStore(environment);
|
|
659
755
|
}
|
|
660
|
-
return environment.wrapNormalizedGraphNode(normalized,
|
|
756
|
+
return environment.wrapNormalizedGraphNode(normalized, stagingStore);
|
|
661
757
|
};
|
|
662
758
|
const rebuildSnapshot = function (snapshot, onRebuild) {
|
|
663
759
|
validateNotDisposed();
|
|
@@ -669,7 +765,7 @@ function makeDurable(environment, { durableStore, instrumentation }) {
|
|
|
669
765
|
return;
|
|
670
766
|
}
|
|
671
767
|
// Do an L2 revive and emit to subscriber using the callback.
|
|
672
|
-
|
|
768
|
+
reviveSnapshotWrapper(rebuilt, () => {
|
|
673
769
|
// reviveSnapshot will revive into L1, and since "records" is a reference
|
|
674
770
|
// (and not a copy) to the L1 records we can use it for rebuild
|
|
675
771
|
let rebuiltSnap;
|
|
@@ -710,10 +806,10 @@ function makeDurable(environment, { durableStore, instrumentation }) {
|
|
|
710
806
|
// the next publishChangesToDurableStore. NOTE: we don't need to call
|
|
711
807
|
// redirect on the base environment store because staging store and base
|
|
712
808
|
// L1 store share the same redirect and reverseRedirectKeys
|
|
713
|
-
if (
|
|
714
|
-
|
|
809
|
+
if (stagingStore === null) {
|
|
810
|
+
stagingStore = buildIngestStagingStore(environment);
|
|
715
811
|
}
|
|
716
|
-
|
|
812
|
+
stagingStore.redirect(existingKey, canonicalKey);
|
|
717
813
|
};
|
|
718
814
|
const storeSetTTLOverride = function (namespace, representationName, ttl) {
|
|
719
815
|
validateNotDisposed();
|
|
@@ -754,7 +850,7 @@ function makeDurable(environment, { durableStore, instrumentation }) {
|
|
|
754
850
|
if (isUnfulfilledSnapshot(snapshot)) {
|
|
755
851
|
const start = Date.now();
|
|
756
852
|
emitDurableEnvironmentAdapterEvent({ type: 'l2-revive-start' }, adapterRequestContext.eventObservers);
|
|
757
|
-
const revivedSnapshot =
|
|
853
|
+
const revivedSnapshot = reviveSnapshotWrapper(snapshot, () => injectedStoreLookup(snapshot.select, snapshot.refresh)).then((result) => {
|
|
758
854
|
emitDurableEnvironmentAdapterEvent({
|
|
759
855
|
type: 'l2-revive-end',
|
|
760
856
|
snapshot: result.snapshot,
|
|
@@ -779,15 +875,15 @@ function makeDurable(environment, { durableStore, instrumentation }) {
|
|
|
779
875
|
};
|
|
780
876
|
const getIngestStagingStoreRecords = function () {
|
|
781
877
|
validateNotDisposed();
|
|
782
|
-
if (
|
|
783
|
-
return
|
|
878
|
+
if (stagingStore !== null) {
|
|
879
|
+
return stagingStore.fallbackStringKeyInMemoryStore.records;
|
|
784
880
|
}
|
|
785
881
|
return {};
|
|
786
882
|
};
|
|
787
883
|
const getIngestStagingStoreMetadata = function () {
|
|
788
884
|
validateNotDisposed();
|
|
789
|
-
if (
|
|
790
|
-
return
|
|
885
|
+
if (stagingStore !== null) {
|
|
886
|
+
return stagingStore.fallbackStringKeyInMemoryStore.metadata;
|
|
791
887
|
}
|
|
792
888
|
return {};
|
|
793
889
|
};
|
|
@@ -826,22 +922,20 @@ function makeDurable(environment, { durableStore, instrumentation }) {
|
|
|
826
922
|
}
|
|
827
923
|
await Promise.all(pendingPromises);
|
|
828
924
|
const entries = await durableStore.getEntries(keysToReviveAsArray, DefaultDurableSegment);
|
|
829
|
-
|
|
925
|
+
stagingStore = buildIngestStagingStore(environment);
|
|
830
926
|
publishDurableStoreEntries(entries, (key, record) => {
|
|
831
927
|
if (typeof key === 'string') {
|
|
832
|
-
|
|
833
|
-
record;
|
|
928
|
+
stagingStore.fallbackStringKeyInMemoryStore.records[key] = record;
|
|
834
929
|
}
|
|
835
930
|
else {
|
|
836
|
-
|
|
931
|
+
stagingStore.recordsMap.set(key, record);
|
|
837
932
|
}
|
|
838
933
|
}, (key, metadata) => {
|
|
839
934
|
if (typeof key === 'string') {
|
|
840
|
-
|
|
841
|
-
metadata;
|
|
935
|
+
stagingStore.fallbackStringKeyInMemoryStore.metadata[key] = metadata;
|
|
842
936
|
}
|
|
843
937
|
else {
|
|
844
|
-
|
|
938
|
+
stagingStore.metadataMap.set(key, metadata);
|
|
845
939
|
}
|
|
846
940
|
});
|
|
847
941
|
snapshotFromMemoryIngest = await ingestAndBroadcastFunc();
|
|
@@ -870,7 +964,7 @@ function makeDurable(environment, { durableStore, instrumentation }) {
|
|
|
870
964
|
// we aren't doing any merging so we don't have to synchronize, the
|
|
871
965
|
// underlying DurableStore implementation takes care of R/W sync
|
|
872
966
|
// so all we have to do is ingest then write to L2
|
|
873
|
-
|
|
967
|
+
stagingStore = buildIngestStagingStore(environment);
|
|
874
968
|
snapshotFromMemoryIngest = await ingestAndBroadcastFunc();
|
|
875
969
|
}
|
|
876
970
|
if (snapshotFromMemoryIngest === undefined) {
|
|
@@ -881,12 +975,12 @@ function makeDurable(environment, { durableStore, instrumentation }) {
|
|
|
881
975
|
}
|
|
882
976
|
// if snapshot from staging store lookup is unfulfilled then do an L2 lookup
|
|
883
977
|
const { select, refresh } = snapshotFromMemoryIngest;
|
|
884
|
-
const result = await
|
|
978
|
+
const result = await reviveSnapshotWrapper(snapshotFromMemoryIngest, () => environment.storeLookup(select, environment.createSnapshot, refresh));
|
|
885
979
|
return result.snapshot;
|
|
886
980
|
};
|
|
887
981
|
const handleErrorResponse = async function (ingestAndBroadcastFunc) {
|
|
888
982
|
validateNotDisposed();
|
|
889
|
-
|
|
983
|
+
stagingStore = buildIngestStagingStore(environment);
|
|
890
984
|
return ingestAndBroadcastFunc();
|
|
891
985
|
};
|
|
892
986
|
const getNotifyChangeStoreEntries = function (keys) {
|
|
@@ -937,6 +1031,27 @@ function makeDurable(environment, { durableStore, instrumentation }) {
|
|
|
937
1031
|
await durableStore.setEntries({ notifyStoreUpdateAvailable: { data: entryKeys } }, MessagingDurableSegment);
|
|
938
1032
|
return Promise.resolve(undefined);
|
|
939
1033
|
};
|
|
1034
|
+
const reviveSnapshotWrapper = function (unavailableSnapshot, buildL1Snapshot) {
|
|
1035
|
+
let revivingStore = undefined;
|
|
1036
|
+
if (useRevivingStore) {
|
|
1037
|
+
// NOTE: `store` is private, there doesn't seem to be a better,
|
|
1038
|
+
// cleaner way of accessing it from a derived environment.
|
|
1039
|
+
let baseStore = environment.store;
|
|
1040
|
+
// If we're rebuilding during an ingest, the existing staging store should be the base store.
|
|
1041
|
+
if (stagingStore) {
|
|
1042
|
+
baseStore = stagingStore;
|
|
1043
|
+
}
|
|
1044
|
+
let revivingStore = buildRevivingStagingStore(baseStore);
|
|
1045
|
+
revivingStores.add(revivingStore);
|
|
1046
|
+
}
|
|
1047
|
+
return reviveSnapshot(environment, durableStore, unavailableSnapshot, durableStoreErrorHandler, () => {
|
|
1048
|
+
const tempStore = stagingStore;
|
|
1049
|
+
const result = buildL1Snapshot();
|
|
1050
|
+
stagingStore = tempStore;
|
|
1051
|
+
return result;
|
|
1052
|
+
}, revivingStore).finally(() => {
|
|
1053
|
+
});
|
|
1054
|
+
};
|
|
940
1055
|
// set the default cache policy of the base environment
|
|
941
1056
|
environment.setDefaultCachePolicy({
|
|
942
1057
|
type: 'stale-while-revalidate',
|
|
@@ -971,7 +1086,7 @@ function makeDurable(environment, { durableStore, instrumentation }) {
|
|
|
971
1086
|
});
|
|
972
1087
|
}
|
|
973
1088
|
|
|
974
|
-
const { keys: keys$1, create: create$1, assign: assign$1, entries } = Object;
|
|
1089
|
+
const { keys: keys$1, create: create$1, assign: assign$1, entries, values: values$1 } = Object;
|
|
975
1090
|
const { stringify, parse } = JSON;
|
|
976
1091
|
|
|
977
1092
|
function selectColumnsFromTableWhereKeyIn(columnNames, table, keyColumnName, whereIn) {
|
|
@@ -1005,6 +1120,22 @@ class LdsDataTable {
|
|
|
1005
1120
|
}, reject);
|
|
1006
1121
|
});
|
|
1007
1122
|
}
|
|
1123
|
+
getMetadataByKeys(keys) {
|
|
1124
|
+
const query = selectColumnsFromTableWhereKeyIn([COLUMN_NAME_KEY$2, COLUMN_NAME_METADATA$1], this.tableName, COLUMN_NAME_KEY$2, keys);
|
|
1125
|
+
return new Promise((resolve, reject) => {
|
|
1126
|
+
this.plugin.query(query, keys, (results) => {
|
|
1127
|
+
resolve(results.rows.reduce((entries, row) => {
|
|
1128
|
+
const [key, stringifiedMetadata] = row;
|
|
1129
|
+
if (stringifiedMetadata !== undefined) {
|
|
1130
|
+
entries[key] = {
|
|
1131
|
+
metadata: parse(stringifiedMetadata),
|
|
1132
|
+
};
|
|
1133
|
+
}
|
|
1134
|
+
return entries;
|
|
1135
|
+
}, {}));
|
|
1136
|
+
}, reject);
|
|
1137
|
+
});
|
|
1138
|
+
}
|
|
1008
1139
|
getAll() {
|
|
1009
1140
|
return new Promise((resolve, reject) => {
|
|
1010
1141
|
this.plugin.query(this.getAllQuery, [], (x) => {
|
|
@@ -1031,6 +1162,24 @@ class LdsDataTable {
|
|
|
1031
1162
|
}, []),
|
|
1032
1163
|
};
|
|
1033
1164
|
}
|
|
1165
|
+
metadataToUpdateOperations(entries, segment) {
|
|
1166
|
+
return {
|
|
1167
|
+
type: 'update',
|
|
1168
|
+
table: this.tableName,
|
|
1169
|
+
keyColumn: COLUMN_NAME_KEY$2,
|
|
1170
|
+
context: {
|
|
1171
|
+
segment,
|
|
1172
|
+
type: 'setMetadata',
|
|
1173
|
+
},
|
|
1174
|
+
columns: [COLUMN_NAME_METADATA$1],
|
|
1175
|
+
values: keys$1(entries).reduce((values, key) => {
|
|
1176
|
+
const { metadata } = entries[key];
|
|
1177
|
+
const row = [metadata ? stringify(metadata) : null];
|
|
1178
|
+
values[key] = row;
|
|
1179
|
+
return values;
|
|
1180
|
+
}, {}),
|
|
1181
|
+
};
|
|
1182
|
+
}
|
|
1034
1183
|
mapToDurableEntries(sqliteResult) {
|
|
1035
1184
|
return sqliteResult.rows.reduce((entries, row) => {
|
|
1036
1185
|
const [key, stringifiedData, stringifiedMetadata] = row;
|
|
@@ -1077,6 +1226,25 @@ class LdsInternalDataTable {
|
|
|
1077
1226
|
}, reject);
|
|
1078
1227
|
});
|
|
1079
1228
|
}
|
|
1229
|
+
getMetadataByKeys(keys, namespace) {
|
|
1230
|
+
if (namespace === undefined) {
|
|
1231
|
+
throw Error('LdsInternalDataTable requires namespace');
|
|
1232
|
+
}
|
|
1233
|
+
const query = selectColumnsFromTableWhereKeyInNamespaced([COLUMN_NAME_KEY$1, COLUMN_NAME_METADATA], this.tableName, COLUMN_NAME_KEY$1, keys, COLUMN_NAME_NAMESPACE);
|
|
1234
|
+
return new Promise((resolve, reject) => {
|
|
1235
|
+
this.plugin.query(query, [namespace].concat(keys), (results) => {
|
|
1236
|
+
resolve(results.rows.reduce((entries, row) => {
|
|
1237
|
+
const [key, stringifiedMetadata] = row;
|
|
1238
|
+
if (stringifiedMetadata !== undefined) {
|
|
1239
|
+
entries[key] = {
|
|
1240
|
+
metadata: parse(stringifiedMetadata),
|
|
1241
|
+
};
|
|
1242
|
+
}
|
|
1243
|
+
return entries;
|
|
1244
|
+
}, {}));
|
|
1245
|
+
}, reject);
|
|
1246
|
+
});
|
|
1247
|
+
}
|
|
1080
1248
|
getAll(namespace) {
|
|
1081
1249
|
return new Promise((resolve, reject) => {
|
|
1082
1250
|
this.plugin.query(this.getAllQuery, [namespace], (x) => {
|
|
@@ -1110,6 +1278,42 @@ class LdsInternalDataTable {
|
|
|
1110
1278
|
}, []),
|
|
1111
1279
|
};
|
|
1112
1280
|
}
|
|
1281
|
+
metadataToUpdateOperations(entries, segment) {
|
|
1282
|
+
return {
|
|
1283
|
+
type: 'update',
|
|
1284
|
+
table: this.tableName,
|
|
1285
|
+
keyColumn: COLUMN_NAME_KEY$1,
|
|
1286
|
+
context: {
|
|
1287
|
+
segment,
|
|
1288
|
+
type: 'setMetadata',
|
|
1289
|
+
},
|
|
1290
|
+
columns: [COLUMN_NAME_METADATA],
|
|
1291
|
+
values: keys$1(entries).reduce((values, key) => {
|
|
1292
|
+
const { metadata } = entries[key];
|
|
1293
|
+
const row = [metadata ? stringify(metadata) : null];
|
|
1294
|
+
values[key] = row;
|
|
1295
|
+
return values;
|
|
1296
|
+
}, {}),
|
|
1297
|
+
};
|
|
1298
|
+
}
|
|
1299
|
+
metadataToUpdateSQLQueries(entries, segment) {
|
|
1300
|
+
return keys$1(entries).reduce((accu, key) => {
|
|
1301
|
+
const { metadata } = entries[key];
|
|
1302
|
+
if (metadata !== undefined) {
|
|
1303
|
+
accu.push({
|
|
1304
|
+
sql: `UPDATE ${this.tableName} SET ${COLUMN_NAME_METADATA} = ? WHERE (${COLUMN_NAME_KEY$1} IS ? AND ${COLUMN_NAME_NAMESPACE} IS ?)`,
|
|
1305
|
+
params: [stringify(metadata), key, segment],
|
|
1306
|
+
change: {
|
|
1307
|
+
ids: [key],
|
|
1308
|
+
segment,
|
|
1309
|
+
type: 'setMetadata',
|
|
1310
|
+
isExternalChange: false,
|
|
1311
|
+
},
|
|
1312
|
+
});
|
|
1313
|
+
}
|
|
1314
|
+
return accu;
|
|
1315
|
+
}, []);
|
|
1316
|
+
}
|
|
1113
1317
|
mapToDurableEntries(sqliteResult) {
|
|
1114
1318
|
return sqliteResult.rows.reduce((entries, row) => {
|
|
1115
1319
|
const [key, stringifiedData, stringifiedMetadata] = row;
|
|
@@ -1146,9 +1350,16 @@ class NimbusSqliteStore {
|
|
|
1146
1350
|
});
|
|
1147
1351
|
});
|
|
1148
1352
|
}
|
|
1353
|
+
batchQuery(queries) {
|
|
1354
|
+
const promises = queries.map((q) => this.query(q.sql, q.params));
|
|
1355
|
+
return Promise.all(promises);
|
|
1356
|
+
}
|
|
1149
1357
|
async getEntries(entryIds, segment) {
|
|
1150
1358
|
return this.getTable(segment).getByKeys(entryIds, segment);
|
|
1151
1359
|
}
|
|
1360
|
+
async getMetadata(entryIds, segment) {
|
|
1361
|
+
return this.getTable(segment).getMetadataByKeys(entryIds, segment);
|
|
1362
|
+
}
|
|
1152
1363
|
getAllEntries(segment) {
|
|
1153
1364
|
return this.getTable(segment).getAll(segment);
|
|
1154
1365
|
}
|
|
@@ -1157,12 +1368,30 @@ class NimbusSqliteStore {
|
|
|
1157
1368
|
const upsertOperation = table.entriesToUpsertOperations(entries, segment);
|
|
1158
1369
|
return this.batchOperationAsPromise([upsertOperation]);
|
|
1159
1370
|
}
|
|
1371
|
+
setMetadata(entries, segment) {
|
|
1372
|
+
const table = this.getTable(segment);
|
|
1373
|
+
const operation = this.plugin.supportsBatchUpdates === undefined ||
|
|
1374
|
+
this.plugin.supportsBatchUpdates() === false
|
|
1375
|
+
? table.entriesToUpsertOperations(entries, segment)
|
|
1376
|
+
: table.metadataToUpdateOperations(entries, segment);
|
|
1377
|
+
return this.batchOperationAsPromise([operation]);
|
|
1378
|
+
}
|
|
1160
1379
|
batchOperations(operations) {
|
|
1161
1380
|
const sqliteOperations = operations.reduce((acc, cur) => {
|
|
1162
1381
|
if (cur.type === 'setEntries') {
|
|
1163
1382
|
const table = this.getTable(cur.segment);
|
|
1164
1383
|
acc.push(table.entriesToUpsertOperations(cur.entries, cur.segment));
|
|
1165
1384
|
}
|
|
1385
|
+
else if (cur.type === 'setMetadata') {
|
|
1386
|
+
const table = this.getTable(cur.segment);
|
|
1387
|
+
if (this.plugin.supportsBatchUpdates === undefined ||
|
|
1388
|
+
this.plugin.supportsBatchUpdates() === false) {
|
|
1389
|
+
acc.push(table.entriesToUpsertOperations(cur.entries, cur.segment));
|
|
1390
|
+
}
|
|
1391
|
+
else {
|
|
1392
|
+
acc.push(table.metadataToUpdateOperations(cur.entries, cur.segment));
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1166
1395
|
else {
|
|
1167
1396
|
acc.push(this.idsToDeleteOperation(cur.ids, cur.segment));
|
|
1168
1397
|
}
|
|
@@ -1179,8 +1408,15 @@ class NimbusSqliteStore {
|
|
|
1179
1408
|
this.plugin
|
|
1180
1409
|
.registerOnChangedListener(async (changes) => {
|
|
1181
1410
|
const durableChanges = changes.map((c) => {
|
|
1411
|
+
let type = c.type === 'upsert' ? 'setEntries' : 'evictEntries';
|
|
1412
|
+
// if our context contains a type then set that as our main level type
|
|
1413
|
+
// allows us in the future of updates to specify the segment change happening
|
|
1414
|
+
// example being update call on metadata only or updating data
|
|
1415
|
+
if (c.type === 'update' && c.context.type !== undefined) {
|
|
1416
|
+
type = c.context.type;
|
|
1417
|
+
}
|
|
1182
1418
|
return {
|
|
1183
|
-
type
|
|
1419
|
+
type,
|
|
1184
1420
|
ids: c.keys,
|
|
1185
1421
|
isExternalChange: false,
|
|
1186
1422
|
segment: c.context.segment,
|
|
@@ -6470,14 +6706,30 @@ function makeRecordDenormalizingDurableStore(luvio, durableStore, getStoreRecord
|
|
|
6470
6706
|
const operationsWithDenormedRecords = [];
|
|
6471
6707
|
for (let i = 0, len = operations.length; i < len; i++) {
|
|
6472
6708
|
const operation = operations[i];
|
|
6473
|
-
if (
|
|
6474
|
-
|
|
6475
|
-
|
|
6709
|
+
if (durableStore.plugin !== undefined &&
|
|
6710
|
+
durableStore.plugin.supportsBatchUpdates !== undefined &&
|
|
6711
|
+
durableStore.plugin.supportsBatchUpdates() === true) {
|
|
6712
|
+
if (operation.segment !== DefaultDurableSegment ||
|
|
6713
|
+
operation.type !== 'setEntries') {
|
|
6714
|
+
operationsWithDenormedRecords.push(operation);
|
|
6715
|
+
continue;
|
|
6716
|
+
}
|
|
6717
|
+
operationsWithDenormedRecords.push({
|
|
6718
|
+
...operation,
|
|
6719
|
+
entries: denormalizeEntries(operation.entries),
|
|
6720
|
+
});
|
|
6721
|
+
}
|
|
6722
|
+
else {
|
|
6723
|
+
if (operation.segment !== DefaultDurableSegment ||
|
|
6724
|
+
operation.type === 'evictEntries') {
|
|
6725
|
+
operationsWithDenormedRecords.push(operation);
|
|
6726
|
+
continue;
|
|
6727
|
+
}
|
|
6728
|
+
operationsWithDenormedRecords.push({
|
|
6729
|
+
...operation,
|
|
6730
|
+
entries: denormalizeEntries(operation.entries),
|
|
6731
|
+
});
|
|
6476
6732
|
}
|
|
6477
|
-
operationsWithDenormedRecords.push({
|
|
6478
|
-
...operation,
|
|
6479
|
-
entries: denormalizeEntries(operation.entries),
|
|
6480
|
-
});
|
|
6481
6733
|
}
|
|
6482
6734
|
return durableStore.batchOperations(operationsWithDenormedRecords);
|
|
6483
6735
|
};
|
|
@@ -6567,4 +6819,4 @@ function ldsRuntimeBridge() {
|
|
|
6567
6819
|
}
|
|
6568
6820
|
|
|
6569
6821
|
export { ldsRuntimeBridge as default };
|
|
6570
|
-
// version: 1.
|
|
6822
|
+
// version: 1.235.0-3790decf0
|