@salesforce/lds-instrumentation 1.124.2 → 1.124.4
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/ldsInstrumentation.js +1235 -1235
- package/dist/{__mocks__ → types/__mocks__}/force/ldsGraphqlParser.d.ts +3 -3
- package/dist/{__mocks__ → types/__mocks__}/o11y/activity.d.ts +2 -2
- package/dist/{__mocks__ → types/__mocks__}/o11y/client.d.ts +5 -5
- package/dist/{__mocks__ → types/__mocks__}/o11y/idleDetector.d.ts +2 -2
- package/dist/{__mocks__ → types/__mocks__}/o11y/instrumentation.d.ts +14 -14
- package/dist/{__mocks__ → types/__mocks__}/o11y_schema/sf_lds.d.ts +1 -1
- package/dist/{main.d.ts → types/main.d.ts} +81 -81
- package/dist/{metric-keys.d.ts → types/metric-keys.d.ts} +191 -191
- package/dist/{utils → types/utils}/language.d.ts +13 -13
- package/dist/{utils → types/utils}/lru-cache.d.ts +14 -14
- package/dist/{utils → types/utils}/observability.d.ts +24 -24
- package/dist/{utils → types/utils}/utils.d.ts +15 -15
- package/package.json +3 -3
|
@@ -106,491 +106,491 @@ function isDurableEnvironmentEvent(event) {
|
|
|
106
106
|
|
|
107
107
|
const { stringify: stringify$1 } = JSON;
|
|
108
108
|
|
|
109
|
-
function isPromise$1(value) {
|
|
110
|
-
// check for Thenable due to test frameworks using custom Promise impls
|
|
111
|
-
return value !== null && value !== undefined && typeof value.then === 'function';
|
|
112
|
-
}
|
|
113
|
-
function isErrorSnapshot(snapshot) {
|
|
114
|
-
return snapshot.state === 'Error';
|
|
109
|
+
function isPromise$1(value) {
|
|
110
|
+
// check for Thenable due to test frameworks using custom Promise impls
|
|
111
|
+
return value !== null && value !== undefined && typeof value.then === 'function';
|
|
112
|
+
}
|
|
113
|
+
function isErrorSnapshot(snapshot) {
|
|
114
|
+
return snapshot.state === 'Error';
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
-
function runAdapterWithReport(adapterName, adapter, adapterConfig, requestContext, onAdapterComplete) {
|
|
118
|
-
let adapterStart;
|
|
119
|
-
let adapterEnd;
|
|
120
|
-
let lookupStart;
|
|
121
|
-
let lookupEnd;
|
|
122
|
-
let cacheStart;
|
|
123
|
-
let cacheEnd;
|
|
124
|
-
let networkStart;
|
|
125
|
-
let networkEnd;
|
|
126
|
-
let staleLookup = false;
|
|
127
|
-
let result;
|
|
128
|
-
let snapshotState = '';
|
|
129
|
-
let error;
|
|
130
|
-
let exceptionMessage;
|
|
131
|
-
let completedNetworkRequests = [];
|
|
132
|
-
let collectedNetworkStartEvents = {};
|
|
133
|
-
let reviveStats = [];
|
|
134
|
-
let rawEvents = [];
|
|
135
|
-
const markEnd = (adapterEndedCallback) => {
|
|
136
|
-
if (adapterStart === undefined) {
|
|
137
|
-
throw Error('adapter has not been started yet');
|
|
138
|
-
}
|
|
139
|
-
if (adapterEnd) {
|
|
140
|
-
throw Error('adapter has already ended');
|
|
141
|
-
}
|
|
142
|
-
if (result === undefined) {
|
|
143
|
-
throw Error('no result type set');
|
|
144
|
-
}
|
|
145
|
-
adapterEnd = Date.now();
|
|
146
|
-
const executionTime = adapterEnd - adapterStart;
|
|
147
|
-
const cacheLookupTime = cacheEnd - cacheStart;
|
|
148
|
-
const lookupTime = lookupEnd - lookupStart;
|
|
149
|
-
const networkLookupTime = networkEnd - networkStart;
|
|
150
|
-
const config = stringify$1(adapterConfig);
|
|
151
|
-
switch (result) {
|
|
152
|
-
case 'l1-hit':
|
|
153
|
-
if (staleLookup === true) {
|
|
154
|
-
adapterEndedCallback({
|
|
155
|
-
result: 'l1-hit',
|
|
156
|
-
adapterName,
|
|
157
|
-
rawEvents,
|
|
158
|
-
config,
|
|
159
|
-
stale: true,
|
|
160
|
-
executionTime,
|
|
161
|
-
cacheLookupTime,
|
|
162
|
-
lookupTime,
|
|
163
|
-
snapshotState,
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
else {
|
|
167
|
-
adapterEndedCallback({
|
|
168
|
-
result: 'l1-hit',
|
|
169
|
-
adapterName,
|
|
170
|
-
rawEvents,
|
|
171
|
-
config,
|
|
172
|
-
stale: false,
|
|
173
|
-
executionTime,
|
|
174
|
-
cacheLookupTime,
|
|
175
|
-
lookupTime,
|
|
176
|
-
snapshotState,
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
break;
|
|
180
|
-
case 'l2-hit':
|
|
181
|
-
if (staleLookup === true) {
|
|
182
|
-
adapterEndedCallback({
|
|
183
|
-
result: 'l2-hit',
|
|
184
|
-
adapterName,
|
|
185
|
-
rawEvents,
|
|
186
|
-
config,
|
|
187
|
-
stale: true,
|
|
188
|
-
executionTime,
|
|
189
|
-
cacheLookupTime,
|
|
190
|
-
lookupTime,
|
|
191
|
-
snapshotState,
|
|
192
|
-
revives: reviveStats,
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
else {
|
|
196
|
-
adapterEndedCallback({
|
|
197
|
-
result: 'l2-hit',
|
|
198
|
-
adapterName,
|
|
199
|
-
rawEvents,
|
|
200
|
-
config,
|
|
201
|
-
stale: false,
|
|
202
|
-
executionTime,
|
|
203
|
-
cacheLookupTime,
|
|
204
|
-
lookupTime,
|
|
205
|
-
snapshotState,
|
|
206
|
-
revives: reviveStats,
|
|
207
|
-
});
|
|
208
|
-
}
|
|
209
|
-
break;
|
|
210
|
-
case 'cache-miss':
|
|
211
|
-
adapterEndedCallback({
|
|
212
|
-
result: 'cache-miss',
|
|
213
|
-
adapterName,
|
|
214
|
-
rawEvents,
|
|
215
|
-
config,
|
|
216
|
-
executionTime,
|
|
217
|
-
cacheLookupTime,
|
|
218
|
-
lookupTime,
|
|
219
|
-
networkLookupTime,
|
|
220
|
-
completedNetworkRequests,
|
|
221
|
-
snapshotState,
|
|
222
|
-
revives: reviveStats,
|
|
223
|
-
});
|
|
224
|
-
break;
|
|
225
|
-
case 'invalid-config':
|
|
226
|
-
adapterEndedCallback({
|
|
227
|
-
result: 'invalid-config',
|
|
228
|
-
rawEvents,
|
|
229
|
-
config,
|
|
230
|
-
adapterName,
|
|
231
|
-
executionTime,
|
|
232
|
-
});
|
|
233
|
-
break;
|
|
234
|
-
case 'error':
|
|
235
|
-
adapterEndedCallback({
|
|
236
|
-
result: 'error',
|
|
237
|
-
rawEvents,
|
|
238
|
-
config,
|
|
239
|
-
error,
|
|
240
|
-
adapterName,
|
|
241
|
-
executionTime,
|
|
242
|
-
cacheLookupTime,
|
|
243
|
-
lookupTime,
|
|
244
|
-
networkLookupTime,
|
|
245
|
-
snapshotState,
|
|
246
|
-
});
|
|
247
|
-
break;
|
|
248
|
-
case 'exception':
|
|
249
|
-
adapterEndedCallback({
|
|
250
|
-
result: 'exception',
|
|
251
|
-
rawEvents,
|
|
252
|
-
config,
|
|
253
|
-
exceptionMessage,
|
|
254
|
-
adapterName,
|
|
255
|
-
executionTime,
|
|
256
|
-
});
|
|
257
|
-
break;
|
|
258
|
-
}
|
|
259
|
-
};
|
|
260
|
-
const markException = (error) => {
|
|
261
|
-
let message = 'Unknown Error';
|
|
262
|
-
if (error instanceof Error)
|
|
263
|
-
message = error.message;
|
|
264
|
-
exceptionMessage = message;
|
|
265
|
-
result = 'exception';
|
|
266
|
-
};
|
|
267
|
-
const metricsEventObserver = {
|
|
268
|
-
onAdapterEvent: (ev) => {
|
|
269
|
-
rawEvents.push(ev);
|
|
270
|
-
switch (ev.type) {
|
|
271
|
-
case 'adapter-lookup-start':
|
|
272
|
-
lookupStart = Date.now();
|
|
273
|
-
break;
|
|
274
|
-
case 'adapter-lookup-end':
|
|
275
|
-
lookupEnd = Date.now();
|
|
276
|
-
break;
|
|
277
|
-
case 'cache-lookup-start':
|
|
278
|
-
cacheStart = Date.now();
|
|
279
|
-
break;
|
|
280
|
-
case 'cache-lookup-end':
|
|
281
|
-
cacheEnd = Date.now();
|
|
282
|
-
if (ev.wasResultAsync === false) {
|
|
283
|
-
// L1 cache hit
|
|
284
|
-
result = 'l1-hit';
|
|
285
|
-
}
|
|
286
|
-
else {
|
|
287
|
-
// L2 cache hit
|
|
288
|
-
result = 'l2-hit';
|
|
289
|
-
}
|
|
290
|
-
if (ev.snapshotState === 'Stale') {
|
|
291
|
-
staleLookup = true;
|
|
292
|
-
}
|
|
293
|
-
break;
|
|
294
|
-
case 'network-lookup-start':
|
|
295
|
-
// if the lookup is stale, the network lookup is
|
|
296
|
-
// caused by an async refresh
|
|
297
|
-
if (staleLookup === false) {
|
|
298
|
-
result = 'cache-miss';
|
|
299
|
-
networkStart = Date.now();
|
|
300
|
-
}
|
|
301
|
-
break;
|
|
302
|
-
case 'network-lookup-end':
|
|
303
|
-
if (staleLookup === false) {
|
|
304
|
-
networkEnd = Date.now();
|
|
305
|
-
}
|
|
306
|
-
break;
|
|
307
|
-
case 'network-request-start':
|
|
308
|
-
collectedNetworkStartEvents[ev.uuid] = ev;
|
|
309
|
-
break;
|
|
310
|
-
case 'network-request-end': {
|
|
311
|
-
const startEvent = collectedNetworkStartEvents[ev.uuid];
|
|
312
|
-
if (startEvent === undefined || startEvent.type !== 'network-request-start') {
|
|
313
|
-
if (process.env.NODE_ENV !== 'production') {
|
|
314
|
-
throw Error('no matching network start event emmited');
|
|
315
|
-
}
|
|
316
|
-
return;
|
|
317
|
-
}
|
|
318
|
-
completedNetworkRequests.push({
|
|
319
|
-
request: startEvent.request,
|
|
320
|
-
response: ev.response,
|
|
321
|
-
duration: ev.timestamp - startEvent.timestamp,
|
|
322
|
-
});
|
|
323
|
-
break;
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
},
|
|
327
|
-
onEnvironmentEvent: (ev) => {
|
|
328
|
-
rawEvents.push(ev);
|
|
329
|
-
if (isDurableEnvironmentEvent(ev)) {
|
|
330
|
-
if (ev.data.type === 'l2-revive-end') {
|
|
331
|
-
let missingKeys;
|
|
332
|
-
const snapshot = ev.data.snapshot;
|
|
333
|
-
if (snapshot.state === 'Unfulfilled') {
|
|
334
|
-
missingKeys = snapshot.missingLinks.keysAsArray();
|
|
335
|
-
if (snapshot.missingLinks.size() === 0) {
|
|
336
|
-
missingKeys.push(snapshot.recordId);
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
reviveStats.push({
|
|
340
|
-
resultState: ev.data.snapshot.state,
|
|
341
|
-
missingKeys,
|
|
342
|
-
l2Trips: ev.data.l2Trips,
|
|
343
|
-
duration: ev.data.duration,
|
|
344
|
-
});
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
},
|
|
348
|
-
onCustomAdapterEvent: (ev) => {
|
|
349
|
-
rawEvents.push(ev);
|
|
350
|
-
},
|
|
351
|
-
};
|
|
352
|
-
const bindObserverToAdapterRequestContext = (requestContext) => {
|
|
353
|
-
let requestContextWithInstrumentationObserver = requestContext;
|
|
354
|
-
if (requestContextWithInstrumentationObserver === undefined) {
|
|
355
|
-
requestContextWithInstrumentationObserver = { eventObservers: [] };
|
|
356
|
-
}
|
|
357
|
-
if (requestContextWithInstrumentationObserver.eventObservers === undefined) {
|
|
358
|
-
requestContextWithInstrumentationObserver.eventObservers = [];
|
|
359
|
-
}
|
|
360
|
-
requestContextWithInstrumentationObserver.eventObservers.push(metricsEventObserver);
|
|
361
|
-
};
|
|
362
|
-
adapterStart = Date.now();
|
|
363
|
-
try {
|
|
364
|
-
bindObserverToAdapterRequestContext(requestContext);
|
|
365
|
-
const adapterResult = adapter(adapterConfig, requestContext);
|
|
366
|
-
if (isPromise$1(adapterResult)) {
|
|
367
|
-
adapterResult
|
|
368
|
-
.then((snapshot) => {
|
|
369
|
-
snapshotState = snapshot.state;
|
|
370
|
-
if (isErrorSnapshot(snapshot)) {
|
|
371
|
-
result = 'error';
|
|
372
|
-
error = stringify$1(snapshot.error);
|
|
373
|
-
}
|
|
374
|
-
markEnd(onAdapterComplete);
|
|
375
|
-
})
|
|
376
|
-
.catch((e) => {
|
|
377
|
-
// async error
|
|
378
|
-
markException(e);
|
|
379
|
-
markEnd(onAdapterComplete);
|
|
380
|
-
});
|
|
381
|
-
}
|
|
382
|
-
else {
|
|
383
|
-
if (adapterResult === null) {
|
|
384
|
-
// invalid config
|
|
385
|
-
result = 'invalid-config';
|
|
386
|
-
}
|
|
387
|
-
else if (isErrorSnapshot(adapterResult)) {
|
|
388
|
-
snapshotState = 'Error';
|
|
389
|
-
result = 'error';
|
|
390
|
-
error = stringify$1(adapterResult.error);
|
|
391
|
-
}
|
|
392
|
-
markEnd(onAdapterComplete);
|
|
393
|
-
}
|
|
394
|
-
return adapterResult;
|
|
395
|
-
}
|
|
396
|
-
catch (error) {
|
|
397
|
-
// synchronous error (lookup exception)
|
|
398
|
-
markException(error);
|
|
399
|
-
markEnd(onAdapterComplete);
|
|
400
|
-
throw error;
|
|
401
|
-
}
|
|
117
|
+
function runAdapterWithReport(adapterName, adapter, adapterConfig, requestContext, onAdapterComplete) {
|
|
118
|
+
let adapterStart;
|
|
119
|
+
let adapterEnd;
|
|
120
|
+
let lookupStart;
|
|
121
|
+
let lookupEnd;
|
|
122
|
+
let cacheStart;
|
|
123
|
+
let cacheEnd;
|
|
124
|
+
let networkStart;
|
|
125
|
+
let networkEnd;
|
|
126
|
+
let staleLookup = false;
|
|
127
|
+
let result;
|
|
128
|
+
let snapshotState = '';
|
|
129
|
+
let error;
|
|
130
|
+
let exceptionMessage;
|
|
131
|
+
let completedNetworkRequests = [];
|
|
132
|
+
let collectedNetworkStartEvents = {};
|
|
133
|
+
let reviveStats = [];
|
|
134
|
+
let rawEvents = [];
|
|
135
|
+
const markEnd = (adapterEndedCallback) => {
|
|
136
|
+
if (adapterStart === undefined) {
|
|
137
|
+
throw Error('adapter has not been started yet');
|
|
138
|
+
}
|
|
139
|
+
if (adapterEnd) {
|
|
140
|
+
throw Error('adapter has already ended');
|
|
141
|
+
}
|
|
142
|
+
if (result === undefined) {
|
|
143
|
+
throw Error('no result type set');
|
|
144
|
+
}
|
|
145
|
+
adapterEnd = Date.now();
|
|
146
|
+
const executionTime = adapterEnd - adapterStart;
|
|
147
|
+
const cacheLookupTime = cacheEnd - cacheStart;
|
|
148
|
+
const lookupTime = lookupEnd - lookupStart;
|
|
149
|
+
const networkLookupTime = networkEnd - networkStart;
|
|
150
|
+
const config = stringify$1(adapterConfig);
|
|
151
|
+
switch (result) {
|
|
152
|
+
case 'l1-hit':
|
|
153
|
+
if (staleLookup === true) {
|
|
154
|
+
adapterEndedCallback({
|
|
155
|
+
result: 'l1-hit',
|
|
156
|
+
adapterName,
|
|
157
|
+
rawEvents,
|
|
158
|
+
config,
|
|
159
|
+
stale: true,
|
|
160
|
+
executionTime,
|
|
161
|
+
cacheLookupTime,
|
|
162
|
+
lookupTime,
|
|
163
|
+
snapshotState,
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
adapterEndedCallback({
|
|
168
|
+
result: 'l1-hit',
|
|
169
|
+
adapterName,
|
|
170
|
+
rawEvents,
|
|
171
|
+
config,
|
|
172
|
+
stale: false,
|
|
173
|
+
executionTime,
|
|
174
|
+
cacheLookupTime,
|
|
175
|
+
lookupTime,
|
|
176
|
+
snapshotState,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
break;
|
|
180
|
+
case 'l2-hit':
|
|
181
|
+
if (staleLookup === true) {
|
|
182
|
+
adapterEndedCallback({
|
|
183
|
+
result: 'l2-hit',
|
|
184
|
+
adapterName,
|
|
185
|
+
rawEvents,
|
|
186
|
+
config,
|
|
187
|
+
stale: true,
|
|
188
|
+
executionTime,
|
|
189
|
+
cacheLookupTime,
|
|
190
|
+
lookupTime,
|
|
191
|
+
snapshotState,
|
|
192
|
+
revives: reviveStats,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
adapterEndedCallback({
|
|
197
|
+
result: 'l2-hit',
|
|
198
|
+
adapterName,
|
|
199
|
+
rawEvents,
|
|
200
|
+
config,
|
|
201
|
+
stale: false,
|
|
202
|
+
executionTime,
|
|
203
|
+
cacheLookupTime,
|
|
204
|
+
lookupTime,
|
|
205
|
+
snapshotState,
|
|
206
|
+
revives: reviveStats,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
break;
|
|
210
|
+
case 'cache-miss':
|
|
211
|
+
adapterEndedCallback({
|
|
212
|
+
result: 'cache-miss',
|
|
213
|
+
adapterName,
|
|
214
|
+
rawEvents,
|
|
215
|
+
config,
|
|
216
|
+
executionTime,
|
|
217
|
+
cacheLookupTime,
|
|
218
|
+
lookupTime,
|
|
219
|
+
networkLookupTime,
|
|
220
|
+
completedNetworkRequests,
|
|
221
|
+
snapshotState,
|
|
222
|
+
revives: reviveStats,
|
|
223
|
+
});
|
|
224
|
+
break;
|
|
225
|
+
case 'invalid-config':
|
|
226
|
+
adapterEndedCallback({
|
|
227
|
+
result: 'invalid-config',
|
|
228
|
+
rawEvents,
|
|
229
|
+
config,
|
|
230
|
+
adapterName,
|
|
231
|
+
executionTime,
|
|
232
|
+
});
|
|
233
|
+
break;
|
|
234
|
+
case 'error':
|
|
235
|
+
adapterEndedCallback({
|
|
236
|
+
result: 'error',
|
|
237
|
+
rawEvents,
|
|
238
|
+
config,
|
|
239
|
+
error,
|
|
240
|
+
adapterName,
|
|
241
|
+
executionTime,
|
|
242
|
+
cacheLookupTime,
|
|
243
|
+
lookupTime,
|
|
244
|
+
networkLookupTime,
|
|
245
|
+
snapshotState,
|
|
246
|
+
});
|
|
247
|
+
break;
|
|
248
|
+
case 'exception':
|
|
249
|
+
adapterEndedCallback({
|
|
250
|
+
result: 'exception',
|
|
251
|
+
rawEvents,
|
|
252
|
+
config,
|
|
253
|
+
exceptionMessage,
|
|
254
|
+
adapterName,
|
|
255
|
+
executionTime,
|
|
256
|
+
});
|
|
257
|
+
break;
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
const markException = (error) => {
|
|
261
|
+
let message = 'Unknown Error';
|
|
262
|
+
if (error instanceof Error)
|
|
263
|
+
message = error.message;
|
|
264
|
+
exceptionMessage = message;
|
|
265
|
+
result = 'exception';
|
|
266
|
+
};
|
|
267
|
+
const metricsEventObserver = {
|
|
268
|
+
onAdapterEvent: (ev) => {
|
|
269
|
+
rawEvents.push(ev);
|
|
270
|
+
switch (ev.type) {
|
|
271
|
+
case 'adapter-lookup-start':
|
|
272
|
+
lookupStart = Date.now();
|
|
273
|
+
break;
|
|
274
|
+
case 'adapter-lookup-end':
|
|
275
|
+
lookupEnd = Date.now();
|
|
276
|
+
break;
|
|
277
|
+
case 'cache-lookup-start':
|
|
278
|
+
cacheStart = Date.now();
|
|
279
|
+
break;
|
|
280
|
+
case 'cache-lookup-end':
|
|
281
|
+
cacheEnd = Date.now();
|
|
282
|
+
if (ev.wasResultAsync === false) {
|
|
283
|
+
// L1 cache hit
|
|
284
|
+
result = 'l1-hit';
|
|
285
|
+
}
|
|
286
|
+
else {
|
|
287
|
+
// L2 cache hit
|
|
288
|
+
result = 'l2-hit';
|
|
289
|
+
}
|
|
290
|
+
if (ev.snapshotState === 'Stale') {
|
|
291
|
+
staleLookup = true;
|
|
292
|
+
}
|
|
293
|
+
break;
|
|
294
|
+
case 'network-lookup-start':
|
|
295
|
+
// if the lookup is stale, the network lookup is
|
|
296
|
+
// caused by an async refresh
|
|
297
|
+
if (staleLookup === false) {
|
|
298
|
+
result = 'cache-miss';
|
|
299
|
+
networkStart = Date.now();
|
|
300
|
+
}
|
|
301
|
+
break;
|
|
302
|
+
case 'network-lookup-end':
|
|
303
|
+
if (staleLookup === false) {
|
|
304
|
+
networkEnd = Date.now();
|
|
305
|
+
}
|
|
306
|
+
break;
|
|
307
|
+
case 'network-request-start':
|
|
308
|
+
collectedNetworkStartEvents[ev.uuid] = ev;
|
|
309
|
+
break;
|
|
310
|
+
case 'network-request-end': {
|
|
311
|
+
const startEvent = collectedNetworkStartEvents[ev.uuid];
|
|
312
|
+
if (startEvent === undefined || startEvent.type !== 'network-request-start') {
|
|
313
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
314
|
+
throw Error('no matching network start event emmited');
|
|
315
|
+
}
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
completedNetworkRequests.push({
|
|
319
|
+
request: startEvent.request,
|
|
320
|
+
response: ev.response,
|
|
321
|
+
duration: ev.timestamp - startEvent.timestamp,
|
|
322
|
+
});
|
|
323
|
+
break;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
},
|
|
327
|
+
onEnvironmentEvent: (ev) => {
|
|
328
|
+
rawEvents.push(ev);
|
|
329
|
+
if (isDurableEnvironmentEvent(ev)) {
|
|
330
|
+
if (ev.data.type === 'l2-revive-end') {
|
|
331
|
+
let missingKeys;
|
|
332
|
+
const snapshot = ev.data.snapshot;
|
|
333
|
+
if (snapshot.state === 'Unfulfilled') {
|
|
334
|
+
missingKeys = snapshot.missingLinks.keysAsArray();
|
|
335
|
+
if (snapshot.missingLinks.size() === 0) {
|
|
336
|
+
missingKeys.push(snapshot.recordId);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
reviveStats.push({
|
|
340
|
+
resultState: ev.data.snapshot.state,
|
|
341
|
+
missingKeys,
|
|
342
|
+
l2Trips: ev.data.l2Trips,
|
|
343
|
+
duration: ev.data.duration,
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
},
|
|
348
|
+
onCustomAdapterEvent: (ev) => {
|
|
349
|
+
rawEvents.push(ev);
|
|
350
|
+
},
|
|
351
|
+
};
|
|
352
|
+
const bindObserverToAdapterRequestContext = (requestContext) => {
|
|
353
|
+
let requestContextWithInstrumentationObserver = requestContext;
|
|
354
|
+
if (requestContextWithInstrumentationObserver === undefined) {
|
|
355
|
+
requestContextWithInstrumentationObserver = { eventObservers: [] };
|
|
356
|
+
}
|
|
357
|
+
if (requestContextWithInstrumentationObserver.eventObservers === undefined) {
|
|
358
|
+
requestContextWithInstrumentationObserver.eventObservers = [];
|
|
359
|
+
}
|
|
360
|
+
requestContextWithInstrumentationObserver.eventObservers.push(metricsEventObserver);
|
|
361
|
+
};
|
|
362
|
+
adapterStart = Date.now();
|
|
363
|
+
try {
|
|
364
|
+
bindObserverToAdapterRequestContext(requestContext);
|
|
365
|
+
const adapterResult = adapter(adapterConfig, requestContext);
|
|
366
|
+
if (isPromise$1(adapterResult)) {
|
|
367
|
+
adapterResult
|
|
368
|
+
.then((snapshot) => {
|
|
369
|
+
snapshotState = snapshot.state;
|
|
370
|
+
if (isErrorSnapshot(snapshot)) {
|
|
371
|
+
result = 'error';
|
|
372
|
+
error = stringify$1(snapshot.error);
|
|
373
|
+
}
|
|
374
|
+
markEnd(onAdapterComplete);
|
|
375
|
+
})
|
|
376
|
+
.catch((e) => {
|
|
377
|
+
// async error
|
|
378
|
+
markException(e);
|
|
379
|
+
markEnd(onAdapterComplete);
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
else {
|
|
383
|
+
if (adapterResult === null) {
|
|
384
|
+
// invalid config
|
|
385
|
+
result = 'invalid-config';
|
|
386
|
+
}
|
|
387
|
+
else if (isErrorSnapshot(adapterResult)) {
|
|
388
|
+
snapshotState = 'Error';
|
|
389
|
+
result = 'error';
|
|
390
|
+
error = stringify$1(adapterResult.error);
|
|
391
|
+
}
|
|
392
|
+
markEnd(onAdapterComplete);
|
|
393
|
+
}
|
|
394
|
+
return adapterResult;
|
|
395
|
+
}
|
|
396
|
+
catch (error) {
|
|
397
|
+
// synchronous error (lookup exception)
|
|
398
|
+
markException(error);
|
|
399
|
+
markEnd(onAdapterComplete);
|
|
400
|
+
throw error;
|
|
401
|
+
}
|
|
402
402
|
}
|
|
403
403
|
|
|
404
|
-
const ADAPTER_CACHE_HIT_COUNT_METRIC_NAME = 'cache-hit-count';
|
|
405
|
-
const ADAPTER_CACHE_HIT_DURATION_METRIC_NAME = 'cache-hit-duration';
|
|
406
|
-
const ADAPTER_CACHE_HIT_L2_COUNT_METRIC_NAME = 'cache-hit-l2-count';
|
|
407
|
-
const ADAPTER_CACHE_HIT_L2_DURATION_METRIC_NAME = 'cache-hit-l2-duration';
|
|
408
|
-
const ADAPTER_CACHE_MISS_COUNT_METRIC_NAME = 'cache-miss-count';
|
|
409
|
-
const ADAPTER_CACHE_MISS_DURATION_METRIC_NAME = 'cache-miss-duration';
|
|
410
|
-
const ADAPTER_CACHE_MISS_OUT_OF_TTL_COUNT_METRIC_NAME = 'cache-miss-out-of-ttl-count';
|
|
411
|
-
const ADAPTER_CACHE_MISS_OUT_OF_TTL_DURATION_METRIC_NAME = 'cache-miss-out-of-ttl-duration';
|
|
412
|
-
const REPRESENTATION_CACHE_MISS_OUT_OF_TTL_DATA_CHANGED_COUNT_METRIC_NAME = 'cache-miss-out-of-ttl-data-changed-count';
|
|
413
|
-
const REPRESENTATION_CACHE_MISS_OUT_OF_TTL_DATA_UNCHANGED_COUNT_METRIC_NAME = 'cache-miss-out-of-ttl-data-unchanged-count';
|
|
414
|
-
const REPRESENTATION_CACHE_MISS_OUT_OF_TTL_DATA_CHANGED_DURATION_METRIC_NAME = 'cache-miss-out-of-ttl-data-changed-duration';
|
|
415
|
-
const REPRESENTATION_CACHE_MISS_OUT_OF_TTL_DATA_UNCHANGED_DURATION_METRIC_NAME = 'cache-miss-out-of-ttl-data-unchanged-duration';
|
|
416
|
-
/**
|
|
417
|
-
* W-8121791
|
|
418
|
-
* Number of subqueries used when aggregateUi is invoked for getRecord
|
|
419
|
-
*/
|
|
420
|
-
const AGGREGATE_UI_CHUNK_COUNT = 'aggregate-ui-chunk-count';
|
|
421
|
-
/**
|
|
422
|
-
* W-6981216
|
|
423
|
-
* Counter for overall LDS cache hits.
|
|
424
|
-
* Note: This is also being recorded in AILTN logging.
|
|
425
|
-
*/
|
|
426
|
-
const CACHE_HIT_COUNT = ADAPTER_CACHE_HIT_COUNT_METRIC_NAME;
|
|
427
|
-
/**
|
|
428
|
-
* W-6981216
|
|
429
|
-
* Counter for overall LDS cache hits.
|
|
430
|
-
* Note: This is also being recorded in AILTN logging.
|
|
431
|
-
*/
|
|
432
|
-
const CACHE_MISS_COUNT = ADAPTER_CACHE_MISS_COUNT_METRIC_NAME;
|
|
433
|
-
/**
|
|
434
|
-
* W-9949353
|
|
435
|
-
* Used to track how often we dedupe HTTP requests
|
|
436
|
-
* Invoked when an HTTP request is deduped against an already in-flight request
|
|
437
|
-
*/
|
|
438
|
-
const DUPLICATE_REQUEST_COUNT = 'duplicate-request-count';
|
|
439
|
-
/**
|
|
440
|
-
* W-7667066
|
|
441
|
-
* This count represents the number of times getRecord() was invoked, but not including
|
|
442
|
-
* executeAggregateUi calls. It can be represented as the sum of the Aura Action invocations
|
|
443
|
-
* GetRecordWithLayouts and GetRecordWithFields.
|
|
444
|
-
*/
|
|
445
|
-
const GET_RECORD_NORMAL_INVOKE_COUNT = 'get-record-normal-invoke-count';
|
|
446
|
-
/**
|
|
447
|
-
* W-7667066
|
|
448
|
-
* This count represents the number of times getRecord() was invoked, with a large enough payload
|
|
449
|
-
* that executeAggregateUi was used.
|
|
450
|
-
*/
|
|
451
|
-
const GET_RECORD_AGGREGATE_INVOKE_COUNT = 'get-record-aggregate-invoke-count';
|
|
452
|
-
/**
|
|
453
|
-
* W-7301684
|
|
454
|
-
* Counter for when getRecordNotifyChange api calls are allowed through.
|
|
455
|
-
*/
|
|
456
|
-
const GET_RECORD_NOTIFY_CHANGE_ALLOW_COUNT = 'get-record-notify-change-allow-count';
|
|
457
|
-
/**
|
|
458
|
-
* W-7301684
|
|
459
|
-
* Counter for when getRecordNotifyChange api calls are dropped/throttled.
|
|
460
|
-
*/
|
|
461
|
-
const GET_RECORD_NOTIFY_CHANGE_DROP_COUNT = 'get-record-notify-change-drop-count';
|
|
462
|
-
/**
|
|
463
|
-
* W-11118785
|
|
464
|
-
* Counter for when notifyRecordUpdateAvailable api calls are allowed through.
|
|
465
|
-
*/
|
|
466
|
-
const NOTIFY_RECORD_UPDATE_AVAILABLE_ALLOW_COUNT = 'notify-record-update-available-allow-count';
|
|
467
|
-
/**
|
|
468
|
-
* W-11118785
|
|
469
|
-
* Counter for when notifyRecordUpdateAvailable api calls are dropped/throttled.
|
|
470
|
-
*/
|
|
471
|
-
const NOTIFY_RECORD_UPDATE_AVAILABLE_DROP_COUNT = 'notify-record-update-available-drop-count';
|
|
472
|
-
/**
|
|
473
|
-
* W-8278006
|
|
474
|
-
* Counter for rate limiting telemetry. Is updated whenever the network adapter hits the specified limit.
|
|
475
|
-
*/
|
|
476
|
-
const NETWORK_RATE_LIMIT_EXCEEDED_COUNT = 'network-rate-limit-exceeded-count';
|
|
477
|
-
/**
|
|
478
|
-
* W-6981216
|
|
479
|
-
* Timer to measure performance for Luvio.storeBroadcast() method.
|
|
480
|
-
*/
|
|
481
|
-
const STORE_BROADCAST_DURATION = 'store-broadcast-duration';
|
|
482
|
-
/**
|
|
483
|
-
* W-6981216
|
|
484
|
-
* Timer to measure performance for Luvio.storeIngest() method.
|
|
485
|
-
*/
|
|
486
|
-
const STORE_INGEST_DURATION = 'store-ingest-duration';
|
|
487
|
-
/**
|
|
488
|
-
* W-6981216
|
|
489
|
-
* Timer to measure performance for Luvio.storeLookup() method.
|
|
490
|
-
*/
|
|
491
|
-
const STORE_LOOKUP_DURATION = 'store-lookup-duration';
|
|
492
|
-
/**
|
|
493
|
-
* W-9805009
|
|
494
|
-
* Timer to measure performance for Luvio.storeSetTTLOverride() method.
|
|
495
|
-
*/
|
|
496
|
-
const STORE_SET_TTL_OVERRIDE_DURATION = 'store-set-ttl-override-duration';
|
|
497
|
-
/**
|
|
498
|
-
* W-9805009
|
|
499
|
-
* Timer to measure performance for Luvio.storeSetDefaultTTLOverride() method.
|
|
500
|
-
*/
|
|
501
|
-
const STORE_SET_DEFAULT_TTL_OVERRIDE_DURATION = 'store-set-default-ttl-override-duration';
|
|
502
|
-
/**
|
|
503
|
-
* W-11118785
|
|
504
|
-
* Timer to measure performance for Luvio.notifyStoreUpdateAvailable() method.
|
|
505
|
-
*/
|
|
506
|
-
const NOTIFY_STORE_UPDATE_AVAILABLE_DURATION = 'notify-store-update-available-duration';
|
|
507
|
-
/**
|
|
508
|
-
* W-6981216
|
|
509
|
-
* Counter for number of records in LDS store. Is updated by periodicLogger invocations.
|
|
510
|
-
* Note: This is also being recorded in AILTN logging.
|
|
511
|
-
*/
|
|
512
|
-
const STORE_SIZE_COUNT = 'store-size-count';
|
|
513
|
-
/**
|
|
514
|
-
* W-6981216
|
|
515
|
-
* Counter for number of LDS snapshot subscription. Is updated by periodicLogger invocations.
|
|
516
|
-
* Note: This is also being recorded in AILTN logging.
|
|
517
|
-
*/
|
|
518
|
-
const STORE_SNAPSHOT_SUBSCRIPTIONS_COUNT = 'store-snapshot-subscriptions-count';
|
|
519
|
-
/**
|
|
520
|
-
* W-6981216
|
|
521
|
-
* Counter for number of LDS watch subscriptions. Is updated by periodicLogger invocations.
|
|
522
|
-
* Note: This is also being recorded in AILTN logging.
|
|
523
|
-
*/
|
|
524
|
-
const STORE_WATCH_SUBSCRIPTIONS_COUNT = 'store-watch-subscriptions-count';
|
|
525
|
-
/**
|
|
526
|
-
* W-9131128
|
|
527
|
-
* Counter for graphQL get adapter response with mixed bag of both data and error in response
|
|
528
|
-
*/
|
|
529
|
-
const GET_GRAPHQL_RESPONSE_MIXED = 'get-graphql-response-mixed-count';
|
|
530
|
-
/**
|
|
531
|
-
* W-9537401
|
|
532
|
-
* Counter for Luvio store trim task invocation
|
|
533
|
-
*/
|
|
534
|
-
const STORE_TRIM_TASK_COUNT = 'store-trim-task-count';
|
|
535
|
-
/**
|
|
536
|
-
* W-9537401
|
|
537
|
-
* Timer to measure performance for Luvio store trim task
|
|
538
|
-
*/
|
|
539
|
-
const STORE_TRIM_TASK_DURATION = 'store-trim-task-duration';
|
|
540
|
-
/**
|
|
541
|
-
* W-9804037
|
|
542
|
-
* Counters for Luvio cache policy usage
|
|
543
|
-
* Note: Undefined cache policy defaults to different cache policies based on runtime
|
|
544
|
-
*/
|
|
545
|
-
const CACHE_POLICY_COUNTERS = {
|
|
546
|
-
'cache-and-network': 'cache-policy-cache-and-network',
|
|
547
|
-
'cache-then-network': 'cache-policy-cache-then-network',
|
|
548
|
-
'no-cache': 'cache-policy-no-cache',
|
|
549
|
-
'only-if-cached': 'cache-policy-only-if-cached',
|
|
550
|
-
'stale-while-revalidate': 'cache-policy-stale-while-revalidate',
|
|
551
|
-
'valid-at': 'cache-policy-valid-at',
|
|
552
|
-
};
|
|
553
|
-
const CACHE_POLICY_UNDEFINED_COUNTER = 'cache-policy-undefined';
|
|
554
|
-
const STALE_TAG = 'stale';
|
|
555
|
-
/**
|
|
556
|
-
* W-9804037
|
|
557
|
-
* Durable Store health metric
|
|
558
|
-
* Counter to track Durable Store read, write and error rates
|
|
559
|
-
*/
|
|
560
|
-
const DURABLE_STORE_COUNT = 'durable-store-count';
|
|
561
|
-
/**
|
|
562
|
-
* W-10490363
|
|
563
|
-
* GraphQL Eval health metric
|
|
564
|
-
* Counter to track Success and Error Rate on Eval
|
|
565
|
-
*/
|
|
566
|
-
const GRAPHQL_ADAPTER_COUNT = 'graphql-adapter-count';
|
|
567
|
-
/**
|
|
568
|
-
* Counter for tracking invalid record type IDs
|
|
569
|
-
*/
|
|
570
|
-
const RECORD_TYPE_ID_IS_NULL_COUNT = 'record-type-id-is-null-count';
|
|
571
|
-
/**
|
|
572
|
-
* W-12293528
|
|
573
|
-
* GraphQL health metric
|
|
574
|
-
* Counter to track size of the top-level GraphQL object
|
|
575
|
-
*/
|
|
576
|
-
const STORE_GRAPHQL_SIZE_COUNT = 'store-graphql-size-count';
|
|
577
|
-
/**
|
|
578
|
-
* W-12293528
|
|
579
|
-
* GraphQL health metric
|
|
580
|
-
* Counter to track validation errors in query
|
|
581
|
-
*/
|
|
582
|
-
const GRAPHQL_QUERY_VALIDATION_ERROR_COUNT = 'graphql-query-validation-error-count';
|
|
583
|
-
/**
|
|
584
|
-
* W-12293528
|
|
585
|
-
* GraphQL health metric
|
|
586
|
-
* Counter to track syntax errors in query
|
|
587
|
-
*/
|
|
588
|
-
const GRAPHQL_QUERY_SYNTAX_ERROR_COUNT = 'graphql-query-syntax-error-count';
|
|
589
|
-
/**
|
|
590
|
-
* W-12293528
|
|
591
|
-
* GraphQL health metric
|
|
592
|
-
* Counter to track miscellaneous errors in query
|
|
593
|
-
*/
|
|
404
|
+
const ADAPTER_CACHE_HIT_COUNT_METRIC_NAME = 'cache-hit-count';
|
|
405
|
+
const ADAPTER_CACHE_HIT_DURATION_METRIC_NAME = 'cache-hit-duration';
|
|
406
|
+
const ADAPTER_CACHE_HIT_L2_COUNT_METRIC_NAME = 'cache-hit-l2-count';
|
|
407
|
+
const ADAPTER_CACHE_HIT_L2_DURATION_METRIC_NAME = 'cache-hit-l2-duration';
|
|
408
|
+
const ADAPTER_CACHE_MISS_COUNT_METRIC_NAME = 'cache-miss-count';
|
|
409
|
+
const ADAPTER_CACHE_MISS_DURATION_METRIC_NAME = 'cache-miss-duration';
|
|
410
|
+
const ADAPTER_CACHE_MISS_OUT_OF_TTL_COUNT_METRIC_NAME = 'cache-miss-out-of-ttl-count';
|
|
411
|
+
const ADAPTER_CACHE_MISS_OUT_OF_TTL_DURATION_METRIC_NAME = 'cache-miss-out-of-ttl-duration';
|
|
412
|
+
const REPRESENTATION_CACHE_MISS_OUT_OF_TTL_DATA_CHANGED_COUNT_METRIC_NAME = 'cache-miss-out-of-ttl-data-changed-count';
|
|
413
|
+
const REPRESENTATION_CACHE_MISS_OUT_OF_TTL_DATA_UNCHANGED_COUNT_METRIC_NAME = 'cache-miss-out-of-ttl-data-unchanged-count';
|
|
414
|
+
const REPRESENTATION_CACHE_MISS_OUT_OF_TTL_DATA_CHANGED_DURATION_METRIC_NAME = 'cache-miss-out-of-ttl-data-changed-duration';
|
|
415
|
+
const REPRESENTATION_CACHE_MISS_OUT_OF_TTL_DATA_UNCHANGED_DURATION_METRIC_NAME = 'cache-miss-out-of-ttl-data-unchanged-duration';
|
|
416
|
+
/**
|
|
417
|
+
* W-8121791
|
|
418
|
+
* Number of subqueries used when aggregateUi is invoked for getRecord
|
|
419
|
+
*/
|
|
420
|
+
const AGGREGATE_UI_CHUNK_COUNT = 'aggregate-ui-chunk-count';
|
|
421
|
+
/**
|
|
422
|
+
* W-6981216
|
|
423
|
+
* Counter for overall LDS cache hits.
|
|
424
|
+
* Note: This is also being recorded in AILTN logging.
|
|
425
|
+
*/
|
|
426
|
+
const CACHE_HIT_COUNT = ADAPTER_CACHE_HIT_COUNT_METRIC_NAME;
|
|
427
|
+
/**
|
|
428
|
+
* W-6981216
|
|
429
|
+
* Counter for overall LDS cache hits.
|
|
430
|
+
* Note: This is also being recorded in AILTN logging.
|
|
431
|
+
*/
|
|
432
|
+
const CACHE_MISS_COUNT = ADAPTER_CACHE_MISS_COUNT_METRIC_NAME;
|
|
433
|
+
/**
|
|
434
|
+
* W-9949353
|
|
435
|
+
* Used to track how often we dedupe HTTP requests
|
|
436
|
+
* Invoked when an HTTP request is deduped against an already in-flight request
|
|
437
|
+
*/
|
|
438
|
+
const DUPLICATE_REQUEST_COUNT = 'duplicate-request-count';
|
|
439
|
+
/**
|
|
440
|
+
* W-7667066
|
|
441
|
+
* This count represents the number of times getRecord() was invoked, but not including
|
|
442
|
+
* executeAggregateUi calls. It can be represented as the sum of the Aura Action invocations
|
|
443
|
+
* GetRecordWithLayouts and GetRecordWithFields.
|
|
444
|
+
*/
|
|
445
|
+
const GET_RECORD_NORMAL_INVOKE_COUNT = 'get-record-normal-invoke-count';
|
|
446
|
+
/**
|
|
447
|
+
* W-7667066
|
|
448
|
+
* This count represents the number of times getRecord() was invoked, with a large enough payload
|
|
449
|
+
* that executeAggregateUi was used.
|
|
450
|
+
*/
|
|
451
|
+
const GET_RECORD_AGGREGATE_INVOKE_COUNT = 'get-record-aggregate-invoke-count';
|
|
452
|
+
/**
|
|
453
|
+
* W-7301684
|
|
454
|
+
* Counter for when getRecordNotifyChange api calls are allowed through.
|
|
455
|
+
*/
|
|
456
|
+
const GET_RECORD_NOTIFY_CHANGE_ALLOW_COUNT = 'get-record-notify-change-allow-count';
|
|
457
|
+
/**
|
|
458
|
+
* W-7301684
|
|
459
|
+
* Counter for when getRecordNotifyChange api calls are dropped/throttled.
|
|
460
|
+
*/
|
|
461
|
+
const GET_RECORD_NOTIFY_CHANGE_DROP_COUNT = 'get-record-notify-change-drop-count';
|
|
462
|
+
/**
|
|
463
|
+
* W-11118785
|
|
464
|
+
* Counter for when notifyRecordUpdateAvailable api calls are allowed through.
|
|
465
|
+
*/
|
|
466
|
+
const NOTIFY_RECORD_UPDATE_AVAILABLE_ALLOW_COUNT = 'notify-record-update-available-allow-count';
|
|
467
|
+
/**
|
|
468
|
+
* W-11118785
|
|
469
|
+
* Counter for when notifyRecordUpdateAvailable api calls are dropped/throttled.
|
|
470
|
+
*/
|
|
471
|
+
const NOTIFY_RECORD_UPDATE_AVAILABLE_DROP_COUNT = 'notify-record-update-available-drop-count';
|
|
472
|
+
/**
|
|
473
|
+
* W-8278006
|
|
474
|
+
* Counter for rate limiting telemetry. Is updated whenever the network adapter hits the specified limit.
|
|
475
|
+
*/
|
|
476
|
+
const NETWORK_RATE_LIMIT_EXCEEDED_COUNT = 'network-rate-limit-exceeded-count';
|
|
477
|
+
/**
|
|
478
|
+
* W-6981216
|
|
479
|
+
* Timer to measure performance for Luvio.storeBroadcast() method.
|
|
480
|
+
*/
|
|
481
|
+
const STORE_BROADCAST_DURATION = 'store-broadcast-duration';
|
|
482
|
+
/**
|
|
483
|
+
* W-6981216
|
|
484
|
+
* Timer to measure performance for Luvio.storeIngest() method.
|
|
485
|
+
*/
|
|
486
|
+
const STORE_INGEST_DURATION = 'store-ingest-duration';
|
|
487
|
+
/**
|
|
488
|
+
* W-6981216
|
|
489
|
+
* Timer to measure performance for Luvio.storeLookup() method.
|
|
490
|
+
*/
|
|
491
|
+
const STORE_LOOKUP_DURATION = 'store-lookup-duration';
|
|
492
|
+
/**
|
|
493
|
+
* W-9805009
|
|
494
|
+
* Timer to measure performance for Luvio.storeSetTTLOverride() method.
|
|
495
|
+
*/
|
|
496
|
+
const STORE_SET_TTL_OVERRIDE_DURATION = 'store-set-ttl-override-duration';
|
|
497
|
+
/**
|
|
498
|
+
* W-9805009
|
|
499
|
+
* Timer to measure performance for Luvio.storeSetDefaultTTLOverride() method.
|
|
500
|
+
*/
|
|
501
|
+
const STORE_SET_DEFAULT_TTL_OVERRIDE_DURATION = 'store-set-default-ttl-override-duration';
|
|
502
|
+
/**
|
|
503
|
+
* W-11118785
|
|
504
|
+
* Timer to measure performance for Luvio.notifyStoreUpdateAvailable() method.
|
|
505
|
+
*/
|
|
506
|
+
const NOTIFY_STORE_UPDATE_AVAILABLE_DURATION = 'notify-store-update-available-duration';
|
|
507
|
+
/**
|
|
508
|
+
* W-6981216
|
|
509
|
+
* Counter for number of records in LDS store. Is updated by periodicLogger invocations.
|
|
510
|
+
* Note: This is also being recorded in AILTN logging.
|
|
511
|
+
*/
|
|
512
|
+
const STORE_SIZE_COUNT = 'store-size-count';
|
|
513
|
+
/**
|
|
514
|
+
* W-6981216
|
|
515
|
+
* Counter for number of LDS snapshot subscription. Is updated by periodicLogger invocations.
|
|
516
|
+
* Note: This is also being recorded in AILTN logging.
|
|
517
|
+
*/
|
|
518
|
+
const STORE_SNAPSHOT_SUBSCRIPTIONS_COUNT = 'store-snapshot-subscriptions-count';
|
|
519
|
+
/**
|
|
520
|
+
* W-6981216
|
|
521
|
+
* Counter for number of LDS watch subscriptions. Is updated by periodicLogger invocations.
|
|
522
|
+
* Note: This is also being recorded in AILTN logging.
|
|
523
|
+
*/
|
|
524
|
+
const STORE_WATCH_SUBSCRIPTIONS_COUNT = 'store-watch-subscriptions-count';
|
|
525
|
+
/**
|
|
526
|
+
* W-9131128
|
|
527
|
+
* Counter for graphQL get adapter response with mixed bag of both data and error in response
|
|
528
|
+
*/
|
|
529
|
+
const GET_GRAPHQL_RESPONSE_MIXED = 'get-graphql-response-mixed-count';
|
|
530
|
+
/**
|
|
531
|
+
* W-9537401
|
|
532
|
+
* Counter for Luvio store trim task invocation
|
|
533
|
+
*/
|
|
534
|
+
const STORE_TRIM_TASK_COUNT = 'store-trim-task-count';
|
|
535
|
+
/**
|
|
536
|
+
* W-9537401
|
|
537
|
+
* Timer to measure performance for Luvio store trim task
|
|
538
|
+
*/
|
|
539
|
+
const STORE_TRIM_TASK_DURATION = 'store-trim-task-duration';
|
|
540
|
+
/**
|
|
541
|
+
* W-9804037
|
|
542
|
+
* Counters for Luvio cache policy usage
|
|
543
|
+
* Note: Undefined cache policy defaults to different cache policies based on runtime
|
|
544
|
+
*/
|
|
545
|
+
const CACHE_POLICY_COUNTERS = {
|
|
546
|
+
'cache-and-network': 'cache-policy-cache-and-network',
|
|
547
|
+
'cache-then-network': 'cache-policy-cache-then-network',
|
|
548
|
+
'no-cache': 'cache-policy-no-cache',
|
|
549
|
+
'only-if-cached': 'cache-policy-only-if-cached',
|
|
550
|
+
'stale-while-revalidate': 'cache-policy-stale-while-revalidate',
|
|
551
|
+
'valid-at': 'cache-policy-valid-at',
|
|
552
|
+
};
|
|
553
|
+
const CACHE_POLICY_UNDEFINED_COUNTER = 'cache-policy-undefined';
|
|
554
|
+
const STALE_TAG = 'stale';
|
|
555
|
+
/**
|
|
556
|
+
* W-9804037
|
|
557
|
+
* Durable Store health metric
|
|
558
|
+
* Counter to track Durable Store read, write and error rates
|
|
559
|
+
*/
|
|
560
|
+
const DURABLE_STORE_COUNT = 'durable-store-count';
|
|
561
|
+
/**
|
|
562
|
+
* W-10490363
|
|
563
|
+
* GraphQL Eval health metric
|
|
564
|
+
* Counter to track Success and Error Rate on Eval
|
|
565
|
+
*/
|
|
566
|
+
const GRAPHQL_ADAPTER_COUNT = 'graphql-adapter-count';
|
|
567
|
+
/**
|
|
568
|
+
* Counter for tracking invalid record type IDs
|
|
569
|
+
*/
|
|
570
|
+
const RECORD_TYPE_ID_IS_NULL_COUNT = 'record-type-id-is-null-count';
|
|
571
|
+
/**
|
|
572
|
+
* W-12293528
|
|
573
|
+
* GraphQL health metric
|
|
574
|
+
* Counter to track size of the top-level GraphQL object
|
|
575
|
+
*/
|
|
576
|
+
const STORE_GRAPHQL_SIZE_COUNT = 'store-graphql-size-count';
|
|
577
|
+
/**
|
|
578
|
+
* W-12293528
|
|
579
|
+
* GraphQL health metric
|
|
580
|
+
* Counter to track validation errors in query
|
|
581
|
+
*/
|
|
582
|
+
const GRAPHQL_QUERY_VALIDATION_ERROR_COUNT = 'graphql-query-validation-error-count';
|
|
583
|
+
/**
|
|
584
|
+
* W-12293528
|
|
585
|
+
* GraphQL health metric
|
|
586
|
+
* Counter to track syntax errors in query
|
|
587
|
+
*/
|
|
588
|
+
const GRAPHQL_QUERY_SYNTAX_ERROR_COUNT = 'graphql-query-syntax-error-count';
|
|
589
|
+
/**
|
|
590
|
+
* W-12293528
|
|
591
|
+
* GraphQL health metric
|
|
592
|
+
* Counter to track miscellaneous errors in query
|
|
593
|
+
*/
|
|
594
594
|
const GRAPHQL_QUERY_OTHER_ERROR_COUNT = 'graphql-query-other-error-count';
|
|
595
595
|
|
|
596
596
|
var metricKeys = /*#__PURE__*/Object.freeze({
|
|
@@ -642,768 +642,768 @@ var metricKeys = /*#__PURE__*/Object.freeze({
|
|
|
642
642
|
GRAPHQL_QUERY_OTHER_ERROR_COUNT: GRAPHQL_QUERY_OTHER_ERROR_COUNT
|
|
643
643
|
});
|
|
644
644
|
|
|
645
|
-
/**
|
|
646
|
-
* Observability / Critical Availability Program (230+)
|
|
647
|
-
*
|
|
648
|
-
* This file is intended to be used as a consolidated place for all definitions, functions,
|
|
649
|
-
* and helpers related to "M1"[1].
|
|
650
|
-
*
|
|
651
|
-
* Below are the R.E.A.D.S. metrics for the Lightning Data Service, defined here[2].
|
|
652
|
-
*
|
|
653
|
-
* [1] https://salesforce.quip.com/NfW9AsbGEaTY
|
|
654
|
-
* [2] https://salesforce.quip.com/1dFvAba1b0eq
|
|
655
|
-
*/
|
|
656
|
-
const OBSERVABILITY_NAMESPACE = 'LIGHTNING.lds.service';
|
|
657
|
-
const ADAPTER_INVOCATION_COUNT_METRIC_NAME = 'request';
|
|
658
|
-
const ADAPTER_ERROR_COUNT_METRIC_NAME = 'error';
|
|
659
|
-
/**
|
|
660
|
-
* W-8828410
|
|
661
|
-
* Counter for the number of UnfulfilledSnapshotErrors the luvio engine has.
|
|
662
|
-
*/
|
|
663
|
-
const TOTAL_ADAPTER_ERROR_COUNT = ADAPTER_ERROR_COUNT_METRIC_NAME;
|
|
664
|
-
/**
|
|
665
|
-
* W-8828410
|
|
666
|
-
* Counter for the number of invocations made into LDS by a wire adapter.
|
|
667
|
-
*/
|
|
645
|
+
/**
|
|
646
|
+
* Observability / Critical Availability Program (230+)
|
|
647
|
+
*
|
|
648
|
+
* This file is intended to be used as a consolidated place for all definitions, functions,
|
|
649
|
+
* and helpers related to "M1"[1].
|
|
650
|
+
*
|
|
651
|
+
* Below are the R.E.A.D.S. metrics for the Lightning Data Service, defined here[2].
|
|
652
|
+
*
|
|
653
|
+
* [1] https://salesforce.quip.com/NfW9AsbGEaTY
|
|
654
|
+
* [2] https://salesforce.quip.com/1dFvAba1b0eq
|
|
655
|
+
*/
|
|
656
|
+
const OBSERVABILITY_NAMESPACE = 'LIGHTNING.lds.service';
|
|
657
|
+
const ADAPTER_INVOCATION_COUNT_METRIC_NAME = 'request';
|
|
658
|
+
const ADAPTER_ERROR_COUNT_METRIC_NAME = 'error';
|
|
659
|
+
/**
|
|
660
|
+
* W-8828410
|
|
661
|
+
* Counter for the number of UnfulfilledSnapshotErrors the luvio engine has.
|
|
662
|
+
*/
|
|
663
|
+
const TOTAL_ADAPTER_ERROR_COUNT = ADAPTER_ERROR_COUNT_METRIC_NAME;
|
|
664
|
+
/**
|
|
665
|
+
* W-8828410
|
|
666
|
+
* Counter for the number of invocations made into LDS by a wire adapter.
|
|
667
|
+
*/
|
|
668
668
|
const TOTAL_ADAPTER_REQUEST_SUCCESS_COUNT = ADAPTER_INVOCATION_COUNT_METRIC_NAME;
|
|
669
669
|
|
|
670
|
-
const { create, keys } = Object;
|
|
671
|
-
const { isArray } = Array;
|
|
670
|
+
const { create, keys } = Object;
|
|
671
|
+
const { isArray } = Array;
|
|
672
672
|
const { parse, stringify } = JSON;
|
|
673
673
|
|
|
674
|
-
/**
|
|
675
|
-
* Inspired by https://www.npmjs.com/package/hashlru
|
|
676
|
-
*/
|
|
677
|
-
class LRUCache {
|
|
678
|
-
constructor(limit) {
|
|
679
|
-
this.oldCache = new Map();
|
|
680
|
-
this.newCache = new Map();
|
|
681
|
-
this.size = 0;
|
|
682
|
-
this.limit = limit;
|
|
683
|
-
}
|
|
684
|
-
checkSize() {
|
|
685
|
-
if (this.size >= this.limit) {
|
|
686
|
-
this.size = 0;
|
|
687
|
-
this.oldCache = this.newCache;
|
|
688
|
-
this.newCache = new Map();
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
get(key) {
|
|
692
|
-
if (this.newCache.has(key)) {
|
|
693
|
-
return this.newCache.get(key);
|
|
694
|
-
}
|
|
695
|
-
else if (this.oldCache.has(key)) {
|
|
696
|
-
const value = this.oldCache.get(key);
|
|
697
|
-
this.oldCache.delete(key);
|
|
698
|
-
this.newCache.set(key, value);
|
|
699
|
-
this.size += 1;
|
|
700
|
-
this.checkSize();
|
|
701
|
-
return value;
|
|
702
|
-
}
|
|
703
|
-
return undefined;
|
|
704
|
-
}
|
|
705
|
-
set(key, value) {
|
|
706
|
-
if (this.newCache.has(key)) {
|
|
707
|
-
this.newCache.set(key, value);
|
|
708
|
-
}
|
|
709
|
-
else {
|
|
710
|
-
this.newCache.set(key, value);
|
|
711
|
-
this.size += 1;
|
|
712
|
-
this.checkSize();
|
|
713
|
-
}
|
|
714
|
-
}
|
|
715
|
-
delete(key) {
|
|
716
|
-
if (this.newCache.has(key)) {
|
|
717
|
-
this.newCache.delete(key);
|
|
718
|
-
this.size -= 1;
|
|
719
|
-
}
|
|
720
|
-
else if (this.oldCache.has(key)) {
|
|
721
|
-
this.oldCache.delete(key);
|
|
722
|
-
}
|
|
723
|
-
}
|
|
674
|
+
/**
|
|
675
|
+
* Inspired by https://www.npmjs.com/package/hashlru
|
|
676
|
+
*/
|
|
677
|
+
class LRUCache {
|
|
678
|
+
constructor(limit) {
|
|
679
|
+
this.oldCache = new Map();
|
|
680
|
+
this.newCache = new Map();
|
|
681
|
+
this.size = 0;
|
|
682
|
+
this.limit = limit;
|
|
683
|
+
}
|
|
684
|
+
checkSize() {
|
|
685
|
+
if (this.size >= this.limit) {
|
|
686
|
+
this.size = 0;
|
|
687
|
+
this.oldCache = this.newCache;
|
|
688
|
+
this.newCache = new Map();
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
get(key) {
|
|
692
|
+
if (this.newCache.has(key)) {
|
|
693
|
+
return this.newCache.get(key);
|
|
694
|
+
}
|
|
695
|
+
else if (this.oldCache.has(key)) {
|
|
696
|
+
const value = this.oldCache.get(key);
|
|
697
|
+
this.oldCache.delete(key);
|
|
698
|
+
this.newCache.set(key, value);
|
|
699
|
+
this.size += 1;
|
|
700
|
+
this.checkSize();
|
|
701
|
+
return value;
|
|
702
|
+
}
|
|
703
|
+
return undefined;
|
|
704
|
+
}
|
|
705
|
+
set(key, value) {
|
|
706
|
+
if (this.newCache.has(key)) {
|
|
707
|
+
this.newCache.set(key, value);
|
|
708
|
+
}
|
|
709
|
+
else {
|
|
710
|
+
this.newCache.set(key, value);
|
|
711
|
+
this.size += 1;
|
|
712
|
+
this.checkSize();
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
delete(key) {
|
|
716
|
+
if (this.newCache.has(key)) {
|
|
717
|
+
this.newCache.delete(key);
|
|
718
|
+
this.size -= 1;
|
|
719
|
+
}
|
|
720
|
+
else if (this.oldCache.has(key)) {
|
|
721
|
+
this.oldCache.delete(key);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
724
|
}
|
|
725
725
|
|
|
726
|
-
/**
|
|
727
|
-
* A deterministic JSON stringify implementation. Heavily adapted from https://github.com/epoberezkin/fast-json-stable-stringify.
|
|
728
|
-
* This is needed because insertion order for JSON.stringify(object) affects output:
|
|
729
|
-
* JSON.stringify({a: 1, b: 2})
|
|
730
|
-
* "{"a":1,"b":2}"
|
|
731
|
-
* JSON.stringify({b: 2, a: 1})
|
|
732
|
-
* "{"b":2,"a":1}"
|
|
733
|
-
* Modified from the apex implementation to sort arrays non-destructively.
|
|
734
|
-
* @param data Data to be JSON-stringified.
|
|
735
|
-
* @returns JSON.stringified value with consistent ordering of keys.
|
|
736
|
-
*/
|
|
737
|
-
function stableJSONStringify(node) {
|
|
738
|
-
// This is for Date values.
|
|
739
|
-
if (node && node.toJSON && typeof node.toJSON === 'function') {
|
|
740
|
-
// eslint-disable-next-line no-param-reassign
|
|
741
|
-
node = node.toJSON();
|
|
742
|
-
}
|
|
743
|
-
if (node === undefined) {
|
|
744
|
-
return;
|
|
745
|
-
}
|
|
746
|
-
if (typeof node === 'number') {
|
|
747
|
-
return isFinite(node) ? '' + node : 'null';
|
|
748
|
-
}
|
|
749
|
-
if (typeof node !== 'object') {
|
|
750
|
-
return stringify(node);
|
|
751
|
-
}
|
|
752
|
-
let i;
|
|
753
|
-
let out;
|
|
754
|
-
if (isArray(node)) {
|
|
755
|
-
// copy any array before sorting so we don't mutate the object.
|
|
756
|
-
// eslint-disable-next-line no-param-reassign
|
|
757
|
-
node = node.slice(0).sort();
|
|
758
|
-
out = '[';
|
|
759
|
-
for (i = 0; i < node.length; i++) {
|
|
760
|
-
if (i) {
|
|
761
|
-
out += ',';
|
|
762
|
-
}
|
|
763
|
-
out += stableJSONStringify(node[i]) || 'null';
|
|
764
|
-
}
|
|
765
|
-
return out + ']';
|
|
766
|
-
}
|
|
767
|
-
if (node === null) {
|
|
768
|
-
return 'null';
|
|
769
|
-
}
|
|
770
|
-
const keys$1 = keys(node).sort();
|
|
771
|
-
out = '';
|
|
772
|
-
for (i = 0; i < keys$1.length; i++) {
|
|
773
|
-
const key = keys$1[i];
|
|
774
|
-
const value = stableJSONStringify(node[key]);
|
|
775
|
-
if (!value) {
|
|
776
|
-
continue;
|
|
777
|
-
}
|
|
778
|
-
if (out) {
|
|
779
|
-
out += ',';
|
|
780
|
-
}
|
|
781
|
-
out += stringify(key) + ':' + value;
|
|
782
|
-
}
|
|
783
|
-
return '{' + out + '}';
|
|
784
|
-
}
|
|
785
|
-
function isPromise(value) {
|
|
786
|
-
// check for Thenable due to test frameworks using custom Promise impls
|
|
787
|
-
return value !== null && value !== undefined && typeof value.then === 'function';
|
|
788
|
-
}
|
|
789
|
-
function isAdapterError(error) {
|
|
790
|
-
if (typeof error !== 'string') {
|
|
791
|
-
return false;
|
|
792
|
-
}
|
|
793
|
-
const parsedError = parse(error);
|
|
794
|
-
return parsedError.errorType !== undefined && parsedError.errorType === 'adapterError';
|
|
795
|
-
}
|
|
796
|
-
function throttle(callback, ms) {
|
|
797
|
-
let waiting = false;
|
|
798
|
-
return () => {
|
|
799
|
-
if (!waiting) {
|
|
800
|
-
callback();
|
|
801
|
-
waiting = true;
|
|
802
|
-
setTimeout(() => (waiting = false), ms);
|
|
803
|
-
}
|
|
804
|
-
};
|
|
726
|
+
/**
|
|
727
|
+
* A deterministic JSON stringify implementation. Heavily adapted from https://github.com/epoberezkin/fast-json-stable-stringify.
|
|
728
|
+
* This is needed because insertion order for JSON.stringify(object) affects output:
|
|
729
|
+
* JSON.stringify({a: 1, b: 2})
|
|
730
|
+
* "{"a":1,"b":2}"
|
|
731
|
+
* JSON.stringify({b: 2, a: 1})
|
|
732
|
+
* "{"b":2,"a":1}"
|
|
733
|
+
* Modified from the apex implementation to sort arrays non-destructively.
|
|
734
|
+
* @param data Data to be JSON-stringified.
|
|
735
|
+
* @returns JSON.stringified value with consistent ordering of keys.
|
|
736
|
+
*/
|
|
737
|
+
function stableJSONStringify(node) {
|
|
738
|
+
// This is for Date values.
|
|
739
|
+
if (node && node.toJSON && typeof node.toJSON === 'function') {
|
|
740
|
+
// eslint-disable-next-line no-param-reassign
|
|
741
|
+
node = node.toJSON();
|
|
742
|
+
}
|
|
743
|
+
if (node === undefined) {
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
if (typeof node === 'number') {
|
|
747
|
+
return isFinite(node) ? '' + node : 'null';
|
|
748
|
+
}
|
|
749
|
+
if (typeof node !== 'object') {
|
|
750
|
+
return stringify(node);
|
|
751
|
+
}
|
|
752
|
+
let i;
|
|
753
|
+
let out;
|
|
754
|
+
if (isArray(node)) {
|
|
755
|
+
// copy any array before sorting so we don't mutate the object.
|
|
756
|
+
// eslint-disable-next-line no-param-reassign
|
|
757
|
+
node = node.slice(0).sort();
|
|
758
|
+
out = '[';
|
|
759
|
+
for (i = 0; i < node.length; i++) {
|
|
760
|
+
if (i) {
|
|
761
|
+
out += ',';
|
|
762
|
+
}
|
|
763
|
+
out += stableJSONStringify(node[i]) || 'null';
|
|
764
|
+
}
|
|
765
|
+
return out + ']';
|
|
766
|
+
}
|
|
767
|
+
if (node === null) {
|
|
768
|
+
return 'null';
|
|
769
|
+
}
|
|
770
|
+
const keys$1 = keys(node).sort();
|
|
771
|
+
out = '';
|
|
772
|
+
for (i = 0; i < keys$1.length; i++) {
|
|
773
|
+
const key = keys$1[i];
|
|
774
|
+
const value = stableJSONStringify(node[key]);
|
|
775
|
+
if (!value) {
|
|
776
|
+
continue;
|
|
777
|
+
}
|
|
778
|
+
if (out) {
|
|
779
|
+
out += ',';
|
|
780
|
+
}
|
|
781
|
+
out += stringify(key) + ':' + value;
|
|
782
|
+
}
|
|
783
|
+
return '{' + out + '}';
|
|
784
|
+
}
|
|
785
|
+
function isPromise(value) {
|
|
786
|
+
// check for Thenable due to test frameworks using custom Promise impls
|
|
787
|
+
return value !== null && value !== undefined && typeof value.then === 'function';
|
|
788
|
+
}
|
|
789
|
+
function isAdapterError(error) {
|
|
790
|
+
if (typeof error !== 'string') {
|
|
791
|
+
return false;
|
|
792
|
+
}
|
|
793
|
+
const parsedError = parse(error);
|
|
794
|
+
return parsedError.errorType !== undefined && parsedError.errorType === 'adapterError';
|
|
795
|
+
}
|
|
796
|
+
function throttle(callback, ms) {
|
|
797
|
+
let waiting = false;
|
|
798
|
+
return () => {
|
|
799
|
+
if (!waiting) {
|
|
800
|
+
callback();
|
|
801
|
+
waiting = true;
|
|
802
|
+
setTimeout(() => (waiting = false), ms);
|
|
803
|
+
}
|
|
804
|
+
};
|
|
805
805
|
}
|
|
806
806
|
|
|
807
|
-
const NAMESPACE = 'lds';
|
|
808
|
-
const APEX_ADAPTER_NAME = 'getApex';
|
|
809
|
-
const NORMALIZED_APEX_ADAPTER_NAME = createMetricsKey('Apex', APEX_ADAPTER_NAME);
|
|
810
|
-
const GRAPHQL_ADAPTER_NAME = 'graphQL';
|
|
811
|
-
const GRAPHQL_RECORDS_KEY = 'GraphQL::graphql__uiapi__query';
|
|
812
|
-
const ldsInstrumentation = getInstrumentation(NAMESPACE);
|
|
813
|
-
const observabilityInstrumentation = getInstrumentation(OBSERVABILITY_NAMESPACE);
|
|
814
|
-
class Instrumentation {
|
|
815
|
-
/**
|
|
816
|
-
* Injected to LDS for Luvio specific instrumentation.
|
|
817
|
-
*
|
|
818
|
-
* @param context The transaction context.
|
|
819
|
-
*/
|
|
820
|
-
instrumentLuvio(_context) {
|
|
821
|
-
// TODO [W-9783151]: refactor luvio.instrument to not require this class
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
|
-
/**
|
|
825
|
-
* Provide this method for the instrument option for a Luvio instance.
|
|
826
|
-
* @param context The transaction context.
|
|
827
|
-
*/
|
|
828
|
-
function instrumentLuvio(context) {
|
|
829
|
-
if (isAdapterUnfulfilledError(context)) {
|
|
830
|
-
// We are consolidating all apex adapter instrumentation calls under a single key
|
|
831
|
-
const normalizedContext = {
|
|
832
|
-
...context,
|
|
833
|
-
adapterName: normalizeAdapterName(context.adapterName),
|
|
834
|
-
};
|
|
835
|
-
incrementAdapterRequestErrorCount(normalizedContext);
|
|
836
|
-
logAdapterRequestError(normalizedContext);
|
|
837
|
-
}
|
|
838
|
-
}
|
|
839
|
-
/**
|
|
840
|
-
* Returns whether or not this is an AdapterUnfulfilledError.
|
|
841
|
-
* @param context The transaction context.
|
|
842
|
-
* @returns Whether or not this is an AdapterUnfulfilledError.
|
|
843
|
-
*/
|
|
844
|
-
function isAdapterUnfulfilledError(context) {
|
|
845
|
-
return context[ADAPTER_UNFULFILLED_ERROR] === true;
|
|
846
|
-
}
|
|
847
|
-
/**
|
|
848
|
-
* W-8620679
|
|
849
|
-
* Increment the counter for an UnfulfilledSnapshotError coming from luvio
|
|
850
|
-
*
|
|
851
|
-
* @param context The transaction context.
|
|
852
|
-
*/
|
|
853
|
-
function incrementAdapterRequestErrorCount(context) {
|
|
854
|
-
const adapterRequestErrorCounter = createMetricsKey(ADAPTER_ERROR_COUNT_METRIC_NAME, context.adapterName);
|
|
855
|
-
observabilityInstrumentation.incrementCounter(adapterRequestErrorCounter);
|
|
856
|
-
observabilityInstrumentation.incrementCounter(TOTAL_ADAPTER_ERROR_COUNT);
|
|
857
|
-
}
|
|
858
|
-
/**
|
|
859
|
-
* W-10495632
|
|
860
|
-
* Logs the missing paths and/or links associated with the UnfulfilledSnapshotError.
|
|
861
|
-
*
|
|
862
|
-
* @param context The transaction context.
|
|
863
|
-
*/
|
|
864
|
-
function logAdapterRequestError(context) {
|
|
865
|
-
ldsInstrumentation.error(ADAPTER_UNFULFILLED_ERROR, adapterUnfulfilledErrorSchema, {
|
|
866
|
-
adapter: context.adapterName,
|
|
867
|
-
missing_paths: keys(context.missingPaths),
|
|
868
|
-
missing_links: keys(context.missingLinks),
|
|
869
|
-
});
|
|
870
|
-
}
|
|
871
|
-
/**
|
|
872
|
-
* Increment the counter based on the cache policy type for an adapter call
|
|
873
|
-
*
|
|
874
|
-
* @param requestContext Adapter request context that includes cache policy
|
|
875
|
-
*/
|
|
876
|
-
function incrementAdapterCachePolicyType(requestContext) {
|
|
877
|
-
const cachePolicy = requestContext && requestContext.cachePolicy && requestContext.cachePolicy.type;
|
|
878
|
-
if (cachePolicy !== undefined) {
|
|
879
|
-
ldsInstrumentation.incrementCounter(CACHE_POLICY_COUNTERS[cachePolicy], 1);
|
|
880
|
-
return;
|
|
881
|
-
}
|
|
882
|
-
ldsInstrumentation.incrementCounter(CACHE_POLICY_UNDEFINED_COUNTER, 1);
|
|
883
|
-
}
|
|
884
|
-
/**
|
|
885
|
-
* Increment the counter based on missing recordTypeId in a record ingest
|
|
886
|
-
*
|
|
887
|
-
* @param apiName incoming API name for bad data
|
|
888
|
-
*/
|
|
889
|
-
function incrementRecordTypeIdIsNullCount(apiName) {
|
|
890
|
-
const adapterRequestErrorCounter = createMetricsKey(RECORD_TYPE_ID_IS_NULL_COUNT, apiName);
|
|
891
|
-
observabilityInstrumentation.incrementCounter(adapterRequestErrorCounter);
|
|
892
|
-
}
|
|
893
|
-
/**
|
|
894
|
-
* Logs when adapter requests come in. If we have subsequent cache misses on a given config, beyond its TTL then log the duration to metrics.
|
|
895
|
-
* Backed by an LRU Cache implementation to prevent too many record entries from being stored in-memory.
|
|
896
|
-
* @param name The wire adapter name.
|
|
897
|
-
* @param config The config passed into wire adapter.
|
|
898
|
-
* @param currentCacheMissTimestamp Timestamp for when the request was made.
|
|
899
|
-
* @param ttl TTL for the wire adapter.
|
|
900
|
-
* @param durationMetricName Name for duration metric.
|
|
901
|
-
* @param counterMetricName Name for counter metric.
|
|
902
|
-
*/
|
|
903
|
-
const adapterCacheMisses = new LRUCache(250);
|
|
904
|
-
function logAdapterCacheMissOutOfTtlDuration(name, config, currentCacheMissTimestamp, ttl, counterMetricName, durationMetricName) {
|
|
905
|
-
const configKey = `${name}:${stableJSONStringify(config)}`;
|
|
906
|
-
const existingCacheMissTimestamp = adapterCacheMisses.get(configKey);
|
|
907
|
-
adapterCacheMisses.set(configKey, currentCacheMissTimestamp);
|
|
908
|
-
if (existingCacheMissTimestamp !== undefined) {
|
|
909
|
-
const duration = currentCacheMissTimestamp - existingCacheMissTimestamp;
|
|
910
|
-
if (duration > ttl) {
|
|
911
|
-
ldsInstrumentation.incrementCounter(counterMetricName, 1);
|
|
912
|
-
ldsInstrumentation.trackValue(durationMetricName, duration);
|
|
913
|
-
}
|
|
914
|
-
}
|
|
915
|
-
}
|
|
916
|
-
function instrumentAdapter(adapter, metadata, adapterInstrumentationOptions) {
|
|
917
|
-
const { apiFamily, name, ttl } = metadata;
|
|
918
|
-
let trackL1Hits = false;
|
|
919
|
-
let trackL2Hits = false;
|
|
920
|
-
let reportObserver = undefined;
|
|
921
|
-
if (adapterInstrumentationOptions !== undefined) {
|
|
922
|
-
({ trackL1Hits, trackL2Hits, reportObserver } = adapterInstrumentationOptions);
|
|
923
|
-
}
|
|
924
|
-
const adapterName = normalizeAdapterName(name, apiFamily);
|
|
925
|
-
/**
|
|
926
|
-
* W-8076905
|
|
927
|
-
* Dynamically generated metric. Simple counter for all requests made by this adapter.
|
|
928
|
-
*/
|
|
929
|
-
const wireAdapterRequestMetric = createMetricsKey(ADAPTER_INVOCATION_COUNT_METRIC_NAME, adapterName);
|
|
930
|
-
/**
|
|
931
|
-
* W-6981216
|
|
932
|
-
* Dynamically generated metric. Simple counter for cache hits by adapter name.
|
|
933
|
-
*/
|
|
934
|
-
const cacheHitCountByAdapterMetric = createMetricsKey(ADAPTER_CACHE_HIT_COUNT_METRIC_NAME, adapterName);
|
|
935
|
-
/**
|
|
936
|
-
* W-10490326
|
|
937
|
-
* Dynamically generated metric. Simple counter for L2 cache hits by adapter name.
|
|
938
|
-
*/
|
|
939
|
-
const l2CacheHitCountByAdapterMetric = createMetricsKey(ADAPTER_CACHE_HIT_L2_COUNT_METRIC_NAME, adapterName);
|
|
940
|
-
/**
|
|
941
|
-
* W-7404607
|
|
942
|
-
* Dynamically generated metric. Timer for cache hits by adapter name.
|
|
943
|
-
*/
|
|
944
|
-
const cacheHitDurationByAdapterMetric = createMetricsKey(ADAPTER_CACHE_HIT_DURATION_METRIC_NAME, adapterName);
|
|
945
|
-
/**
|
|
946
|
-
* W-10490326
|
|
947
|
-
* Dynamically generated metric. Timer for L2 cache hits by adapter name.
|
|
948
|
-
*/
|
|
949
|
-
const l2CacheHitDurationByAdapterMetric = createMetricsKey(ADAPTER_CACHE_HIT_L2_DURATION_METRIC_NAME, adapterName);
|
|
950
|
-
/**
|
|
951
|
-
* W-6981216
|
|
952
|
-
* Dynamically generated metric. Simple counter for cache misses by adapter name.
|
|
953
|
-
*/
|
|
954
|
-
const cacheMissCountByAdapterMetric = createMetricsKey(ADAPTER_CACHE_MISS_COUNT_METRIC_NAME, adapterName);
|
|
955
|
-
/**
|
|
956
|
-
* W-7404607
|
|
957
|
-
* Dynamically generated metric. Timer for cache hits by adapter name.
|
|
958
|
-
*/
|
|
959
|
-
const cacheMissDurationByAdapterMetric = createMetricsKey(ADAPTER_CACHE_MISS_DURATION_METRIC_NAME, adapterName);
|
|
960
|
-
/**
|
|
961
|
-
* W-7376275
|
|
962
|
-
* Dynamically generated metric. Measures the amount of time it takes for LDS to get another cache miss on
|
|
963
|
-
* a request we've made in the past.
|
|
964
|
-
* Request Record 1 -> Record 2 -> Back to Record 1 outside of TTL is an example of when this metric will fire.
|
|
965
|
-
*/
|
|
966
|
-
const cacheMissOutOfTtlDurationByAdapterMetric = createMetricsKey(ADAPTER_CACHE_MISS_OUT_OF_TTL_DURATION_METRIC_NAME, adapterName);
|
|
967
|
-
const cacheMissOutOfTtlCountByAdapterMetric = createMetricsKey(ADAPTER_CACHE_MISS_OUT_OF_TTL_COUNT_METRIC_NAME, adapterName);
|
|
968
|
-
const instrumentedAdapter = (config, requestContext) => {
|
|
969
|
-
// increment adapter request metrics
|
|
970
|
-
observabilityInstrumentation.incrementCounter(wireAdapterRequestMetric, 1);
|
|
971
|
-
observabilityInstrumentation.incrementCounter(TOTAL_ADAPTER_REQUEST_SUCCESS_COUNT, 1);
|
|
972
|
-
// increment cache policy metrics
|
|
973
|
-
incrementAdapterCachePolicyType(requestContext);
|
|
974
|
-
// start collecting
|
|
975
|
-
const activity = ldsInstrumentation.startActivity(adapterName);
|
|
976
|
-
return runAdapterWithReport(metadata.name, adapter, config, requestContext || {}, (report) => {
|
|
977
|
-
const { executionTime } = report;
|
|
978
|
-
switch (report.result) {
|
|
979
|
-
case 'l1-hit': {
|
|
980
|
-
ldsInstrumentation.incrementCounter(ADAPTER_CACHE_HIT_COUNT_METRIC_NAME, 1);
|
|
981
|
-
ldsInstrumentation.incrementCounter(cacheHitCountByAdapterMetric, 1);
|
|
982
|
-
ldsInstrumentation.trackValue(cacheHitDurationByAdapterMetric, executionTime);
|
|
983
|
-
if (trackL1Hits) {
|
|
984
|
-
activity.stop('l1-hit');
|
|
985
|
-
}
|
|
986
|
-
else {
|
|
987
|
-
activity.discard();
|
|
988
|
-
}
|
|
989
|
-
break;
|
|
990
|
-
}
|
|
991
|
-
case 'l2-hit': {
|
|
992
|
-
let tags = undefined;
|
|
993
|
-
if (report.stale === true) {
|
|
994
|
-
tags = { [STALE_TAG]: true };
|
|
995
|
-
}
|
|
996
|
-
ldsInstrumentation.incrementCounter(ADAPTER_CACHE_HIT_L2_COUNT_METRIC_NAME, 1, undefined, tags);
|
|
997
|
-
ldsInstrumentation.incrementCounter(l2CacheHitCountByAdapterMetric, 1, undefined, tags);
|
|
998
|
-
ldsInstrumentation.trackValue(l2CacheHitDurationByAdapterMetric, executionTime, undefined, tags);
|
|
999
|
-
if (trackL2Hits) {
|
|
1000
|
-
activity.stop('l2-hit');
|
|
1001
|
-
}
|
|
1002
|
-
else {
|
|
1003
|
-
activity.discard();
|
|
1004
|
-
}
|
|
1005
|
-
break;
|
|
1006
|
-
}
|
|
1007
|
-
case 'cache-miss':
|
|
1008
|
-
{
|
|
1009
|
-
ldsInstrumentation.trackValue(cacheMissDurationByAdapterMetric, executionTime);
|
|
1010
|
-
// TODO [W-10484306]: Remove typecasting after this type bug is solved
|
|
1011
|
-
activity.stop('cache-miss');
|
|
1012
|
-
ldsInstrumentation.incrementCounter(ADAPTER_CACHE_MISS_COUNT_METRIC_NAME, 1);
|
|
1013
|
-
ldsInstrumentation.incrementCounter(cacheMissCountByAdapterMetric, 1);
|
|
1014
|
-
if (ttl !== undefined) {
|
|
1015
|
-
logAdapterCacheMissOutOfTtlDuration(adapterName, config, Date.now(), ttl, cacheMissOutOfTtlCountByAdapterMetric, cacheMissOutOfTtlDurationByAdapterMetric);
|
|
1016
|
-
}
|
|
1017
|
-
}
|
|
1018
|
-
break;
|
|
1019
|
-
case 'error':
|
|
1020
|
-
{
|
|
1021
|
-
const error = report.error;
|
|
1022
|
-
// We are capturing userland transient errors through here, and
|
|
1023
|
-
// there is a chance that these errors could contain PII, as seen in W-12224448.
|
|
1024
|
-
// Log AdapterErrors only
|
|
1025
|
-
if (isAdapterError(error)) {
|
|
1026
|
-
activity.error(error);
|
|
1027
|
-
}
|
|
1028
|
-
// TODO [W-10484306]: Remove typecasting after this type bug is solved
|
|
1029
|
-
activity.stop('error');
|
|
1030
|
-
}
|
|
1031
|
-
break;
|
|
1032
|
-
case 'exception':
|
|
1033
|
-
case 'invalid-config':
|
|
1034
|
-
{
|
|
1035
|
-
activity.discard();
|
|
1036
|
-
}
|
|
1037
|
-
break;
|
|
1038
|
-
default: {
|
|
1039
|
-
if ('production' !== process.env.NODE_ENV) {
|
|
1040
|
-
throw new Error(`unsupported adapter result`);
|
|
1041
|
-
}
|
|
1042
|
-
}
|
|
1043
|
-
}
|
|
1044
|
-
if (reportObserver !== undefined) {
|
|
1045
|
-
reportObserver(report);
|
|
1046
|
-
}
|
|
1047
|
-
});
|
|
1048
|
-
};
|
|
1049
|
-
// Set the name property on the function for debugging purposes.
|
|
1050
|
-
Object.defineProperty(instrumentedAdapter, 'name', {
|
|
1051
|
-
value: name + '__instrumented',
|
|
1052
|
-
});
|
|
1053
|
-
return isGraphqlAdapter(name) === true
|
|
1054
|
-
? instrumentGraphqlAdapter(instrumentedAdapter)
|
|
1055
|
-
: instrumentedAdapter;
|
|
1056
|
-
}
|
|
1057
|
-
/**
|
|
1058
|
-
* Any graphql get adapter specific instrumentation that we need to log
|
|
1059
|
-
* @param snapshot from either in-memory or built after a network hit
|
|
1060
|
-
*/
|
|
1061
|
-
function logGraphqlMetrics(snapshot) {
|
|
1062
|
-
// We have both data and error in the returned response
|
|
1063
|
-
const { data: snapshotData } = snapshot;
|
|
1064
|
-
if (snapshotData &&
|
|
1065
|
-
snapshotData.data &&
|
|
1066
|
-
keys(snapshotData.data).length > 0 &&
|
|
1067
|
-
snapshotData.errors &&
|
|
1068
|
-
snapshotData.errors.length > 0) {
|
|
1069
|
-
ldsInstrumentation.incrementCounter(GET_GRAPHQL_RESPONSE_MIXED);
|
|
1070
|
-
}
|
|
1071
|
-
if (snapshotData.errors && snapshotData.errors.length > 0) {
|
|
1072
|
-
// W-12293528 GraphQL metrics
|
|
1073
|
-
// log counts of returned errors
|
|
1074
|
-
let validationErrorCount = 0;
|
|
1075
|
-
let syntaxErrorCount = 0;
|
|
1076
|
-
let otherErrorCount = 0;
|
|
1077
|
-
// conslidate instrumentation calls with # of occurrences
|
|
1078
|
-
snapshotData.errors.forEach((e) => {
|
|
1079
|
-
if (e.message && e.message.startsWith('Validation error')) {
|
|
1080
|
-
validationErrorCount++;
|
|
1081
|
-
}
|
|
1082
|
-
else if (e.message && e.message.startsWith('Invalid Syntax')) {
|
|
1083
|
-
syntaxErrorCount++;
|
|
1084
|
-
}
|
|
1085
|
-
else {
|
|
1086
|
-
otherErrorCount++;
|
|
1087
|
-
}
|
|
1088
|
-
});
|
|
1089
|
-
if (validationErrorCount > 0) {
|
|
1090
|
-
ldsInstrumentation.incrementCounter(GRAPHQL_QUERY_VALIDATION_ERROR_COUNT, validationErrorCount);
|
|
1091
|
-
}
|
|
1092
|
-
if (syntaxErrorCount > 0) {
|
|
1093
|
-
ldsInstrumentation.incrementCounter(GRAPHQL_QUERY_SYNTAX_ERROR_COUNT, syntaxErrorCount);
|
|
1094
|
-
}
|
|
1095
|
-
if (otherErrorCount > 0) {
|
|
1096
|
-
ldsInstrumentation.incrementCounter(GRAPHQL_QUERY_OTHER_ERROR_COUNT, otherErrorCount);
|
|
1097
|
-
}
|
|
1098
|
-
}
|
|
1099
|
-
}
|
|
1100
|
-
/**
|
|
1101
|
-
* Wraps methods to collect runtime performance using o11y's trackValue API
|
|
1102
|
-
* @param obj Object instance containing the methods to instrument
|
|
1103
|
-
* @param methods array containing objects with keys for the method name and the metric key to use in o11y
|
|
1104
|
-
*/
|
|
1105
|
-
function instrumentMethods(obj, methods) {
|
|
1106
|
-
for (let i = 0, len = methods.length; i < len; i++) {
|
|
1107
|
-
const { methodName, metricKey } = methods[i];
|
|
1108
|
-
const originalMethod = obj[methodName];
|
|
1109
|
-
obj[methodName] = function (...args) {
|
|
1110
|
-
const startTime = Date.now();
|
|
1111
|
-
try {
|
|
1112
|
-
const res = originalMethod.call(this, ...args);
|
|
1113
|
-
const executionTime = Date.now() - startTime;
|
|
1114
|
-
// handle async resolved/rejected
|
|
1115
|
-
if (isPromise(res)) {
|
|
1116
|
-
res.then(() => {
|
|
1117
|
-
ldsInstrumentation.trackValue(metricKey, Date.now() - startTime);
|
|
1118
|
-
}).catch((_error) => {
|
|
1119
|
-
ldsInstrumentation.trackValue(metricKey, Date.now() - startTime, true);
|
|
1120
|
-
});
|
|
1121
|
-
}
|
|
1122
|
-
else {
|
|
1123
|
-
// handle synchronous success
|
|
1124
|
-
ldsInstrumentation.trackValue(metricKey, executionTime);
|
|
1125
|
-
}
|
|
1126
|
-
return res;
|
|
1127
|
-
}
|
|
1128
|
-
catch (error) {
|
|
1129
|
-
// handle synchronous throw
|
|
1130
|
-
ldsInstrumentation.trackValue(metricKey, Date.now() - startTime, true);
|
|
1131
|
-
// rethrow error
|
|
1132
|
-
throw error;
|
|
1133
|
-
}
|
|
1134
|
-
};
|
|
1135
|
-
}
|
|
1136
|
-
}
|
|
1137
|
-
function createMetricsKey(name, unit) {
|
|
1138
|
-
let metricName = name;
|
|
1139
|
-
if (unit) {
|
|
1140
|
-
metricName = metricName + '.' + unit;
|
|
1141
|
-
}
|
|
1142
|
-
return metricName;
|
|
1143
|
-
}
|
|
1144
|
-
/**
|
|
1145
|
-
* Returns whether adapter is an Apex one or not.
|
|
1146
|
-
* @param adapterName The name of the adapter.
|
|
1147
|
-
*/
|
|
1148
|
-
function isApexAdapter(adapterName) {
|
|
1149
|
-
return adapterName.indexOf(APEX_ADAPTER_NAME) > -1;
|
|
1150
|
-
}
|
|
1151
|
-
/**
|
|
1152
|
-
* Returns boolean whether adapter is a graphQL one or not.
|
|
1153
|
-
* @param adapterName The name of the adapter.
|
|
1154
|
-
*/
|
|
1155
|
-
function isGraphqlAdapter(adapterName) {
|
|
1156
|
-
return adapterName === GRAPHQL_ADAPTER_NAME;
|
|
1157
|
-
}
|
|
1158
|
-
/**
|
|
1159
|
-
* Normalizes getApex adapter names to `Apex.getApex`. Non-Apex adapters will be prefixed with
|
|
1160
|
-
* API family, if supplied. Example: `UiApi.getRecord`.
|
|
1161
|
-
*
|
|
1162
|
-
* Note: If you are adding additional logging that can come from getApex adapter contexts that provide
|
|
1163
|
-
* the full getApex adapter name (i.e. getApex_[namespace]_[class]_[function]_[continuation]),
|
|
1164
|
-
* ensure to call this method to normalize all logging to 'getApex'. This
|
|
1165
|
-
* is because Argus has a 50k key cardinality limit. More context: W-8379680.
|
|
1166
|
-
*
|
|
1167
|
-
* @param adapterName The name of the adapter.
|
|
1168
|
-
* @param apiFamily The API family of the adapter.
|
|
1169
|
-
*/
|
|
1170
|
-
function normalizeAdapterName(adapterName, apiFamily) {
|
|
1171
|
-
if (isApexAdapter(adapterName)) {
|
|
1172
|
-
return NORMALIZED_APEX_ADAPTER_NAME;
|
|
1173
|
-
}
|
|
1174
|
-
return apiFamily ? `${apiFamily}.${adapterName}` : adapterName;
|
|
1175
|
-
}
|
|
1176
|
-
/**
|
|
1177
|
-
* Calls instrumentation/service telemetry counter
|
|
1178
|
-
* @param name Name of the metric
|
|
1179
|
-
* @param value number to increment by, if undefined increment by 1
|
|
1180
|
-
*/
|
|
1181
|
-
function incrementCounterMetric(name, number) {
|
|
1182
|
-
ldsInstrumentation.incrementCounter(name, number);
|
|
1183
|
-
}
|
|
1184
|
-
/**
|
|
1185
|
-
* Calls instrumentation/service telemetry percentileHistogram
|
|
1186
|
-
* @param name Name of the metric
|
|
1187
|
-
* @param value number used to update the percentileHistogram
|
|
1188
|
-
*/
|
|
1189
|
-
function updatePercentileHistogramMetric(name, value) {
|
|
1190
|
-
ldsInstrumentation.trackValue(name, value);
|
|
1191
|
-
}
|
|
1192
|
-
function setAggregateUiChunkCountMetric(chunkCount) {
|
|
1193
|
-
updatePercentileHistogramMetric(AGGREGATE_UI_CHUNK_COUNT, chunkCount);
|
|
1194
|
-
}
|
|
1195
|
-
function incrementGetRecordNormalInvokeCount() {
|
|
1196
|
-
incrementCounterMetric(GET_RECORD_NORMAL_INVOKE_COUNT);
|
|
1197
|
-
}
|
|
1198
|
-
function incrementGetRecordAggregateInvokeCount() {
|
|
1199
|
-
incrementCounterMetric(GET_RECORD_AGGREGATE_INVOKE_COUNT);
|
|
1200
|
-
}
|
|
1201
|
-
function incrementGetRecordNotifyChangeAllowCount() {
|
|
1202
|
-
incrementCounterMetric(GET_RECORD_NOTIFY_CHANGE_ALLOW_COUNT);
|
|
1203
|
-
}
|
|
1204
|
-
function incrementGetRecordNotifyChangeDropCount() {
|
|
1205
|
-
incrementCounterMetric(GET_RECORD_NOTIFY_CHANGE_DROP_COUNT);
|
|
1206
|
-
}
|
|
1207
|
-
function incrementNotifyRecordUpdateAvailableAllowCount() {
|
|
1208
|
-
incrementCounterMetric(NOTIFY_RECORD_UPDATE_AVAILABLE_ALLOW_COUNT);
|
|
1209
|
-
}
|
|
1210
|
-
function incrementNotifyRecordUpdateAvailableDropCount() {
|
|
1211
|
-
incrementCounterMetric(NOTIFY_RECORD_UPDATE_AVAILABLE_DROP_COUNT);
|
|
1212
|
-
}
|
|
1213
|
-
function incrementNetworkRateLimitExceededCount() {
|
|
1214
|
-
incrementCounterMetric(NETWORK_RATE_LIMIT_EXCEEDED_COUNT);
|
|
1215
|
-
}
|
|
1216
|
-
function instrumentStoreTrimTask(callback) {
|
|
1217
|
-
return () => {
|
|
1218
|
-
ldsInstrumentation.incrementCounter(STORE_TRIM_TASK_COUNT);
|
|
1219
|
-
const startTime = Date.now();
|
|
1220
|
-
const res = callback();
|
|
1221
|
-
ldsInstrumentation.trackValue(STORE_TRIM_TASK_DURATION, Date.now() - startTime);
|
|
1222
|
-
// TODO [W-10060579]: replace record count per trim task with metric
|
|
1223
|
-
return res;
|
|
1224
|
-
};
|
|
1225
|
-
}
|
|
1226
|
-
function setStoreScheduler(store) {
|
|
1227
|
-
const originalScheduler = store.scheduler;
|
|
1228
|
-
store.scheduler = (callback, done) => {
|
|
1229
|
-
originalScheduler(instrumentStoreTrimTask(callback), done);
|
|
1230
|
-
};
|
|
1231
|
-
}
|
|
1232
|
-
function instrumentStoreStatsCallback(store) {
|
|
1233
|
-
return () => {
|
|
1234
|
-
const { snapshotSubscriptions, watchSubscriptions } = store;
|
|
1235
|
-
const records = store.fallbackStringKeyInMemoryStore.records;
|
|
1236
|
-
updatePercentileHistogramMetric(STORE_SIZE_COUNT, keys(records).length);
|
|
1237
|
-
if (GRAPHQL_RECORDS_KEY in records) {
|
|
1238
|
-
const graphQLRecordSize = keys(records[GRAPHQL_RECORDS_KEY]).length;
|
|
1239
|
-
updatePercentileHistogramMetric(STORE_GRAPHQL_SIZE_COUNT, graphQLRecordSize);
|
|
1240
|
-
}
|
|
1241
|
-
updatePercentileHistogramMetric(STORE_SNAPSHOT_SUBSCRIPTIONS_COUNT, keys(snapshotSubscriptions).length);
|
|
1242
|
-
updatePercentileHistogramMetric(STORE_WATCH_SUBSCRIPTIONS_COUNT, keys(watchSubscriptions).length);
|
|
1243
|
-
};
|
|
1244
|
-
}
|
|
1245
|
-
/**
|
|
1246
|
-
* Collects additional store statistics by tying its periodic,
|
|
1247
|
-
* point-in-time data collection with a luvio method
|
|
1248
|
-
* @param luvio
|
|
1249
|
-
* @param store
|
|
1250
|
-
*/
|
|
1251
|
-
function setupStoreStatsCollection(luvio, callback) {
|
|
1252
|
-
const wrapMethod = 'storeBroadcast';
|
|
1253
|
-
const originalMethod = luvio[wrapMethod];
|
|
1254
|
-
const throttledCallback = throttle(callback, 200);
|
|
1255
|
-
luvio[wrapMethod] = function (...args) {
|
|
1256
|
-
throttledCallback();
|
|
1257
|
-
return originalMethod.call(this, ...args);
|
|
1258
|
-
};
|
|
1259
|
-
}
|
|
1260
|
-
/**
|
|
1261
|
-
* @param instrumentedAdapter
|
|
1262
|
-
* @returns instrumentedGraphqlAdapter, which logs additional metrics for get graphQL adapter
|
|
1263
|
-
*/
|
|
1264
|
-
function instrumentGraphqlAdapter(instrumentedAdapter) {
|
|
1265
|
-
const instrumentedGraphqlAdapter = (config, requestContext) => {
|
|
1266
|
-
const result = instrumentedAdapter(config, requestContext);
|
|
1267
|
-
if (result === null) {
|
|
1268
|
-
return result;
|
|
1269
|
-
}
|
|
1270
|
-
if (isPromise(result)) {
|
|
1271
|
-
result.then((_snapshot) => {
|
|
1272
|
-
logGraphqlMetrics(_snapshot);
|
|
1273
|
-
});
|
|
1274
|
-
}
|
|
1275
|
-
else {
|
|
1276
|
-
logGraphqlMetrics(result);
|
|
1277
|
-
}
|
|
1278
|
-
return result;
|
|
1279
|
-
};
|
|
1280
|
-
return instrumentedGraphqlAdapter;
|
|
1281
|
-
}
|
|
1282
|
-
/**
|
|
1283
|
-
* Sets up instrumentation for @salesforce/lds-adapters-uiapi
|
|
1284
|
-
*/
|
|
1285
|
-
function setLdsAdaptersUiapiInstrumentation(uiapiRegistration) {
|
|
1286
|
-
uiapiRegistration.instrument({
|
|
1287
|
-
recordConflictsResolved: (serverRequestCount) => {
|
|
1288
|
-
// Ignore 0 values which can originate from ADS bridge
|
|
1289
|
-
if (serverRequestCount > 0) {
|
|
1290
|
-
updatePercentileHistogramMetric('record-conflicts-resolved', serverRequestCount);
|
|
1291
|
-
}
|
|
1292
|
-
},
|
|
1293
|
-
nullDisplayValueConflict: ({ fieldType, areValuesEqual }) => {
|
|
1294
|
-
const metricName = `merge-null-dv-count.${fieldType}`;
|
|
1295
|
-
if (fieldType === 'scalar') {
|
|
1296
|
-
incrementCounterMetric(`${metricName}.${areValuesEqual}`);
|
|
1297
|
-
}
|
|
1298
|
-
else {
|
|
1299
|
-
incrementCounterMetric(metricName);
|
|
1300
|
-
}
|
|
1301
|
-
},
|
|
1302
|
-
getRecordNotifyChangeAllowed: incrementGetRecordNotifyChangeAllowCount,
|
|
1303
|
-
getRecordNotifyChangeDropped: incrementGetRecordNotifyChangeDropCount,
|
|
1304
|
-
notifyRecordUpdateAvailableAllowed: incrementNotifyRecordUpdateAvailableAllowCount,
|
|
1305
|
-
notifyRecordUpdateAvailableDropped: incrementNotifyRecordUpdateAvailableDropCount,
|
|
1306
|
-
recordTypeIdIsNull: incrementRecordTypeIdIsNullCount,
|
|
1307
|
-
});
|
|
1308
|
-
}
|
|
1309
|
-
/**
|
|
1310
|
-
* Sets up instrumentation for @salesforce/lds-network-adapter
|
|
1311
|
-
*/
|
|
1312
|
-
function setLdsNetworkAdapterInstrumentation(networkAdapterRegistration) {
|
|
1313
|
-
networkAdapterRegistration.instrument({
|
|
1314
|
-
aggregateUiChunkCount: (cb) => setAggregateUiChunkCountMetric(cb()),
|
|
1315
|
-
duplicateRequest: () => incrementCounterMetric(DUPLICATE_REQUEST_COUNT),
|
|
1316
|
-
getRecordAggregateInvoke: incrementGetRecordAggregateInvokeCount,
|
|
1317
|
-
getRecordNormalInvoke: incrementGetRecordNormalInvokeCount,
|
|
1318
|
-
networkRateLimitExceeded: incrementNetworkRateLimitExceededCount,
|
|
1319
|
-
});
|
|
1320
|
-
}
|
|
1321
|
-
/**
|
|
1322
|
-
* Provides concrete implementations using o11y/client for instrumentation hooks
|
|
1323
|
-
*/
|
|
1324
|
-
function setInstrumentationHooks() {
|
|
1325
|
-
instrument({
|
|
1326
|
-
instrumentAdapter: instrumentAdapter,
|
|
1327
|
-
});
|
|
1328
|
-
}
|
|
1329
|
-
/**
|
|
1330
|
-
* Initialize the instrumentation and instrument the LDS instance and the InMemoryStore.
|
|
1331
|
-
*
|
|
1332
|
-
* @param luvio The Luvio instance to instrument.
|
|
1333
|
-
* @param store The InMemoryStore to instrument.
|
|
1334
|
-
*/
|
|
1335
|
-
function setupInstrumentation(luvio, store) {
|
|
1336
|
-
setInstrumentationHooks();
|
|
1337
|
-
instrumentStoreMethods(luvio);
|
|
1338
|
-
setupStoreStatsCollection(luvio, instrumentStoreStatsCallback(store));
|
|
1339
|
-
setStoreScheduler(store);
|
|
1340
|
-
setStoreEventObservers(store);
|
|
1341
|
-
// TODO [W-10061321]: use periodic logger to log aggregated store stats
|
|
1342
|
-
}
|
|
1343
|
-
function instrumentStoreMethods(luvio, _store) {
|
|
1344
|
-
instrumentMethods(luvio, [
|
|
1345
|
-
{ methodName: 'storeBroadcast', metricKey: STORE_BROADCAST_DURATION },
|
|
1346
|
-
{ methodName: 'storeIngest', metricKey: STORE_INGEST_DURATION },
|
|
1347
|
-
{ methodName: 'storeLookup', metricKey: STORE_LOOKUP_DURATION },
|
|
1348
|
-
{ methodName: 'storeSetTTLOverride', metricKey: STORE_SET_TTL_OVERRIDE_DURATION },
|
|
1349
|
-
{
|
|
1350
|
-
methodName: 'storeSetDefaultTTLOverride',
|
|
1351
|
-
metricKey: STORE_SET_DEFAULT_TTL_OVERRIDE_DURATION,
|
|
1352
|
-
},
|
|
1353
|
-
{
|
|
1354
|
-
methodName: 'notifyStoreUpdateAvailable',
|
|
1355
|
-
metricKey: NOTIFY_STORE_UPDATE_AVAILABLE_DURATION,
|
|
1356
|
-
},
|
|
1357
|
-
]);
|
|
1358
|
-
}
|
|
1359
|
-
function handleIngestedNewData(event) {
|
|
1360
|
-
if (event.type === 'cache-miss-out-of-ttl') {
|
|
1361
|
-
const { recordMetadata, oldSnapshot, newSnapshot } = event;
|
|
1362
|
-
const lastExpiredDurationEntry = cacheMissOutOfTtlDurations.get(event.recordId);
|
|
1363
|
-
if (lastExpiredDurationEntry !== undefined) {
|
|
1364
|
-
const representationName = `${recordMetadata.namespace}__${recordMetadata.representationName}`;
|
|
1365
|
-
let durationMetricName;
|
|
1366
|
-
let countMetricName;
|
|
1367
|
-
if (oldSnapshot !== newSnapshot) {
|
|
1368
|
-
durationMetricName =
|
|
1369
|
-
REPRESENTATION_CACHE_MISS_OUT_OF_TTL_DATA_CHANGED_DURATION_METRIC_NAME;
|
|
1370
|
-
countMetricName =
|
|
1371
|
-
REPRESENTATION_CACHE_MISS_OUT_OF_TTL_DATA_CHANGED_COUNT_METRIC_NAME;
|
|
1372
|
-
}
|
|
1373
|
-
else {
|
|
1374
|
-
durationMetricName =
|
|
1375
|
-
REPRESENTATION_CACHE_MISS_OUT_OF_TTL_DATA_UNCHANGED_DURATION_METRIC_NAME;
|
|
1376
|
-
countMetricName =
|
|
1377
|
-
REPRESENTATION_CACHE_MISS_OUT_OF_TTL_DATA_UNCHANGED_COUNT_METRIC_NAME;
|
|
1378
|
-
}
|
|
1379
|
-
const metricTags = {
|
|
1380
|
-
state: lastExpiredDurationEntry.storeResolveResultState,
|
|
1381
|
-
representationName: representationName,
|
|
1382
|
-
};
|
|
1383
|
-
ldsInstrumentation.trackValue(durationMetricName, lastExpiredDurationEntry.value, undefined, metricTags);
|
|
1384
|
-
ldsInstrumentation.incrementCounter(countMetricName, 1, undefined, metricTags);
|
|
1385
|
-
cacheMissOutOfTtlDurations.delete(event.recordId);
|
|
1386
|
-
}
|
|
1387
|
-
}
|
|
1388
|
-
}
|
|
1389
|
-
const cacheMissOutOfTtlDurations = new LRUCache(250);
|
|
1390
|
-
function handleOnDataOutOfTtlDurationUpdate(event) {
|
|
1391
|
-
cacheMissOutOfTtlDurations.set(event.recordId, {
|
|
1392
|
-
value: event.lastExpiredDuration,
|
|
1393
|
-
storeResolveResultState: event.storeResolveResultState,
|
|
1394
|
-
});
|
|
1395
|
-
}
|
|
1396
|
-
function setStoreEventObservers(store) {
|
|
1397
|
-
const cacheMissOutOfTtlEventObserver = {
|
|
1398
|
-
onCacheMissOutOfTtl: handleIngestedNewData,
|
|
1399
|
-
};
|
|
1400
|
-
const cacheMissOutOfTtlDurationUpdateEventObserver = {
|
|
1401
|
-
onDataOutOfTtlDurationUpdate: handleOnDataOutOfTtlDurationUpdate,
|
|
1402
|
-
};
|
|
1403
|
-
store.addStoreEventObserver(cacheMissOutOfTtlEventObserver);
|
|
1404
|
-
store.addStoreEventObserver(cacheMissOutOfTtlDurationUpdateEventObserver);
|
|
1405
|
-
}
|
|
807
|
+
const NAMESPACE = 'lds';
|
|
808
|
+
const APEX_ADAPTER_NAME = 'getApex';
|
|
809
|
+
const NORMALIZED_APEX_ADAPTER_NAME = createMetricsKey('Apex', APEX_ADAPTER_NAME);
|
|
810
|
+
const GRAPHQL_ADAPTER_NAME = 'graphQL';
|
|
811
|
+
const GRAPHQL_RECORDS_KEY = 'GraphQL::graphql__uiapi__query';
|
|
812
|
+
const ldsInstrumentation = getInstrumentation(NAMESPACE);
|
|
813
|
+
const observabilityInstrumentation = getInstrumentation(OBSERVABILITY_NAMESPACE);
|
|
814
|
+
class Instrumentation {
|
|
815
|
+
/**
|
|
816
|
+
* Injected to LDS for Luvio specific instrumentation.
|
|
817
|
+
*
|
|
818
|
+
* @param context The transaction context.
|
|
819
|
+
*/
|
|
820
|
+
instrumentLuvio(_context) {
|
|
821
|
+
// TODO [W-9783151]: refactor luvio.instrument to not require this class
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
/**
|
|
825
|
+
* Provide this method for the instrument option for a Luvio instance.
|
|
826
|
+
* @param context The transaction context.
|
|
827
|
+
*/
|
|
828
|
+
function instrumentLuvio(context) {
|
|
829
|
+
if (isAdapterUnfulfilledError(context)) {
|
|
830
|
+
// We are consolidating all apex adapter instrumentation calls under a single key
|
|
831
|
+
const normalizedContext = {
|
|
832
|
+
...context,
|
|
833
|
+
adapterName: normalizeAdapterName(context.adapterName),
|
|
834
|
+
};
|
|
835
|
+
incrementAdapterRequestErrorCount(normalizedContext);
|
|
836
|
+
logAdapterRequestError(normalizedContext);
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
/**
|
|
840
|
+
* Returns whether or not this is an AdapterUnfulfilledError.
|
|
841
|
+
* @param context The transaction context.
|
|
842
|
+
* @returns Whether or not this is an AdapterUnfulfilledError.
|
|
843
|
+
*/
|
|
844
|
+
function isAdapterUnfulfilledError(context) {
|
|
845
|
+
return context[ADAPTER_UNFULFILLED_ERROR] === true;
|
|
846
|
+
}
|
|
847
|
+
/**
|
|
848
|
+
* W-8620679
|
|
849
|
+
* Increment the counter for an UnfulfilledSnapshotError coming from luvio
|
|
850
|
+
*
|
|
851
|
+
* @param context The transaction context.
|
|
852
|
+
*/
|
|
853
|
+
function incrementAdapterRequestErrorCount(context) {
|
|
854
|
+
const adapterRequestErrorCounter = createMetricsKey(ADAPTER_ERROR_COUNT_METRIC_NAME, context.adapterName);
|
|
855
|
+
observabilityInstrumentation.incrementCounter(adapterRequestErrorCounter);
|
|
856
|
+
observabilityInstrumentation.incrementCounter(TOTAL_ADAPTER_ERROR_COUNT);
|
|
857
|
+
}
|
|
858
|
+
/**
|
|
859
|
+
* W-10495632
|
|
860
|
+
* Logs the missing paths and/or links associated with the UnfulfilledSnapshotError.
|
|
861
|
+
*
|
|
862
|
+
* @param context The transaction context.
|
|
863
|
+
*/
|
|
864
|
+
function logAdapterRequestError(context) {
|
|
865
|
+
ldsInstrumentation.error(ADAPTER_UNFULFILLED_ERROR, adapterUnfulfilledErrorSchema, {
|
|
866
|
+
adapter: context.adapterName,
|
|
867
|
+
missing_paths: keys(context.missingPaths),
|
|
868
|
+
missing_links: keys(context.missingLinks),
|
|
869
|
+
});
|
|
870
|
+
}
|
|
871
|
+
/**
|
|
872
|
+
* Increment the counter based on the cache policy type for an adapter call
|
|
873
|
+
*
|
|
874
|
+
* @param requestContext Adapter request context that includes cache policy
|
|
875
|
+
*/
|
|
876
|
+
function incrementAdapterCachePolicyType(requestContext) {
|
|
877
|
+
const cachePolicy = requestContext && requestContext.cachePolicy && requestContext.cachePolicy.type;
|
|
878
|
+
if (cachePolicy !== undefined) {
|
|
879
|
+
ldsInstrumentation.incrementCounter(CACHE_POLICY_COUNTERS[cachePolicy], 1);
|
|
880
|
+
return;
|
|
881
|
+
}
|
|
882
|
+
ldsInstrumentation.incrementCounter(CACHE_POLICY_UNDEFINED_COUNTER, 1);
|
|
883
|
+
}
|
|
884
|
+
/**
|
|
885
|
+
* Increment the counter based on missing recordTypeId in a record ingest
|
|
886
|
+
*
|
|
887
|
+
* @param apiName incoming API name for bad data
|
|
888
|
+
*/
|
|
889
|
+
function incrementRecordTypeIdIsNullCount(apiName) {
|
|
890
|
+
const adapterRequestErrorCounter = createMetricsKey(RECORD_TYPE_ID_IS_NULL_COUNT, apiName);
|
|
891
|
+
observabilityInstrumentation.incrementCounter(adapterRequestErrorCounter);
|
|
892
|
+
}
|
|
893
|
+
/**
|
|
894
|
+
* Logs when adapter requests come in. If we have subsequent cache misses on a given config, beyond its TTL then log the duration to metrics.
|
|
895
|
+
* Backed by an LRU Cache implementation to prevent too many record entries from being stored in-memory.
|
|
896
|
+
* @param name The wire adapter name.
|
|
897
|
+
* @param config The config passed into wire adapter.
|
|
898
|
+
* @param currentCacheMissTimestamp Timestamp for when the request was made.
|
|
899
|
+
* @param ttl TTL for the wire adapter.
|
|
900
|
+
* @param durationMetricName Name for duration metric.
|
|
901
|
+
* @param counterMetricName Name for counter metric.
|
|
902
|
+
*/
|
|
903
|
+
const adapterCacheMisses = new LRUCache(250);
|
|
904
|
+
function logAdapterCacheMissOutOfTtlDuration(name, config, currentCacheMissTimestamp, ttl, counterMetricName, durationMetricName) {
|
|
905
|
+
const configKey = `${name}:${stableJSONStringify(config)}`;
|
|
906
|
+
const existingCacheMissTimestamp = adapterCacheMisses.get(configKey);
|
|
907
|
+
adapterCacheMisses.set(configKey, currentCacheMissTimestamp);
|
|
908
|
+
if (existingCacheMissTimestamp !== undefined) {
|
|
909
|
+
const duration = currentCacheMissTimestamp - existingCacheMissTimestamp;
|
|
910
|
+
if (duration > ttl) {
|
|
911
|
+
ldsInstrumentation.incrementCounter(counterMetricName, 1);
|
|
912
|
+
ldsInstrumentation.trackValue(durationMetricName, duration);
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
function instrumentAdapter(adapter, metadata, adapterInstrumentationOptions) {
|
|
917
|
+
const { apiFamily, name, ttl } = metadata;
|
|
918
|
+
let trackL1Hits = false;
|
|
919
|
+
let trackL2Hits = false;
|
|
920
|
+
let reportObserver = undefined;
|
|
921
|
+
if (adapterInstrumentationOptions !== undefined) {
|
|
922
|
+
({ trackL1Hits, trackL2Hits, reportObserver } = adapterInstrumentationOptions);
|
|
923
|
+
}
|
|
924
|
+
const adapterName = normalizeAdapterName(name, apiFamily);
|
|
925
|
+
/**
|
|
926
|
+
* W-8076905
|
|
927
|
+
* Dynamically generated metric. Simple counter for all requests made by this adapter.
|
|
928
|
+
*/
|
|
929
|
+
const wireAdapterRequestMetric = createMetricsKey(ADAPTER_INVOCATION_COUNT_METRIC_NAME, adapterName);
|
|
930
|
+
/**
|
|
931
|
+
* W-6981216
|
|
932
|
+
* Dynamically generated metric. Simple counter for cache hits by adapter name.
|
|
933
|
+
*/
|
|
934
|
+
const cacheHitCountByAdapterMetric = createMetricsKey(ADAPTER_CACHE_HIT_COUNT_METRIC_NAME, adapterName);
|
|
935
|
+
/**
|
|
936
|
+
* W-10490326
|
|
937
|
+
* Dynamically generated metric. Simple counter for L2 cache hits by adapter name.
|
|
938
|
+
*/
|
|
939
|
+
const l2CacheHitCountByAdapterMetric = createMetricsKey(ADAPTER_CACHE_HIT_L2_COUNT_METRIC_NAME, adapterName);
|
|
940
|
+
/**
|
|
941
|
+
* W-7404607
|
|
942
|
+
* Dynamically generated metric. Timer for cache hits by adapter name.
|
|
943
|
+
*/
|
|
944
|
+
const cacheHitDurationByAdapterMetric = createMetricsKey(ADAPTER_CACHE_HIT_DURATION_METRIC_NAME, adapterName);
|
|
945
|
+
/**
|
|
946
|
+
* W-10490326
|
|
947
|
+
* Dynamically generated metric. Timer for L2 cache hits by adapter name.
|
|
948
|
+
*/
|
|
949
|
+
const l2CacheHitDurationByAdapterMetric = createMetricsKey(ADAPTER_CACHE_HIT_L2_DURATION_METRIC_NAME, adapterName);
|
|
950
|
+
/**
|
|
951
|
+
* W-6981216
|
|
952
|
+
* Dynamically generated metric. Simple counter for cache misses by adapter name.
|
|
953
|
+
*/
|
|
954
|
+
const cacheMissCountByAdapterMetric = createMetricsKey(ADAPTER_CACHE_MISS_COUNT_METRIC_NAME, adapterName);
|
|
955
|
+
/**
|
|
956
|
+
* W-7404607
|
|
957
|
+
* Dynamically generated metric. Timer for cache hits by adapter name.
|
|
958
|
+
*/
|
|
959
|
+
const cacheMissDurationByAdapterMetric = createMetricsKey(ADAPTER_CACHE_MISS_DURATION_METRIC_NAME, adapterName);
|
|
960
|
+
/**
|
|
961
|
+
* W-7376275
|
|
962
|
+
* Dynamically generated metric. Measures the amount of time it takes for LDS to get another cache miss on
|
|
963
|
+
* a request we've made in the past.
|
|
964
|
+
* Request Record 1 -> Record 2 -> Back to Record 1 outside of TTL is an example of when this metric will fire.
|
|
965
|
+
*/
|
|
966
|
+
const cacheMissOutOfTtlDurationByAdapterMetric = createMetricsKey(ADAPTER_CACHE_MISS_OUT_OF_TTL_DURATION_METRIC_NAME, adapterName);
|
|
967
|
+
const cacheMissOutOfTtlCountByAdapterMetric = createMetricsKey(ADAPTER_CACHE_MISS_OUT_OF_TTL_COUNT_METRIC_NAME, adapterName);
|
|
968
|
+
const instrumentedAdapter = (config, requestContext) => {
|
|
969
|
+
// increment adapter request metrics
|
|
970
|
+
observabilityInstrumentation.incrementCounter(wireAdapterRequestMetric, 1);
|
|
971
|
+
observabilityInstrumentation.incrementCounter(TOTAL_ADAPTER_REQUEST_SUCCESS_COUNT, 1);
|
|
972
|
+
// increment cache policy metrics
|
|
973
|
+
incrementAdapterCachePolicyType(requestContext);
|
|
974
|
+
// start collecting
|
|
975
|
+
const activity = ldsInstrumentation.startActivity(adapterName);
|
|
976
|
+
return runAdapterWithReport(metadata.name, adapter, config, requestContext || {}, (report) => {
|
|
977
|
+
const { executionTime } = report;
|
|
978
|
+
switch (report.result) {
|
|
979
|
+
case 'l1-hit': {
|
|
980
|
+
ldsInstrumentation.incrementCounter(ADAPTER_CACHE_HIT_COUNT_METRIC_NAME, 1);
|
|
981
|
+
ldsInstrumentation.incrementCounter(cacheHitCountByAdapterMetric, 1);
|
|
982
|
+
ldsInstrumentation.trackValue(cacheHitDurationByAdapterMetric, executionTime);
|
|
983
|
+
if (trackL1Hits) {
|
|
984
|
+
activity.stop('l1-hit');
|
|
985
|
+
}
|
|
986
|
+
else {
|
|
987
|
+
activity.discard();
|
|
988
|
+
}
|
|
989
|
+
break;
|
|
990
|
+
}
|
|
991
|
+
case 'l2-hit': {
|
|
992
|
+
let tags = undefined;
|
|
993
|
+
if (report.stale === true) {
|
|
994
|
+
tags = { [STALE_TAG]: true };
|
|
995
|
+
}
|
|
996
|
+
ldsInstrumentation.incrementCounter(ADAPTER_CACHE_HIT_L2_COUNT_METRIC_NAME, 1, undefined, tags);
|
|
997
|
+
ldsInstrumentation.incrementCounter(l2CacheHitCountByAdapterMetric, 1, undefined, tags);
|
|
998
|
+
ldsInstrumentation.trackValue(l2CacheHitDurationByAdapterMetric, executionTime, undefined, tags);
|
|
999
|
+
if (trackL2Hits) {
|
|
1000
|
+
activity.stop('l2-hit');
|
|
1001
|
+
}
|
|
1002
|
+
else {
|
|
1003
|
+
activity.discard();
|
|
1004
|
+
}
|
|
1005
|
+
break;
|
|
1006
|
+
}
|
|
1007
|
+
case 'cache-miss':
|
|
1008
|
+
{
|
|
1009
|
+
ldsInstrumentation.trackValue(cacheMissDurationByAdapterMetric, executionTime);
|
|
1010
|
+
// TODO [W-10484306]: Remove typecasting after this type bug is solved
|
|
1011
|
+
activity.stop('cache-miss');
|
|
1012
|
+
ldsInstrumentation.incrementCounter(ADAPTER_CACHE_MISS_COUNT_METRIC_NAME, 1);
|
|
1013
|
+
ldsInstrumentation.incrementCounter(cacheMissCountByAdapterMetric, 1);
|
|
1014
|
+
if (ttl !== undefined) {
|
|
1015
|
+
logAdapterCacheMissOutOfTtlDuration(adapterName, config, Date.now(), ttl, cacheMissOutOfTtlCountByAdapterMetric, cacheMissOutOfTtlDurationByAdapterMetric);
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
break;
|
|
1019
|
+
case 'error':
|
|
1020
|
+
{
|
|
1021
|
+
const error = report.error;
|
|
1022
|
+
// We are capturing userland transient errors through here, and
|
|
1023
|
+
// there is a chance that these errors could contain PII, as seen in W-12224448.
|
|
1024
|
+
// Log AdapterErrors only
|
|
1025
|
+
if (isAdapterError(error)) {
|
|
1026
|
+
activity.error(error);
|
|
1027
|
+
}
|
|
1028
|
+
// TODO [W-10484306]: Remove typecasting after this type bug is solved
|
|
1029
|
+
activity.stop('error');
|
|
1030
|
+
}
|
|
1031
|
+
break;
|
|
1032
|
+
case 'exception':
|
|
1033
|
+
case 'invalid-config':
|
|
1034
|
+
{
|
|
1035
|
+
activity.discard();
|
|
1036
|
+
}
|
|
1037
|
+
break;
|
|
1038
|
+
default: {
|
|
1039
|
+
if ('production' !== process.env.NODE_ENV) {
|
|
1040
|
+
throw new Error(`unsupported adapter result`);
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
if (reportObserver !== undefined) {
|
|
1045
|
+
reportObserver(report);
|
|
1046
|
+
}
|
|
1047
|
+
});
|
|
1048
|
+
};
|
|
1049
|
+
// Set the name property on the function for debugging purposes.
|
|
1050
|
+
Object.defineProperty(instrumentedAdapter, 'name', {
|
|
1051
|
+
value: name + '__instrumented',
|
|
1052
|
+
});
|
|
1053
|
+
return isGraphqlAdapter(name) === true
|
|
1054
|
+
? instrumentGraphqlAdapter(instrumentedAdapter)
|
|
1055
|
+
: instrumentedAdapter;
|
|
1056
|
+
}
|
|
1057
|
+
/**
|
|
1058
|
+
* Any graphql get adapter specific instrumentation that we need to log
|
|
1059
|
+
* @param snapshot from either in-memory or built after a network hit
|
|
1060
|
+
*/
|
|
1061
|
+
function logGraphqlMetrics(snapshot) {
|
|
1062
|
+
// We have both data and error in the returned response
|
|
1063
|
+
const { data: snapshotData } = snapshot;
|
|
1064
|
+
if (snapshotData &&
|
|
1065
|
+
snapshotData.data &&
|
|
1066
|
+
keys(snapshotData.data).length > 0 &&
|
|
1067
|
+
snapshotData.errors &&
|
|
1068
|
+
snapshotData.errors.length > 0) {
|
|
1069
|
+
ldsInstrumentation.incrementCounter(GET_GRAPHQL_RESPONSE_MIXED);
|
|
1070
|
+
}
|
|
1071
|
+
if (snapshotData.errors && snapshotData.errors.length > 0) {
|
|
1072
|
+
// W-12293528 GraphQL metrics
|
|
1073
|
+
// log counts of returned errors
|
|
1074
|
+
let validationErrorCount = 0;
|
|
1075
|
+
let syntaxErrorCount = 0;
|
|
1076
|
+
let otherErrorCount = 0;
|
|
1077
|
+
// conslidate instrumentation calls with # of occurrences
|
|
1078
|
+
snapshotData.errors.forEach((e) => {
|
|
1079
|
+
if (e.message && e.message.startsWith('Validation error')) {
|
|
1080
|
+
validationErrorCount++;
|
|
1081
|
+
}
|
|
1082
|
+
else if (e.message && e.message.startsWith('Invalid Syntax')) {
|
|
1083
|
+
syntaxErrorCount++;
|
|
1084
|
+
}
|
|
1085
|
+
else {
|
|
1086
|
+
otherErrorCount++;
|
|
1087
|
+
}
|
|
1088
|
+
});
|
|
1089
|
+
if (validationErrorCount > 0) {
|
|
1090
|
+
ldsInstrumentation.incrementCounter(GRAPHQL_QUERY_VALIDATION_ERROR_COUNT, validationErrorCount);
|
|
1091
|
+
}
|
|
1092
|
+
if (syntaxErrorCount > 0) {
|
|
1093
|
+
ldsInstrumentation.incrementCounter(GRAPHQL_QUERY_SYNTAX_ERROR_COUNT, syntaxErrorCount);
|
|
1094
|
+
}
|
|
1095
|
+
if (otherErrorCount > 0) {
|
|
1096
|
+
ldsInstrumentation.incrementCounter(GRAPHQL_QUERY_OTHER_ERROR_COUNT, otherErrorCount);
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
/**
|
|
1101
|
+
* Wraps methods to collect runtime performance using o11y's trackValue API
|
|
1102
|
+
* @param obj Object instance containing the methods to instrument
|
|
1103
|
+
* @param methods array containing objects with keys for the method name and the metric key to use in o11y
|
|
1104
|
+
*/
|
|
1105
|
+
function instrumentMethods(obj, methods) {
|
|
1106
|
+
for (let i = 0, len = methods.length; i < len; i++) {
|
|
1107
|
+
const { methodName, metricKey } = methods[i];
|
|
1108
|
+
const originalMethod = obj[methodName];
|
|
1109
|
+
obj[methodName] = function (...args) {
|
|
1110
|
+
const startTime = Date.now();
|
|
1111
|
+
try {
|
|
1112
|
+
const res = originalMethod.call(this, ...args);
|
|
1113
|
+
const executionTime = Date.now() - startTime;
|
|
1114
|
+
// handle async resolved/rejected
|
|
1115
|
+
if (isPromise(res)) {
|
|
1116
|
+
res.then(() => {
|
|
1117
|
+
ldsInstrumentation.trackValue(metricKey, Date.now() - startTime);
|
|
1118
|
+
}).catch((_error) => {
|
|
1119
|
+
ldsInstrumentation.trackValue(metricKey, Date.now() - startTime, true);
|
|
1120
|
+
});
|
|
1121
|
+
}
|
|
1122
|
+
else {
|
|
1123
|
+
// handle synchronous success
|
|
1124
|
+
ldsInstrumentation.trackValue(metricKey, executionTime);
|
|
1125
|
+
}
|
|
1126
|
+
return res;
|
|
1127
|
+
}
|
|
1128
|
+
catch (error) {
|
|
1129
|
+
// handle synchronous throw
|
|
1130
|
+
ldsInstrumentation.trackValue(metricKey, Date.now() - startTime, true);
|
|
1131
|
+
// rethrow error
|
|
1132
|
+
throw error;
|
|
1133
|
+
}
|
|
1134
|
+
};
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
function createMetricsKey(name, unit) {
|
|
1138
|
+
let metricName = name;
|
|
1139
|
+
if (unit) {
|
|
1140
|
+
metricName = metricName + '.' + unit;
|
|
1141
|
+
}
|
|
1142
|
+
return metricName;
|
|
1143
|
+
}
|
|
1144
|
+
/**
|
|
1145
|
+
* Returns whether adapter is an Apex one or not.
|
|
1146
|
+
* @param adapterName The name of the adapter.
|
|
1147
|
+
*/
|
|
1148
|
+
function isApexAdapter(adapterName) {
|
|
1149
|
+
return adapterName.indexOf(APEX_ADAPTER_NAME) > -1;
|
|
1150
|
+
}
|
|
1151
|
+
/**
|
|
1152
|
+
* Returns boolean whether adapter is a graphQL one or not.
|
|
1153
|
+
* @param adapterName The name of the adapter.
|
|
1154
|
+
*/
|
|
1155
|
+
function isGraphqlAdapter(adapterName) {
|
|
1156
|
+
return adapterName === GRAPHQL_ADAPTER_NAME;
|
|
1157
|
+
}
|
|
1158
|
+
/**
|
|
1159
|
+
* Normalizes getApex adapter names to `Apex.getApex`. Non-Apex adapters will be prefixed with
|
|
1160
|
+
* API family, if supplied. Example: `UiApi.getRecord`.
|
|
1161
|
+
*
|
|
1162
|
+
* Note: If you are adding additional logging that can come from getApex adapter contexts that provide
|
|
1163
|
+
* the full getApex adapter name (i.e. getApex_[namespace]_[class]_[function]_[continuation]),
|
|
1164
|
+
* ensure to call this method to normalize all logging to 'getApex'. This
|
|
1165
|
+
* is because Argus has a 50k key cardinality limit. More context: W-8379680.
|
|
1166
|
+
*
|
|
1167
|
+
* @param adapterName The name of the adapter.
|
|
1168
|
+
* @param apiFamily The API family of the adapter.
|
|
1169
|
+
*/
|
|
1170
|
+
function normalizeAdapterName(adapterName, apiFamily) {
|
|
1171
|
+
if (isApexAdapter(adapterName)) {
|
|
1172
|
+
return NORMALIZED_APEX_ADAPTER_NAME;
|
|
1173
|
+
}
|
|
1174
|
+
return apiFamily ? `${apiFamily}.${adapterName}` : adapterName;
|
|
1175
|
+
}
|
|
1176
|
+
/**
|
|
1177
|
+
* Calls instrumentation/service telemetry counter
|
|
1178
|
+
* @param name Name of the metric
|
|
1179
|
+
* @param value number to increment by, if undefined increment by 1
|
|
1180
|
+
*/
|
|
1181
|
+
function incrementCounterMetric(name, number) {
|
|
1182
|
+
ldsInstrumentation.incrementCounter(name, number);
|
|
1183
|
+
}
|
|
1184
|
+
/**
|
|
1185
|
+
* Calls instrumentation/service telemetry percentileHistogram
|
|
1186
|
+
* @param name Name of the metric
|
|
1187
|
+
* @param value number used to update the percentileHistogram
|
|
1188
|
+
*/
|
|
1189
|
+
function updatePercentileHistogramMetric(name, value) {
|
|
1190
|
+
ldsInstrumentation.trackValue(name, value);
|
|
1191
|
+
}
|
|
1192
|
+
function setAggregateUiChunkCountMetric(chunkCount) {
|
|
1193
|
+
updatePercentileHistogramMetric(AGGREGATE_UI_CHUNK_COUNT, chunkCount);
|
|
1194
|
+
}
|
|
1195
|
+
function incrementGetRecordNormalInvokeCount() {
|
|
1196
|
+
incrementCounterMetric(GET_RECORD_NORMAL_INVOKE_COUNT);
|
|
1197
|
+
}
|
|
1198
|
+
function incrementGetRecordAggregateInvokeCount() {
|
|
1199
|
+
incrementCounterMetric(GET_RECORD_AGGREGATE_INVOKE_COUNT);
|
|
1200
|
+
}
|
|
1201
|
+
function incrementGetRecordNotifyChangeAllowCount() {
|
|
1202
|
+
incrementCounterMetric(GET_RECORD_NOTIFY_CHANGE_ALLOW_COUNT);
|
|
1203
|
+
}
|
|
1204
|
+
function incrementGetRecordNotifyChangeDropCount() {
|
|
1205
|
+
incrementCounterMetric(GET_RECORD_NOTIFY_CHANGE_DROP_COUNT);
|
|
1206
|
+
}
|
|
1207
|
+
function incrementNotifyRecordUpdateAvailableAllowCount() {
|
|
1208
|
+
incrementCounterMetric(NOTIFY_RECORD_UPDATE_AVAILABLE_ALLOW_COUNT);
|
|
1209
|
+
}
|
|
1210
|
+
function incrementNotifyRecordUpdateAvailableDropCount() {
|
|
1211
|
+
incrementCounterMetric(NOTIFY_RECORD_UPDATE_AVAILABLE_DROP_COUNT);
|
|
1212
|
+
}
|
|
1213
|
+
function incrementNetworkRateLimitExceededCount() {
|
|
1214
|
+
incrementCounterMetric(NETWORK_RATE_LIMIT_EXCEEDED_COUNT);
|
|
1215
|
+
}
|
|
1216
|
+
function instrumentStoreTrimTask(callback) {
|
|
1217
|
+
return () => {
|
|
1218
|
+
ldsInstrumentation.incrementCounter(STORE_TRIM_TASK_COUNT);
|
|
1219
|
+
const startTime = Date.now();
|
|
1220
|
+
const res = callback();
|
|
1221
|
+
ldsInstrumentation.trackValue(STORE_TRIM_TASK_DURATION, Date.now() - startTime);
|
|
1222
|
+
// TODO [W-10060579]: replace record count per trim task with metric
|
|
1223
|
+
return res;
|
|
1224
|
+
};
|
|
1225
|
+
}
|
|
1226
|
+
function setStoreScheduler(store) {
|
|
1227
|
+
const originalScheduler = store.scheduler;
|
|
1228
|
+
store.scheduler = (callback, done) => {
|
|
1229
|
+
originalScheduler(instrumentStoreTrimTask(callback), done);
|
|
1230
|
+
};
|
|
1231
|
+
}
|
|
1232
|
+
function instrumentStoreStatsCallback(store) {
|
|
1233
|
+
return () => {
|
|
1234
|
+
const { snapshotSubscriptions, watchSubscriptions } = store;
|
|
1235
|
+
const records = store.fallbackStringKeyInMemoryStore.records;
|
|
1236
|
+
updatePercentileHistogramMetric(STORE_SIZE_COUNT, keys(records).length);
|
|
1237
|
+
if (GRAPHQL_RECORDS_KEY in records) {
|
|
1238
|
+
const graphQLRecordSize = keys(records[GRAPHQL_RECORDS_KEY]).length;
|
|
1239
|
+
updatePercentileHistogramMetric(STORE_GRAPHQL_SIZE_COUNT, graphQLRecordSize);
|
|
1240
|
+
}
|
|
1241
|
+
updatePercentileHistogramMetric(STORE_SNAPSHOT_SUBSCRIPTIONS_COUNT, keys(snapshotSubscriptions).length);
|
|
1242
|
+
updatePercentileHistogramMetric(STORE_WATCH_SUBSCRIPTIONS_COUNT, keys(watchSubscriptions).length);
|
|
1243
|
+
};
|
|
1244
|
+
}
|
|
1245
|
+
/**
|
|
1246
|
+
* Collects additional store statistics by tying its periodic,
|
|
1247
|
+
* point-in-time data collection with a luvio method
|
|
1248
|
+
* @param luvio
|
|
1249
|
+
* @param store
|
|
1250
|
+
*/
|
|
1251
|
+
function setupStoreStatsCollection(luvio, callback) {
|
|
1252
|
+
const wrapMethod = 'storeBroadcast';
|
|
1253
|
+
const originalMethod = luvio[wrapMethod];
|
|
1254
|
+
const throttledCallback = throttle(callback, 200);
|
|
1255
|
+
luvio[wrapMethod] = function (...args) {
|
|
1256
|
+
throttledCallback();
|
|
1257
|
+
return originalMethod.call(this, ...args);
|
|
1258
|
+
};
|
|
1259
|
+
}
|
|
1260
|
+
/**
|
|
1261
|
+
* @param instrumentedAdapter
|
|
1262
|
+
* @returns instrumentedGraphqlAdapter, which logs additional metrics for get graphQL adapter
|
|
1263
|
+
*/
|
|
1264
|
+
function instrumentGraphqlAdapter(instrumentedAdapter) {
|
|
1265
|
+
const instrumentedGraphqlAdapter = (config, requestContext) => {
|
|
1266
|
+
const result = instrumentedAdapter(config, requestContext);
|
|
1267
|
+
if (result === null) {
|
|
1268
|
+
return result;
|
|
1269
|
+
}
|
|
1270
|
+
if (isPromise(result)) {
|
|
1271
|
+
result.then((_snapshot) => {
|
|
1272
|
+
logGraphqlMetrics(_snapshot);
|
|
1273
|
+
});
|
|
1274
|
+
}
|
|
1275
|
+
else {
|
|
1276
|
+
logGraphqlMetrics(result);
|
|
1277
|
+
}
|
|
1278
|
+
return result;
|
|
1279
|
+
};
|
|
1280
|
+
return instrumentedGraphqlAdapter;
|
|
1281
|
+
}
|
|
1282
|
+
/**
|
|
1283
|
+
* Sets up instrumentation for @salesforce/lds-adapters-uiapi
|
|
1284
|
+
*/
|
|
1285
|
+
function setLdsAdaptersUiapiInstrumentation(uiapiRegistration) {
|
|
1286
|
+
uiapiRegistration.instrument({
|
|
1287
|
+
recordConflictsResolved: (serverRequestCount) => {
|
|
1288
|
+
// Ignore 0 values which can originate from ADS bridge
|
|
1289
|
+
if (serverRequestCount > 0) {
|
|
1290
|
+
updatePercentileHistogramMetric('record-conflicts-resolved', serverRequestCount);
|
|
1291
|
+
}
|
|
1292
|
+
},
|
|
1293
|
+
nullDisplayValueConflict: ({ fieldType, areValuesEqual }) => {
|
|
1294
|
+
const metricName = `merge-null-dv-count.${fieldType}`;
|
|
1295
|
+
if (fieldType === 'scalar') {
|
|
1296
|
+
incrementCounterMetric(`${metricName}.${areValuesEqual}`);
|
|
1297
|
+
}
|
|
1298
|
+
else {
|
|
1299
|
+
incrementCounterMetric(metricName);
|
|
1300
|
+
}
|
|
1301
|
+
},
|
|
1302
|
+
getRecordNotifyChangeAllowed: incrementGetRecordNotifyChangeAllowCount,
|
|
1303
|
+
getRecordNotifyChangeDropped: incrementGetRecordNotifyChangeDropCount,
|
|
1304
|
+
notifyRecordUpdateAvailableAllowed: incrementNotifyRecordUpdateAvailableAllowCount,
|
|
1305
|
+
notifyRecordUpdateAvailableDropped: incrementNotifyRecordUpdateAvailableDropCount,
|
|
1306
|
+
recordTypeIdIsNull: incrementRecordTypeIdIsNullCount,
|
|
1307
|
+
});
|
|
1308
|
+
}
|
|
1309
|
+
/**
|
|
1310
|
+
* Sets up instrumentation for @salesforce/lds-network-adapter
|
|
1311
|
+
*/
|
|
1312
|
+
function setLdsNetworkAdapterInstrumentation(networkAdapterRegistration) {
|
|
1313
|
+
networkAdapterRegistration.instrument({
|
|
1314
|
+
aggregateUiChunkCount: (cb) => setAggregateUiChunkCountMetric(cb()),
|
|
1315
|
+
duplicateRequest: () => incrementCounterMetric(DUPLICATE_REQUEST_COUNT),
|
|
1316
|
+
getRecordAggregateInvoke: incrementGetRecordAggregateInvokeCount,
|
|
1317
|
+
getRecordNormalInvoke: incrementGetRecordNormalInvokeCount,
|
|
1318
|
+
networkRateLimitExceeded: incrementNetworkRateLimitExceededCount,
|
|
1319
|
+
});
|
|
1320
|
+
}
|
|
1321
|
+
/**
|
|
1322
|
+
* Provides concrete implementations using o11y/client for instrumentation hooks
|
|
1323
|
+
*/
|
|
1324
|
+
function setInstrumentationHooks() {
|
|
1325
|
+
instrument({
|
|
1326
|
+
instrumentAdapter: instrumentAdapter,
|
|
1327
|
+
});
|
|
1328
|
+
}
|
|
1329
|
+
/**
|
|
1330
|
+
* Initialize the instrumentation and instrument the LDS instance and the InMemoryStore.
|
|
1331
|
+
*
|
|
1332
|
+
* @param luvio The Luvio instance to instrument.
|
|
1333
|
+
* @param store The InMemoryStore to instrument.
|
|
1334
|
+
*/
|
|
1335
|
+
function setupInstrumentation(luvio, store) {
|
|
1336
|
+
setInstrumentationHooks();
|
|
1337
|
+
instrumentStoreMethods(luvio);
|
|
1338
|
+
setupStoreStatsCollection(luvio, instrumentStoreStatsCallback(store));
|
|
1339
|
+
setStoreScheduler(store);
|
|
1340
|
+
setStoreEventObservers(store);
|
|
1341
|
+
// TODO [W-10061321]: use periodic logger to log aggregated store stats
|
|
1342
|
+
}
|
|
1343
|
+
function instrumentStoreMethods(luvio, _store) {
|
|
1344
|
+
instrumentMethods(luvio, [
|
|
1345
|
+
{ methodName: 'storeBroadcast', metricKey: STORE_BROADCAST_DURATION },
|
|
1346
|
+
{ methodName: 'storeIngest', metricKey: STORE_INGEST_DURATION },
|
|
1347
|
+
{ methodName: 'storeLookup', metricKey: STORE_LOOKUP_DURATION },
|
|
1348
|
+
{ methodName: 'storeSetTTLOverride', metricKey: STORE_SET_TTL_OVERRIDE_DURATION },
|
|
1349
|
+
{
|
|
1350
|
+
methodName: 'storeSetDefaultTTLOverride',
|
|
1351
|
+
metricKey: STORE_SET_DEFAULT_TTL_OVERRIDE_DURATION,
|
|
1352
|
+
},
|
|
1353
|
+
{
|
|
1354
|
+
methodName: 'notifyStoreUpdateAvailable',
|
|
1355
|
+
metricKey: NOTIFY_STORE_UPDATE_AVAILABLE_DURATION,
|
|
1356
|
+
},
|
|
1357
|
+
]);
|
|
1358
|
+
}
|
|
1359
|
+
function handleIngestedNewData(event) {
|
|
1360
|
+
if (event.type === 'cache-miss-out-of-ttl') {
|
|
1361
|
+
const { recordMetadata, oldSnapshot, newSnapshot } = event;
|
|
1362
|
+
const lastExpiredDurationEntry = cacheMissOutOfTtlDurations.get(event.recordId);
|
|
1363
|
+
if (lastExpiredDurationEntry !== undefined) {
|
|
1364
|
+
const representationName = `${recordMetadata.namespace}__${recordMetadata.representationName}`;
|
|
1365
|
+
let durationMetricName;
|
|
1366
|
+
let countMetricName;
|
|
1367
|
+
if (oldSnapshot !== newSnapshot) {
|
|
1368
|
+
durationMetricName =
|
|
1369
|
+
REPRESENTATION_CACHE_MISS_OUT_OF_TTL_DATA_CHANGED_DURATION_METRIC_NAME;
|
|
1370
|
+
countMetricName =
|
|
1371
|
+
REPRESENTATION_CACHE_MISS_OUT_OF_TTL_DATA_CHANGED_COUNT_METRIC_NAME;
|
|
1372
|
+
}
|
|
1373
|
+
else {
|
|
1374
|
+
durationMetricName =
|
|
1375
|
+
REPRESENTATION_CACHE_MISS_OUT_OF_TTL_DATA_UNCHANGED_DURATION_METRIC_NAME;
|
|
1376
|
+
countMetricName =
|
|
1377
|
+
REPRESENTATION_CACHE_MISS_OUT_OF_TTL_DATA_UNCHANGED_COUNT_METRIC_NAME;
|
|
1378
|
+
}
|
|
1379
|
+
const metricTags = {
|
|
1380
|
+
state: lastExpiredDurationEntry.storeResolveResultState,
|
|
1381
|
+
representationName: representationName,
|
|
1382
|
+
};
|
|
1383
|
+
ldsInstrumentation.trackValue(durationMetricName, lastExpiredDurationEntry.value, undefined, metricTags);
|
|
1384
|
+
ldsInstrumentation.incrementCounter(countMetricName, 1, undefined, metricTags);
|
|
1385
|
+
cacheMissOutOfTtlDurations.delete(event.recordId);
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
const cacheMissOutOfTtlDurations = new LRUCache(250);
|
|
1390
|
+
function handleOnDataOutOfTtlDurationUpdate(event) {
|
|
1391
|
+
cacheMissOutOfTtlDurations.set(event.recordId, {
|
|
1392
|
+
value: event.lastExpiredDuration,
|
|
1393
|
+
storeResolveResultState: event.storeResolveResultState,
|
|
1394
|
+
});
|
|
1395
|
+
}
|
|
1396
|
+
function setStoreEventObservers(store) {
|
|
1397
|
+
const cacheMissOutOfTtlEventObserver = {
|
|
1398
|
+
onCacheMissOutOfTtl: handleIngestedNewData,
|
|
1399
|
+
};
|
|
1400
|
+
const cacheMissOutOfTtlDurationUpdateEventObserver = {
|
|
1401
|
+
onDataOutOfTtlDurationUpdate: handleOnDataOutOfTtlDurationUpdate,
|
|
1402
|
+
};
|
|
1403
|
+
store.addStoreEventObserver(cacheMissOutOfTtlEventObserver);
|
|
1404
|
+
store.addStoreEventObserver(cacheMissOutOfTtlDurationUpdateEventObserver);
|
|
1405
|
+
}
|
|
1406
1406
|
const instrumentation = new Instrumentation();
|
|
1407
1407
|
|
|
1408
1408
|
export { Instrumentation, LRUCache, metricKeys as METRIC_KEYS, handleIngestedNewData, handleOnDataOutOfTtlDurationUpdate, incrementCounterMetric, incrementGetRecordNormalInvokeCount, incrementGetRecordNotifyChangeAllowCount, incrementGetRecordNotifyChangeDropCount, incrementNotifyRecordUpdateAvailableAllowCount, incrementNotifyRecordUpdateAvailableDropCount, instrumentAdapter, instrumentLuvio, instrumentMethods, instrumentStoreMethods, instrumentation, setInstrumentationHooks, setLdsAdaptersUiapiInstrumentation, setLdsNetworkAdapterInstrumentation, setStoreEventObservers, setupInstrumentation, updatePercentileHistogramMetric };
|
|
1409
|
-
// version: 1.124.
|
|
1409
|
+
// version: 1.124.4-f07bfc14d
|