@salesforce/lds-instrumentation 1.124.1 → 1.124.3

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.
@@ -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.1-b9e603b9e
1409
+ // version: 1.124.3-cf2dbb2fa