@luvio/environments 0.138.7 → 0.138.8-244.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.
@@ -18,6 +18,7 @@ function isDeprecatedDurableStoreEntry(durableRecord) {
18
18
  return false;
19
19
  }
20
20
  const DefaultDurableSegment = 'DEFAULT';
21
+ const RedirectDurableSegment = 'REDIRECT_KEYS';
21
22
 
22
23
  const { keys, create, assign, freeze } = Object;
23
24
  const { isArray } = Array;
@@ -291,7 +292,7 @@ function copy(source) {
291
292
  }
292
293
  return { ...source };
293
294
  }
294
- function flushInMemoryStoreValuesToDurableStore(store, durableStore, durableStoreErrorHandler) {
295
+ function flushInMemoryStoreValuesToDurableStore(store, durableStore, durableStoreErrorHandler, redirects, additionalDurableStoreOperations = []) {
295
296
  const durableRecords = create(null);
296
297
  const evictedRecords = create(null);
297
298
  const { records, metadata: storeMetadata, visitedIds, refreshedIds, } = store.fallbackStringKeyInMemoryStore;
@@ -320,7 +321,7 @@ function flushInMemoryStoreValuesToDurableStore(store, durableStore, durableStor
320
321
  };
321
322
  }
322
323
  }
323
- const durableStoreOperations = [];
324
+ const durableStoreOperations = additionalDurableStoreOperations;
324
325
  // publishes
325
326
  const recordKeys = keys(durableRecords);
326
327
  if (recordKeys.length > 0) {
@@ -330,6 +331,18 @@ function flushInMemoryStoreValuesToDurableStore(store, durableStore, durableStor
330
331
  segment: DefaultDurableSegment,
331
332
  });
332
333
  }
334
+ // redirects
335
+ redirects.forEach((value, key) => {
336
+ durableStoreOperations.push({
337
+ type: 'setEntries',
338
+ entries: {
339
+ [key]: {
340
+ data: { key, redirect: value },
341
+ },
342
+ },
343
+ segment: RedirectDurableSegment,
344
+ });
345
+ });
333
346
  // evicts
334
347
  const evictedKeys = keys(evictedRecords);
335
348
  if (evictedKeys.length > 0) {
@@ -378,6 +391,19 @@ function buildIngestStagingStore(environment) {
378
391
  return environment.storeBuildIngestionStagingStore();
379
392
  }
380
393
 
394
+ async function reviveRedirects(durableStore, env) {
395
+ const entries = await durableStore.getAllEntries(RedirectDurableSegment);
396
+ if (entries) {
397
+ for (const durableEntry of Object.keys(entries)) {
398
+ const entry = entries[durableEntry];
399
+ const { data: { key, redirect }, } = entry;
400
+ if (entry) {
401
+ env.storeRedirect(key, redirect);
402
+ }
403
+ }
404
+ }
405
+ }
406
+
381
407
  const AdapterContextSegment = 'ADAPTER-CONTEXT';
382
408
  const ADAPTER_CONTEXT_ID_SUFFIX = '__NAMED_CONTEXT';
383
409
  async function reviveOrCreateContext(adapterId, durableStore, durableStoreErrorHandler, contextStores, pendingContextStoreKeys, onContextLoaded) {
@@ -440,13 +466,18 @@ function makeDurable(environment, { durableStore, instrumentation }) {
440
466
  // event. If this instance of makeDurable caused that L2 write we can ignore that
441
467
  // on change event. This Set helps us do that.
442
468
  const pendingContextStoreKeys = new Set();
469
+ // redirects that need to be flushed to the durable store
470
+ const pendingStoreRedirects = new Map();
443
471
  const contextStores = create(null);
444
472
  let initializationPromise = new Promise((resolve) => {
445
473
  const finish = () => {
446
474
  resolve();
447
475
  initializationPromise = undefined;
448
476
  };
449
- reviveTTLOverrides(durableTTLStore, environment).then(finish);
477
+ Promise.all([
478
+ reviveTTLOverrides(durableTTLStore, environment),
479
+ reviveRedirects(durableStore, environment),
480
+ ]).then(finish);
450
481
  });
451
482
  //instrumentation for durable store errors
452
483
  const durableStoreErrorHandler = handleDurableStoreRejection(instrumentation);
@@ -459,6 +490,8 @@ function makeDurable(environment, { durableStore, instrumentation }) {
459
490
  const unsubscribe = durableStore.registerOnChangedListener(async (changes) => {
460
491
  const defaultSegmentKeys = [];
461
492
  const adapterContextSegmentKeys = [];
493
+ const redirectSegmentKeys = [];
494
+ let shouldBroadcast = false;
462
495
  for (let i = 0, len = changes.length; i < len; i++) {
463
496
  const change = changes[i];
464
497
  // we only care about changes to the data which is stored in the default
@@ -469,6 +502,20 @@ function makeDurable(environment, { durableStore, instrumentation }) {
469
502
  else if (change.segment === AdapterContextSegment) {
470
503
  adapterContextSegmentKeys.push(...change.ids);
471
504
  }
505
+ else if (change.segment === RedirectDurableSegment) {
506
+ redirectSegmentKeys.push(...change.ids);
507
+ }
508
+ }
509
+ if (redirectSegmentKeys.length > 0) {
510
+ const redirectEntries = await durableStore.getEntries(redirectSegmentKeys, RedirectDurableSegment);
511
+ if (redirectEntries !== undefined) {
512
+ const redirectKeys = Object.keys(redirectEntries);
513
+ for (const key of redirectKeys) {
514
+ const redirectData = redirectEntries[key];
515
+ environment.storeRedirect(redirectData.data.key, redirectData.data.redirect);
516
+ shouldBroadcast = true;
517
+ }
518
+ }
472
519
  }
473
520
  // process adapter context changes
474
521
  const adapterContextKeysFromDifferentInstance = [];
@@ -505,10 +552,6 @@ function makeDurable(environment, { durableStore, instrumentation }) {
505
552
  if (defaultSegmentKeysLength > 0) {
506
553
  for (let i = 0; i < defaultSegmentKeysLength; i++) {
507
554
  const key = defaultSegmentKeys[i];
508
- const canonical = environment.storeGetCanonicalKey(key);
509
- if (canonical !== key) {
510
- continue;
511
- }
512
555
  // TODO: W-8909393 If expiration is the only thing that changed we should not evict the data... so
513
556
  // if we stored expiration and data at different keys (or same keys in different segments)
514
557
  // then we could know if only the expiration has changed and we wouldn't need to evict
@@ -516,6 +559,9 @@ function makeDurable(environment, { durableStore, instrumentation }) {
516
559
  // call base environment storeEvict so this evict is not tracked for durable deletion
517
560
  environment.storeEvict(key);
518
561
  }
562
+ shouldBroadcast = true;
563
+ }
564
+ if (shouldBroadcast) {
519
565
  await environment.storeBroadcast(rebuildSnapshot, environment.snapshotAvailable);
520
566
  }
521
567
  });
@@ -566,12 +612,13 @@ function makeDurable(environment, { durableStore, instrumentation }) {
566
612
  // call the base storeBroadcast
567
613
  return publishChangesToDurableStore();
568
614
  };
569
- const publishChangesToDurableStore = function () {
615
+ const publishChangesToDurableStore = function (additionalDurableStoreOperations) {
570
616
  validateNotDisposed();
571
617
  if (ingestStagingStore === null) {
572
618
  return Promise.resolve();
573
619
  }
574
- const promise = flushInMemoryStoreValuesToDurableStore(ingestStagingStore, durableStore, durableStoreErrorHandler);
620
+ const promise = flushInMemoryStoreValuesToDurableStore(ingestStagingStore, durableStore, durableStoreErrorHandler, new Map(pendingStoreRedirects), additionalDurableStoreOperations);
621
+ pendingStoreRedirects.clear();
575
622
  ingestStagingStore = null;
576
623
  return promise;
577
624
  };
@@ -653,6 +700,7 @@ function makeDurable(environment, { durableStore, instrumentation }) {
653
700
  };
654
701
  const storeRedirect = function (existingKey, canonicalKey) {
655
702
  validateNotDisposed();
703
+ pendingStoreRedirects.set(existingKey, canonicalKey);
656
704
  // call redirect on staging store so "old" keys are removed from L2 on
657
705
  // the next publishChangesToDurableStore. NOTE: we don't need to call
658
706
  // redirect on the base environment store because staging store and base
@@ -746,7 +794,8 @@ function makeDurable(environment, { durableStore, instrumentation }) {
746
794
  for (const cacheKeyMapKey of cacheKeyMapKeys) {
747
795
  const cacheKey = cacheKeyMap.get(cacheKeyMapKey);
748
796
  if (cacheKey.mergeable === true) {
749
- keysToRevive.add(cacheKeyMapKey);
797
+ const canonical = environment.storeGetCanonicalKey(cacheKeyMapKey);
798
+ keysToRevive.add(canonical);
750
799
  }
751
800
  }
752
801
  let snapshotFromMemoryIngest = undefined;
@@ -42,7 +42,9 @@ export interface DurableStoreEvictOperation extends BaseOperation {
42
42
  }
43
43
  export type DurableStoreOperation<T> = DurableStoreSetOperation<T> | DurableStoreEvictOperation;
44
44
  export type DefaultDurableSegmentName = 'DEFAULT';
45
+ export type RedirectDurableSegmentName = 'REDIRECT_KEYS';
45
46
  export declare const DefaultDurableSegment: DefaultDurableSegmentName;
47
+ export declare const RedirectDurableSegment: RedirectDurableSegmentName;
46
48
  export interface DurableStoreChange {
47
49
  /** The entry IDs of the entries that have changed */
48
50
  ids: string[];
@@ -1,4 +1,4 @@
1
1
  import type { InMemoryStore } from '@luvio/engine';
2
- import type { DurableStore } from '../DurableStore';
2
+ import type { DurableStore, DurableStoreOperation } from '../DurableStore';
3
3
  import type { DurableStoreRejectionHandler } from './error';
4
- export declare function flushInMemoryStoreValuesToDurableStore(store: InMemoryStore, durableStore: DurableStore, durableStoreErrorHandler: DurableStoreRejectionHandler): Promise<void>;
4
+ export declare function flushInMemoryStoreValuesToDurableStore(store: InMemoryStore, durableStore: DurableStore, durableStoreErrorHandler: DurableStoreRejectionHandler, redirects: Map<string, string>, additionalDurableStoreOperations?: DurableStoreOperation<unknown>[]): Promise<void>;
@@ -0,0 +1,7 @@
1
+ import type { Environment } from '@luvio/engine';
2
+ import type { DurableStore } from '../DurableStore';
3
+ export interface RedirectData {
4
+ key: string;
5
+ redirect: string;
6
+ }
7
+ export declare function reviveRedirects(durableStore: DurableStore, env: Environment): Promise<void>;
@@ -1,5 +1,5 @@
1
1
  import type { Environment, RecordSource, InMemoryStore } from '@luvio/engine';
2
- import type { DurableStore } from './DurableStore';
2
+ import type { DurableStore, DurableStoreOperation } from './DurableStore';
3
3
  import type { InstrumentationFunction } from './makeDurable/error';
4
4
  import type { TTLOverridesMap } from './DurableTTLStore';
5
5
  export interface DurableEnvironment extends Environment {
@@ -8,9 +8,12 @@ export interface DurableEnvironment extends Environment {
8
8
  */
9
9
  dispose(): Promise<void>;
10
10
  /**
11
- * publishes the pending changes to the durable store
11
+ * Publishes the pending changes in the ingestStagingStore to the durable store.
12
+ *
13
+ * Optionally takes in additional DurableStore operations to allow for an
14
+ * "atomic" transaction to the DurableStore.
12
15
  */
13
- publishChangesToDurableStore(): Promise<void>;
16
+ publishChangesToDurableStore(additionalDurableStoreOperations?: DurableStoreOperation<unknown>[]): Promise<void>;
14
17
  /**
15
18
  * gets all the stored ttl overrides stored in the durable store
16
19
  */
@@ -22,6 +22,7 @@
22
22
  return false;
23
23
  }
24
24
  const DefaultDurableSegment = 'DEFAULT';
25
+ const RedirectDurableSegment = 'REDIRECT_KEYS';
25
26
 
26
27
  const { keys, create, assign, freeze } = Object;
27
28
  const { isArray } = Array;
@@ -295,7 +296,7 @@
295
296
  }
296
297
  return { ...source };
297
298
  }
298
- function flushInMemoryStoreValuesToDurableStore(store, durableStore, durableStoreErrorHandler) {
299
+ function flushInMemoryStoreValuesToDurableStore(store, durableStore, durableStoreErrorHandler, redirects, additionalDurableStoreOperations = []) {
299
300
  const durableRecords = create(null);
300
301
  const evictedRecords = create(null);
301
302
  const { records, metadata: storeMetadata, visitedIds, refreshedIds, } = store.fallbackStringKeyInMemoryStore;
@@ -324,7 +325,7 @@
324
325
  };
325
326
  }
326
327
  }
327
- const durableStoreOperations = [];
328
+ const durableStoreOperations = additionalDurableStoreOperations;
328
329
  // publishes
329
330
  const recordKeys = keys(durableRecords);
330
331
  if (recordKeys.length > 0) {
@@ -334,6 +335,18 @@
334
335
  segment: DefaultDurableSegment,
335
336
  });
336
337
  }
338
+ // redirects
339
+ redirects.forEach((value, key) => {
340
+ durableStoreOperations.push({
341
+ type: 'setEntries',
342
+ entries: {
343
+ [key]: {
344
+ data: { key, redirect: value },
345
+ },
346
+ },
347
+ segment: RedirectDurableSegment,
348
+ });
349
+ });
337
350
  // evicts
338
351
  const evictedKeys = keys(evictedRecords);
339
352
  if (evictedKeys.length > 0) {
@@ -382,6 +395,19 @@
382
395
  return environment.storeBuildIngestionStagingStore();
383
396
  }
384
397
 
398
+ async function reviveRedirects(durableStore, env) {
399
+ const entries = await durableStore.getAllEntries(RedirectDurableSegment);
400
+ if (entries) {
401
+ for (const durableEntry of Object.keys(entries)) {
402
+ const entry = entries[durableEntry];
403
+ const { data: { key, redirect }, } = entry;
404
+ if (entry) {
405
+ env.storeRedirect(key, redirect);
406
+ }
407
+ }
408
+ }
409
+ }
410
+
385
411
  const AdapterContextSegment = 'ADAPTER-CONTEXT';
386
412
  const ADAPTER_CONTEXT_ID_SUFFIX = '__NAMED_CONTEXT';
387
413
  async function reviveOrCreateContext(adapterId, durableStore, durableStoreErrorHandler, contextStores, pendingContextStoreKeys, onContextLoaded) {
@@ -444,13 +470,18 @@
444
470
  // event. If this instance of makeDurable caused that L2 write we can ignore that
445
471
  // on change event. This Set helps us do that.
446
472
  const pendingContextStoreKeys = new Set();
473
+ // redirects that need to be flushed to the durable store
474
+ const pendingStoreRedirects = new Map();
447
475
  const contextStores = create(null);
448
476
  let initializationPromise = new Promise((resolve) => {
449
477
  const finish = () => {
450
478
  resolve();
451
479
  initializationPromise = undefined;
452
480
  };
453
- reviveTTLOverrides(durableTTLStore, environment).then(finish);
481
+ Promise.all([
482
+ reviveTTLOverrides(durableTTLStore, environment),
483
+ reviveRedirects(durableStore, environment),
484
+ ]).then(finish);
454
485
  });
455
486
  //instrumentation for durable store errors
456
487
  const durableStoreErrorHandler = handleDurableStoreRejection(instrumentation);
@@ -463,6 +494,8 @@
463
494
  const unsubscribe = durableStore.registerOnChangedListener(async (changes) => {
464
495
  const defaultSegmentKeys = [];
465
496
  const adapterContextSegmentKeys = [];
497
+ const redirectSegmentKeys = [];
498
+ let shouldBroadcast = false;
466
499
  for (let i = 0, len = changes.length; i < len; i++) {
467
500
  const change = changes[i];
468
501
  // we only care about changes to the data which is stored in the default
@@ -473,6 +506,20 @@
473
506
  else if (change.segment === AdapterContextSegment) {
474
507
  adapterContextSegmentKeys.push(...change.ids);
475
508
  }
509
+ else if (change.segment === RedirectDurableSegment) {
510
+ redirectSegmentKeys.push(...change.ids);
511
+ }
512
+ }
513
+ if (redirectSegmentKeys.length > 0) {
514
+ const redirectEntries = await durableStore.getEntries(redirectSegmentKeys, RedirectDurableSegment);
515
+ if (redirectEntries !== undefined) {
516
+ const redirectKeys = Object.keys(redirectEntries);
517
+ for (const key of redirectKeys) {
518
+ const redirectData = redirectEntries[key];
519
+ environment.storeRedirect(redirectData.data.key, redirectData.data.redirect);
520
+ shouldBroadcast = true;
521
+ }
522
+ }
476
523
  }
477
524
  // process adapter context changes
478
525
  const adapterContextKeysFromDifferentInstance = [];
@@ -509,10 +556,6 @@
509
556
  if (defaultSegmentKeysLength > 0) {
510
557
  for (let i = 0; i < defaultSegmentKeysLength; i++) {
511
558
  const key = defaultSegmentKeys[i];
512
- const canonical = environment.storeGetCanonicalKey(key);
513
- if (canonical !== key) {
514
- continue;
515
- }
516
559
  // TODO: W-8909393 If expiration is the only thing that changed we should not evict the data... so
517
560
  // if we stored expiration and data at different keys (or same keys in different segments)
518
561
  // then we could know if only the expiration has changed and we wouldn't need to evict
@@ -520,6 +563,9 @@
520
563
  // call base environment storeEvict so this evict is not tracked for durable deletion
521
564
  environment.storeEvict(key);
522
565
  }
566
+ shouldBroadcast = true;
567
+ }
568
+ if (shouldBroadcast) {
523
569
  await environment.storeBroadcast(rebuildSnapshot, environment.snapshotAvailable);
524
570
  }
525
571
  });
@@ -570,12 +616,13 @@
570
616
  // call the base storeBroadcast
571
617
  return publishChangesToDurableStore();
572
618
  };
573
- const publishChangesToDurableStore = function () {
619
+ const publishChangesToDurableStore = function (additionalDurableStoreOperations) {
574
620
  validateNotDisposed();
575
621
  if (ingestStagingStore === null) {
576
622
  return Promise.resolve();
577
623
  }
578
- const promise = flushInMemoryStoreValuesToDurableStore(ingestStagingStore, durableStore, durableStoreErrorHandler);
624
+ const promise = flushInMemoryStoreValuesToDurableStore(ingestStagingStore, durableStore, durableStoreErrorHandler, new Map(pendingStoreRedirects), additionalDurableStoreOperations);
625
+ pendingStoreRedirects.clear();
579
626
  ingestStagingStore = null;
580
627
  return promise;
581
628
  };
@@ -657,6 +704,7 @@
657
704
  };
658
705
  const storeRedirect = function (existingKey, canonicalKey) {
659
706
  validateNotDisposed();
707
+ pendingStoreRedirects.set(existingKey, canonicalKey);
660
708
  // call redirect on staging store so "old" keys are removed from L2 on
661
709
  // the next publishChangesToDurableStore. NOTE: we don't need to call
662
710
  // redirect on the base environment store because staging store and base
@@ -750,7 +798,8 @@
750
798
  for (const cacheKeyMapKey of cacheKeyMapKeys) {
751
799
  const cacheKey = cacheKeyMap.get(cacheKeyMapKey);
752
800
  if (cacheKey.mergeable === true) {
753
- keysToRevive.add(cacheKeyMapKey);
801
+ const canonical = environment.storeGetCanonicalKey(cacheKeyMapKey);
802
+ keysToRevive.add(canonical);
754
803
  }
755
804
  }
756
805
  let snapshotFromMemoryIngest = undefined;
@@ -42,7 +42,9 @@ export interface DurableStoreEvictOperation extends BaseOperation {
42
42
  }
43
43
  export type DurableStoreOperation<T> = DurableStoreSetOperation<T> | DurableStoreEvictOperation;
44
44
  export type DefaultDurableSegmentName = 'DEFAULT';
45
+ export type RedirectDurableSegmentName = 'REDIRECT_KEYS';
45
46
  export declare const DefaultDurableSegment: DefaultDurableSegmentName;
47
+ export declare const RedirectDurableSegment: RedirectDurableSegmentName;
46
48
  export interface DurableStoreChange {
47
49
  /** The entry IDs of the entries that have changed */
48
50
  ids: string[];
@@ -1,4 +1,4 @@
1
1
  import type { InMemoryStore } from '@luvio/engine';
2
- import type { DurableStore } from '../DurableStore';
2
+ import type { DurableStore, DurableStoreOperation } from '../DurableStore';
3
3
  import type { DurableStoreRejectionHandler } from './error';
4
- export declare function flushInMemoryStoreValuesToDurableStore(store: InMemoryStore, durableStore: DurableStore, durableStoreErrorHandler: DurableStoreRejectionHandler): Promise<void>;
4
+ export declare function flushInMemoryStoreValuesToDurableStore(store: InMemoryStore, durableStore: DurableStore, durableStoreErrorHandler: DurableStoreRejectionHandler, redirects: Map<string, string>, additionalDurableStoreOperations?: DurableStoreOperation<unknown>[]): Promise<void>;
@@ -0,0 +1,7 @@
1
+ import type { Environment } from '@luvio/engine';
2
+ import type { DurableStore } from '../DurableStore';
3
+ export interface RedirectData {
4
+ key: string;
5
+ redirect: string;
6
+ }
7
+ export declare function reviveRedirects(durableStore: DurableStore, env: Environment): Promise<void>;
@@ -1,5 +1,5 @@
1
1
  import type { Environment, RecordSource, InMemoryStore } from '@luvio/engine';
2
- import type { DurableStore } from './DurableStore';
2
+ import type { DurableStore, DurableStoreOperation } from './DurableStore';
3
3
  import type { InstrumentationFunction } from './makeDurable/error';
4
4
  import type { TTLOverridesMap } from './DurableTTLStore';
5
5
  export interface DurableEnvironment extends Environment {
@@ -8,9 +8,12 @@ export interface DurableEnvironment extends Environment {
8
8
  */
9
9
  dispose(): Promise<void>;
10
10
  /**
11
- * publishes the pending changes to the durable store
11
+ * Publishes the pending changes in the ingestStagingStore to the durable store.
12
+ *
13
+ * Optionally takes in additional DurableStore operations to allow for an
14
+ * "atomic" transaction to the DurableStore.
12
15
  */
13
- publishChangesToDurableStore(): Promise<void>;
16
+ publishChangesToDurableStore(additionalDurableStoreOperations?: DurableStoreOperation<unknown>[]): Promise<void>;
14
17
  /**
15
18
  * gets all the stored ttl overrides stored in the durable store
16
19
  */
@@ -22,6 +22,7 @@
22
22
  return false;
23
23
  }
24
24
  var DefaultDurableSegment = 'DEFAULT';
25
+ var RedirectDurableSegment = 'REDIRECT_KEYS';
25
26
 
26
27
  /******************************************************************************
27
28
  Copyright (c) Microsoft Corporation.
@@ -376,7 +377,8 @@
376
377
  }
377
378
  return __assign({}, source);
378
379
  }
379
- function flushInMemoryStoreValuesToDurableStore(store, durableStore, durableStoreErrorHandler) {
380
+ function flushInMemoryStoreValuesToDurableStore(store, durableStore, durableStoreErrorHandler, redirects, additionalDurableStoreOperations) {
381
+ if (additionalDurableStoreOperations === void 0) { additionalDurableStoreOperations = []; }
380
382
  var durableRecords = create(null);
381
383
  var evictedRecords = create(null);
382
384
  var _a = store.fallbackStringKeyInMemoryStore, records = _a.records, storeMetadata = _a.metadata, visitedIds = _a.visitedIds, refreshedIds = _a.refreshedIds;
@@ -402,7 +404,7 @@
402
404
  durableRecords[key].metadata = __assign(__assign({}, metadata), { metadataVersion: DURABLE_METADATA_VERSION });
403
405
  }
404
406
  }
405
- var durableStoreOperations = [];
407
+ var durableStoreOperations = additionalDurableStoreOperations;
406
408
  // publishes
407
409
  var recordKeys = keys(durableRecords);
408
410
  if (recordKeys.length > 0) {
@@ -412,6 +414,19 @@
412
414
  segment: DefaultDurableSegment,
413
415
  });
414
416
  }
417
+ // redirects
418
+ redirects.forEach(function (value, key) {
419
+ var _a;
420
+ durableStoreOperations.push({
421
+ type: 'setEntries',
422
+ entries: (_a = {},
423
+ _a[key] = {
424
+ data: { key: key, redirect: value },
425
+ },
426
+ _a),
427
+ segment: RedirectDurableSegment,
428
+ });
429
+ });
415
430
  // evicts
416
431
  var evictedKeys = keys(evictedRecords);
417
432
  if (evictedKeys.length > 0) {
@@ -470,6 +485,30 @@
470
485
  return environment.storeBuildIngestionStagingStore();
471
486
  }
472
487
 
488
+ function reviveRedirects(durableStore, env) {
489
+ return __awaiter(this, void 0, void 0, function () {
490
+ var entries, _i, _a, durableEntry, entry, _b, key, redirect;
491
+ return __generator(this, function (_c) {
492
+ switch (_c.label) {
493
+ case 0: return [4 /*yield*/, durableStore.getAllEntries(RedirectDurableSegment)];
494
+ case 1:
495
+ entries = _c.sent();
496
+ if (entries) {
497
+ for (_i = 0, _a = Object.keys(entries); _i < _a.length; _i++) {
498
+ durableEntry = _a[_i];
499
+ entry = entries[durableEntry];
500
+ _b = entry.data, key = _b.key, redirect = _b.redirect;
501
+ if (entry) {
502
+ env.storeRedirect(key, redirect);
503
+ }
504
+ }
505
+ }
506
+ return [2 /*return*/];
507
+ }
508
+ });
509
+ });
510
+ }
511
+
473
512
  var AdapterContextSegment = 'ADAPTER-CONTEXT';
474
513
  var ADAPTER_CONTEXT_ID_SUFFIX = '__NAMED_CONTEXT';
475
514
  function reviveOrCreateContext(adapterId, durableStore, durableStoreErrorHandler, contextStores, pendingContextStoreKeys, onContextLoaded) {
@@ -548,13 +587,18 @@
548
587
  // event. If this instance of makeDurable caused that L2 write we can ignore that
549
588
  // on change event. This Set helps us do that.
550
589
  var pendingContextStoreKeys = new Set();
590
+ // redirects that need to be flushed to the durable store
591
+ var pendingStoreRedirects = new Map();
551
592
  var contextStores = create(null);
552
593
  var initializationPromise = new Promise(function (resolve) {
553
594
  var finish = function () {
554
595
  resolve();
555
596
  initializationPromise = undefined;
556
597
  };
557
- reviveTTLOverrides(durableTTLStore, environment).then(finish);
598
+ Promise.all([
599
+ reviveTTLOverrides(durableTTLStore, environment),
600
+ reviveRedirects(durableStore, environment),
601
+ ]).then(finish);
558
602
  });
559
603
  //instrumentation for durable store errors
560
604
  var durableStoreErrorHandler = handleDurableStoreRejection(instrumentation);
@@ -565,12 +609,14 @@
565
609
  }
566
610
  };
567
611
  var unsubscribe = durableStore.registerOnChangedListener(function (changes) { return __awaiter(_this, void 0, void 0, function () {
568
- var defaultSegmentKeys, adapterContextSegmentKeys, i, len, change, adapterContextKeysFromDifferentInstance, _i, adapterContextSegmentKeys_1, key, entries, entryKeys, i, len, entryKey, entry, error_2, defaultSegmentKeysLength, i, key, canonical;
569
- return __generator(this, function (_a) {
570
- switch (_a.label) {
612
+ var defaultSegmentKeys, adapterContextSegmentKeys, redirectSegmentKeys, shouldBroadcast, i, len, change, redirectEntries, redirectKeys, _i, redirectKeys_1, key, redirectData, adapterContextKeysFromDifferentInstance, _a, adapterContextSegmentKeys_1, key, entries, entryKeys, i, len, entryKey, entry, error_2, defaultSegmentKeysLength, i, key;
613
+ return __generator(this, function (_b) {
614
+ switch (_b.label) {
571
615
  case 0:
572
616
  defaultSegmentKeys = [];
573
617
  adapterContextSegmentKeys = [];
618
+ redirectSegmentKeys = [];
619
+ shouldBroadcast = false;
574
620
  for (i = 0, len = changes.length; i < len; i++) {
575
621
  change = changes[i];
576
622
  // we only care about changes to the data which is stored in the default
@@ -581,10 +627,28 @@
581
627
  else if (change.segment === AdapterContextSegment) {
582
628
  adapterContextSegmentKeys.push.apply(adapterContextSegmentKeys, change.ids);
583
629
  }
630
+ else if (change.segment === RedirectDurableSegment) {
631
+ redirectSegmentKeys.push.apply(redirectSegmentKeys, change.ids);
632
+ }
633
+ }
634
+ if (!(redirectSegmentKeys.length > 0)) return [3 /*break*/, 2];
635
+ return [4 /*yield*/, durableStore.getEntries(redirectSegmentKeys, RedirectDurableSegment)];
636
+ case 1:
637
+ redirectEntries = _b.sent();
638
+ if (redirectEntries !== undefined) {
639
+ redirectKeys = Object.keys(redirectEntries);
640
+ for (_i = 0, redirectKeys_1 = redirectKeys; _i < redirectKeys_1.length; _i++) {
641
+ key = redirectKeys_1[_i];
642
+ redirectData = redirectEntries[key];
643
+ environment.storeRedirect(redirectData.data.key, redirectData.data.redirect);
644
+ shouldBroadcast = true;
645
+ }
584
646
  }
647
+ _b.label = 2;
648
+ case 2:
585
649
  adapterContextKeysFromDifferentInstance = [];
586
- for (_i = 0, adapterContextSegmentKeys_1 = adapterContextSegmentKeys; _i < adapterContextSegmentKeys_1.length; _i++) {
587
- key = adapterContextSegmentKeys_1[_i];
650
+ for (_a = 0, adapterContextSegmentKeys_1 = adapterContextSegmentKeys; _a < adapterContextSegmentKeys_1.length; _a++) {
651
+ key = adapterContextSegmentKeys_1[_a];
588
652
  if (pendingContextStoreKeys.has(key)) {
589
653
  // if this instance caused the L2 write then remove from the
590
654
  // "pending" Set and move on
@@ -596,13 +660,13 @@
596
660
  adapterContextKeysFromDifferentInstance.push(key);
597
661
  }
598
662
  }
599
- if (!(adapterContextKeysFromDifferentInstance.length > 0)) return [3 /*break*/, 4];
600
- _a.label = 1;
601
- case 1:
602
- _a.trys.push([1, 3, , 4]);
663
+ if (!(adapterContextKeysFromDifferentInstance.length > 0)) return [3 /*break*/, 6];
664
+ _b.label = 3;
665
+ case 3:
666
+ _b.trys.push([3, 5, , 6]);
603
667
  return [4 /*yield*/, durableStore.getEntries(adapterContextKeysFromDifferentInstance, AdapterContextSegment)];
604
- case 2:
605
- entries = _a.sent();
668
+ case 4:
669
+ entries = _b.sent();
606
670
  if (entries !== undefined) {
607
671
  entryKeys = keys(entries);
608
672
  for (i = 0, len = entryKeys.length; i < len; i++) {
@@ -611,32 +675,31 @@
611
675
  contextStores[entryKey] = entry.data;
612
676
  }
613
677
  }
614
- return [3 /*break*/, 4];
615
- case 3:
616
- error_2 = _a.sent();
678
+ return [3 /*break*/, 6];
679
+ case 5:
680
+ error_2 = _b.sent();
617
681
  durableStoreErrorHandler(error_2);
618
- return [3 /*break*/, 4];
619
- case 4:
682
+ return [3 /*break*/, 6];
683
+ case 6:
620
684
  defaultSegmentKeysLength = defaultSegmentKeys.length;
621
- if (!(defaultSegmentKeysLength > 0)) return [3 /*break*/, 6];
622
- for (i = 0; i < defaultSegmentKeysLength; i++) {
623
- key = defaultSegmentKeys[i];
624
- canonical = environment.storeGetCanonicalKey(key);
625
- if (canonical !== key) {
626
- continue;
685
+ if (defaultSegmentKeysLength > 0) {
686
+ for (i = 0; i < defaultSegmentKeysLength; i++) {
687
+ key = defaultSegmentKeys[i];
688
+ // TODO: W-8909393 If expiration is the only thing that changed we should not evict the data... so
689
+ // if we stored expiration and data at different keys (or same keys in different segments)
690
+ // then we could know if only the expiration has changed and we wouldn't need to evict
691
+ // and go through an entire broadcast/revive cycle for unchanged data
692
+ // call base environment storeEvict so this evict is not tracked for durable deletion
693
+ environment.storeEvict(key);
627
694
  }
628
- // TODO: W-8909393 If expiration is the only thing that changed we should not evict the data... so
629
- // if we stored expiration and data at different keys (or same keys in different segments)
630
- // then we could know if only the expiration has changed and we wouldn't need to evict
631
- // and go through an entire broadcast/revive cycle for unchanged data
632
- // call base environment storeEvict so this evict is not tracked for durable deletion
633
- environment.storeEvict(key);
695
+ shouldBroadcast = true;
634
696
  }
697
+ if (!shouldBroadcast) return [3 /*break*/, 8];
635
698
  return [4 /*yield*/, environment.storeBroadcast(rebuildSnapshot, environment.snapshotAvailable)];
636
- case 5:
637
- _a.sent();
638
- _a.label = 6;
639
- case 6: return [2 /*return*/];
699
+ case 7:
700
+ _b.sent();
701
+ _b.label = 8;
702
+ case 8: return [2 /*return*/];
640
703
  }
641
704
  });
642
705
  }); });
@@ -687,12 +750,13 @@
687
750
  // call the base storeBroadcast
688
751
  return publishChangesToDurableStore();
689
752
  };
690
- var publishChangesToDurableStore = function () {
753
+ var publishChangesToDurableStore = function (additionalDurableStoreOperations) {
691
754
  validateNotDisposed();
692
755
  if (ingestStagingStore === null) {
693
756
  return Promise.resolve();
694
757
  }
695
- var promise = flushInMemoryStoreValuesToDurableStore(ingestStagingStore, durableStore, durableStoreErrorHandler);
758
+ var promise = flushInMemoryStoreValuesToDurableStore(ingestStagingStore, durableStore, durableStoreErrorHandler, new Map(pendingStoreRedirects), additionalDurableStoreOperations);
759
+ pendingStoreRedirects.clear();
696
760
  ingestStagingStore = null;
697
761
  return promise;
698
762
  };
@@ -774,6 +838,7 @@
774
838
  };
775
839
  var storeRedirect = function (existingKey, canonicalKey) {
776
840
  validateNotDisposed();
841
+ pendingStoreRedirects.set(existingKey, canonicalKey);
777
842
  // call redirect on staging store so "old" keys are removed from L2 on
778
843
  // the next publishChangesToDurableStore. NOTE: we don't need to call
779
844
  // redirect on the base environment store because staging store and base
@@ -868,7 +933,7 @@
868
933
  };
869
934
  var handleSuccessResponse = function (ingestAndBroadcastFunc, getResponseCacheKeysFunc) {
870
935
  return __awaiter(this, void 0, void 0, function () {
871
- var cacheKeyMap, cacheKeyMapKeys, keysToRevive, _i, cacheKeyMapKeys_1, cacheKeyMapKey, cacheKey, snapshotFromMemoryIngest, keysToReviveAsArray, readWritePromise, _a, keysToReviveAsArray_1, key, _b, keysToReviveAsArray_2, key, pendingPromise, _c, select, refresh, result;
936
+ var cacheKeyMap, cacheKeyMapKeys, keysToRevive, _i, cacheKeyMapKeys_1, cacheKeyMapKey, cacheKey, canonical, snapshotFromMemoryIngest, keysToReviveAsArray, readWritePromise, _a, keysToReviveAsArray_1, key, _b, keysToReviveAsArray_2, key, pendingPromise, _c, select, refresh, result;
872
937
  var _this = this;
873
938
  return __generator(this, function (_d) {
874
939
  switch (_d.label) {
@@ -881,7 +946,8 @@
881
946
  cacheKeyMapKey = cacheKeyMapKeys_1[_i];
882
947
  cacheKey = cacheKeyMap.get(cacheKeyMapKey);
883
948
  if (cacheKey.mergeable === true) {
884
- keysToRevive.add(cacheKeyMapKey);
949
+ canonical = environment.storeGetCanonicalKey(cacheKeyMapKey);
950
+ keysToRevive.add(canonical);
885
951
  }
886
952
  }
887
953
  snapshotFromMemoryIngest = undefined;
@@ -42,7 +42,9 @@ export interface DurableStoreEvictOperation extends BaseOperation {
42
42
  }
43
43
  export type DurableStoreOperation<T> = DurableStoreSetOperation<T> | DurableStoreEvictOperation;
44
44
  export type DefaultDurableSegmentName = 'DEFAULT';
45
+ export type RedirectDurableSegmentName = 'REDIRECT_KEYS';
45
46
  export declare const DefaultDurableSegment: DefaultDurableSegmentName;
47
+ export declare const RedirectDurableSegment: RedirectDurableSegmentName;
46
48
  export interface DurableStoreChange {
47
49
  /** The entry IDs of the entries that have changed */
48
50
  ids: string[];
@@ -1,4 +1,4 @@
1
1
  import type { InMemoryStore } from '@luvio/engine';
2
- import type { DurableStore } from '../DurableStore';
2
+ import type { DurableStore, DurableStoreOperation } from '../DurableStore';
3
3
  import type { DurableStoreRejectionHandler } from './error';
4
- export declare function flushInMemoryStoreValuesToDurableStore(store: InMemoryStore, durableStore: DurableStore, durableStoreErrorHandler: DurableStoreRejectionHandler): Promise<void>;
4
+ export declare function flushInMemoryStoreValuesToDurableStore(store: InMemoryStore, durableStore: DurableStore, durableStoreErrorHandler: DurableStoreRejectionHandler, redirects: Map<string, string>, additionalDurableStoreOperations?: DurableStoreOperation<unknown>[]): Promise<void>;
@@ -0,0 +1,7 @@
1
+ import type { Environment } from '@luvio/engine';
2
+ import type { DurableStore } from '../DurableStore';
3
+ export interface RedirectData {
4
+ key: string;
5
+ redirect: string;
6
+ }
7
+ export declare function reviveRedirects(durableStore: DurableStore, env: Environment): Promise<void>;
@@ -1,5 +1,5 @@
1
1
  import type { Environment, RecordSource, InMemoryStore } from '@luvio/engine';
2
- import type { DurableStore } from './DurableStore';
2
+ import type { DurableStore, DurableStoreOperation } from './DurableStore';
3
3
  import type { InstrumentationFunction } from './makeDurable/error';
4
4
  import type { TTLOverridesMap } from './DurableTTLStore';
5
5
  export interface DurableEnvironment extends Environment {
@@ -8,9 +8,12 @@ export interface DurableEnvironment extends Environment {
8
8
  */
9
9
  dispose(): Promise<void>;
10
10
  /**
11
- * publishes the pending changes to the durable store
11
+ * Publishes the pending changes in the ingestStagingStore to the durable store.
12
+ *
13
+ * Optionally takes in additional DurableStore operations to allow for an
14
+ * "atomic" transaction to the DurableStore.
12
15
  */
13
- publishChangesToDurableStore(): Promise<void>;
16
+ publishChangesToDurableStore(additionalDurableStoreOperations?: DurableStoreOperation<unknown>[]): Promise<void>;
14
17
  /**
15
18
  * gets all the stored ttl overrides stored in the durable store
16
19
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@luvio/environments",
3
- "version": "0.138.7",
3
+ "version": "0.138.8-244.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.138.7"
26
+ "@luvio/engine": "0.138.8-244.0"
27
27
  },
28
28
  "volta": {
29
29
  "extends": "../../../package.json"
@@ -32,8 +32,8 @@
32
32
  {
33
33
  "path": "./dist/es/es2018/environments.js",
34
34
  "maxSize": {
35
- "none": "40 kB",
36
- "min": "11.5 kB",
35
+ "none": "41 kB",
36
+ "min": "12 kB",
37
37
  "compressed": "8 kB"
38
38
  }
39
39
  }