@luvio/environments 0.68.0 → 0.72.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.
@@ -22,7 +22,11 @@
22
22
  const { isArray } = Array;
23
23
 
24
24
  function appendTTLStrategy(storeLookup, ttlStrategy) {
25
- return (sel, refresh) => storeLookup(sel, refresh, ttlStrategy);
25
+ const returnStoreLookup = (sel, refresh) => storeLookup(sel, refresh, ttlStrategy);
26
+ // append ttlStrategy to storeLookup function (in cases where custom adapter
27
+ // wants to perform it's own lookup)
28
+ returnStoreLookup.ttlStrategy = ttlStrategy;
29
+ return returnStoreLookup;
26
30
  }
27
31
  function buildNetworkSnapshot(args) {
28
32
  const { buildNetworkSnapshot, buildSnapshotContext, coercedAdapterRequestContext } = args;
@@ -45,102 +49,112 @@
45
49
  }
46
50
  return engine.StoreResolveResultState.Found;
47
51
  };
52
+ }
53
+ // TODO - update userland-facing APIs to return `AvailableSnapshot` instead of `Snapshot`
54
+ // and then the signatures here can be updated as well
55
+ function buildAvailableSnapshotFromCachedSnapshotResponse(cachedSnapshot, availableSnapshotFunc) {
56
+ if (isPromise(cachedSnapshot)) {
57
+ return cachedSnapshot.then(availableSnapshotFunc);
58
+ }
59
+ return availableSnapshotFunc(cachedSnapshot);
60
+ }
61
+ function isPromise(value) {
62
+ if (value === undefined) {
63
+ return false;
64
+ }
65
+ // check for Thenable due to test frameworks using custom Promise impls
66
+ return value.then !== undefined;
48
67
  }
49
68
 
50
- function buildCacheAndNetworkImplementation(funcs, staleDurationSeconds) {
69
+ function buildCacheAndNetworkImplementation(funcs, staleDurationSeconds = 0) {
51
70
  return function (args) {
52
71
  funcs.validateNotDisposed();
53
- const { buildInMemorySnapshot, buildNetworkSnapshot: buildNetworkSnapshot$1, buildSnapshotContext, storeLookup, coercedAdapterRequestContext, } = args;
54
- const staleDurationMilliseconds = staleDurationSeconds === undefined ? undefined : staleDurationSeconds * 1000;
72
+ const { buildCachedSnapshot, buildNetworkSnapshot: buildNetworkSnapshot$1, buildSnapshotContext, storeLookup, coercedAdapterRequestContext, } = args;
73
+ const staleDurationMilliseconds = staleDurationSeconds * 1000;
55
74
  const cachePolicyStoreLookup = appendTTLStrategy(storeLookup, buildTTLStrategy(staleDurationMilliseconds));
56
- const snapshot = buildInMemorySnapshot(buildSnapshotContext, cachePolicyStoreLookup);
57
- if (snapshot !== undefined) {
58
- // data found in L1 cache
59
- if (snapshot.state === 'Fulfilled' || snapshot.state === 'Error') {
60
- // kick off network request, do not await it
61
- buildNetworkSnapshot$1(buildSnapshotContext, coercedAdapterRequestContext);
62
- // return the cached snapshot to caller
63
- return snapshot;
64
- }
65
- // network request outstanding
66
- if (snapshot.state === 'Pending') {
67
- // kick off another network request, do not await it
68
- buildNetworkSnapshot$1(buildSnapshotContext, coercedAdapterRequestContext);
69
- return args.resolvePendingSnapshot(snapshot);
70
- }
71
- // stale data found in L1 cache
72
- if (snapshot.state === 'Stale') {
73
- // kick off network request, do not await it
74
- buildNetworkSnapshot$1(buildSnapshotContext, coercedAdapterRequestContext);
75
- // return the cached snapshot to caller
76
- return snapshot;
77
- }
78
- // if unfulfilled we have enough info to do an L2 lookup
79
- if (snapshot.state === 'Unfulfilled') {
80
- return funcs
81
- .reviveSnapshotWithCachePolicy(snapshot, cachePolicyStoreLookup)
82
- .then((revivedSnapshot) => {
83
- // data found in L2 cache
84
- if (revivedSnapshot.state === 'Fulfilled' ||
85
- revivedSnapshot.state === 'Error') {
86
- // kick off network request, do not await it
87
- buildNetworkSnapshot$1(buildSnapshotContext, coercedAdapterRequestContext);
88
- // return the L2 cached snapshot to caller
89
- return revivedSnapshot;
90
- }
91
- if (revivedSnapshot.state === 'Pending') {
92
- // kick off network request, do not await it
93
- buildNetworkSnapshot$1(buildSnapshotContext, coercedAdapterRequestContext);
94
- return args.resolvePendingSnapshot(revivedSnapshot);
95
- }
96
- // stale data found in L2 cache
97
- if (revivedSnapshot.state === 'Stale') {
98
- // kick off network request, do not await it
99
- buildNetworkSnapshot$1(buildSnapshotContext, coercedAdapterRequestContext);
100
- // return the L2 cached snapshot to caller
101
- return revivedSnapshot;
102
- }
103
- // data not found in L2 cache, go to the network
104
- return buildNetworkSnapshot(args);
105
- });
75
+ const cachedSnapshot = buildCachedSnapshot(buildSnapshotContext, cachePolicyStoreLookup);
76
+ return buildAvailableSnapshotFromCachedSnapshotResponse(cachedSnapshot, (snapshot) => {
77
+ if (snapshot !== undefined) {
78
+ // data found in L1 cache
79
+ if (snapshot.state === 'Fulfilled' ||
80
+ snapshot.state === 'Error' ||
81
+ snapshot.state === 'Stale') {
82
+ // kick off network request, do not await it
83
+ buildNetworkSnapshot$1(buildSnapshotContext, coercedAdapterRequestContext);
84
+ // return the cached snapshot to caller
85
+ return snapshot;
86
+ }
87
+ // network request outstanding
88
+ if (snapshot.state === 'Pending') {
89
+ // kick off another network request, do not await it
90
+ buildNetworkSnapshot$1(buildSnapshotContext, coercedAdapterRequestContext);
91
+ return args.resolvePendingSnapshot(snapshot);
92
+ }
93
+ // if unfulfilled we have enough info to do an L2 lookup
94
+ if (snapshot.state === 'Unfulfilled') {
95
+ return funcs
96
+ .reviveSnapshotWithCachePolicy(snapshot, cachePolicyStoreLookup)
97
+ .then((revivedSnapshot) => {
98
+ // data found in L2 cache
99
+ if (revivedSnapshot.state === 'Fulfilled' ||
100
+ revivedSnapshot.state === 'Error' ||
101
+ revivedSnapshot.state === 'Stale') {
102
+ // kick off network request, do not await it
103
+ buildNetworkSnapshot$1(buildSnapshotContext, coercedAdapterRequestContext);
104
+ // return the L2 cached snapshot to caller
105
+ return revivedSnapshot;
106
+ }
107
+ if (revivedSnapshot.state === 'Pending') {
108
+ // kick off network request, do not await it
109
+ buildNetworkSnapshot$1(buildSnapshotContext, coercedAdapterRequestContext);
110
+ return args.resolvePendingSnapshot(revivedSnapshot);
111
+ }
112
+ // data not found in L2 cache, go to the network
113
+ return buildNetworkSnapshot(args);
114
+ });
115
+ }
106
116
  }
107
- }
108
- return buildNetworkSnapshot(args);
117
+ return buildNetworkSnapshot(args);
118
+ });
109
119
  };
110
120
  }
111
121
 
112
122
  function buildCacheThenNetworkImplementation(funcs) {
113
123
  return function (args) {
114
124
  funcs.validateNotDisposed();
115
- const { buildInMemorySnapshot, buildSnapshotContext, storeLookup } = args;
125
+ const { buildCachedSnapshot, buildSnapshotContext, storeLookup } = args;
116
126
  const cachePolicyStoreLookup = appendTTLStrategy(storeLookup, buildTTLStrategy());
117
- const snapshot = buildInMemorySnapshot(buildSnapshotContext, cachePolicyStoreLookup);
118
- if (snapshot !== undefined) {
119
- // data found in L1 cache
120
- if (snapshot.state === 'Fulfilled' || snapshot.state === 'Error') {
121
- return snapshot;
122
- }
123
- if (snapshot.state === 'Pending') {
124
- return args.resolvePendingSnapshot(snapshot);
125
- }
126
- // data not found in L1 cache, try L2 cache
127
- return funcs
128
- .reviveSnapshotWithCachePolicy(snapshot, cachePolicyStoreLookup)
129
- .then((revivedSnapshot) => {
130
- // data found in L2 cache
131
- if (revivedSnapshot.state === 'Fulfilled' ||
132
- revivedSnapshot.state === 'Error') {
133
- return revivedSnapshot;
127
+ const cachedSnapshot = buildCachedSnapshot(buildSnapshotContext, cachePolicyStoreLookup);
128
+ return buildAvailableSnapshotFromCachedSnapshotResponse(cachedSnapshot, (snapshot) => {
129
+ if (snapshot !== undefined) {
130
+ // data found in L1 cache
131
+ if (snapshot.state === 'Fulfilled' || snapshot.state === 'Error') {
132
+ return snapshot;
134
133
  }
135
- if (revivedSnapshot.state === 'Pending') {
136
- return args.resolvePendingSnapshot(revivedSnapshot);
134
+ if (snapshot.state === 'Pending') {
135
+ return args.resolvePendingSnapshot(snapshot);
137
136
  }
138
- // data not found in L2 cache, go to the network
139
- return buildNetworkSnapshot(args);
140
- });
141
- }
142
- // L1 lookup could not find enough information to even construct a snapshot, go to the network
143
- return buildNetworkSnapshot(args);
137
+ // if unfulfilled we have enough info to do an L2 lookup
138
+ if (snapshot.state === 'Unfulfilled') {
139
+ return funcs
140
+ .reviveSnapshotWithCachePolicy(snapshot, cachePolicyStoreLookup)
141
+ .then((revivedSnapshot) => {
142
+ // data found in L2 cache
143
+ if (revivedSnapshot.state === 'Fulfilled' ||
144
+ revivedSnapshot.state === 'Error') {
145
+ return revivedSnapshot;
146
+ }
147
+ if (revivedSnapshot.state === 'Pending') {
148
+ return args.resolvePendingSnapshot(revivedSnapshot);
149
+ }
150
+ // data not found in L2 cache, go to the network
151
+ return buildNetworkSnapshot(args);
152
+ });
153
+ }
154
+ }
155
+ // L1 lookup could not find enough information to even construct a snapshot, go to the network
156
+ return buildNetworkSnapshot(args);
157
+ });
144
158
  };
145
159
  }
146
160
 
@@ -184,84 +198,90 @@
184
198
  error,
185
199
  state: 'Error',
186
200
  data: undefined,
187
- // TODO[@W-10164067]: copy refresh data from the snapshot returned by buildInMemorySnapshot (if any)
201
+ // TODO[@W-10164067]: copy refresh data from the snapshot returned by buildCachedSnapshot (if any)
188
202
  // refresh: ...
189
203
  };
190
204
  }
191
205
  function buildOnlyIfCachedImplementation(funcs) {
192
206
  return function (args) {
193
207
  funcs.validateNotDisposed();
194
- const { buildInMemorySnapshot, buildSnapshotContext, storeLookup } = args;
208
+ const { buildCachedSnapshot, buildSnapshotContext, storeLookup } = args;
195
209
  const cachePolicyStoreLookup = appendTTLStrategy(storeLookup, buildTTLStrategy());
196
- const snapshot = buildInMemorySnapshot(buildSnapshotContext, cachePolicyStoreLookup);
197
- if (snapshot !== undefined) {
198
- // data found in L1 cache
199
- if (snapshot.state === 'Fulfilled' || snapshot.state === 'Error') {
200
- return snapshot;
201
- }
202
- // network request outstanding, data is not cached
203
- if (snapshot.state === 'Pending') {
204
- return buildNotCachedErrorSnapshot();
205
- }
206
- // data not found in L1 cache, try L2 cache
207
- return funcs
208
- .reviveSnapshotWithCachePolicy(snapshot, cachePolicyStoreLookup)
209
- .then((revivedSnapshot) => {
210
- // data found in L2 cache
211
- if (revivedSnapshot.state === 'Fulfilled' ||
212
- revivedSnapshot.state === 'Error') {
213
- return revivedSnapshot;
210
+ const cachedSnapshot = buildCachedSnapshot(buildSnapshotContext, cachePolicyStoreLookup);
211
+ return buildAvailableSnapshotFromCachedSnapshotResponse(cachedSnapshot, (snapshot) => {
212
+ if (snapshot !== undefined) {
213
+ // data found in L1 cache
214
+ if (snapshot.state === 'Fulfilled' || snapshot.state === 'Error') {
215
+ return snapshot;
214
216
  }
215
- // data is not cached
216
- return buildNotCachedErrorSnapshot();
217
- });
218
- }
219
- return buildNotCachedErrorSnapshot();
217
+ // network request outstanding, data is not cached
218
+ if (snapshot.state === 'Pending') {
219
+ return buildNotCachedErrorSnapshot();
220
+ }
221
+ // if unfulfilled we have enough info to do an L2 lookup
222
+ if (snapshot.state === 'Unfulfilled') {
223
+ return funcs
224
+ .reviveSnapshotWithCachePolicy(snapshot, cachePolicyStoreLookup)
225
+ .then((revivedSnapshot) => {
226
+ // data found in L2 cache
227
+ if (revivedSnapshot.state === 'Fulfilled' ||
228
+ revivedSnapshot.state === 'Error') {
229
+ return revivedSnapshot;
230
+ }
231
+ // data is not cached
232
+ return buildNotCachedErrorSnapshot();
233
+ });
234
+ }
235
+ }
236
+ return buildNotCachedErrorSnapshot();
237
+ });
220
238
  };
221
239
  }
222
240
 
223
241
  function buildStaleWhileRevalidateImplementation(funcs, staleDurationSeconds) {
224
242
  return function (args) {
225
243
  funcs.validateNotDisposed();
226
- const { buildInMemorySnapshot, buildSnapshotContext, storeLookup } = args;
244
+ const { buildCachedSnapshot, buildSnapshotContext, storeLookup } = args;
227
245
  const cachePolicyStoreLookup = appendTTLStrategy(storeLookup, buildTTLStrategy(staleDurationSeconds * 1000));
228
- const snapshot = buildInMemorySnapshot(buildSnapshotContext, cachePolicyStoreLookup);
229
- if (snapshot !== undefined) {
230
- // data found in L1 cache
231
- if (snapshot.state === 'Fulfilled' || snapshot.state === 'Error') {
232
- return snapshot;
233
- }
234
- if (snapshot.state === 'Pending') {
235
- return args.resolvePendingSnapshot(snapshot);
236
- }
237
- // stale data found in L1 cache
238
- if (snapshot.state === 'Stale') {
239
- buildNetworkSnapshot(args);
240
- return snapshot;
241
- }
242
- // data not found in L1 cache, try L2 cache
243
- return funcs
244
- .reviveSnapshotWithCachePolicy(snapshot, cachePolicyStoreLookup)
245
- .then((revivedSnapshot) => {
246
- // data found in L2 cache
247
- if (revivedSnapshot.state === 'Fulfilled' ||
248
- revivedSnapshot.state === 'Error') {
249
- return revivedSnapshot;
246
+ const cachedSnapshot = buildCachedSnapshot(buildSnapshotContext, cachePolicyStoreLookup);
247
+ return buildAvailableSnapshotFromCachedSnapshotResponse(cachedSnapshot, (snapshot) => {
248
+ if (snapshot !== undefined) {
249
+ // data found in L1 cache
250
+ if (snapshot.state === 'Fulfilled' || snapshot.state === 'Error') {
251
+ return snapshot;
250
252
  }
251
- if (revivedSnapshot.state === 'Pending') {
252
- return args.resolvePendingSnapshot(revivedSnapshot);
253
+ if (snapshot.state === 'Pending') {
254
+ return args.resolvePendingSnapshot(snapshot);
253
255
  }
254
- // stale data found in L2 cache
255
- if (revivedSnapshot.state === 'Stale') {
256
+ // stale data found in L1 cache
257
+ if (snapshot.state === 'Stale') {
256
258
  buildNetworkSnapshot(args);
257
- return revivedSnapshot;
259
+ return snapshot;
258
260
  }
259
- // data not found in L2 cache, go to the network
260
- return buildNetworkSnapshot(args);
261
- });
262
- }
263
- // L1 lookup could not find enough information to even construct a snapshot, go to the network
264
- return buildNetworkSnapshot(args);
261
+ // data not found in L1 cache, try L2 cache
262
+ return funcs
263
+ .reviveSnapshotWithCachePolicy(snapshot, cachePolicyStoreLookup)
264
+ .then((revivedSnapshot) => {
265
+ // data found in L2 cache
266
+ if (revivedSnapshot.state === 'Fulfilled' ||
267
+ revivedSnapshot.state === 'Error') {
268
+ return revivedSnapshot;
269
+ }
270
+ if (revivedSnapshot.state === 'Pending') {
271
+ return args.resolvePendingSnapshot(revivedSnapshot);
272
+ }
273
+ // stale data found in L2 cache
274
+ if (revivedSnapshot.state === 'Stale') {
275
+ buildNetworkSnapshot(args);
276
+ return revivedSnapshot;
277
+ }
278
+ // data not found in L2 cache, go to the network
279
+ return buildNetworkSnapshot(args);
280
+ });
281
+ }
282
+ // L1 lookup could not find enough information to even construct a snapshot, go to the network
283
+ return buildNetworkSnapshot(args);
284
+ });
265
285
  };
266
286
  }
267
287
 
@@ -272,7 +292,7 @@
272
292
  // TTLStrategy to use the the valid-at cache policy's timestamp. The flow goes:
273
293
  //
274
294
  // Environment.applyCachePolicy => validAtImplementation (this function) =>
275
- // basePolicyImplementation => adapter's buildInMemorySnapshot =>
295
+ // basePolicyImplementation => adapter's buildCachedSnapshot =>
276
296
  // basePolicyImplementation's storeLookup => validAtStoreLookup (below) =>
277
297
  // Environment.applyCachePolicy's storeLookup => Store/Reader code =>
278
298
  // valid-at TTLStrategy (below) =>
@@ -379,9 +399,6 @@
379
399
  return storeRecord.__type === 'error';
380
400
  }
381
401
 
382
- function isStoreEntryExpiredAndError(storeRecord, expirationTimestamp, now) {
383
- return isStoreEntryError(storeRecord) && expirationTimestamp < now;
384
- }
385
402
  /**
386
403
  * Takes a set of entries from DurableStore and publishes them via the passed in funcs.
387
404
  * This respects expiration and checks for valid DurableStore data shapes. This should
@@ -404,7 +421,6 @@
404
421
  // no records to revive
405
422
  return { revivedKeys, hadUnexpectedShape };
406
423
  }
407
- const now = Date.now();
408
424
  for (let i = 0, len = durableKeys.length; i < len; i += 1) {
409
425
  const key = durableKeys[i];
410
426
  const durableRecord = durableRecords[key];
@@ -420,27 +436,12 @@
420
436
  continue;
421
437
  }
422
438
  if (metadata !== undefined) {
423
- const { expirationTimestamp, staleTimestamp } = metadata;
439
+ const { expirationTimestamp } = metadata;
424
440
  if (expirationTimestamp === undefined) {
425
441
  // if unexpected expiration data skip reviving
426
442
  hadUnexpectedShape = true;
427
443
  continue;
428
444
  }
429
- // if past stale TTL then don't revive
430
- if (staleTimestamp !== undefined && staleTimestamp < now) {
431
- continue;
432
- }
433
- // We don't want to revive a cached value if it's an error and it's
434
- // expirationTimestamp TTL is expired, otherwise we would never hit the network
435
- // during resolveSnapshot or rebuildSnapshot because the way the Reader works.
436
- // If a StoreEntry is an error, the Reader will return UnfulfilledSnapshot
437
- // if stale TTL is expired. But it will return ErrorSnapshot (instead of
438
- // StaleSnapshot) if expirationTimestamp TTL is expired (but stale TTL isn't).
439
- // In our environment the stale TTL is maxed out so Reader will always
440
- // return ErrorSnapshot.
441
- if (isStoreEntryExpiredAndError(data, expirationTimestamp, now)) {
442
- continue;
443
- }
444
445
  publishMetadata(key, metadata);
445
446
  }
446
447
  if (isStoreEntryError(data)) {
@@ -831,7 +832,7 @@
831
832
  if (ingestStagingStore !== null) {
832
833
  return ingestStagingStore.lookup(sel, createSnapshot, refresh, ttlStrategy);
833
834
  }
834
- // otherwise this is from buildInMemorySnapshot and we should use the luvio
835
+ // otherwise this is from buildCachedSnapshot and we should use the luvio
835
836
  // L1 store
836
837
  return environment.storeLookup(sel, createSnapshot, refresh, ttlStrategy);
837
838
  };
@@ -992,7 +993,7 @@
992
993
  }
993
994
  }
994
995
  }
995
- const applyCachePolicy = function (adapterRequestContext, buildSnapshotContext, buildInMemorySnapshot, buildNetworkSnapshot) {
996
+ const applyCachePolicy = function (adapterRequestContext, buildSnapshotContext, buildCachedSnapshot, buildNetworkSnapshot) {
996
997
  validateNotDisposed();
997
998
  const { cachePolicy } = adapterRequestContext;
998
999
  const cachePolicyImpl = resolveCachePolicy(cachePolicy);
@@ -1000,7 +1001,7 @@
1000
1001
  const storeLookup = (sel, refresh, ttlStrategy) => environment.storeLookup(sel, environment.createSnapshot, refresh, ttlStrategy);
1001
1002
  const applyCachePolicy = () => {
1002
1003
  return cachePolicyImpl({
1003
- buildInMemorySnapshot,
1004
+ buildCachedSnapshot,
1004
1005
  buildNetworkSnapshot,
1005
1006
  buildSnapshotContext,
1006
1007
  resolvePendingSnapshot,
@@ -1,8 +1,9 @@
1
- import { BuildInMemorySnapshot, CachePolicyImplementationArgs, Snapshot, StoreLookup, TTLStrategy, UnAvailableSnapshot } from '@luvio/engine';
1
+ import { BuildCachedSnapshot, CachePolicyImplementationArgs, Snapshot, StoreLookup, TTLStrategy, UnAvailableSnapshot } from '@luvio/engine';
2
2
  export declare type DurableCachePolicyFunctions = {
3
3
  validateNotDisposed: () => void;
4
4
  reviveSnapshotWithCachePolicy: <D, V>(unavailableSnapshot: UnAvailableSnapshot<D, V>, storeLookup: StoreLookup<D, V>) => Promise<Snapshot<D, V>>;
5
5
  };
6
- export declare function appendTTLStrategy<C, D>(storeLookup: CachePolicyImplementationArgs<C, D>['storeLookup'], ttlStrategy: TTLStrategy): Parameters<BuildInMemorySnapshot<C, D>>[1];
6
+ export declare function appendTTLStrategy<C, D>(storeLookup: CachePolicyImplementationArgs<C, D>['storeLookup'], ttlStrategy: TTLStrategy): Parameters<BuildCachedSnapshot<C, D>>[1];
7
7
  export declare function buildNetworkSnapshot<C, D>(args: CachePolicyImplementationArgs<C, D>): Promise<Snapshot<D, unknown>>;
8
8
  export declare function buildTTLStrategy(staleDurationMilliseconds?: number): TTLStrategy;
9
+ export declare function buildAvailableSnapshotFromCachedSnapshotResponse<C, D>(cachedSnapshot: ReturnType<BuildCachedSnapshot<C, D>>, availableSnapshotFunc: (snapshot: Snapshot<D> | undefined) => Snapshot<D> | Promise<Snapshot<D>>): Snapshot<D> | Promise<Snapshot<D>>;
@@ -12,7 +12,6 @@ export interface DurableStoreEntry<T = unknown> {
12
12
  metadata?: {
13
13
  ingestionTimestamp: number;
14
14
  expirationTimestamp: number;
15
- staleTimestamp?: number;
16
15
  namespace: string;
17
16
  representationName: string;
18
17
  };