@luvio/environments 0.138.0 → 0.138.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/es/es2018/environments.js +858 -852
- package/dist/es/es2018/{DurableStore.d.ts → types/DurableStore.d.ts} +134 -134
- package/dist/{umd/es5 → es/es2018/types}/DurableTTLStore.d.ts +25 -25
- package/dist/{umd/es5 → es/es2018/types}/events.d.ts +18 -18
- package/dist/{umd/es5 → es/es2018/types}/main.d.ts +5 -5
- package/dist/es/es2018/{makeDurable → types/makeDurable}/error.d.ts +11 -11
- package/dist/es/es2018/{makeDurable → types/makeDurable}/flush.d.ts +4 -4
- package/dist/{umd/es5 → es/es2018/types}/makeDurable/revive.d.ts +38 -38
- package/dist/es/es2018/{makeDurable → types/makeDurable}/stagingStore.d.ts +6 -6
- package/dist/es/es2018/{makeDurable → types/makeDurable}/ttl.d.ts +3 -3
- package/dist/es/es2018/{makeDurable → types/makeDurable}/utils.d.ts +2 -2
- package/dist/es/es2018/{makeDurable.d.ts → types/makeDurable.d.ts} +44 -44
- package/dist/es/es2018/{utils → types/utils}/deep-freeze.d.ts +1 -1
- package/dist/es/es2018/{utils → types/utils}/language.d.ts +19 -19
- package/dist/umd/es2018/environments.js +858 -852
- package/dist/umd/es2018/{DurableStore.d.ts → types/DurableStore.d.ts} +134 -134
- package/dist/{es/es2018 → umd/es2018/types}/DurableTTLStore.d.ts +25 -25
- package/dist/{es/es2018 → umd/es2018/types}/events.d.ts +18 -18
- package/dist/umd/es2018/{main.d.ts → types/main.d.ts} +5 -5
- package/dist/umd/{es5 → es2018/types}/makeDurable/error.d.ts +11 -11
- package/dist/umd/es2018/{makeDurable → types/makeDurable}/flush.d.ts +4 -4
- package/dist/{es/es2018 → umd/es2018/types}/makeDurable/revive.d.ts +38 -38
- package/dist/umd/{es5 → es2018/types}/makeDurable/stagingStore.d.ts +6 -6
- package/dist/umd/es2018/{makeDurable → types/makeDurable}/ttl.d.ts +3 -3
- package/dist/umd/es2018/{makeDurable → types/makeDurable}/utils.d.ts +2 -2
- package/dist/umd/{es5 → es2018/types}/makeDurable.d.ts +44 -44
- package/dist/umd/es2018/{utils → types/utils}/deep-freeze.d.ts +1 -1
- package/dist/umd/es2018/{utils → types/utils}/language.d.ts +19 -19
- package/dist/umd/es5/environments.js +942 -936
- package/dist/umd/es5/{DurableStore.d.ts → types/DurableStore.d.ts} +134 -134
- package/dist/umd/{es2018 → es5/types}/DurableTTLStore.d.ts +25 -25
- package/dist/umd/{es2018 → es5/types}/events.d.ts +18 -18
- package/dist/{es/es2018 → umd/es5/types}/main.d.ts +5 -5
- package/dist/umd/{es2018 → es5/types}/makeDurable/error.d.ts +11 -11
- package/dist/umd/es5/{makeDurable → types/makeDurable}/flush.d.ts +4 -4
- package/dist/umd/{es2018 → es5/types}/makeDurable/revive.d.ts +38 -38
- package/dist/umd/{es2018 → es5/types}/makeDurable/stagingStore.d.ts +6 -6
- package/dist/umd/es5/{makeDurable → types/makeDurable}/ttl.d.ts +3 -3
- package/dist/umd/es5/{makeDurable → types/makeDurable}/utils.d.ts +2 -2
- package/dist/umd/{es2018 → es5/types}/makeDurable.d.ts +44 -44
- package/dist/umd/es5/{utils → types/utils}/deep-freeze.d.ts +1 -1
- package/dist/umd/es5/{utils → types/utils}/language.d.ts +19 -19
- package/package.json +4 -4
|
@@ -4,23 +4,23 @@
|
|
|
4
4
|
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.luvioEnvironments = {}, global.luvioEngine));
|
|
5
5
|
})(this, (function (exports, engine) { 'use strict';
|
|
6
6
|
|
|
7
|
-
// the last version the metadata shape was altered
|
|
8
|
-
var DURABLE_METADATA_VERSION = '0.111.0';
|
|
9
|
-
function isDeprecatedDurableStoreEntry(durableRecord) {
|
|
10
|
-
if (durableRecord.expiration !== undefined) {
|
|
11
|
-
return true;
|
|
12
|
-
}
|
|
13
|
-
var metadata = durableRecord.metadata;
|
|
14
|
-
if (metadata !== undefined) {
|
|
15
|
-
var metadataVersion = metadata.metadataVersion;
|
|
16
|
-
// eventually we will want to assert that metadataVersion is defined
|
|
17
|
-
if (metadataVersion !== undefined && metadataVersion !== DURABLE_METADATA_VERSION) {
|
|
18
|
-
return true;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
// Add more deprecated shape checks here
|
|
22
|
-
return false;
|
|
23
|
-
}
|
|
7
|
+
// the last version the metadata shape was altered
|
|
8
|
+
var DURABLE_METADATA_VERSION = '0.111.0';
|
|
9
|
+
function isDeprecatedDurableStoreEntry(durableRecord) {
|
|
10
|
+
if (durableRecord.expiration !== undefined) {
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
var metadata = durableRecord.metadata;
|
|
14
|
+
if (metadata !== undefined) {
|
|
15
|
+
var metadataVersion = metadata.metadataVersion;
|
|
16
|
+
// eventually we will want to assert that metadataVersion is defined
|
|
17
|
+
if (metadataVersion !== undefined && metadataVersion !== DURABLE_METADATA_VERSION) {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
// Add more deprecated shape checks here
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
24
|
var DefaultDurableSegment = 'DEFAULT';
|
|
25
25
|
|
|
26
26
|
/******************************************************************************
|
|
@@ -97,945 +97,951 @@
|
|
|
97
97
|
return to.concat(ar || Array.prototype.slice.call(from));
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
var keys = Object.keys, create = Object.create, freeze = Object.freeze;
|
|
100
|
+
var keys = Object.keys, create = Object.create, freeze = Object.freeze;
|
|
101
101
|
var isArray = Array.isArray;
|
|
102
102
|
|
|
103
|
-
//Durable store error instrumentation key
|
|
104
|
-
var DURABLE_STORE_ERROR = 'durable-store-error';
|
|
105
|
-
/**
|
|
106
|
-
* Returns a function that processes errors from durable store promise rejections.
|
|
107
|
-
* If running in a non-production environment, the error is rethrown.
|
|
108
|
-
* When running in production the error is sent to instrumentation.
|
|
109
|
-
* @param instrument Instrumentation function implementation
|
|
110
|
-
*/
|
|
111
|
-
function handleDurableStoreRejection(instrument) {
|
|
112
|
-
return function (error) {
|
|
113
|
-
if (process.env.NODE_ENV !== 'production') {
|
|
114
|
-
throw error;
|
|
115
|
-
}
|
|
116
|
-
if (instrument !== undefined) {
|
|
117
|
-
instrument(function () {
|
|
118
|
-
var _a;
|
|
119
|
-
return _a = {},
|
|
120
|
-
_a[DURABLE_STORE_ERROR] = true,
|
|
121
|
-
_a.error = error,
|
|
122
|
-
_a;
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
};
|
|
103
|
+
//Durable store error instrumentation key
|
|
104
|
+
var DURABLE_STORE_ERROR = 'durable-store-error';
|
|
105
|
+
/**
|
|
106
|
+
* Returns a function that processes errors from durable store promise rejections.
|
|
107
|
+
* If running in a non-production environment, the error is rethrown.
|
|
108
|
+
* When running in production the error is sent to instrumentation.
|
|
109
|
+
* @param instrument Instrumentation function implementation
|
|
110
|
+
*/
|
|
111
|
+
function handleDurableStoreRejection(instrument) {
|
|
112
|
+
return function (error) {
|
|
113
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
114
|
+
throw error;
|
|
115
|
+
}
|
|
116
|
+
if (instrument !== undefined) {
|
|
117
|
+
instrument(function () {
|
|
118
|
+
var _a;
|
|
119
|
+
return _a = {},
|
|
120
|
+
_a[DURABLE_STORE_ERROR] = true,
|
|
121
|
+
_a.error = error,
|
|
122
|
+
_a;
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
function deepFreeze(value) {
|
|
129
|
-
// No need to freeze primitives
|
|
130
|
-
if (typeof value !== 'object' || value === null) {
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
if (isArray(value)) {
|
|
134
|
-
for (var i = 0, len = value.length; i < len; i += 1) {
|
|
135
|
-
deepFreeze(value[i]);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
else {
|
|
139
|
-
var keys$1 = keys(value);
|
|
140
|
-
for (var i = 0, len = keys$1.length; i < len; i += 1) {
|
|
141
|
-
deepFreeze(value[keys$1[i]]);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
freeze(value);
|
|
128
|
+
function deepFreeze(value) {
|
|
129
|
+
// No need to freeze primitives
|
|
130
|
+
if (typeof value !== 'object' || value === null) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (isArray(value)) {
|
|
134
|
+
for (var i = 0, len = value.length; i < len; i += 1) {
|
|
135
|
+
deepFreeze(value[i]);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
var keys$1 = keys(value);
|
|
140
|
+
for (var i = 0, len = keys$1.length; i < len; i += 1) {
|
|
141
|
+
deepFreeze(value[keys$1[i]]);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
freeze(value);
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
-
function isStoreEntryError(storeRecord) {
|
|
148
|
-
return storeRecord.__type === 'error';
|
|
147
|
+
function isStoreEntryError(storeRecord) {
|
|
148
|
+
return storeRecord.__type === 'error';
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
/**
|
|
152
|
-
* Takes a set of entries from DurableStore and publishes them via the passed in funcs.
|
|
153
|
-
* This respects expiration and checks for valid DurableStore data shapes. This should
|
|
154
|
-
* be used over manually parsing DurableStoreEntries
|
|
155
|
-
*
|
|
156
|
-
* @param durableRecords The DurableStoreEntries to parse
|
|
157
|
-
* @param publish A function to call with the data of each DurableStoreEntry
|
|
158
|
-
* @param publishMetadata A function to call with the metadata of each DurableStoreEntry
|
|
159
|
-
* @param pendingWriter the PendingWriter (this is going away soon)
|
|
160
|
-
* @returns
|
|
161
|
-
*/
|
|
162
|
-
function publishDurableStoreEntries(durableRecords, publish, publishMetadata) {
|
|
163
|
-
var revivedKeys = new engine.StoreKeySet();
|
|
164
|
-
var hadUnexpectedShape = false;
|
|
165
|
-
if (durableRecords === undefined) {
|
|
166
|
-
return { revivedKeys: revivedKeys, hadUnexpectedShape: hadUnexpectedShape };
|
|
167
|
-
}
|
|
168
|
-
var durableKeys = keys(durableRecords);
|
|
169
|
-
if (durableKeys.length === 0) {
|
|
170
|
-
// no records to revive
|
|
171
|
-
return { revivedKeys: revivedKeys, hadUnexpectedShape: hadUnexpectedShape };
|
|
172
|
-
}
|
|
173
|
-
for (var i = 0, len = durableKeys.length; i < len; i += 1) {
|
|
174
|
-
var key = durableKeys[i];
|
|
175
|
-
var durableRecord = durableRecords[key];
|
|
176
|
-
if (isDeprecatedDurableStoreEntry(durableRecord)) {
|
|
177
|
-
// had the old shape, skip reviving this entry.
|
|
178
|
-
hadUnexpectedShape = true;
|
|
179
|
-
continue;
|
|
180
|
-
}
|
|
181
|
-
var metadata = durableRecord.metadata, data = durableRecord.data;
|
|
182
|
-
if (data === undefined) {
|
|
183
|
-
// if unexpected data skip reviving
|
|
184
|
-
hadUnexpectedShape = true;
|
|
185
|
-
continue;
|
|
186
|
-
}
|
|
187
|
-
if (metadata !== undefined) {
|
|
188
|
-
var expirationTimestamp = metadata.expirationTimestamp;
|
|
189
|
-
if (expirationTimestamp === undefined) {
|
|
190
|
-
// if unexpected expiration data skip reviving
|
|
191
|
-
hadUnexpectedShape = true;
|
|
192
|
-
continue;
|
|
193
|
-
}
|
|
194
|
-
publishMetadata(key, metadata);
|
|
195
|
-
}
|
|
196
|
-
if (isStoreEntryError(data)) {
|
|
197
|
-
// freeze errors on way into L1
|
|
198
|
-
deepFreeze(data.error);
|
|
199
|
-
}
|
|
200
|
-
publish(key, data);
|
|
201
|
-
revivedKeys.add(key);
|
|
202
|
-
}
|
|
203
|
-
return { revivedKeys: revivedKeys, hadUnexpectedShape: hadUnexpectedShape };
|
|
204
|
-
}
|
|
205
|
-
/**
|
|
206
|
-
* This method returns a Promise to a snapshot that is revived from L2 cache. If
|
|
207
|
-
* L2 does not have the entries necessary to fulfill the snapshot then this method
|
|
208
|
-
* will refresh the snapshot from network, and then run the results from network
|
|
209
|
-
* through L2 ingestion, returning the subsequent revived snapshot.
|
|
210
|
-
*/
|
|
211
|
-
function reviveSnapshot(baseEnvironment, durableStore,
|
|
212
|
-
// TODO [W-10165787]: We should only allow Unfulfilled snapshot be passed in
|
|
213
|
-
unavailableSnapshot, durableStoreErrorHandler, buildL1Snapshot, reviveMetrics) {
|
|
214
|
-
if (reviveMetrics === void 0) { reviveMetrics = { l2Trips: [] }; }
|
|
215
|
-
var recordId = unavailableSnapshot.recordId, select = unavailableSnapshot.select, seenRecords = unavailableSnapshot.seenRecords, state = unavailableSnapshot.state;
|
|
216
|
-
// L2 can only revive Unfulfilled snapshots that have a selector since they have the
|
|
217
|
-
// info needed to revive (like missingLinks) and rebuild. Otherwise return L1 snapshot.
|
|
218
|
-
if (state !== 'Unfulfilled' || select === undefined) {
|
|
219
|
-
return Promise.resolve({
|
|
220
|
-
snapshot: unavailableSnapshot,
|
|
221
|
-
metrics: reviveMetrics,
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
// in case L1 store changes/deallocs a record while we are doing the async read
|
|
225
|
-
// we attempt to read all keys from L2 - so combine recordId with any seenRecords
|
|
226
|
-
var keysToReviveSet = new engine.StoreKeySet().add(recordId);
|
|
227
|
-
keysToReviveSet.merge(seenRecords);
|
|
228
|
-
var keysToRevive = keysToReviveSet.keysAsArray();
|
|
229
|
-
var canonicalKeys = keysToRevive.map(function (x) {
|
|
230
|
-
return engine.serializeStructuredKey(baseEnvironment.storeGetCanonicalKey(x));
|
|
231
|
-
});
|
|
232
|
-
var start = Date.now();
|
|
233
|
-
var l2Trips = reviveMetrics.l2Trips;
|
|
234
|
-
return durableStore.getEntries(canonicalKeys, DefaultDurableSegment).then(function (durableRecords) {
|
|
235
|
-
l2Trips.push({
|
|
236
|
-
duration: Date.now() - start,
|
|
237
|
-
keysRequestedCount: canonicalKeys.length,
|
|
238
|
-
});
|
|
239
|
-
var _a = publishDurableStoreEntries(durableRecords,
|
|
240
|
-
// TODO [W-10072584]: instead of implicitly using L1 we should take in
|
|
241
|
-
// publish and publishMetadata funcs, so callers can decide where to
|
|
242
|
-
// revive to (like they pass in how to do the buildL1Snapshot)
|
|
243
|
-
baseEnvironment.storePublish.bind(baseEnvironment), baseEnvironment.publishStoreMetadata.bind(baseEnvironment)), revivedKeys = _a.revivedKeys, hadUnexpectedShape = _a.hadUnexpectedShape;
|
|
244
|
-
// if the data coming back from DS had an unexpected shape then just
|
|
245
|
-
// return the L1 snapshot
|
|
246
|
-
if (hadUnexpectedShape === true) {
|
|
247
|
-
return { snapshot: unavailableSnapshot, metrics: reviveMetrics };
|
|
248
|
-
}
|
|
249
|
-
if (revivedKeys.size() === 0) {
|
|
250
|
-
// durable store doesn't have what we asked for so return L1 snapshot
|
|
251
|
-
return { snapshot: unavailableSnapshot, metrics: reviveMetrics };
|
|
252
|
-
}
|
|
253
|
-
// try building the snapshot from L1 now that we have revived the missingLinks
|
|
254
|
-
var snapshot = buildL1Snapshot();
|
|
255
|
-
// if snapshot is pending then some other in-flight refresh will broadcast
|
|
256
|
-
// later
|
|
257
|
-
if (snapshot.state === 'Pending') {
|
|
258
|
-
return { snapshot: snapshot, metrics: reviveMetrics };
|
|
259
|
-
}
|
|
260
|
-
if (snapshot.state === 'Unfulfilled') {
|
|
261
|
-
// have to check if the new snapshot has any additional seenRecords
|
|
262
|
-
// and revive again if so
|
|
263
|
-
var newSnapshotSeenRecords = snapshot.seenRecords, newSnapshotRecordId = snapshot.recordId;
|
|
264
|
-
var newKeysToReviveSet = new engine.StoreKeySet();
|
|
265
|
-
newKeysToReviveSet.add(newSnapshotRecordId);
|
|
266
|
-
newKeysToReviveSet.merge(newSnapshotSeenRecords);
|
|
267
|
-
var newKeys = newKeysToReviveSet.keysAsArray();
|
|
268
|
-
// in case DS returned additional entries we combine the requested
|
|
269
|
-
// and returned keys
|
|
270
|
-
var alreadyRequestedOrRevivedSet = keysToReviveSet;
|
|
271
|
-
alreadyRequestedOrRevivedSet.merge(revivedKeys);
|
|
272
|
-
// if there's any seen keys in the newly rebuilt snapshot that
|
|
273
|
-
// haven't already been requested or returned then revive again
|
|
274
|
-
for (var i = 0, len = newKeys.length; i < len; i++) {
|
|
275
|
-
var newSnapshotSeenKey = newKeys[i];
|
|
276
|
-
if (!alreadyRequestedOrRevivedSet.has(newSnapshotSeenKey)) {
|
|
277
|
-
return reviveSnapshot(baseEnvironment, durableStore, snapshot, durableStoreErrorHandler, buildL1Snapshot, reviveMetrics);
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
return { snapshot: snapshot, metrics: reviveMetrics };
|
|
282
|
-
}, function (error) {
|
|
283
|
-
durableStoreErrorHandler(error);
|
|
284
|
-
// getEntries failed, return the L1 snapshot
|
|
285
|
-
return { snapshot: unavailableSnapshot, metrics: reviveMetrics };
|
|
286
|
-
});
|
|
151
|
+
/**
|
|
152
|
+
* Takes a set of entries from DurableStore and publishes them via the passed in funcs.
|
|
153
|
+
* This respects expiration and checks for valid DurableStore data shapes. This should
|
|
154
|
+
* be used over manually parsing DurableStoreEntries
|
|
155
|
+
*
|
|
156
|
+
* @param durableRecords The DurableStoreEntries to parse
|
|
157
|
+
* @param publish A function to call with the data of each DurableStoreEntry
|
|
158
|
+
* @param publishMetadata A function to call with the metadata of each DurableStoreEntry
|
|
159
|
+
* @param pendingWriter the PendingWriter (this is going away soon)
|
|
160
|
+
* @returns
|
|
161
|
+
*/
|
|
162
|
+
function publishDurableStoreEntries(durableRecords, publish, publishMetadata) {
|
|
163
|
+
var revivedKeys = new engine.StoreKeySet();
|
|
164
|
+
var hadUnexpectedShape = false;
|
|
165
|
+
if (durableRecords === undefined) {
|
|
166
|
+
return { revivedKeys: revivedKeys, hadUnexpectedShape: hadUnexpectedShape };
|
|
167
|
+
}
|
|
168
|
+
var durableKeys = keys(durableRecords);
|
|
169
|
+
if (durableKeys.length === 0) {
|
|
170
|
+
// no records to revive
|
|
171
|
+
return { revivedKeys: revivedKeys, hadUnexpectedShape: hadUnexpectedShape };
|
|
172
|
+
}
|
|
173
|
+
for (var i = 0, len = durableKeys.length; i < len; i += 1) {
|
|
174
|
+
var key = durableKeys[i];
|
|
175
|
+
var durableRecord = durableRecords[key];
|
|
176
|
+
if (isDeprecatedDurableStoreEntry(durableRecord)) {
|
|
177
|
+
// had the old shape, skip reviving this entry.
|
|
178
|
+
hadUnexpectedShape = true;
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
var metadata = durableRecord.metadata, data = durableRecord.data;
|
|
182
|
+
if (data === undefined) {
|
|
183
|
+
// if unexpected data skip reviving
|
|
184
|
+
hadUnexpectedShape = true;
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
if (metadata !== undefined) {
|
|
188
|
+
var expirationTimestamp = metadata.expirationTimestamp;
|
|
189
|
+
if (expirationTimestamp === undefined) {
|
|
190
|
+
// if unexpected expiration data skip reviving
|
|
191
|
+
hadUnexpectedShape = true;
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
publishMetadata(key, metadata);
|
|
195
|
+
}
|
|
196
|
+
if (isStoreEntryError(data)) {
|
|
197
|
+
// freeze errors on way into L1
|
|
198
|
+
deepFreeze(data.error);
|
|
199
|
+
}
|
|
200
|
+
publish(key, data);
|
|
201
|
+
revivedKeys.add(key);
|
|
202
|
+
}
|
|
203
|
+
return { revivedKeys: revivedKeys, hadUnexpectedShape: hadUnexpectedShape };
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* This method returns a Promise to a snapshot that is revived from L2 cache. If
|
|
207
|
+
* L2 does not have the entries necessary to fulfill the snapshot then this method
|
|
208
|
+
* will refresh the snapshot from network, and then run the results from network
|
|
209
|
+
* through L2 ingestion, returning the subsequent revived snapshot.
|
|
210
|
+
*/
|
|
211
|
+
function reviveSnapshot(baseEnvironment, durableStore,
|
|
212
|
+
// TODO [W-10165787]: We should only allow Unfulfilled snapshot be passed in
|
|
213
|
+
unavailableSnapshot, durableStoreErrorHandler, buildL1Snapshot, reviveMetrics) {
|
|
214
|
+
if (reviveMetrics === void 0) { reviveMetrics = { l2Trips: [] }; }
|
|
215
|
+
var recordId = unavailableSnapshot.recordId, select = unavailableSnapshot.select, seenRecords = unavailableSnapshot.seenRecords, state = unavailableSnapshot.state;
|
|
216
|
+
// L2 can only revive Unfulfilled snapshots that have a selector since they have the
|
|
217
|
+
// info needed to revive (like missingLinks) and rebuild. Otherwise return L1 snapshot.
|
|
218
|
+
if (state !== 'Unfulfilled' || select === undefined) {
|
|
219
|
+
return Promise.resolve({
|
|
220
|
+
snapshot: unavailableSnapshot,
|
|
221
|
+
metrics: reviveMetrics,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
// in case L1 store changes/deallocs a record while we are doing the async read
|
|
225
|
+
// we attempt to read all keys from L2 - so combine recordId with any seenRecords
|
|
226
|
+
var keysToReviveSet = new engine.StoreKeySet().add(recordId);
|
|
227
|
+
keysToReviveSet.merge(seenRecords);
|
|
228
|
+
var keysToRevive = keysToReviveSet.keysAsArray();
|
|
229
|
+
var canonicalKeys = keysToRevive.map(function (x) {
|
|
230
|
+
return engine.serializeStructuredKey(baseEnvironment.storeGetCanonicalKey(x));
|
|
231
|
+
});
|
|
232
|
+
var start = Date.now();
|
|
233
|
+
var l2Trips = reviveMetrics.l2Trips;
|
|
234
|
+
return durableStore.getEntries(canonicalKeys, DefaultDurableSegment).then(function (durableRecords) {
|
|
235
|
+
l2Trips.push({
|
|
236
|
+
duration: Date.now() - start,
|
|
237
|
+
keysRequestedCount: canonicalKeys.length,
|
|
238
|
+
});
|
|
239
|
+
var _a = publishDurableStoreEntries(durableRecords,
|
|
240
|
+
// TODO [W-10072584]: instead of implicitly using L1 we should take in
|
|
241
|
+
// publish and publishMetadata funcs, so callers can decide where to
|
|
242
|
+
// revive to (like they pass in how to do the buildL1Snapshot)
|
|
243
|
+
baseEnvironment.storePublish.bind(baseEnvironment), baseEnvironment.publishStoreMetadata.bind(baseEnvironment)), revivedKeys = _a.revivedKeys, hadUnexpectedShape = _a.hadUnexpectedShape;
|
|
244
|
+
// if the data coming back from DS had an unexpected shape then just
|
|
245
|
+
// return the L1 snapshot
|
|
246
|
+
if (hadUnexpectedShape === true) {
|
|
247
|
+
return { snapshot: unavailableSnapshot, metrics: reviveMetrics };
|
|
248
|
+
}
|
|
249
|
+
if (revivedKeys.size() === 0) {
|
|
250
|
+
// durable store doesn't have what we asked for so return L1 snapshot
|
|
251
|
+
return { snapshot: unavailableSnapshot, metrics: reviveMetrics };
|
|
252
|
+
}
|
|
253
|
+
// try building the snapshot from L1 now that we have revived the missingLinks
|
|
254
|
+
var snapshot = buildL1Snapshot();
|
|
255
|
+
// if snapshot is pending then some other in-flight refresh will broadcast
|
|
256
|
+
// later
|
|
257
|
+
if (snapshot.state === 'Pending') {
|
|
258
|
+
return { snapshot: snapshot, metrics: reviveMetrics };
|
|
259
|
+
}
|
|
260
|
+
if (snapshot.state === 'Unfulfilled') {
|
|
261
|
+
// have to check if the new snapshot has any additional seenRecords
|
|
262
|
+
// and revive again if so
|
|
263
|
+
var newSnapshotSeenRecords = snapshot.seenRecords, newSnapshotRecordId = snapshot.recordId;
|
|
264
|
+
var newKeysToReviveSet = new engine.StoreKeySet();
|
|
265
|
+
newKeysToReviveSet.add(newSnapshotRecordId);
|
|
266
|
+
newKeysToReviveSet.merge(newSnapshotSeenRecords);
|
|
267
|
+
var newKeys = newKeysToReviveSet.keysAsArray();
|
|
268
|
+
// in case DS returned additional entries we combine the requested
|
|
269
|
+
// and returned keys
|
|
270
|
+
var alreadyRequestedOrRevivedSet = keysToReviveSet;
|
|
271
|
+
alreadyRequestedOrRevivedSet.merge(revivedKeys);
|
|
272
|
+
// if there's any seen keys in the newly rebuilt snapshot that
|
|
273
|
+
// haven't already been requested or returned then revive again
|
|
274
|
+
for (var i = 0, len = newKeys.length; i < len; i++) {
|
|
275
|
+
var newSnapshotSeenKey = newKeys[i];
|
|
276
|
+
if (!alreadyRequestedOrRevivedSet.has(newSnapshotSeenKey)) {
|
|
277
|
+
return reviveSnapshot(baseEnvironment, durableStore, snapshot, durableStoreErrorHandler, buildL1Snapshot, reviveMetrics);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return { snapshot: snapshot, metrics: reviveMetrics };
|
|
282
|
+
}, function (error) {
|
|
283
|
+
durableStoreErrorHandler(error);
|
|
284
|
+
// getEntries failed, return the L1 snapshot
|
|
285
|
+
return { snapshot: unavailableSnapshot, metrics: reviveMetrics };
|
|
286
|
+
});
|
|
287
287
|
}
|
|
288
288
|
|
|
289
|
-
var TTL_DURABLE_SEGMENT = 'TTL_DURABLE_SEGMENT';
|
|
290
|
-
var TTL_DEFAULT_KEY = 'TTL_DEFAULT_KEY';
|
|
291
|
-
function buildDurableTTLOverrideStoreKey(namespace, representationName) {
|
|
292
|
-
return "".concat(namespace, "::").concat(representationName);
|
|
293
|
-
}
|
|
294
|
-
function isEntryDurableTTLOverride(entry) {
|
|
295
|
-
if (typeof entry === 'object' && entry !== undefined && entry !== null) {
|
|
296
|
-
var data = entry.data;
|
|
297
|
-
if (data !== undefined) {
|
|
298
|
-
return (data.namespace !== undefined &&
|
|
299
|
-
data.representationName !== undefined &&
|
|
300
|
-
data.ttl !== undefined);
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
return false;
|
|
304
|
-
}
|
|
305
|
-
function isDefaultDurableTTLOverride(override) {
|
|
306
|
-
return (override.namespace === TTL_DEFAULT_KEY && override.representationName === TTL_DEFAULT_KEY);
|
|
307
|
-
}
|
|
308
|
-
/**
|
|
309
|
-
* Class to set and get the TTL override values in the Durable Store
|
|
310
|
-
*/
|
|
311
|
-
var DurableTTLStore = /** @class */ (function () {
|
|
312
|
-
function DurableTTLStore(durableStore) {
|
|
313
|
-
this.durableStore = durableStore;
|
|
314
|
-
}
|
|
315
|
-
DurableTTLStore.prototype.setDefaultDurableTTLOverrides = function (ttl) {
|
|
316
|
-
var _a;
|
|
317
|
-
return this.durableStore.setEntries((_a = {},
|
|
318
|
-
_a[buildDurableTTLOverrideStoreKey(TTL_DEFAULT_KEY, TTL_DEFAULT_KEY)] = {
|
|
319
|
-
data: {
|
|
320
|
-
namespace: TTL_DEFAULT_KEY,
|
|
321
|
-
representationName: TTL_DEFAULT_KEY,
|
|
322
|
-
ttl: ttl,
|
|
323
|
-
},
|
|
324
|
-
},
|
|
325
|
-
_a), TTL_DURABLE_SEGMENT);
|
|
326
|
-
};
|
|
327
|
-
DurableTTLStore.prototype.setDurableTTLOverride = function (namespace, representationName, ttl) {
|
|
328
|
-
var _a;
|
|
329
|
-
return this.durableStore.setEntries((_a = {},
|
|
330
|
-
_a[buildDurableTTLOverrideStoreKey(namespace, representationName)] = {
|
|
331
|
-
data: { namespace: namespace, representationName: representationName, ttl: ttl },
|
|
332
|
-
},
|
|
333
|
-
_a), TTL_DURABLE_SEGMENT);
|
|
334
|
-
};
|
|
335
|
-
DurableTTLStore.prototype.getDurableTTLOverrides = function () {
|
|
336
|
-
return this.durableStore
|
|
337
|
-
.getAllEntries(TTL_DURABLE_SEGMENT)
|
|
338
|
-
.then(function (entries) {
|
|
339
|
-
var overrides = [];
|
|
340
|
-
var defaultTTL = undefined;
|
|
341
|
-
if (entries === undefined) {
|
|
342
|
-
return {
|
|
343
|
-
defaultTTL: defaultTTL,
|
|
344
|
-
overrides: overrides,
|
|
345
|
-
};
|
|
346
|
-
}
|
|
347
|
-
var keys$1 = keys(entries);
|
|
348
|
-
for (var i = 0, len = keys$1.length; i < len; i++) {
|
|
349
|
-
var key = keys$1[i];
|
|
350
|
-
var entry = entries[key];
|
|
351
|
-
if (entry !== undefined && isEntryDurableTTLOverride(entry)) {
|
|
352
|
-
if (isDefaultDurableTTLOverride(entry.data)) {
|
|
353
|
-
defaultTTL = entry.data;
|
|
354
|
-
}
|
|
355
|
-
else {
|
|
356
|
-
overrides.push(entry.data);
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
return {
|
|
361
|
-
defaultTTL: defaultTTL,
|
|
362
|
-
overrides: overrides,
|
|
363
|
-
};
|
|
364
|
-
});
|
|
365
|
-
};
|
|
366
|
-
return DurableTTLStore;
|
|
289
|
+
var TTL_DURABLE_SEGMENT = 'TTL_DURABLE_SEGMENT';
|
|
290
|
+
var TTL_DEFAULT_KEY = 'TTL_DEFAULT_KEY';
|
|
291
|
+
function buildDurableTTLOverrideStoreKey(namespace, representationName) {
|
|
292
|
+
return "".concat(namespace, "::").concat(representationName);
|
|
293
|
+
}
|
|
294
|
+
function isEntryDurableTTLOverride(entry) {
|
|
295
|
+
if (typeof entry === 'object' && entry !== undefined && entry !== null) {
|
|
296
|
+
var data = entry.data;
|
|
297
|
+
if (data !== undefined) {
|
|
298
|
+
return (data.namespace !== undefined &&
|
|
299
|
+
data.representationName !== undefined &&
|
|
300
|
+
data.ttl !== undefined);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
return false;
|
|
304
|
+
}
|
|
305
|
+
function isDefaultDurableTTLOverride(override) {
|
|
306
|
+
return (override.namespace === TTL_DEFAULT_KEY && override.representationName === TTL_DEFAULT_KEY);
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Class to set and get the TTL override values in the Durable Store
|
|
310
|
+
*/
|
|
311
|
+
var DurableTTLStore = /** @class */ (function () {
|
|
312
|
+
function DurableTTLStore(durableStore) {
|
|
313
|
+
this.durableStore = durableStore;
|
|
314
|
+
}
|
|
315
|
+
DurableTTLStore.prototype.setDefaultDurableTTLOverrides = function (ttl) {
|
|
316
|
+
var _a;
|
|
317
|
+
return this.durableStore.setEntries((_a = {},
|
|
318
|
+
_a[buildDurableTTLOverrideStoreKey(TTL_DEFAULT_KEY, TTL_DEFAULT_KEY)] = {
|
|
319
|
+
data: {
|
|
320
|
+
namespace: TTL_DEFAULT_KEY,
|
|
321
|
+
representationName: TTL_DEFAULT_KEY,
|
|
322
|
+
ttl: ttl,
|
|
323
|
+
},
|
|
324
|
+
},
|
|
325
|
+
_a), TTL_DURABLE_SEGMENT);
|
|
326
|
+
};
|
|
327
|
+
DurableTTLStore.prototype.setDurableTTLOverride = function (namespace, representationName, ttl) {
|
|
328
|
+
var _a;
|
|
329
|
+
return this.durableStore.setEntries((_a = {},
|
|
330
|
+
_a[buildDurableTTLOverrideStoreKey(namespace, representationName)] = {
|
|
331
|
+
data: { namespace: namespace, representationName: representationName, ttl: ttl },
|
|
332
|
+
},
|
|
333
|
+
_a), TTL_DURABLE_SEGMENT);
|
|
334
|
+
};
|
|
335
|
+
DurableTTLStore.prototype.getDurableTTLOverrides = function () {
|
|
336
|
+
return this.durableStore
|
|
337
|
+
.getAllEntries(TTL_DURABLE_SEGMENT)
|
|
338
|
+
.then(function (entries) {
|
|
339
|
+
var overrides = [];
|
|
340
|
+
var defaultTTL = undefined;
|
|
341
|
+
if (entries === undefined) {
|
|
342
|
+
return {
|
|
343
|
+
defaultTTL: defaultTTL,
|
|
344
|
+
overrides: overrides,
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
var keys$1 = keys(entries);
|
|
348
|
+
for (var i = 0, len = keys$1.length; i < len; i++) {
|
|
349
|
+
var key = keys$1[i];
|
|
350
|
+
var entry = entries[key];
|
|
351
|
+
if (entry !== undefined && isEntryDurableTTLOverride(entry)) {
|
|
352
|
+
if (isDefaultDurableTTLOverride(entry.data)) {
|
|
353
|
+
defaultTTL = entry.data;
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
overrides.push(entry.data);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return {
|
|
361
|
+
defaultTTL: defaultTTL,
|
|
362
|
+
overrides: overrides,
|
|
363
|
+
};
|
|
364
|
+
});
|
|
365
|
+
};
|
|
366
|
+
return DurableTTLStore;
|
|
367
367
|
}());
|
|
368
368
|
|
|
369
|
-
function copy(source) {
|
|
370
|
-
if (typeof source !== 'object' || source === null) {
|
|
371
|
-
return source;
|
|
372
|
-
}
|
|
373
|
-
if (isArray(source)) {
|
|
374
|
-
// TS doesn't trust that this new array is an array unless we cast it
|
|
375
|
-
return __spreadArray([], source, true);
|
|
376
|
-
}
|
|
377
|
-
return __assign({}, source);
|
|
378
|
-
}
|
|
379
|
-
function flushInMemoryStoreValuesToDurableStore(store, durableStore, durableStoreErrorHandler) {
|
|
380
|
-
var durableRecords = create(null);
|
|
381
|
-
var evictedRecords = create(null);
|
|
382
|
-
var _a = store.fallbackStringKeyInMemoryStore, records = _a.records, storeMetadata = _a.metadata, visitedIds = _a.visitedIds, refreshedIds = _a.refreshedIds;
|
|
383
|
-
// TODO: W-8909393 Once metadata is stored in its own segment we need to
|
|
384
|
-
// call setEntries for the visitedIds on default segment and call setEntries
|
|
385
|
-
// on the metadata segment for the refreshedIds
|
|
386
|
-
var keys$1 = keys(__assign(__assign({}, visitedIds), refreshedIds));
|
|
387
|
-
for (var i = 0, len = keys$1.length; i < len; i += 1) {
|
|
388
|
-
var key = keys$1[i];
|
|
389
|
-
var record = records[key];
|
|
390
|
-
// this record has been evicted, evict from DS
|
|
391
|
-
if (record === undefined) {
|
|
392
|
-
evictedRecords[key] = true;
|
|
393
|
-
continue;
|
|
394
|
-
}
|
|
395
|
-
var metadata = storeMetadata[key];
|
|
396
|
-
durableRecords[key] = {
|
|
397
|
-
// copy the data in case the store is mutated during the
|
|
398
|
-
// async setEntries call
|
|
399
|
-
data: copy(record),
|
|
400
|
-
};
|
|
401
|
-
if (metadata !== undefined) {
|
|
402
|
-
durableRecords[key].metadata = __assign(__assign({}, metadata), { metadataVersion: DURABLE_METADATA_VERSION });
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
var durableStoreOperations = [];
|
|
406
|
-
// publishes
|
|
407
|
-
var recordKeys = keys(durableRecords);
|
|
408
|
-
if (recordKeys.length > 0) {
|
|
409
|
-
durableStoreOperations.push({
|
|
410
|
-
type: 'setEntries',
|
|
411
|
-
entries: durableRecords,
|
|
412
|
-
segment: DefaultDurableSegment,
|
|
413
|
-
});
|
|
414
|
-
}
|
|
415
|
-
// evicts
|
|
416
|
-
var evictedKeys = keys(evictedRecords);
|
|
417
|
-
if (evictedKeys.length > 0) {
|
|
418
|
-
durableStoreOperations.push({
|
|
419
|
-
type: 'evictEntries',
|
|
420
|
-
ids: evictedKeys,
|
|
421
|
-
segment: DefaultDurableSegment,
|
|
422
|
-
});
|
|
423
|
-
}
|
|
424
|
-
if (durableStoreOperations.length > 0) {
|
|
425
|
-
return durableStore.batchOperations(durableStoreOperations).catch(durableStoreErrorHandler);
|
|
426
|
-
}
|
|
427
|
-
return Promise.resolve();
|
|
369
|
+
function copy(source) {
|
|
370
|
+
if (typeof source !== 'object' || source === null) {
|
|
371
|
+
return source;
|
|
372
|
+
}
|
|
373
|
+
if (isArray(source)) {
|
|
374
|
+
// TS doesn't trust that this new array is an array unless we cast it
|
|
375
|
+
return __spreadArray([], source, true);
|
|
376
|
+
}
|
|
377
|
+
return __assign({}, source);
|
|
378
|
+
}
|
|
379
|
+
function flushInMemoryStoreValuesToDurableStore(store, durableStore, durableStoreErrorHandler) {
|
|
380
|
+
var durableRecords = create(null);
|
|
381
|
+
var evictedRecords = create(null);
|
|
382
|
+
var _a = store.fallbackStringKeyInMemoryStore, records = _a.records, storeMetadata = _a.metadata, visitedIds = _a.visitedIds, refreshedIds = _a.refreshedIds;
|
|
383
|
+
// TODO: W-8909393 Once metadata is stored in its own segment we need to
|
|
384
|
+
// call setEntries for the visitedIds on default segment and call setEntries
|
|
385
|
+
// on the metadata segment for the refreshedIds
|
|
386
|
+
var keys$1 = keys(__assign(__assign({}, visitedIds), refreshedIds));
|
|
387
|
+
for (var i = 0, len = keys$1.length; i < len; i += 1) {
|
|
388
|
+
var key = keys$1[i];
|
|
389
|
+
var record = records[key];
|
|
390
|
+
// this record has been evicted, evict from DS
|
|
391
|
+
if (record === undefined) {
|
|
392
|
+
evictedRecords[key] = true;
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
var metadata = storeMetadata[key];
|
|
396
|
+
durableRecords[key] = {
|
|
397
|
+
// copy the data in case the store is mutated during the
|
|
398
|
+
// async setEntries call
|
|
399
|
+
data: copy(record),
|
|
400
|
+
};
|
|
401
|
+
if (metadata !== undefined) {
|
|
402
|
+
durableRecords[key].metadata = __assign(__assign({}, metadata), { metadataVersion: DURABLE_METADATA_VERSION });
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
var durableStoreOperations = [];
|
|
406
|
+
// publishes
|
|
407
|
+
var recordKeys = keys(durableRecords);
|
|
408
|
+
if (recordKeys.length > 0) {
|
|
409
|
+
durableStoreOperations.push({
|
|
410
|
+
type: 'setEntries',
|
|
411
|
+
entries: durableRecords,
|
|
412
|
+
segment: DefaultDurableSegment,
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
// evicts
|
|
416
|
+
var evictedKeys = keys(evictedRecords);
|
|
417
|
+
if (evictedKeys.length > 0) {
|
|
418
|
+
durableStoreOperations.push({
|
|
419
|
+
type: 'evictEntries',
|
|
420
|
+
ids: evictedKeys,
|
|
421
|
+
segment: DefaultDurableSegment,
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
if (durableStoreOperations.length > 0) {
|
|
425
|
+
return durableStore.batchOperations(durableStoreOperations).catch(durableStoreErrorHandler);
|
|
426
|
+
}
|
|
427
|
+
return Promise.resolve();
|
|
428
428
|
}
|
|
429
429
|
|
|
430
|
-
var DurableEnvironmentEventDiscriminator = 'durable';
|
|
431
|
-
function isDurableEnvironmentEvent(event) {
|
|
432
|
-
return (event.type === 'environment' && event.environment === DurableEnvironmentEventDiscriminator);
|
|
433
|
-
}
|
|
434
|
-
function emitDurableEnvironmentAdapterEvent(eventData, observers) {
|
|
435
|
-
engine.emitAdapterEvent({
|
|
436
|
-
type: 'environment',
|
|
437
|
-
timestamp: Date.now(),
|
|
438
|
-
environment: DurableEnvironmentEventDiscriminator,
|
|
439
|
-
data: eventData,
|
|
440
|
-
}, observers);
|
|
430
|
+
var DurableEnvironmentEventDiscriminator = 'durable';
|
|
431
|
+
function isDurableEnvironmentEvent(event) {
|
|
432
|
+
return (event.type === 'environment' && event.environment === DurableEnvironmentEventDiscriminator);
|
|
433
|
+
}
|
|
434
|
+
function emitDurableEnvironmentAdapterEvent(eventData, observers) {
|
|
435
|
+
engine.emitAdapterEvent({
|
|
436
|
+
type: 'environment',
|
|
437
|
+
timestamp: Date.now(),
|
|
438
|
+
environment: DurableEnvironmentEventDiscriminator,
|
|
439
|
+
data: eventData,
|
|
440
|
+
}, observers);
|
|
441
441
|
}
|
|
442
442
|
|
|
443
|
-
function reviveTTLOverrides(ttlStore, environment) {
|
|
444
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
445
|
-
var map, defaultTTL, overrides, i, len, _a, namespace, representationName, ttl;
|
|
446
|
-
return __generator(this, function (_b) {
|
|
447
|
-
switch (_b.label) {
|
|
448
|
-
case 0: return [4 /*yield*/, ttlStore.getDurableTTLOverrides()];
|
|
449
|
-
case 1:
|
|
450
|
-
map = _b.sent();
|
|
451
|
-
defaultTTL = map.defaultTTL, overrides = map.overrides;
|
|
452
|
-
if (defaultTTL !== undefined) {
|
|
453
|
-
environment.storeSetDefaultTTLOverride(defaultTTL.ttl);
|
|
454
|
-
}
|
|
455
|
-
for (i = 0, len = overrides.length; i < len; i++) {
|
|
456
|
-
_a = overrides[i], namespace = _a.namespace, representationName = _a.representationName, ttl = _a.ttl;
|
|
457
|
-
environment.storeSetTTLOverride(namespace, representationName, ttl);
|
|
458
|
-
}
|
|
459
|
-
return [2 /*return*/];
|
|
460
|
-
}
|
|
461
|
-
});
|
|
462
|
-
});
|
|
443
|
+
function reviveTTLOverrides(ttlStore, environment) {
|
|
444
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
445
|
+
var map, defaultTTL, overrides, i, len, _a, namespace, representationName, ttl;
|
|
446
|
+
return __generator(this, function (_b) {
|
|
447
|
+
switch (_b.label) {
|
|
448
|
+
case 0: return [4 /*yield*/, ttlStore.getDurableTTLOverrides()];
|
|
449
|
+
case 1:
|
|
450
|
+
map = _b.sent();
|
|
451
|
+
defaultTTL = map.defaultTTL, overrides = map.overrides;
|
|
452
|
+
if (defaultTTL !== undefined) {
|
|
453
|
+
environment.storeSetDefaultTTLOverride(defaultTTL.ttl);
|
|
454
|
+
}
|
|
455
|
+
for (i = 0, len = overrides.length; i < len; i++) {
|
|
456
|
+
_a = overrides[i], namespace = _a.namespace, representationName = _a.representationName, ttl = _a.ttl;
|
|
457
|
+
environment.storeSetTTLOverride(namespace, representationName, ttl);
|
|
458
|
+
}
|
|
459
|
+
return [2 /*return*/];
|
|
460
|
+
}
|
|
461
|
+
});
|
|
462
|
+
});
|
|
463
463
|
}
|
|
464
464
|
|
|
465
|
-
/**
|
|
466
|
-
* Returns an empty InMemoryStore that can be used for ingestion. Copies over
|
|
467
|
-
* the TTLOverrides from the given Environment's Store.
|
|
468
|
-
*/
|
|
469
|
-
function buildIngestStagingStore(environment) {
|
|
470
|
-
return environment.storeBuildIngestionStagingStore();
|
|
465
|
+
/**
|
|
466
|
+
* Returns an empty InMemoryStore that can be used for ingestion. Copies over
|
|
467
|
+
* the TTLOverrides from the given Environment's Store.
|
|
468
|
+
*/
|
|
469
|
+
function buildIngestStagingStore(environment) {
|
|
470
|
+
return environment.storeBuildIngestionStagingStore();
|
|
471
471
|
}
|
|
472
472
|
|
|
473
|
-
var AdapterContextSegment = 'ADAPTER-CONTEXT';
|
|
474
|
-
var ADAPTER_CONTEXT_ID_SUFFIX = '__NAMED_CONTEXT';
|
|
475
|
-
function reviveOrCreateContext(adapterId, durableStore, durableStoreErrorHandler, contextStores, pendingContextStoreKeys, onContextLoaded) {
|
|
476
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
477
|
-
var context, contextReturn, entries, error_1;
|
|
478
|
-
return __generator(this, function (_a) {
|
|
479
|
-
switch (_a.label) {
|
|
480
|
-
case 0:
|
|
481
|
-
// initialize empty context store
|
|
482
|
-
contextStores[adapterId] = create(null);
|
|
483
|
-
context = {
|
|
484
|
-
set: function (key, value) {
|
|
485
|
-
var _a;
|
|
486
|
-
contextStores[adapterId][key] = value;
|
|
487
|
-
durableStore.setEntries((_a = {},
|
|
488
|
-
_a[adapterId] = { data: contextStores[adapterId] },
|
|
489
|
-
_a), AdapterContextSegment);
|
|
490
|
-
pendingContextStoreKeys.add(adapterId);
|
|
491
|
-
},
|
|
492
|
-
get: function (key) {
|
|
493
|
-
return contextStores[adapterId][key];
|
|
494
|
-
},
|
|
495
|
-
};
|
|
496
|
-
contextReturn = function () {
|
|
497
|
-
if (onContextLoaded !== undefined) {
|
|
498
|
-
return onContextLoaded(context).then(function () {
|
|
499
|
-
return context;
|
|
500
|
-
});
|
|
501
|
-
}
|
|
502
|
-
return context;
|
|
503
|
-
};
|
|
504
|
-
_a.label = 1;
|
|
505
|
-
case 1:
|
|
506
|
-
_a.trys.push([1, 3, , 4]);
|
|
507
|
-
return [4 /*yield*/, durableStore.getEntries([adapterId], AdapterContextSegment)];
|
|
508
|
-
case 2:
|
|
509
|
-
entries = _a.sent();
|
|
510
|
-
if (entries !== undefined && entries[adapterId] !== undefined) {
|
|
511
|
-
// if durable store has a saved context then load it in the store
|
|
512
|
-
contextStores[adapterId] = entries[adapterId].data;
|
|
513
|
-
}
|
|
514
|
-
return [3 /*break*/, 4];
|
|
515
|
-
case 3:
|
|
516
|
-
error_1 = _a.sent();
|
|
517
|
-
durableStoreErrorHandler(error_1);
|
|
518
|
-
return [3 /*break*/, 4];
|
|
519
|
-
case 4: return [2 /*return*/, contextReturn()];
|
|
520
|
-
}
|
|
521
|
-
});
|
|
522
|
-
});
|
|
523
|
-
}
|
|
524
|
-
function isUnfulfilledSnapshot(cachedSnapshotResult) {
|
|
525
|
-
if (cachedSnapshotResult === undefined) {
|
|
526
|
-
return false;
|
|
527
|
-
}
|
|
528
|
-
if ('then' in cachedSnapshotResult) {
|
|
529
|
-
return false;
|
|
530
|
-
}
|
|
531
|
-
return cachedSnapshotResult.state === 'Unfulfilled';
|
|
532
|
-
}
|
|
533
|
-
/**
|
|
534
|
-
* Configures the environment to persist data into a durable store and attempt to resolve
|
|
535
|
-
* data from the persistent store before hitting the network.
|
|
536
|
-
*
|
|
537
|
-
* @param environment The base environment
|
|
538
|
-
* @param durableStore A DurableStore implementation
|
|
539
|
-
* @param instrumentation An instrumentation function implementation
|
|
540
|
-
*/
|
|
541
|
-
function makeDurable(environment, _a) {
|
|
542
|
-
var _this = this;
|
|
543
|
-
var durableStore = _a.durableStore, instrumentation = _a.instrumentation;
|
|
544
|
-
var ingestStagingStore = null;
|
|
545
|
-
var durableTTLStore = new DurableTTLStore(durableStore);
|
|
546
|
-
var mergeKeysPromiseMap = new engine.StoreKeyMap();
|
|
547
|
-
// When a context store is mutated we write it to L2, which causes DS on change
|
|
548
|
-
// event. If this instance of makeDurable caused that L2 write we can ignore that
|
|
549
|
-
// on change event. This Set helps us do that.
|
|
550
|
-
var pendingContextStoreKeys = new Set();
|
|
551
|
-
var contextStores = create(null);
|
|
552
|
-
var initializationPromise = new Promise(function (resolve) {
|
|
553
|
-
var finish = function () {
|
|
554
|
-
resolve();
|
|
555
|
-
initializationPromise = undefined;
|
|
556
|
-
};
|
|
557
|
-
reviveTTLOverrides(durableTTLStore, environment).then(finish);
|
|
558
|
-
});
|
|
559
|
-
//instrumentation for durable store errors
|
|
560
|
-
var durableStoreErrorHandler = handleDurableStoreRejection(instrumentation);
|
|
561
|
-
var disposed = false;
|
|
562
|
-
var validateNotDisposed = function () {
|
|
563
|
-
if (disposed === true) {
|
|
564
|
-
throw new Error('This makeDurable instance has been disposed');
|
|
565
|
-
}
|
|
566
|
-
};
|
|
567
|
-
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) {
|
|
571
|
-
case 0:
|
|
572
|
-
defaultSegmentKeys = [];
|
|
573
|
-
adapterContextSegmentKeys = [];
|
|
574
|
-
for (i = 0, len = changes.length; i < len; i++) {
|
|
575
|
-
change = changes[i];
|
|
576
|
-
// we only care about changes to the data which is stored in the default
|
|
577
|
-
// segment or the adapter context
|
|
578
|
-
if (change.segment === DefaultDurableSegment) {
|
|
579
|
-
defaultSegmentKeys.push.apply(defaultSegmentKeys, change.ids);
|
|
580
|
-
}
|
|
581
|
-
else if (change.segment === AdapterContextSegment) {
|
|
582
|
-
adapterContextSegmentKeys.push.apply(adapterContextSegmentKeys, change.ids);
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
adapterContextKeysFromDifferentInstance = [];
|
|
586
|
-
for (_i = 0, adapterContextSegmentKeys_1 = adapterContextSegmentKeys; _i < adapterContextSegmentKeys_1.length; _i++) {
|
|
587
|
-
key = adapterContextSegmentKeys_1[_i];
|
|
588
|
-
if (pendingContextStoreKeys.has(key)) {
|
|
589
|
-
// if this instance caused the L2 write then remove from the
|
|
590
|
-
// "pending" Set and move on
|
|
591
|
-
pendingContextStoreKeys.delete(key);
|
|
592
|
-
}
|
|
593
|
-
else {
|
|
594
|
-
// else it came from another luvio instance and we need to
|
|
595
|
-
// read from L2
|
|
596
|
-
adapterContextKeysFromDifferentInstance.push(key);
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
|
-
if (!(adapterContextKeysFromDifferentInstance.length > 0)) return [3 /*break*/, 4];
|
|
600
|
-
_a.label = 1;
|
|
601
|
-
case 1:
|
|
602
|
-
_a.trys.push([1, 3, , 4]);
|
|
603
|
-
return [4 /*yield*/, durableStore.getEntries(adapterContextKeysFromDifferentInstance, AdapterContextSegment)];
|
|
604
|
-
case 2:
|
|
605
|
-
entries = _a.sent();
|
|
606
|
-
if (entries !== undefined) {
|
|
607
|
-
entryKeys = keys(entries);
|
|
608
|
-
for (i = 0, len = entryKeys.length; i < len; i++) {
|
|
609
|
-
entryKey = entryKeys[i];
|
|
610
|
-
entry = entries[entryKey];
|
|
611
|
-
contextStores[entryKey] = entry.data;
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
return [3 /*break*/, 4];
|
|
615
|
-
case 3:
|
|
616
|
-
error_2 = _a.sent();
|
|
617
|
-
durableStoreErrorHandler(error_2);
|
|
618
|
-
return [3 /*break*/, 4];
|
|
619
|
-
case 4:
|
|
620
|
-
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;
|
|
627
|
-
}
|
|
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);
|
|
634
|
-
}
|
|
635
|
-
return [4 /*yield*/, environment.storeBroadcast(rebuildSnapshot, environment.snapshotAvailable)];
|
|
636
|
-
case 5:
|
|
637
|
-
_a.sent();
|
|
638
|
-
_a.label = 6;
|
|
639
|
-
case 6: return [2 /*return*/];
|
|
640
|
-
}
|
|
641
|
-
});
|
|
642
|
-
}); });
|
|
643
|
-
var dispose = function () {
|
|
644
|
-
validateNotDisposed();
|
|
645
|
-
disposed = true;
|
|
646
|
-
return unsubscribe();
|
|
647
|
-
};
|
|
648
|
-
var storePublish = function (key, data) {
|
|
649
|
-
validateNotDisposed();
|
|
650
|
-
if (ingestStagingStore === null) {
|
|
651
|
-
ingestStagingStore = buildIngestStagingStore(environment);
|
|
652
|
-
}
|
|
653
|
-
ingestStagingStore.publish(key, data);
|
|
654
|
-
// remove record from main luvio L1 cache while we are on the synchronous path
|
|
655
|
-
// because we do not want some other code attempting to use the
|
|
656
|
-
// in-memory values before the durable store onChanged handler
|
|
657
|
-
// calls back and revives the values to in-memory
|
|
658
|
-
environment.storeEvict(key);
|
|
659
|
-
};
|
|
660
|
-
var publishStoreMetadata = function (recordId, storeMetadata) {
|
|
661
|
-
validateNotDisposed();
|
|
662
|
-
if (ingestStagingStore === null) {
|
|
663
|
-
ingestStagingStore = buildIngestStagingStore(environment);
|
|
664
|
-
}
|
|
665
|
-
ingestStagingStore.publishMetadata(recordId, storeMetadata);
|
|
666
|
-
};
|
|
667
|
-
var storeIngest = function (key, ingest, response, luvio) {
|
|
668
|
-
validateNotDisposed();
|
|
669
|
-
// we don't ingest to the luvio L1 store from network directly, we ingest to
|
|
670
|
-
// L2 and let DurableStore on change event revive keys into luvio L1 store
|
|
671
|
-
if (ingestStagingStore === null) {
|
|
672
|
-
ingestStagingStore = buildIngestStagingStore(environment);
|
|
673
|
-
}
|
|
674
|
-
environment.storeIngest(key, ingest, response, luvio, ingestStagingStore);
|
|
675
|
-
};
|
|
676
|
-
var storeIngestError = function (key, errorSnapshot, storeMetadataParams, _storeOverride) {
|
|
677
|
-
validateNotDisposed();
|
|
678
|
-
if (ingestStagingStore === null) {
|
|
679
|
-
ingestStagingStore = buildIngestStagingStore(environment);
|
|
680
|
-
}
|
|
681
|
-
environment.storeIngestError(key, errorSnapshot, storeMetadataParams, ingestStagingStore);
|
|
682
|
-
};
|
|
683
|
-
var storeBroadcast = function (_rebuildSnapshot, _snapshotDataAvailable) {
|
|
684
|
-
validateNotDisposed();
|
|
685
|
-
// publishing to L2 is essentially "broadcasting" because the onChanged
|
|
686
|
-
// handler will fire which will revive records to the main L1 store and
|
|
687
|
-
// call the base storeBroadcast
|
|
688
|
-
return publishChangesToDurableStore();
|
|
689
|
-
};
|
|
690
|
-
var publishChangesToDurableStore = function () {
|
|
691
|
-
validateNotDisposed();
|
|
692
|
-
if (ingestStagingStore === null) {
|
|
693
|
-
return Promise.resolve();
|
|
694
|
-
}
|
|
695
|
-
var promise = flushInMemoryStoreValuesToDurableStore(ingestStagingStore, durableStore, durableStoreErrorHandler);
|
|
696
|
-
ingestStagingStore = null;
|
|
697
|
-
return promise;
|
|
698
|
-
};
|
|
699
|
-
var storeLookup = function (sel, createSnapshot, refresh, ttlStrategy) {
|
|
700
|
-
validateNotDisposed();
|
|
701
|
-
// if this lookup is right after an ingest there will be a staging store
|
|
702
|
-
if (ingestStagingStore !== null) {
|
|
703
|
-
var reader = new engine.Reader(ingestStagingStore, sel.variables, refresh, undefined, ttlStrategy);
|
|
704
|
-
return reader.read(sel);
|
|
705
|
-
}
|
|
706
|
-
// otherwise this is from buildCachedSnapshot and we should use the luvio
|
|
707
|
-
// L1 store
|
|
708
|
-
return environment.storeLookup(sel, createSnapshot, refresh, ttlStrategy);
|
|
709
|
-
};
|
|
710
|
-
var storeEvict = function (key) {
|
|
711
|
-
validateNotDisposed();
|
|
712
|
-
if (ingestStagingStore === null) {
|
|
713
|
-
ingestStagingStore = buildIngestStagingStore(environment);
|
|
714
|
-
}
|
|
715
|
-
ingestStagingStore.evict(key);
|
|
716
|
-
};
|
|
717
|
-
var getNode = function (key) {
|
|
718
|
-
validateNotDisposed();
|
|
719
|
-
if (ingestStagingStore === null) {
|
|
720
|
-
ingestStagingStore = buildIngestStagingStore(environment);
|
|
721
|
-
}
|
|
722
|
-
return environment.getNode(key, ingestStagingStore);
|
|
723
|
-
};
|
|
724
|
-
var wrapNormalizedGraphNode = function (normalized) {
|
|
725
|
-
validateNotDisposed();
|
|
726
|
-
if (ingestStagingStore === null) {
|
|
727
|
-
ingestStagingStore = buildIngestStagingStore(environment);
|
|
728
|
-
}
|
|
729
|
-
return environment.wrapNormalizedGraphNode(normalized, ingestStagingStore);
|
|
730
|
-
};
|
|
731
|
-
var rebuildSnapshot = function (snapshot, onRebuild) {
|
|
732
|
-
validateNotDisposed();
|
|
733
|
-
// try rebuilding from memory
|
|
734
|
-
environment.rebuildSnapshot(snapshot, function (rebuilt) {
|
|
735
|
-
// only try reviving from durable store if snapshot is unfulfilled
|
|
736
|
-
if (rebuilt.state !== 'Unfulfilled') {
|
|
737
|
-
onRebuild(rebuilt);
|
|
738
|
-
return;
|
|
739
|
-
}
|
|
740
|
-
// Do an L2 revive and emit to subscriber using the callback.
|
|
741
|
-
reviveSnapshot(environment, durableStore, rebuilt, durableStoreErrorHandler, function () {
|
|
742
|
-
// reviveSnapshot will revive into L1, and since "records" is a reference
|
|
743
|
-
// (and not a copy) to the L1 records we can use it for rebuild
|
|
744
|
-
var rebuiltSnap;
|
|
745
|
-
environment.rebuildSnapshot(snapshot, function (rebuilt) {
|
|
746
|
-
rebuiltSnap = rebuilt;
|
|
747
|
-
});
|
|
748
|
-
return rebuiltSnap;
|
|
749
|
-
}).then(function (result) {
|
|
750
|
-
onRebuild(result.snapshot);
|
|
751
|
-
});
|
|
752
|
-
});
|
|
753
|
-
};
|
|
754
|
-
var withContext = function (adapter, options) {
|
|
755
|
-
validateNotDisposed();
|
|
756
|
-
var contextId = options.contextId, contextVersion = options.contextVersion, onContextLoaded = options.onContextLoaded;
|
|
757
|
-
var context = undefined;
|
|
758
|
-
var contextKey = "".concat(contextId);
|
|
759
|
-
// if a context version is supplied, key with the version encoded
|
|
760
|
-
if (contextVersion !== undefined) {
|
|
761
|
-
contextKey += "::".concat(contextVersion);
|
|
762
|
-
}
|
|
763
|
-
contextKey += ADAPTER_CONTEXT_ID_SUFFIX;
|
|
764
|
-
var contextAsPromise = reviveOrCreateContext(contextKey, durableStore, durableStoreErrorHandler, contextStores, pendingContextStoreKeys, onContextLoaded);
|
|
765
|
-
return function (config, requestContext) {
|
|
766
|
-
if (context === undefined) {
|
|
767
|
-
return contextAsPromise.then(function (revivedContext) {
|
|
768
|
-
context = revivedContext;
|
|
769
|
-
return adapter(config, context, requestContext); // TODO - remove as any cast after https://github.com/salesforce-experience-platform-emu/luvio/pull/230
|
|
770
|
-
});
|
|
771
|
-
}
|
|
772
|
-
return adapter(config, context, requestContext);
|
|
773
|
-
};
|
|
774
|
-
};
|
|
775
|
-
var storeRedirect = function (existingKey, canonicalKey) {
|
|
776
|
-
validateNotDisposed();
|
|
777
|
-
// call redirect on staging store so "old" keys are removed from L2 on
|
|
778
|
-
// the next publishChangesToDurableStore. NOTE: we don't need to call
|
|
779
|
-
// redirect on the base environment store because staging store and base
|
|
780
|
-
// L1 store share the same redirect and reverseRedirectKeys
|
|
781
|
-
if (ingestStagingStore === null) {
|
|
782
|
-
ingestStagingStore = buildIngestStagingStore(environment);
|
|
783
|
-
}
|
|
784
|
-
ingestStagingStore.redirect(existingKey, canonicalKey);
|
|
785
|
-
};
|
|
786
|
-
var storeSetTTLOverride = function (namespace, representationName, ttl) {
|
|
787
|
-
validateNotDisposed();
|
|
788
|
-
return Promise.all([
|
|
789
|
-
environment.storeSetTTLOverride(namespace, representationName, ttl),
|
|
790
|
-
durableTTLStore.setDurableTTLOverride(namespace, representationName, ttl),
|
|
791
|
-
]).then();
|
|
792
|
-
};
|
|
793
|
-
var storeSetDefaultTTLOverride = function (ttl) {
|
|
794
|
-
validateNotDisposed();
|
|
795
|
-
return Promise.all([
|
|
796
|
-
environment.storeSetDefaultTTLOverride(ttl),
|
|
797
|
-
durableTTLStore.setDefaultDurableTTLOverrides(ttl),
|
|
798
|
-
]).then();
|
|
799
|
-
};
|
|
800
|
-
var getDurableTTLOverrides = function () {
|
|
801
|
-
validateNotDisposed();
|
|
802
|
-
return durableTTLStore.getDurableTTLOverrides();
|
|
803
|
-
};
|
|
804
|
-
var dispatchResourceRequest = function (request, context, eventObservers) {
|
|
805
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
806
|
-
return __generator(this, function (_a) {
|
|
807
|
-
switch (_a.label) {
|
|
808
|
-
case 0:
|
|
809
|
-
validateNotDisposed();
|
|
810
|
-
if (!(initializationPromise !== undefined)) return [3 /*break*/, 2];
|
|
811
|
-
return [4 /*yield*/, initializationPromise];
|
|
812
|
-
case 1:
|
|
813
|
-
_a.sent();
|
|
814
|
-
_a.label = 2;
|
|
815
|
-
case 2: return [2 /*return*/, environment.dispatchResourceRequest(request, context, eventObservers)];
|
|
816
|
-
}
|
|
817
|
-
});
|
|
818
|
-
});
|
|
819
|
-
};
|
|
820
|
-
// NOTE: we can't use "async" keyword on this function because that would
|
|
821
|
-
// force it to always be an async response. The signature is a union
|
|
822
|
-
// of sync/async so no "awaiting" in this function, just promise-chaining
|
|
823
|
-
var applyCachePolicy = function (luvio, adapterRequestContext, buildSnapshotContext, buildCachedSnapshot, buildNetworkSnapshot) {
|
|
824
|
-
validateNotDisposed();
|
|
825
|
-
var wrappedCacheLookup = function (injectedBuildSnapshotContext, injectedStoreLookup) {
|
|
826
|
-
var snapshot = buildCachedSnapshot(injectedBuildSnapshotContext, injectedStoreLookup, luvio);
|
|
827
|
-
// if the adapter attempted to do an L1 lookup and it was unfulfilled
|
|
828
|
-
// then we can attempt an L2 lookup
|
|
829
|
-
if (isUnfulfilledSnapshot(snapshot)) {
|
|
830
|
-
var start_1 = Date.now();
|
|
831
|
-
emitDurableEnvironmentAdapterEvent({ type: 'l2-revive-start' }, adapterRequestContext.eventObservers);
|
|
832
|
-
var revivedSnapshot = reviveSnapshot(environment, durableStore, snapshot, durableStoreErrorHandler, function () { return injectedStoreLookup(snapshot.select, snapshot.refresh); }).then(function (result) {
|
|
833
|
-
emitDurableEnvironmentAdapterEvent({
|
|
834
|
-
type: 'l2-revive-end',
|
|
835
|
-
snapshot: result.snapshot,
|
|
836
|
-
duration: Date.now() - start_1,
|
|
837
|
-
l2Trips: result.metrics.l2Trips,
|
|
838
|
-
}, adapterRequestContext.eventObservers);
|
|
839
|
-
return result.snapshot;
|
|
840
|
-
});
|
|
841
|
-
return revivedSnapshot;
|
|
842
|
-
}
|
|
843
|
-
// otherwise just return what buildCachedSnapshot gave us
|
|
844
|
-
return snapshot;
|
|
845
|
-
};
|
|
846
|
-
var wrappedApplyCachePolicy = function () {
|
|
847
|
-
return environment.applyCachePolicy(luvio, adapterRequestContext, buildSnapshotContext, wrappedCacheLookup, buildNetworkSnapshot);
|
|
848
|
-
};
|
|
849
|
-
// GET adapters call applyCachePolicy before any other luvio
|
|
850
|
-
// function so this is our chance to ensure we're initialized
|
|
851
|
-
return initializationPromise !== undefined
|
|
852
|
-
? initializationPromise.then(wrappedApplyCachePolicy)
|
|
853
|
-
: wrappedApplyCachePolicy();
|
|
854
|
-
};
|
|
855
|
-
var getIngestStagingStoreRecords = function () {
|
|
856
|
-
validateNotDisposed();
|
|
857
|
-
if (ingestStagingStore !== null) {
|
|
858
|
-
return ingestStagingStore.fallbackStringKeyInMemoryStore.records;
|
|
859
|
-
}
|
|
860
|
-
return {};
|
|
861
|
-
};
|
|
862
|
-
var getIngestStagingStoreMetadata = function () {
|
|
863
|
-
validateNotDisposed();
|
|
864
|
-
if (ingestStagingStore !== null) {
|
|
865
|
-
return ingestStagingStore.fallbackStringKeyInMemoryStore.metadata;
|
|
866
|
-
}
|
|
867
|
-
return {};
|
|
868
|
-
};
|
|
869
|
-
var handleSuccessResponse = function (ingestAndBroadcastFunc, getResponseCacheKeysFunc) {
|
|
870
|
-
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;
|
|
872
|
-
var _this = this;
|
|
873
|
-
return __generator(this, function (_d) {
|
|
874
|
-
switch (_d.label) {
|
|
875
|
-
case 0:
|
|
876
|
-
validateNotDisposed();
|
|
877
|
-
cacheKeyMap = getResponseCacheKeysFunc();
|
|
878
|
-
cacheKeyMapKeys = cacheKeyMap.keysAsArray();
|
|
879
|
-
keysToRevive = new engine.StoreKeySet();
|
|
880
|
-
for (_i = 0, cacheKeyMapKeys_1 = cacheKeyMapKeys; _i < cacheKeyMapKeys_1.length; _i++) {
|
|
881
|
-
cacheKeyMapKey = cacheKeyMapKeys_1[_i];
|
|
882
|
-
cacheKey = cacheKeyMap.get(cacheKeyMapKey);
|
|
883
|
-
if (cacheKey.mergeable === true) {
|
|
884
|
-
keysToRevive.add(cacheKeyMapKey);
|
|
885
|
-
}
|
|
886
|
-
}
|
|
887
|
-
snapshotFromMemoryIngest = undefined;
|
|
888
|
-
keysToReviveAsArray = Array.from(keysToRevive.keysAsStrings());
|
|
889
|
-
if (!(keysToReviveAsArray.length > 0)) return [3 /*break*/, 5];
|
|
890
|
-
readWritePromise = (function () { return __awaiter(_this, void 0, void 0, function () {
|
|
891
|
-
var pendingPromises, _i, keysToReviveAsArray_3, key, pendingPromise, entries;
|
|
892
|
-
return __generator(this, function (_a) {
|
|
893
|
-
switch (_a.label) {
|
|
894
|
-
case 0:
|
|
895
|
-
pendingPromises = [];
|
|
896
|
-
for (_i = 0, keysToReviveAsArray_3 = keysToReviveAsArray; _i < keysToReviveAsArray_3.length; _i++) {
|
|
897
|
-
key = keysToReviveAsArray_3[_i];
|
|
898
|
-
pendingPromise = mergeKeysPromiseMap.get(key);
|
|
899
|
-
if (pendingPromise !== undefined) {
|
|
900
|
-
// IMPORTANT: while on the synchronous code path we get a
|
|
901
|
-
// handle to pendingPromise and push it onto the array.
|
|
902
|
-
// This is important because later in this synchronous code
|
|
903
|
-
// path we will upsert readWritePromise into the
|
|
904
|
-
// mergeKeysPromiseMap (essentially overwriting pendingPromise
|
|
905
|
-
// in the map).
|
|
906
|
-
pendingPromises.push(pendingPromise);
|
|
907
|
-
}
|
|
908
|
-
}
|
|
909
|
-
return [4 /*yield*/, Promise.all(pendingPromises)];
|
|
910
|
-
case 1:
|
|
911
|
-
_a.sent();
|
|
912
|
-
return [4 /*yield*/, durableStore.getEntries(keysToReviveAsArray, DefaultDurableSegment)];
|
|
913
|
-
case 2:
|
|
914
|
-
entries = _a.sent();
|
|
915
|
-
ingestStagingStore = buildIngestStagingStore(environment);
|
|
916
|
-
publishDurableStoreEntries(entries, function (key, record) {
|
|
917
|
-
if (typeof key === 'string') {
|
|
918
|
-
ingestStagingStore.fallbackStringKeyInMemoryStore.records[key] =
|
|
919
|
-
record;
|
|
920
|
-
}
|
|
921
|
-
else {
|
|
922
|
-
ingestStagingStore.recordsMap.set(key, record);
|
|
923
|
-
}
|
|
924
|
-
},
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
case
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
case
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
473
|
+
var AdapterContextSegment = 'ADAPTER-CONTEXT';
|
|
474
|
+
var ADAPTER_CONTEXT_ID_SUFFIX = '__NAMED_CONTEXT';
|
|
475
|
+
function reviveOrCreateContext(adapterId, durableStore, durableStoreErrorHandler, contextStores, pendingContextStoreKeys, onContextLoaded) {
|
|
476
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
477
|
+
var context, contextReturn, entries, error_1;
|
|
478
|
+
return __generator(this, function (_a) {
|
|
479
|
+
switch (_a.label) {
|
|
480
|
+
case 0:
|
|
481
|
+
// initialize empty context store
|
|
482
|
+
contextStores[adapterId] = create(null);
|
|
483
|
+
context = {
|
|
484
|
+
set: function (key, value) {
|
|
485
|
+
var _a;
|
|
486
|
+
contextStores[adapterId][key] = value;
|
|
487
|
+
durableStore.setEntries((_a = {},
|
|
488
|
+
_a[adapterId] = { data: contextStores[adapterId] },
|
|
489
|
+
_a), AdapterContextSegment);
|
|
490
|
+
pendingContextStoreKeys.add(adapterId);
|
|
491
|
+
},
|
|
492
|
+
get: function (key) {
|
|
493
|
+
return contextStores[adapterId][key];
|
|
494
|
+
},
|
|
495
|
+
};
|
|
496
|
+
contextReturn = function () {
|
|
497
|
+
if (onContextLoaded !== undefined) {
|
|
498
|
+
return onContextLoaded(context).then(function () {
|
|
499
|
+
return context;
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
return context;
|
|
503
|
+
};
|
|
504
|
+
_a.label = 1;
|
|
505
|
+
case 1:
|
|
506
|
+
_a.trys.push([1, 3, , 4]);
|
|
507
|
+
return [4 /*yield*/, durableStore.getEntries([adapterId], AdapterContextSegment)];
|
|
508
|
+
case 2:
|
|
509
|
+
entries = _a.sent();
|
|
510
|
+
if (entries !== undefined && entries[adapterId] !== undefined) {
|
|
511
|
+
// if durable store has a saved context then load it in the store
|
|
512
|
+
contextStores[adapterId] = entries[adapterId].data;
|
|
513
|
+
}
|
|
514
|
+
return [3 /*break*/, 4];
|
|
515
|
+
case 3:
|
|
516
|
+
error_1 = _a.sent();
|
|
517
|
+
durableStoreErrorHandler(error_1);
|
|
518
|
+
return [3 /*break*/, 4];
|
|
519
|
+
case 4: return [2 /*return*/, contextReturn()];
|
|
520
|
+
}
|
|
521
|
+
});
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
function isUnfulfilledSnapshot(cachedSnapshotResult) {
|
|
525
|
+
if (cachedSnapshotResult === undefined) {
|
|
526
|
+
return false;
|
|
527
|
+
}
|
|
528
|
+
if ('then' in cachedSnapshotResult) {
|
|
529
|
+
return false;
|
|
530
|
+
}
|
|
531
|
+
return cachedSnapshotResult.state === 'Unfulfilled';
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* Configures the environment to persist data into a durable store and attempt to resolve
|
|
535
|
+
* data from the persistent store before hitting the network.
|
|
536
|
+
*
|
|
537
|
+
* @param environment The base environment
|
|
538
|
+
* @param durableStore A DurableStore implementation
|
|
539
|
+
* @param instrumentation An instrumentation function implementation
|
|
540
|
+
*/
|
|
541
|
+
function makeDurable(environment, _a) {
|
|
542
|
+
var _this = this;
|
|
543
|
+
var durableStore = _a.durableStore, instrumentation = _a.instrumentation;
|
|
544
|
+
var ingestStagingStore = null;
|
|
545
|
+
var durableTTLStore = new DurableTTLStore(durableStore);
|
|
546
|
+
var mergeKeysPromiseMap = new engine.StoreKeyMap();
|
|
547
|
+
// When a context store is mutated we write it to L2, which causes DS on change
|
|
548
|
+
// event. If this instance of makeDurable caused that L2 write we can ignore that
|
|
549
|
+
// on change event. This Set helps us do that.
|
|
550
|
+
var pendingContextStoreKeys = new Set();
|
|
551
|
+
var contextStores = create(null);
|
|
552
|
+
var initializationPromise = new Promise(function (resolve) {
|
|
553
|
+
var finish = function () {
|
|
554
|
+
resolve();
|
|
555
|
+
initializationPromise = undefined;
|
|
556
|
+
};
|
|
557
|
+
reviveTTLOverrides(durableTTLStore, environment).then(finish);
|
|
558
|
+
});
|
|
559
|
+
//instrumentation for durable store errors
|
|
560
|
+
var durableStoreErrorHandler = handleDurableStoreRejection(instrumentation);
|
|
561
|
+
var disposed = false;
|
|
562
|
+
var validateNotDisposed = function () {
|
|
563
|
+
if (disposed === true) {
|
|
564
|
+
throw new Error('This makeDurable instance has been disposed');
|
|
565
|
+
}
|
|
566
|
+
};
|
|
567
|
+
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) {
|
|
571
|
+
case 0:
|
|
572
|
+
defaultSegmentKeys = [];
|
|
573
|
+
adapterContextSegmentKeys = [];
|
|
574
|
+
for (i = 0, len = changes.length; i < len; i++) {
|
|
575
|
+
change = changes[i];
|
|
576
|
+
// we only care about changes to the data which is stored in the default
|
|
577
|
+
// segment or the adapter context
|
|
578
|
+
if (change.segment === DefaultDurableSegment) {
|
|
579
|
+
defaultSegmentKeys.push.apply(defaultSegmentKeys, change.ids);
|
|
580
|
+
}
|
|
581
|
+
else if (change.segment === AdapterContextSegment) {
|
|
582
|
+
adapterContextSegmentKeys.push.apply(adapterContextSegmentKeys, change.ids);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
adapterContextKeysFromDifferentInstance = [];
|
|
586
|
+
for (_i = 0, adapterContextSegmentKeys_1 = adapterContextSegmentKeys; _i < adapterContextSegmentKeys_1.length; _i++) {
|
|
587
|
+
key = adapterContextSegmentKeys_1[_i];
|
|
588
|
+
if (pendingContextStoreKeys.has(key)) {
|
|
589
|
+
// if this instance caused the L2 write then remove from the
|
|
590
|
+
// "pending" Set and move on
|
|
591
|
+
pendingContextStoreKeys.delete(key);
|
|
592
|
+
}
|
|
593
|
+
else {
|
|
594
|
+
// else it came from another luvio instance and we need to
|
|
595
|
+
// read from L2
|
|
596
|
+
adapterContextKeysFromDifferentInstance.push(key);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
if (!(adapterContextKeysFromDifferentInstance.length > 0)) return [3 /*break*/, 4];
|
|
600
|
+
_a.label = 1;
|
|
601
|
+
case 1:
|
|
602
|
+
_a.trys.push([1, 3, , 4]);
|
|
603
|
+
return [4 /*yield*/, durableStore.getEntries(adapterContextKeysFromDifferentInstance, AdapterContextSegment)];
|
|
604
|
+
case 2:
|
|
605
|
+
entries = _a.sent();
|
|
606
|
+
if (entries !== undefined) {
|
|
607
|
+
entryKeys = keys(entries);
|
|
608
|
+
for (i = 0, len = entryKeys.length; i < len; i++) {
|
|
609
|
+
entryKey = entryKeys[i];
|
|
610
|
+
entry = entries[entryKey];
|
|
611
|
+
contextStores[entryKey] = entry.data;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
return [3 /*break*/, 4];
|
|
615
|
+
case 3:
|
|
616
|
+
error_2 = _a.sent();
|
|
617
|
+
durableStoreErrorHandler(error_2);
|
|
618
|
+
return [3 /*break*/, 4];
|
|
619
|
+
case 4:
|
|
620
|
+
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;
|
|
627
|
+
}
|
|
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);
|
|
634
|
+
}
|
|
635
|
+
return [4 /*yield*/, environment.storeBroadcast(rebuildSnapshot, environment.snapshotAvailable)];
|
|
636
|
+
case 5:
|
|
637
|
+
_a.sent();
|
|
638
|
+
_a.label = 6;
|
|
639
|
+
case 6: return [2 /*return*/];
|
|
640
|
+
}
|
|
641
|
+
});
|
|
642
|
+
}); });
|
|
643
|
+
var dispose = function () {
|
|
644
|
+
validateNotDisposed();
|
|
645
|
+
disposed = true;
|
|
646
|
+
return unsubscribe();
|
|
647
|
+
};
|
|
648
|
+
var storePublish = function (key, data) {
|
|
649
|
+
validateNotDisposed();
|
|
650
|
+
if (ingestStagingStore === null) {
|
|
651
|
+
ingestStagingStore = buildIngestStagingStore(environment);
|
|
652
|
+
}
|
|
653
|
+
ingestStagingStore.publish(key, data);
|
|
654
|
+
// remove record from main luvio L1 cache while we are on the synchronous path
|
|
655
|
+
// because we do not want some other code attempting to use the
|
|
656
|
+
// in-memory values before the durable store onChanged handler
|
|
657
|
+
// calls back and revives the values to in-memory
|
|
658
|
+
environment.storeEvict(key);
|
|
659
|
+
};
|
|
660
|
+
var publishStoreMetadata = function (recordId, storeMetadata) {
|
|
661
|
+
validateNotDisposed();
|
|
662
|
+
if (ingestStagingStore === null) {
|
|
663
|
+
ingestStagingStore = buildIngestStagingStore(environment);
|
|
664
|
+
}
|
|
665
|
+
ingestStagingStore.publishMetadata(recordId, storeMetadata);
|
|
666
|
+
};
|
|
667
|
+
var storeIngest = function (key, ingest, response, luvio) {
|
|
668
|
+
validateNotDisposed();
|
|
669
|
+
// we don't ingest to the luvio L1 store from network directly, we ingest to
|
|
670
|
+
// L2 and let DurableStore on change event revive keys into luvio L1 store
|
|
671
|
+
if (ingestStagingStore === null) {
|
|
672
|
+
ingestStagingStore = buildIngestStagingStore(environment);
|
|
673
|
+
}
|
|
674
|
+
environment.storeIngest(key, ingest, response, luvio, ingestStagingStore);
|
|
675
|
+
};
|
|
676
|
+
var storeIngestError = function (key, errorSnapshot, storeMetadataParams, _storeOverride) {
|
|
677
|
+
validateNotDisposed();
|
|
678
|
+
if (ingestStagingStore === null) {
|
|
679
|
+
ingestStagingStore = buildIngestStagingStore(environment);
|
|
680
|
+
}
|
|
681
|
+
environment.storeIngestError(key, errorSnapshot, storeMetadataParams, ingestStagingStore);
|
|
682
|
+
};
|
|
683
|
+
var storeBroadcast = function (_rebuildSnapshot, _snapshotDataAvailable) {
|
|
684
|
+
validateNotDisposed();
|
|
685
|
+
// publishing to L2 is essentially "broadcasting" because the onChanged
|
|
686
|
+
// handler will fire which will revive records to the main L1 store and
|
|
687
|
+
// call the base storeBroadcast
|
|
688
|
+
return publishChangesToDurableStore();
|
|
689
|
+
};
|
|
690
|
+
var publishChangesToDurableStore = function () {
|
|
691
|
+
validateNotDisposed();
|
|
692
|
+
if (ingestStagingStore === null) {
|
|
693
|
+
return Promise.resolve();
|
|
694
|
+
}
|
|
695
|
+
var promise = flushInMemoryStoreValuesToDurableStore(ingestStagingStore, durableStore, durableStoreErrorHandler);
|
|
696
|
+
ingestStagingStore = null;
|
|
697
|
+
return promise;
|
|
698
|
+
};
|
|
699
|
+
var storeLookup = function (sel, createSnapshot, refresh, ttlStrategy) {
|
|
700
|
+
validateNotDisposed();
|
|
701
|
+
// if this lookup is right after an ingest there will be a staging store
|
|
702
|
+
if (ingestStagingStore !== null) {
|
|
703
|
+
var reader = new engine.Reader(ingestStagingStore, sel.variables, refresh, undefined, ttlStrategy);
|
|
704
|
+
return reader.read(sel);
|
|
705
|
+
}
|
|
706
|
+
// otherwise this is from buildCachedSnapshot and we should use the luvio
|
|
707
|
+
// L1 store
|
|
708
|
+
return environment.storeLookup(sel, createSnapshot, refresh, ttlStrategy);
|
|
709
|
+
};
|
|
710
|
+
var storeEvict = function (key) {
|
|
711
|
+
validateNotDisposed();
|
|
712
|
+
if (ingestStagingStore === null) {
|
|
713
|
+
ingestStagingStore = buildIngestStagingStore(environment);
|
|
714
|
+
}
|
|
715
|
+
ingestStagingStore.evict(key);
|
|
716
|
+
};
|
|
717
|
+
var getNode = function (key) {
|
|
718
|
+
validateNotDisposed();
|
|
719
|
+
if (ingestStagingStore === null) {
|
|
720
|
+
ingestStagingStore = buildIngestStagingStore(environment);
|
|
721
|
+
}
|
|
722
|
+
return environment.getNode(key, ingestStagingStore);
|
|
723
|
+
};
|
|
724
|
+
var wrapNormalizedGraphNode = function (normalized) {
|
|
725
|
+
validateNotDisposed();
|
|
726
|
+
if (ingestStagingStore === null) {
|
|
727
|
+
ingestStagingStore = buildIngestStagingStore(environment);
|
|
728
|
+
}
|
|
729
|
+
return environment.wrapNormalizedGraphNode(normalized, ingestStagingStore);
|
|
730
|
+
};
|
|
731
|
+
var rebuildSnapshot = function (snapshot, onRebuild) {
|
|
732
|
+
validateNotDisposed();
|
|
733
|
+
// try rebuilding from memory
|
|
734
|
+
environment.rebuildSnapshot(snapshot, function (rebuilt) {
|
|
735
|
+
// only try reviving from durable store if snapshot is unfulfilled
|
|
736
|
+
if (rebuilt.state !== 'Unfulfilled') {
|
|
737
|
+
onRebuild(rebuilt);
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
// Do an L2 revive and emit to subscriber using the callback.
|
|
741
|
+
reviveSnapshot(environment, durableStore, rebuilt, durableStoreErrorHandler, function () {
|
|
742
|
+
// reviveSnapshot will revive into L1, and since "records" is a reference
|
|
743
|
+
// (and not a copy) to the L1 records we can use it for rebuild
|
|
744
|
+
var rebuiltSnap;
|
|
745
|
+
environment.rebuildSnapshot(snapshot, function (rebuilt) {
|
|
746
|
+
rebuiltSnap = rebuilt;
|
|
747
|
+
});
|
|
748
|
+
return rebuiltSnap;
|
|
749
|
+
}).then(function (result) {
|
|
750
|
+
onRebuild(result.snapshot);
|
|
751
|
+
});
|
|
752
|
+
});
|
|
753
|
+
};
|
|
754
|
+
var withContext = function (adapter, options) {
|
|
755
|
+
validateNotDisposed();
|
|
756
|
+
var contextId = options.contextId, contextVersion = options.contextVersion, onContextLoaded = options.onContextLoaded;
|
|
757
|
+
var context = undefined;
|
|
758
|
+
var contextKey = "".concat(contextId);
|
|
759
|
+
// if a context version is supplied, key with the version encoded
|
|
760
|
+
if (contextVersion !== undefined) {
|
|
761
|
+
contextKey += "::".concat(contextVersion);
|
|
762
|
+
}
|
|
763
|
+
contextKey += ADAPTER_CONTEXT_ID_SUFFIX;
|
|
764
|
+
var contextAsPromise = reviveOrCreateContext(contextKey, durableStore, durableStoreErrorHandler, contextStores, pendingContextStoreKeys, onContextLoaded);
|
|
765
|
+
return function (config, requestContext) {
|
|
766
|
+
if (context === undefined) {
|
|
767
|
+
return contextAsPromise.then(function (revivedContext) {
|
|
768
|
+
context = revivedContext;
|
|
769
|
+
return adapter(config, context, requestContext); // TODO - remove as any cast after https://github.com/salesforce-experience-platform-emu/luvio/pull/230
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
return adapter(config, context, requestContext);
|
|
773
|
+
};
|
|
774
|
+
};
|
|
775
|
+
var storeRedirect = function (existingKey, canonicalKey) {
|
|
776
|
+
validateNotDisposed();
|
|
777
|
+
// call redirect on staging store so "old" keys are removed from L2 on
|
|
778
|
+
// the next publishChangesToDurableStore. NOTE: we don't need to call
|
|
779
|
+
// redirect on the base environment store because staging store and base
|
|
780
|
+
// L1 store share the same redirect and reverseRedirectKeys
|
|
781
|
+
if (ingestStagingStore === null) {
|
|
782
|
+
ingestStagingStore = buildIngestStagingStore(environment);
|
|
783
|
+
}
|
|
784
|
+
ingestStagingStore.redirect(existingKey, canonicalKey);
|
|
785
|
+
};
|
|
786
|
+
var storeSetTTLOverride = function (namespace, representationName, ttl) {
|
|
787
|
+
validateNotDisposed();
|
|
788
|
+
return Promise.all([
|
|
789
|
+
environment.storeSetTTLOverride(namespace, representationName, ttl),
|
|
790
|
+
durableTTLStore.setDurableTTLOverride(namespace, representationName, ttl),
|
|
791
|
+
]).then();
|
|
792
|
+
};
|
|
793
|
+
var storeSetDefaultTTLOverride = function (ttl) {
|
|
794
|
+
validateNotDisposed();
|
|
795
|
+
return Promise.all([
|
|
796
|
+
environment.storeSetDefaultTTLOverride(ttl),
|
|
797
|
+
durableTTLStore.setDefaultDurableTTLOverrides(ttl),
|
|
798
|
+
]).then();
|
|
799
|
+
};
|
|
800
|
+
var getDurableTTLOverrides = function () {
|
|
801
|
+
validateNotDisposed();
|
|
802
|
+
return durableTTLStore.getDurableTTLOverrides();
|
|
803
|
+
};
|
|
804
|
+
var dispatchResourceRequest = function (request, context, eventObservers) {
|
|
805
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
806
|
+
return __generator(this, function (_a) {
|
|
807
|
+
switch (_a.label) {
|
|
808
|
+
case 0:
|
|
809
|
+
validateNotDisposed();
|
|
810
|
+
if (!(initializationPromise !== undefined)) return [3 /*break*/, 2];
|
|
811
|
+
return [4 /*yield*/, initializationPromise];
|
|
812
|
+
case 1:
|
|
813
|
+
_a.sent();
|
|
814
|
+
_a.label = 2;
|
|
815
|
+
case 2: return [2 /*return*/, environment.dispatchResourceRequest(request, context, eventObservers)];
|
|
816
|
+
}
|
|
817
|
+
});
|
|
818
|
+
});
|
|
819
|
+
};
|
|
820
|
+
// NOTE: we can't use "async" keyword on this function because that would
|
|
821
|
+
// force it to always be an async response. The signature is a union
|
|
822
|
+
// of sync/async so no "awaiting" in this function, just promise-chaining
|
|
823
|
+
var applyCachePolicy = function (luvio, adapterRequestContext, buildSnapshotContext, buildCachedSnapshot, buildNetworkSnapshot) {
|
|
824
|
+
validateNotDisposed();
|
|
825
|
+
var wrappedCacheLookup = function (injectedBuildSnapshotContext, injectedStoreLookup) {
|
|
826
|
+
var snapshot = buildCachedSnapshot(injectedBuildSnapshotContext, injectedStoreLookup, luvio);
|
|
827
|
+
// if the adapter attempted to do an L1 lookup and it was unfulfilled
|
|
828
|
+
// then we can attempt an L2 lookup
|
|
829
|
+
if (isUnfulfilledSnapshot(snapshot)) {
|
|
830
|
+
var start_1 = Date.now();
|
|
831
|
+
emitDurableEnvironmentAdapterEvent({ type: 'l2-revive-start' }, adapterRequestContext.eventObservers);
|
|
832
|
+
var revivedSnapshot = reviveSnapshot(environment, durableStore, snapshot, durableStoreErrorHandler, function () { return injectedStoreLookup(snapshot.select, snapshot.refresh); }).then(function (result) {
|
|
833
|
+
emitDurableEnvironmentAdapterEvent({
|
|
834
|
+
type: 'l2-revive-end',
|
|
835
|
+
snapshot: result.snapshot,
|
|
836
|
+
duration: Date.now() - start_1,
|
|
837
|
+
l2Trips: result.metrics.l2Trips,
|
|
838
|
+
}, adapterRequestContext.eventObservers);
|
|
839
|
+
return result.snapshot;
|
|
840
|
+
});
|
|
841
|
+
return revivedSnapshot;
|
|
842
|
+
}
|
|
843
|
+
// otherwise just return what buildCachedSnapshot gave us
|
|
844
|
+
return snapshot;
|
|
845
|
+
};
|
|
846
|
+
var wrappedApplyCachePolicy = function () {
|
|
847
|
+
return environment.applyCachePolicy(luvio, adapterRequestContext, buildSnapshotContext, wrappedCacheLookup, buildNetworkSnapshot);
|
|
848
|
+
};
|
|
849
|
+
// GET adapters call applyCachePolicy before any other luvio
|
|
850
|
+
// function so this is our chance to ensure we're initialized
|
|
851
|
+
return initializationPromise !== undefined
|
|
852
|
+
? initializationPromise.then(wrappedApplyCachePolicy)
|
|
853
|
+
: wrappedApplyCachePolicy();
|
|
854
|
+
};
|
|
855
|
+
var getIngestStagingStoreRecords = function () {
|
|
856
|
+
validateNotDisposed();
|
|
857
|
+
if (ingestStagingStore !== null) {
|
|
858
|
+
return ingestStagingStore.fallbackStringKeyInMemoryStore.records;
|
|
859
|
+
}
|
|
860
|
+
return {};
|
|
861
|
+
};
|
|
862
|
+
var getIngestStagingStoreMetadata = function () {
|
|
863
|
+
validateNotDisposed();
|
|
864
|
+
if (ingestStagingStore !== null) {
|
|
865
|
+
return ingestStagingStore.fallbackStringKeyInMemoryStore.metadata;
|
|
866
|
+
}
|
|
867
|
+
return {};
|
|
868
|
+
};
|
|
869
|
+
var handleSuccessResponse = function (ingestAndBroadcastFunc, getResponseCacheKeysFunc) {
|
|
870
|
+
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;
|
|
872
|
+
var _this = this;
|
|
873
|
+
return __generator(this, function (_d) {
|
|
874
|
+
switch (_d.label) {
|
|
875
|
+
case 0:
|
|
876
|
+
validateNotDisposed();
|
|
877
|
+
cacheKeyMap = getResponseCacheKeysFunc();
|
|
878
|
+
cacheKeyMapKeys = cacheKeyMap.keysAsArray();
|
|
879
|
+
keysToRevive = new engine.StoreKeySet();
|
|
880
|
+
for (_i = 0, cacheKeyMapKeys_1 = cacheKeyMapKeys; _i < cacheKeyMapKeys_1.length; _i++) {
|
|
881
|
+
cacheKeyMapKey = cacheKeyMapKeys_1[_i];
|
|
882
|
+
cacheKey = cacheKeyMap.get(cacheKeyMapKey);
|
|
883
|
+
if (cacheKey.mergeable === true) {
|
|
884
|
+
keysToRevive.add(cacheKeyMapKey);
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
snapshotFromMemoryIngest = undefined;
|
|
888
|
+
keysToReviveAsArray = Array.from(keysToRevive.keysAsStrings());
|
|
889
|
+
if (!(keysToReviveAsArray.length > 0)) return [3 /*break*/, 5];
|
|
890
|
+
readWritePromise = (function () { return __awaiter(_this, void 0, void 0, function () {
|
|
891
|
+
var pendingPromises, _i, keysToReviveAsArray_3, key, pendingPromise, entries;
|
|
892
|
+
return __generator(this, function (_a) {
|
|
893
|
+
switch (_a.label) {
|
|
894
|
+
case 0:
|
|
895
|
+
pendingPromises = [];
|
|
896
|
+
for (_i = 0, keysToReviveAsArray_3 = keysToReviveAsArray; _i < keysToReviveAsArray_3.length; _i++) {
|
|
897
|
+
key = keysToReviveAsArray_3[_i];
|
|
898
|
+
pendingPromise = mergeKeysPromiseMap.get(key);
|
|
899
|
+
if (pendingPromise !== undefined) {
|
|
900
|
+
// IMPORTANT: while on the synchronous code path we get a
|
|
901
|
+
// handle to pendingPromise and push it onto the array.
|
|
902
|
+
// This is important because later in this synchronous code
|
|
903
|
+
// path we will upsert readWritePromise into the
|
|
904
|
+
// mergeKeysPromiseMap (essentially overwriting pendingPromise
|
|
905
|
+
// in the map).
|
|
906
|
+
pendingPromises.push(pendingPromise);
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
return [4 /*yield*/, Promise.all(pendingPromises)];
|
|
910
|
+
case 1:
|
|
911
|
+
_a.sent();
|
|
912
|
+
return [4 /*yield*/, durableStore.getEntries(keysToReviveAsArray, DefaultDurableSegment)];
|
|
913
|
+
case 2:
|
|
914
|
+
entries = _a.sent();
|
|
915
|
+
ingestStagingStore = buildIngestStagingStore(environment);
|
|
916
|
+
publishDurableStoreEntries(entries, function (key, record) {
|
|
917
|
+
if (typeof key === 'string') {
|
|
918
|
+
ingestStagingStore.fallbackStringKeyInMemoryStore.records[key] =
|
|
919
|
+
record;
|
|
920
|
+
}
|
|
921
|
+
else {
|
|
922
|
+
ingestStagingStore.recordsMap.set(key, record);
|
|
923
|
+
}
|
|
924
|
+
}, function (key, metadata) {
|
|
925
|
+
if (typeof key === 'string') {
|
|
926
|
+
ingestStagingStore.fallbackStringKeyInMemoryStore.metadata[key] =
|
|
927
|
+
metadata;
|
|
928
|
+
}
|
|
929
|
+
else {
|
|
930
|
+
ingestStagingStore.metadataMap.set(key, metadata);
|
|
931
|
+
}
|
|
932
|
+
});
|
|
933
|
+
return [4 /*yield*/, ingestAndBroadcastFunc()];
|
|
934
|
+
case 3:
|
|
935
|
+
snapshotFromMemoryIngest = _a.sent();
|
|
936
|
+
return [2 /*return*/];
|
|
937
|
+
}
|
|
938
|
+
});
|
|
939
|
+
}); })();
|
|
940
|
+
for (_a = 0, keysToReviveAsArray_1 = keysToReviveAsArray; _a < keysToReviveAsArray_1.length; _a++) {
|
|
941
|
+
key = keysToReviveAsArray_1[_a];
|
|
942
|
+
// we are overwriting the previous promise at this key, but that
|
|
943
|
+
// is ok because we got a handle to it earlier (see the IMPORTANT
|
|
944
|
+
// comment about 35 lines up)
|
|
945
|
+
mergeKeysPromiseMap.set(key, readWritePromise);
|
|
946
|
+
}
|
|
947
|
+
_d.label = 1;
|
|
948
|
+
case 1:
|
|
949
|
+
_d.trys.push([1, , 3, 4]);
|
|
950
|
+
return [4 /*yield*/, readWritePromise];
|
|
951
|
+
case 2:
|
|
952
|
+
_d.sent();
|
|
953
|
+
return [3 /*break*/, 4];
|
|
954
|
+
case 3:
|
|
955
|
+
for (_b = 0, keysToReviveAsArray_2 = keysToReviveAsArray; _b < keysToReviveAsArray_2.length; _b++) {
|
|
956
|
+
key = keysToReviveAsArray_2[_b];
|
|
957
|
+
pendingPromise = mergeKeysPromiseMap.get(key);
|
|
958
|
+
// cleanup the entry from the map if this is the last promise
|
|
959
|
+
// for that key
|
|
960
|
+
if (pendingPromise === readWritePromise) {
|
|
961
|
+
mergeKeysPromiseMap.delete(key);
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
return [7 /*endfinally*/];
|
|
965
|
+
case 4: return [3 /*break*/, 7];
|
|
966
|
+
case 5:
|
|
967
|
+
// we aren't doing any merging so we don't have to synchronize, the
|
|
968
|
+
// underlying DurableStore implementation takes care of R/W sync
|
|
969
|
+
// so all we have to do is ingest then write to L2
|
|
970
|
+
ingestStagingStore = buildIngestStagingStore(environment);
|
|
971
|
+
return [4 /*yield*/, ingestAndBroadcastFunc()];
|
|
972
|
+
case 6:
|
|
973
|
+
snapshotFromMemoryIngest = _d.sent();
|
|
974
|
+
_d.label = 7;
|
|
975
|
+
case 7:
|
|
976
|
+
if (snapshotFromMemoryIngest === undefined) {
|
|
977
|
+
return [2 /*return*/, undefined];
|
|
978
|
+
}
|
|
979
|
+
if (snapshotFromMemoryIngest.state !== 'Unfulfilled') {
|
|
980
|
+
return [2 /*return*/, snapshotFromMemoryIngest];
|
|
981
|
+
}
|
|
982
|
+
_c = snapshotFromMemoryIngest, select = _c.select, refresh = _c.refresh;
|
|
983
|
+
return [4 /*yield*/, reviveSnapshot(environment, durableStore, snapshotFromMemoryIngest, durableStoreErrorHandler, function () { return environment.storeLookup(select, environment.createSnapshot, refresh); })];
|
|
984
|
+
case 8:
|
|
985
|
+
result = _d.sent();
|
|
986
|
+
return [2 /*return*/, result.snapshot];
|
|
987
|
+
}
|
|
988
|
+
});
|
|
989
|
+
});
|
|
990
|
+
};
|
|
991
|
+
var handleErrorResponse = function (ingestAndBroadcastFunc) {
|
|
992
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
993
|
+
return __generator(this, function (_a) {
|
|
994
|
+
validateNotDisposed();
|
|
995
|
+
ingestStagingStore = buildIngestStagingStore(environment);
|
|
996
|
+
return [2 /*return*/, ingestAndBroadcastFunc()];
|
|
997
|
+
});
|
|
998
|
+
});
|
|
999
|
+
};
|
|
1000
|
+
var getNotifyChangeStoreEntries = function (keys) {
|
|
1001
|
+
validateNotDisposed();
|
|
1002
|
+
return durableStore
|
|
1003
|
+
.getEntries(keys.map(engine.serializeStructuredKey), DefaultDurableSegment)
|
|
1004
|
+
.then(function (durableRecords) {
|
|
1005
|
+
var entries = [];
|
|
1006
|
+
publishDurableStoreEntries(durableRecords, function (key, record) {
|
|
1007
|
+
entries.push({
|
|
1008
|
+
key: key,
|
|
1009
|
+
record: record,
|
|
1010
|
+
});
|
|
1011
|
+
}, function () { });
|
|
1012
|
+
return entries;
|
|
1013
|
+
});
|
|
1014
|
+
};
|
|
1015
|
+
environment.defaultCachePolicy = {
|
|
1016
|
+
type: 'stale-while-revalidate',
|
|
1017
|
+
implementation: engine.buildStaleWhileRevalidateImplementation(Number.MAX_SAFE_INTEGER),
|
|
1018
|
+
};
|
|
1019
|
+
return create(environment, {
|
|
1020
|
+
publishStoreMetadata: { value: publishStoreMetadata },
|
|
1021
|
+
storeIngest: { value: storeIngest },
|
|
1022
|
+
storeIngestError: { value: storeIngestError },
|
|
1023
|
+
storeBroadcast: { value: storeBroadcast },
|
|
1024
|
+
storeLookup: { value: storeLookup },
|
|
1025
|
+
storeEvict: { value: storeEvict },
|
|
1026
|
+
wrapNormalizedGraphNode: { value: wrapNormalizedGraphNode },
|
|
1027
|
+
getNode: { value: getNode },
|
|
1028
|
+
rebuildSnapshot: { value: rebuildSnapshot },
|
|
1029
|
+
withContext: { value: withContext },
|
|
1030
|
+
storeSetTTLOverride: { value: storeSetTTLOverride },
|
|
1031
|
+
storeSetDefaultTTLOverride: { value: storeSetDefaultTTLOverride },
|
|
1032
|
+
storePublish: { value: storePublish },
|
|
1033
|
+
storeRedirect: { value: storeRedirect },
|
|
1034
|
+
dispose: { value: dispose },
|
|
1035
|
+
publishChangesToDurableStore: { value: publishChangesToDurableStore },
|
|
1036
|
+
getDurableTTLOverrides: { value: getDurableTTLOverrides },
|
|
1037
|
+
dispatchResourceRequest: { value: dispatchResourceRequest },
|
|
1038
|
+
applyCachePolicy: { value: applyCachePolicy },
|
|
1039
|
+
getIngestStagingStoreRecords: { value: getIngestStagingStoreRecords },
|
|
1040
|
+
getIngestStagingStoreMetadata: { value: getIngestStagingStoreMetadata },
|
|
1041
|
+
handleSuccessResponse: { value: handleSuccessResponse },
|
|
1042
|
+
handleErrorResponse: { value: handleErrorResponse },
|
|
1043
|
+
getNotifyChangeStoreEntries: { value: getNotifyChangeStoreEntries },
|
|
1044
|
+
});
|
|
1039
1045
|
}
|
|
1040
1046
|
|
|
1041
1047
|
exports.DURABLE_METADATA_VERSION = DURABLE_METADATA_VERSION;
|