@luvio/environments 0.105.0 → 0.108.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.
@@ -27,19 +27,17 @@ export interface DeprecatedDurableStoreEntry1<T = unknown> {
27
27
  export interface DurableStoreEntries<EntryTypes> {
28
28
  [key: string]: DurableStoreEntry<Extract<EntryTypes, unknown>>;
29
29
  }
30
- export declare enum DurableStoreOperationType {
31
- SetEntries = "setEntries",
32
- EvictEntries = "evictEntries"
33
- }
30
+ export declare type DurableStoreOperationType = 'setEntries' | 'evictEntries';
34
31
  export interface BaseOperation {
32
+ type: DurableStoreOperationType;
35
33
  segment: string;
36
34
  }
37
35
  export interface DurableStoreSetOperation<T> extends BaseOperation {
38
- type: DurableStoreOperationType.SetEntries;
36
+ type: 'setEntries';
39
37
  entries: DurableStoreEntries<T>;
40
38
  }
41
39
  export interface DurableStoreEvictOperation extends BaseOperation {
42
- type: DurableStoreOperationType.EvictEntries;
40
+ type: 'evictEntries';
43
41
  ids: string[];
44
42
  }
45
43
  export declare type DurableStoreOperation<T> = DurableStoreSetOperation<T> | DurableStoreEvictOperation;
@@ -7,11 +7,6 @@ function isDeprecatedDurableStoreEntry(durableRecord) {
7
7
  // Add more deprecated shape checks here
8
8
  return false;
9
9
  }
10
- var DurableStoreOperationType;
11
- (function (DurableStoreOperationType) {
12
- DurableStoreOperationType["SetEntries"] = "setEntries";
13
- DurableStoreOperationType["EvictEntries"] = "evictEntries";
14
- })(DurableStoreOperationType || (DurableStoreOperationType = {}));
15
10
  const DefaultDurableSegment = 'DEFAULT';
16
11
 
17
12
  const { keys, create, assign, freeze } = Object;
@@ -310,7 +305,7 @@ function flushInMemoryStoreValuesToDurableStore(store, durableStore, durableStor
310
305
  const recordKeys = keys(durableRecords);
311
306
  if (recordKeys.length > 0) {
312
307
  durableStoreOperations.push({
313
- type: DurableStoreOperationType.SetEntries,
308
+ type: 'setEntries',
314
309
  entries: durableRecords,
315
310
  segment: DefaultDurableSegment,
316
311
  });
@@ -319,7 +314,7 @@ function flushInMemoryStoreValuesToDurableStore(store, durableStore, durableStor
319
314
  const evictedKeys = keys(evictedRecords);
320
315
  if (evictedKeys.length > 0) {
321
316
  durableStoreOperations.push({
322
- type: DurableStoreOperationType.EvictEntries,
317
+ type: 'evictEntries',
323
318
  ids: evictedKeys,
324
319
  segment: DefaultDurableSegment,
325
320
  });
@@ -424,6 +419,7 @@ function isUnfulfilledSnapshot(cachedSnapshotResult) {
424
419
  function makeDurable(environment, { durableStore, instrumentation }) {
425
420
  let ingestStagingStore = null;
426
421
  const durableTTLStore = new DurableTTLStore(durableStore);
422
+ const mergeKeysPromiseMap = new Map();
427
423
  let initializationPromise = new Promise((resolve) => {
428
424
  const finish = () => {
429
425
  resolve();
@@ -682,52 +678,84 @@ function makeDurable(environment, { durableStore, instrumentation }) {
682
678
  }
683
679
  return {};
684
680
  };
685
- const handleSuccessResponse = function (ingestAndBroadcastFunc, getResponseCacheKeysFunc, existingRecords) {
681
+ const handleSuccessResponse = async function (ingestAndBroadcastFunc, getResponseCacheKeysFunc) {
686
682
  validateNotDisposed();
687
683
  const cacheKeySet = getResponseCacheKeysFunc();
688
- const cacheKeys = new Set(keys(cacheKeySet));
684
+ const cacheKeySetKeys = keys(cacheKeySet);
689
685
  const keysToRevive = {};
690
- cacheKeys.forEach((cacheKey) => {
691
- const key = cacheKeySet[cacheKey];
692
- if (key.mergeable === true &&
693
- (existingRecords === undefined || existingRecords[cacheKey] === undefined)) {
694
- keysToRevive[cacheKey] = true;
686
+ for (const cacheKeySetKey of cacheKeySetKeys) {
687
+ const cacheKey = cacheKeySet[cacheKeySetKey];
688
+ if (cacheKey.mergeable === true) {
689
+ keysToRevive[cacheKeySetKey] = true;
695
690
  }
696
- });
697
- const ingestAndPublish = (revivedRecords) => {
698
- const toPrime = existingRecords !== undefined
699
- ? { ...revivedRecords, ...existingRecords }
700
- : revivedRecords;
701
- ingestStagingStore = buildIngestStagingStore(environment);
702
- ingestStagingStore.records = toPrime;
703
- const snapshotFromMemoryIngest = ingestAndBroadcastFunc();
704
- return publishChangesToDurableStore().then(() => {
705
- if (snapshotFromMemoryIngest === undefined) {
706
- return undefined;
691
+ }
692
+ let snapshotFromMemoryIngest = undefined;
693
+ const keysAsArray = keys(keysToRevive);
694
+ if (keysAsArray.length > 0) {
695
+ // if we need to do an L2 read then L2 write then we need to synchronize
696
+ // our read/merge/ingest/write Promise based on the keys so we don't
697
+ // stomp over any data
698
+ const readWritePromise = (async () => {
699
+ const pendingPromises = [];
700
+ for (const key of keysAsArray) {
701
+ const pendingPromise = mergeKeysPromiseMap.get(key);
702
+ if (pendingPromise !== undefined) {
703
+ // IMPORTANT: while on the synchronous code path we get a
704
+ // handle to pendingPromise and push it onto the array.
705
+ // This is important because later in this synchronous code
706
+ // path we will upsert readWritePromise into the
707
+ // mergeKeysPromiseMap (essentially overwriting pendingPromise
708
+ // in the map).
709
+ pendingPromises.push(pendingPromise);
710
+ }
707
711
  }
708
- if (snapshotFromMemoryIngest.state !== 'Unfulfilled') {
709
- return snapshotFromMemoryIngest;
712
+ await Promise.all(pendingPromises);
713
+ const entries = await durableStore.getEntries(keysAsArray, DefaultDurableSegment);
714
+ ingestStagingStore = buildIngestStagingStore(environment);
715
+ publishDurableStoreEntries(entries, ingestStagingStore.publish.bind(ingestStagingStore),
716
+ // we don't need to prime metadata
717
+ () => { });
718
+ snapshotFromMemoryIngest = ingestAndBroadcastFunc();
719
+ await publishChangesToDurableStore();
720
+ })();
721
+ for (const key of keysAsArray) {
722
+ // we are overwriting the previous promise at this key, but that
723
+ // is ok because we got a handle to it earlier (see the IMPORTANT
724
+ // comment about 35 lines up)
725
+ mergeKeysPromiseMap.set(key, readWritePromise);
726
+ }
727
+ try {
728
+ await readWritePromise;
729
+ }
730
+ finally {
731
+ for (const key of keysAsArray) {
732
+ const pendingPromise = mergeKeysPromiseMap.get(key);
733
+ // cleanup the entry from the map if this is the last promise
734
+ // for that key
735
+ if (pendingPromise === readWritePromise) {
736
+ mergeKeysPromiseMap.delete(key);
737
+ }
710
738
  }
711
- // if snapshot from staging store lookup is unfulfilled then do an L2 lookup
712
- return reviveSnapshot(environment, durableStore, snapshotFromMemoryIngest, durableStoreErrorHandler, () => environment.storeLookup(snapshotFromMemoryIngest.select, environment.createSnapshot, snapshotFromMemoryIngest.refresh)).then((result) => {
713
- return result.snapshot;
714
- });
715
- });
716
- };
717
- if (keys(keysToRevive).length === 0) {
718
- return ingestAndPublish({});
739
+ }
719
740
  }
720
- return durableStore
721
- .getEntries(keys(keysToRevive), DefaultDurableSegment)
722
- .then((entries) => {
723
- const existingL2Records = create(null);
724
- publishDurableStoreEntries(entries, (key, record) => {
725
- existingL2Records[key] = record;
726
- },
727
- // we don't need to prime metadata
728
- () => { });
729
- return ingestAndPublish(existingL2Records);
730
- });
741
+ else {
742
+ // we aren't doing any merging so we don't have to synchronize, the
743
+ // underlying DurableStore implementation takes care of R/W sync
744
+ // so all we have to do is ingest then write to L2
745
+ ingestStagingStore = buildIngestStagingStore(environment);
746
+ snapshotFromMemoryIngest = ingestAndBroadcastFunc();
747
+ await publishChangesToDurableStore();
748
+ }
749
+ if (snapshotFromMemoryIngest === undefined) {
750
+ return undefined;
751
+ }
752
+ if (snapshotFromMemoryIngest.state !== 'Unfulfilled') {
753
+ return snapshotFromMemoryIngest;
754
+ }
755
+ // if snapshot from staging store lookup is unfulfilled then do an L2 lookup
756
+ const { select, refresh } = snapshotFromMemoryIngest;
757
+ const result = await reviveSnapshot(environment, durableStore, snapshotFromMemoryIngest, durableStoreErrorHandler, () => environment.storeLookup(select, environment.createSnapshot, refresh));
758
+ return result.snapshot;
731
759
  };
732
760
  const handleErrorResponse = function (ingestAndBroadcastFunc) {
733
761
  validateNotDisposed();
@@ -778,4 +806,4 @@ function makeDurable(environment, { durableStore, instrumentation }) {
778
806
  });
779
807
  }
780
808
 
781
- export { DefaultDurableSegment, DurableStoreOperationType, isDurableEnvironmentEvent, makeDurable, publishDurableStoreEntries };
809
+ export { DefaultDurableSegment, isDurableEnvironmentEvent, makeDurable, publishDurableStoreEntries };
@@ -1,4 +1,4 @@
1
- import type { CacheKeySet, Environment, RecordSource, Snapshot, InMemoryStore } from '@luvio/engine';
1
+ import type { Environment, RecordSource, InMemoryStore } from '@luvio/engine';
2
2
  import type { DurableStore } from './DurableStore';
3
3
  import type { InstrumentationFunction } from './makeDurable/error';
4
4
  import type { TTLOverridesMap } from './DurableTTLStore';
@@ -25,12 +25,6 @@ export interface DurableEnvironment extends Environment {
25
25
  * flow, otherwise returns an empty object.
26
26
  */
27
27
  getIngestStagingStoreMetadata(): InMemoryStore['metadata'];
28
- /**
29
- * Overload of Environment.handleSuccessResponse that takes in an optional
30
- * RecordSource to "prime" the ingest staging store with before calling
31
- * ingest. Useful for merge-able record types
32
- */
33
- handleSuccessResponse<IngestionReturnType extends Snapshot<D, V> | undefined, D, V = unknown>(ingestAndBroadcastFunc: () => IngestionReturnType, getResponseCacheKeysFunc: () => CacheKeySet, existingRecords?: RecordSource): IngestionReturnType | Promise<IngestionReturnType>;
34
28
  }
35
29
  export declare const AdapterContextSegment = "ADAPTER-CONTEXT";
36
30
  export declare const ADAPTER_CONTEXT_ID_SUFFIX = "__NAMED_CONTEXT";
@@ -27,19 +27,17 @@ export interface DeprecatedDurableStoreEntry1<T = unknown> {
27
27
  export interface DurableStoreEntries<EntryTypes> {
28
28
  [key: string]: DurableStoreEntry<Extract<EntryTypes, unknown>>;
29
29
  }
30
- export declare enum DurableStoreOperationType {
31
- SetEntries = "setEntries",
32
- EvictEntries = "evictEntries"
33
- }
30
+ export declare type DurableStoreOperationType = 'setEntries' | 'evictEntries';
34
31
  export interface BaseOperation {
32
+ type: DurableStoreOperationType;
35
33
  segment: string;
36
34
  }
37
35
  export interface DurableStoreSetOperation<T> extends BaseOperation {
38
- type: DurableStoreOperationType.SetEntries;
36
+ type: 'setEntries';
39
37
  entries: DurableStoreEntries<T>;
40
38
  }
41
39
  export interface DurableStoreEvictOperation extends BaseOperation {
42
- type: DurableStoreOperationType.EvictEntries;
40
+ type: 'evictEntries';
43
41
  ids: string[];
44
42
  }
45
43
  export declare type DurableStoreOperation<T> = DurableStoreSetOperation<T> | DurableStoreEvictOperation;
@@ -11,11 +11,6 @@
11
11
  // Add more deprecated shape checks here
12
12
  return false;
13
13
  }
14
- exports.DurableStoreOperationType = void 0;
15
- (function (DurableStoreOperationType) {
16
- DurableStoreOperationType["SetEntries"] = "setEntries";
17
- DurableStoreOperationType["EvictEntries"] = "evictEntries";
18
- })(exports.DurableStoreOperationType || (exports.DurableStoreOperationType = {}));
19
14
  const DefaultDurableSegment = 'DEFAULT';
20
15
 
21
16
  const { keys, create, assign, freeze } = Object;
@@ -314,7 +309,7 @@
314
309
  const recordKeys = keys(durableRecords);
315
310
  if (recordKeys.length > 0) {
316
311
  durableStoreOperations.push({
317
- type: exports.DurableStoreOperationType.SetEntries,
312
+ type: 'setEntries',
318
313
  entries: durableRecords,
319
314
  segment: DefaultDurableSegment,
320
315
  });
@@ -323,7 +318,7 @@
323
318
  const evictedKeys = keys(evictedRecords);
324
319
  if (evictedKeys.length > 0) {
325
320
  durableStoreOperations.push({
326
- type: exports.DurableStoreOperationType.EvictEntries,
321
+ type: 'evictEntries',
327
322
  ids: evictedKeys,
328
323
  segment: DefaultDurableSegment,
329
324
  });
@@ -428,6 +423,7 @@
428
423
  function makeDurable(environment, { durableStore, instrumentation }) {
429
424
  let ingestStagingStore = null;
430
425
  const durableTTLStore = new DurableTTLStore(durableStore);
426
+ const mergeKeysPromiseMap = new Map();
431
427
  let initializationPromise = new Promise((resolve) => {
432
428
  const finish = () => {
433
429
  resolve();
@@ -686,52 +682,84 @@
686
682
  }
687
683
  return {};
688
684
  };
689
- const handleSuccessResponse = function (ingestAndBroadcastFunc, getResponseCacheKeysFunc, existingRecords) {
685
+ const handleSuccessResponse = async function (ingestAndBroadcastFunc, getResponseCacheKeysFunc) {
690
686
  validateNotDisposed();
691
687
  const cacheKeySet = getResponseCacheKeysFunc();
692
- const cacheKeys = new Set(keys(cacheKeySet));
688
+ const cacheKeySetKeys = keys(cacheKeySet);
693
689
  const keysToRevive = {};
694
- cacheKeys.forEach((cacheKey) => {
695
- const key = cacheKeySet[cacheKey];
696
- if (key.mergeable === true &&
697
- (existingRecords === undefined || existingRecords[cacheKey] === undefined)) {
698
- keysToRevive[cacheKey] = true;
690
+ for (const cacheKeySetKey of cacheKeySetKeys) {
691
+ const cacheKey = cacheKeySet[cacheKeySetKey];
692
+ if (cacheKey.mergeable === true) {
693
+ keysToRevive[cacheKeySetKey] = true;
699
694
  }
700
- });
701
- const ingestAndPublish = (revivedRecords) => {
702
- const toPrime = existingRecords !== undefined
703
- ? { ...revivedRecords, ...existingRecords }
704
- : revivedRecords;
705
- ingestStagingStore = buildIngestStagingStore(environment);
706
- ingestStagingStore.records = toPrime;
707
- const snapshotFromMemoryIngest = ingestAndBroadcastFunc();
708
- return publishChangesToDurableStore().then(() => {
709
- if (snapshotFromMemoryIngest === undefined) {
710
- return undefined;
695
+ }
696
+ let snapshotFromMemoryIngest = undefined;
697
+ const keysAsArray = keys(keysToRevive);
698
+ if (keysAsArray.length > 0) {
699
+ // if we need to do an L2 read then L2 write then we need to synchronize
700
+ // our read/merge/ingest/write Promise based on the keys so we don't
701
+ // stomp over any data
702
+ const readWritePromise = (async () => {
703
+ const pendingPromises = [];
704
+ for (const key of keysAsArray) {
705
+ const pendingPromise = mergeKeysPromiseMap.get(key);
706
+ if (pendingPromise !== undefined) {
707
+ // IMPORTANT: while on the synchronous code path we get a
708
+ // handle to pendingPromise and push it onto the array.
709
+ // This is important because later in this synchronous code
710
+ // path we will upsert readWritePromise into the
711
+ // mergeKeysPromiseMap (essentially overwriting pendingPromise
712
+ // in the map).
713
+ pendingPromises.push(pendingPromise);
714
+ }
711
715
  }
712
- if (snapshotFromMemoryIngest.state !== 'Unfulfilled') {
713
- return snapshotFromMemoryIngest;
716
+ await Promise.all(pendingPromises);
717
+ const entries = await durableStore.getEntries(keysAsArray, DefaultDurableSegment);
718
+ ingestStagingStore = buildIngestStagingStore(environment);
719
+ publishDurableStoreEntries(entries, ingestStagingStore.publish.bind(ingestStagingStore),
720
+ // we don't need to prime metadata
721
+ () => { });
722
+ snapshotFromMemoryIngest = ingestAndBroadcastFunc();
723
+ await publishChangesToDurableStore();
724
+ })();
725
+ for (const key of keysAsArray) {
726
+ // we are overwriting the previous promise at this key, but that
727
+ // is ok because we got a handle to it earlier (see the IMPORTANT
728
+ // comment about 35 lines up)
729
+ mergeKeysPromiseMap.set(key, readWritePromise);
730
+ }
731
+ try {
732
+ await readWritePromise;
733
+ }
734
+ finally {
735
+ for (const key of keysAsArray) {
736
+ const pendingPromise = mergeKeysPromiseMap.get(key);
737
+ // cleanup the entry from the map if this is the last promise
738
+ // for that key
739
+ if (pendingPromise === readWritePromise) {
740
+ mergeKeysPromiseMap.delete(key);
741
+ }
714
742
  }
715
- // if snapshot from staging store lookup is unfulfilled then do an L2 lookup
716
- return reviveSnapshot(environment, durableStore, snapshotFromMemoryIngest, durableStoreErrorHandler, () => environment.storeLookup(snapshotFromMemoryIngest.select, environment.createSnapshot, snapshotFromMemoryIngest.refresh)).then((result) => {
717
- return result.snapshot;
718
- });
719
- });
720
- };
721
- if (keys(keysToRevive).length === 0) {
722
- return ingestAndPublish({});
743
+ }
723
744
  }
724
- return durableStore
725
- .getEntries(keys(keysToRevive), DefaultDurableSegment)
726
- .then((entries) => {
727
- const existingL2Records = create(null);
728
- publishDurableStoreEntries(entries, (key, record) => {
729
- existingL2Records[key] = record;
730
- },
731
- // we don't need to prime metadata
732
- () => { });
733
- return ingestAndPublish(existingL2Records);
734
- });
745
+ else {
746
+ // we aren't doing any merging so we don't have to synchronize, the
747
+ // underlying DurableStore implementation takes care of R/W sync
748
+ // so all we have to do is ingest then write to L2
749
+ ingestStagingStore = buildIngestStagingStore(environment);
750
+ snapshotFromMemoryIngest = ingestAndBroadcastFunc();
751
+ await publishChangesToDurableStore();
752
+ }
753
+ if (snapshotFromMemoryIngest === undefined) {
754
+ return undefined;
755
+ }
756
+ if (snapshotFromMemoryIngest.state !== 'Unfulfilled') {
757
+ return snapshotFromMemoryIngest;
758
+ }
759
+ // if snapshot from staging store lookup is unfulfilled then do an L2 lookup
760
+ const { select, refresh } = snapshotFromMemoryIngest;
761
+ const result = await reviveSnapshot(environment, durableStore, snapshotFromMemoryIngest, durableStoreErrorHandler, () => environment.storeLookup(select, environment.createSnapshot, refresh));
762
+ return result.snapshot;
735
763
  };
736
764
  const handleErrorResponse = function (ingestAndBroadcastFunc) {
737
765
  validateNotDisposed();
@@ -1,4 +1,4 @@
1
- import type { CacheKeySet, Environment, RecordSource, Snapshot, InMemoryStore } from '@luvio/engine';
1
+ import type { Environment, RecordSource, InMemoryStore } from '@luvio/engine';
2
2
  import type { DurableStore } from './DurableStore';
3
3
  import type { InstrumentationFunction } from './makeDurable/error';
4
4
  import type { TTLOverridesMap } from './DurableTTLStore';
@@ -25,12 +25,6 @@ export interface DurableEnvironment extends Environment {
25
25
  * flow, otherwise returns an empty object.
26
26
  */
27
27
  getIngestStagingStoreMetadata(): InMemoryStore['metadata'];
28
- /**
29
- * Overload of Environment.handleSuccessResponse that takes in an optional
30
- * RecordSource to "prime" the ingest staging store with before calling
31
- * ingest. Useful for merge-able record types
32
- */
33
- handleSuccessResponse<IngestionReturnType extends Snapshot<D, V> | undefined, D, V = unknown>(ingestAndBroadcastFunc: () => IngestionReturnType, getResponseCacheKeysFunc: () => CacheKeySet, existingRecords?: RecordSource): IngestionReturnType | Promise<IngestionReturnType>;
34
28
  }
35
29
  export declare const AdapterContextSegment = "ADAPTER-CONTEXT";
36
30
  export declare const ADAPTER_CONTEXT_ID_SUFFIX = "__NAMED_CONTEXT";
@@ -27,19 +27,17 @@ export interface DeprecatedDurableStoreEntry1<T = unknown> {
27
27
  export interface DurableStoreEntries<EntryTypes> {
28
28
  [key: string]: DurableStoreEntry<Extract<EntryTypes, unknown>>;
29
29
  }
30
- export declare enum DurableStoreOperationType {
31
- SetEntries = "setEntries",
32
- EvictEntries = "evictEntries"
33
- }
30
+ export declare type DurableStoreOperationType = 'setEntries' | 'evictEntries';
34
31
  export interface BaseOperation {
32
+ type: DurableStoreOperationType;
35
33
  segment: string;
36
34
  }
37
35
  export interface DurableStoreSetOperation<T> extends BaseOperation {
38
- type: DurableStoreOperationType.SetEntries;
36
+ type: 'setEntries';
39
37
  entries: DurableStoreEntries<T>;
40
38
  }
41
39
  export interface DurableStoreEvictOperation extends BaseOperation {
42
- type: DurableStoreOperationType.EvictEntries;
40
+ type: 'evictEntries';
43
41
  ids: string[];
44
42
  }
45
43
  export declare type DurableStoreOperation<T> = DurableStoreSetOperation<T> | DurableStoreEvictOperation;
@@ -11,11 +11,6 @@
11
11
  // Add more deprecated shape checks here
12
12
  return false;
13
13
  }
14
- exports.DurableStoreOperationType = void 0;
15
- (function (DurableStoreOperationType) {
16
- DurableStoreOperationType["SetEntries"] = "setEntries";
17
- DurableStoreOperationType["EvictEntries"] = "evictEntries";
18
- })(exports.DurableStoreOperationType || (exports.DurableStoreOperationType = {}));
19
14
  var DefaultDurableSegment = 'DEFAULT';
20
15
 
21
16
  /*! *****************************************************************************
@@ -395,7 +390,7 @@
395
390
  var recordKeys = keys(durableRecords);
396
391
  if (recordKeys.length > 0) {
397
392
  durableStoreOperations.push({
398
- type: exports.DurableStoreOperationType.SetEntries,
393
+ type: 'setEntries',
399
394
  entries: durableRecords,
400
395
  segment: DefaultDurableSegment,
401
396
  });
@@ -404,7 +399,7 @@
404
399
  var evictedKeys = keys(evictedRecords);
405
400
  if (evictedKeys.length > 0) {
406
401
  durableStoreOperations.push({
407
- type: exports.DurableStoreOperationType.EvictEntries,
402
+ type: 'evictEntries',
408
403
  ids: evictedKeys,
409
404
  segment: DefaultDurableSegment,
410
405
  });
@@ -521,6 +516,7 @@
521
516
  var durableStore = _a.durableStore, instrumentation = _a.instrumentation;
522
517
  var ingestStagingStore = null;
523
518
  var durableTTLStore = new DurableTTLStore(durableStore);
519
+ var mergeKeysPromiseMap = new Map();
524
520
  var initializationPromise = new Promise(function (resolve) {
525
521
  var finish = function () {
526
522
  resolve();
@@ -784,52 +780,114 @@
784
780
  }
785
781
  return {};
786
782
  };
787
- var handleSuccessResponse = function (ingestAndBroadcastFunc, getResponseCacheKeysFunc, existingRecords) {
788
- validateNotDisposed();
789
- var cacheKeySet = getResponseCacheKeysFunc();
790
- var cacheKeys = new Set(keys(cacheKeySet));
791
- var keysToRevive = {};
792
- cacheKeys.forEach(function (cacheKey) {
793
- var key = cacheKeySet[cacheKey];
794
- if (key.mergeable === true &&
795
- (existingRecords === undefined || existingRecords[cacheKey] === undefined)) {
796
- keysToRevive[cacheKey] = true;
797
- }
798
- });
799
- var ingestAndPublish = function (revivedRecords) {
800
- var toPrime = existingRecords !== undefined
801
- ? __assign(__assign({}, revivedRecords), existingRecords) : revivedRecords;
802
- ingestStagingStore = buildIngestStagingStore(environment);
803
- ingestStagingStore.records = toPrime;
804
- var snapshotFromMemoryIngest = ingestAndBroadcastFunc();
805
- return publishChangesToDurableStore().then(function () {
806
- if (snapshotFromMemoryIngest === undefined) {
807
- return undefined;
808
- }
809
- if (snapshotFromMemoryIngest.state !== 'Unfulfilled') {
810
- return snapshotFromMemoryIngest;
783
+ var handleSuccessResponse = function (ingestAndBroadcastFunc, getResponseCacheKeysFunc) {
784
+ return __awaiter(this, void 0, void 0, function () {
785
+ var cacheKeySet, cacheKeySetKeys, keysToRevive, _i, cacheKeySetKeys_1, cacheKeySetKey, cacheKey, snapshotFromMemoryIngest, keysAsArray, readWritePromise, _a, keysAsArray_1, key, _b, keysAsArray_2, key, pendingPromise, _c, select, refresh, result;
786
+ var _this = this;
787
+ return __generator(this, function (_d) {
788
+ switch (_d.label) {
789
+ case 0:
790
+ validateNotDisposed();
791
+ cacheKeySet = getResponseCacheKeysFunc();
792
+ cacheKeySetKeys = keys(cacheKeySet);
793
+ keysToRevive = {};
794
+ for (_i = 0, cacheKeySetKeys_1 = cacheKeySetKeys; _i < cacheKeySetKeys_1.length; _i++) {
795
+ cacheKeySetKey = cacheKeySetKeys_1[_i];
796
+ cacheKey = cacheKeySet[cacheKeySetKey];
797
+ if (cacheKey.mergeable === true) {
798
+ keysToRevive[cacheKeySetKey] = true;
799
+ }
800
+ }
801
+ snapshotFromMemoryIngest = undefined;
802
+ keysAsArray = keys(keysToRevive);
803
+ if (!(keysAsArray.length > 0)) return [3 /*break*/, 5];
804
+ readWritePromise = (function () { return __awaiter(_this, void 0, void 0, function () {
805
+ var pendingPromises, _i, keysAsArray_3, key, pendingPromise, entries;
806
+ return __generator(this, function (_a) {
807
+ switch (_a.label) {
808
+ case 0:
809
+ pendingPromises = [];
810
+ for (_i = 0, keysAsArray_3 = keysAsArray; _i < keysAsArray_3.length; _i++) {
811
+ key = keysAsArray_3[_i];
812
+ pendingPromise = mergeKeysPromiseMap.get(key);
813
+ if (pendingPromise !== undefined) {
814
+ // IMPORTANT: while on the synchronous code path we get a
815
+ // handle to pendingPromise and push it onto the array.
816
+ // This is important because later in this synchronous code
817
+ // path we will upsert readWritePromise into the
818
+ // mergeKeysPromiseMap (essentially overwriting pendingPromise
819
+ // in the map).
820
+ pendingPromises.push(pendingPromise);
821
+ }
822
+ }
823
+ return [4 /*yield*/, Promise.all(pendingPromises)];
824
+ case 1:
825
+ _a.sent();
826
+ return [4 /*yield*/, durableStore.getEntries(keysAsArray, DefaultDurableSegment)];
827
+ case 2:
828
+ entries = _a.sent();
829
+ ingestStagingStore = buildIngestStagingStore(environment);
830
+ publishDurableStoreEntries(entries, ingestStagingStore.publish.bind(ingestStagingStore),
831
+ // we don't need to prime metadata
832
+ function () { });
833
+ snapshotFromMemoryIngest = ingestAndBroadcastFunc();
834
+ return [4 /*yield*/, publishChangesToDurableStore()];
835
+ case 3:
836
+ _a.sent();
837
+ return [2 /*return*/];
838
+ }
839
+ });
840
+ }); })();
841
+ for (_a = 0, keysAsArray_1 = keysAsArray; _a < keysAsArray_1.length; _a++) {
842
+ key = keysAsArray_1[_a];
843
+ // we are overwriting the previous promise at this key, but that
844
+ // is ok because we got a handle to it earlier (see the IMPORTANT
845
+ // comment about 35 lines up)
846
+ mergeKeysPromiseMap.set(key, readWritePromise);
847
+ }
848
+ _d.label = 1;
849
+ case 1:
850
+ _d.trys.push([1, , 3, 4]);
851
+ return [4 /*yield*/, readWritePromise];
852
+ case 2:
853
+ _d.sent();
854
+ return [3 /*break*/, 4];
855
+ case 3:
856
+ for (_b = 0, keysAsArray_2 = keysAsArray; _b < keysAsArray_2.length; _b++) {
857
+ key = keysAsArray_2[_b];
858
+ pendingPromise = mergeKeysPromiseMap.get(key);
859
+ // cleanup the entry from the map if this is the last promise
860
+ // for that key
861
+ if (pendingPromise === readWritePromise) {
862
+ mergeKeysPromiseMap.delete(key);
863
+ }
864
+ }
865
+ return [7 /*endfinally*/];
866
+ case 4: return [3 /*break*/, 7];
867
+ case 5:
868
+ // we aren't doing any merging so we don't have to synchronize, the
869
+ // underlying DurableStore implementation takes care of R/W sync
870
+ // so all we have to do is ingest then write to L2
871
+ ingestStagingStore = buildIngestStagingStore(environment);
872
+ snapshotFromMemoryIngest = ingestAndBroadcastFunc();
873
+ return [4 /*yield*/, publishChangesToDurableStore()];
874
+ case 6:
875
+ _d.sent();
876
+ _d.label = 7;
877
+ case 7:
878
+ if (snapshotFromMemoryIngest === undefined) {
879
+ return [2 /*return*/, undefined];
880
+ }
881
+ if (snapshotFromMemoryIngest.state !== 'Unfulfilled') {
882
+ return [2 /*return*/, snapshotFromMemoryIngest];
883
+ }
884
+ _c = snapshotFromMemoryIngest, select = _c.select, refresh = _c.refresh;
885
+ return [4 /*yield*/, reviveSnapshot(environment, durableStore, snapshotFromMemoryIngest, durableStoreErrorHandler, function () { return environment.storeLookup(select, environment.createSnapshot, refresh); })];
886
+ case 8:
887
+ result = _d.sent();
888
+ return [2 /*return*/, result.snapshot];
811
889
  }
812
- // if snapshot from staging store lookup is unfulfilled then do an L2 lookup
813
- return reviveSnapshot(environment, durableStore, snapshotFromMemoryIngest, durableStoreErrorHandler, function () {
814
- return environment.storeLookup(snapshotFromMemoryIngest.select, environment.createSnapshot, snapshotFromMemoryIngest.refresh);
815
- }).then(function (result) {
816
- return result.snapshot;
817
- });
818
890
  });
819
- };
820
- if (keys(keysToRevive).length === 0) {
821
- return ingestAndPublish({});
822
- }
823
- return durableStore
824
- .getEntries(keys(keysToRevive), DefaultDurableSegment)
825
- .then(function (entries) {
826
- var existingL2Records = create(null);
827
- publishDurableStoreEntries(entries, function (key, record) {
828
- existingL2Records[key] = record;
829
- },
830
- // we don't need to prime metadata
831
- function () { });
832
- return ingestAndPublish(existingL2Records);
833
891
  });
834
892
  };
835
893
  var handleErrorResponse = function (ingestAndBroadcastFunc) {
@@ -1,4 +1,4 @@
1
- import type { CacheKeySet, Environment, RecordSource, Snapshot, InMemoryStore } from '@luvio/engine';
1
+ import type { Environment, RecordSource, InMemoryStore } from '@luvio/engine';
2
2
  import type { DurableStore } from './DurableStore';
3
3
  import type { InstrumentationFunction } from './makeDurable/error';
4
4
  import type { TTLOverridesMap } from './DurableTTLStore';
@@ -25,12 +25,6 @@ export interface DurableEnvironment extends Environment {
25
25
  * flow, otherwise returns an empty object.
26
26
  */
27
27
  getIngestStagingStoreMetadata(): InMemoryStore['metadata'];
28
- /**
29
- * Overload of Environment.handleSuccessResponse that takes in an optional
30
- * RecordSource to "prime" the ingest staging store with before calling
31
- * ingest. Useful for merge-able record types
32
- */
33
- handleSuccessResponse<IngestionReturnType extends Snapshot<D, V> | undefined, D, V = unknown>(ingestAndBroadcastFunc: () => IngestionReturnType, getResponseCacheKeysFunc: () => CacheKeySet, existingRecords?: RecordSource): IngestionReturnType | Promise<IngestionReturnType>;
34
28
  }
35
29
  export declare const AdapterContextSegment = "ADAPTER-CONTEXT";
36
30
  export declare const ADAPTER_CONTEXT_ID_SUFFIX = "__NAMED_CONTEXT";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@luvio/environments",
3
- "version": "0.105.0",
3
+ "version": "0.108.0",
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.105.0"
26
+ "@luvio/engine": "0.108.0"
27
27
  },
28
28
  "bundlesize": [
29
29
  {