@salesforce/lds-instrumentation 0.1.0-dev1

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.
@@ -0,0 +1,1582 @@
1
+ /**
2
+ * Copyright (c) 2022, Salesforce, Inc.,
3
+ * All rights reserved.
4
+ * For full license text, see the LICENSE.txt file
5
+ */
6
+
7
+ /*
8
+ * ATTENTION!
9
+ * THIS IS A GENERATED FILE FROM https://github.com/salesforce-experience-platform-emu/lds-lightning-platform
10
+ * If you would like to contribute to LDS, please follow the steps outlined in the git repo.
11
+ * Any changes made to this file in p4 will be automatically overwritten.
12
+ * *******************************************************************************************
13
+ */
14
+ /* proxy-compat-disable */
15
+ import { getInstrumentation, idleDetector } from 'o11y/client';
16
+ import { adapterUnfulfilledErrorSchema } from 'o11y_schema/sf_lds';
17
+ import { ADAPTER_UNFULFILLED_ERROR, instrument } from 'force/ldsBindings';
18
+
19
+ var SnapshotState;
20
+ (function (SnapshotState) {
21
+ SnapshotState["Fulfilled"] = "Fulfilled";
22
+ SnapshotState["Unfulfilled"] = "Unfulfilled";
23
+ SnapshotState["Error"] = "Error";
24
+ SnapshotState["Pending"] = "Pending";
25
+ SnapshotState["Stale"] = "Stale";
26
+ })(SnapshotState || (SnapshotState = {}));
27
+
28
+ Promise.resolve();
29
+
30
+ var StoreErrorStatus;
31
+ (function (StoreErrorStatus) {
32
+ StoreErrorStatus[StoreErrorStatus["RESOURCE_NOT_FOUND"] = 404] = "RESOURCE_NOT_FOUND";
33
+ })(StoreErrorStatus || (StoreErrorStatus = {}));
34
+ var StoreRecordType;
35
+ (function (StoreRecordType) {
36
+ StoreRecordType["Error"] = "error";
37
+ })(StoreRecordType || (StoreRecordType = {}));
38
+ var StoreLinkStateValues$1;
39
+ (function (StoreLinkStateValues) {
40
+ StoreLinkStateValues[StoreLinkStateValues["NotPresent"] = 0] = "NotPresent";
41
+ StoreLinkStateValues[StoreLinkStateValues["RefNotPresent"] = 1] = "RefNotPresent";
42
+ StoreLinkStateValues[StoreLinkStateValues["RefPresent"] = 2] = "RefPresent";
43
+ StoreLinkStateValues[StoreLinkStateValues["Null"] = 3] = "Null";
44
+ StoreLinkStateValues[StoreLinkStateValues["Missing"] = 4] = "Missing";
45
+ StoreLinkStateValues[StoreLinkStateValues["Pending"] = 5] = "Pending";
46
+ })(StoreLinkStateValues$1 || (StoreLinkStateValues$1 = {}));
47
+ var StoreResolveResultState;
48
+ (function (StoreResolveResultState) {
49
+ StoreResolveResultState[StoreResolveResultState["Found"] = 0] = "Found";
50
+ StoreResolveResultState[StoreResolveResultState["Error"] = 1] = "Error";
51
+ StoreResolveResultState[StoreResolveResultState["Null"] = 2] = "Null";
52
+ StoreResolveResultState[StoreResolveResultState["NotPresent"] = 3] = "NotPresent";
53
+ StoreResolveResultState[StoreResolveResultState["Stale"] = 4] = "Stale";
54
+ })(StoreResolveResultState || (StoreResolveResultState = {}));
55
+ var HttpStatusCode;
56
+ (function (HttpStatusCode) {
57
+ HttpStatusCode[HttpStatusCode["Ok"] = 200] = "Ok";
58
+ HttpStatusCode[HttpStatusCode["Created"] = 201] = "Created";
59
+ HttpStatusCode[HttpStatusCode["NoContent"] = 204] = "NoContent";
60
+ HttpStatusCode[HttpStatusCode["NotModified"] = 304] = "NotModified";
61
+ HttpStatusCode[HttpStatusCode["BadRequest"] = 400] = "BadRequest";
62
+ HttpStatusCode[HttpStatusCode["Unauthorized"] = 401] = "Unauthorized";
63
+ HttpStatusCode[HttpStatusCode["Forbidden"] = 403] = "Forbidden";
64
+ HttpStatusCode[HttpStatusCode["NotFound"] = 404] = "NotFound";
65
+ HttpStatusCode[HttpStatusCode["ServerError"] = 500] = "ServerError";
66
+ HttpStatusCode[HttpStatusCode["GatewayTimeout"] = 504] = "GatewayTimeout";
67
+ })(HttpStatusCode || (HttpStatusCode = {}));
68
+ var GraphNodeType;
69
+ (function (GraphNodeType) {
70
+ GraphNodeType["Link"] = "Link";
71
+ GraphNodeType["Node"] = "Node";
72
+ GraphNodeType["Error"] = "Error";
73
+ GraphNodeType["Locked"] = "Locked";
74
+ })(GraphNodeType || (GraphNodeType = {}));
75
+
76
+ var StoreLinkStateValues;
77
+ (function (StoreLinkStateValues) {
78
+ StoreLinkStateValues[StoreLinkStateValues["NotPresent"] = 0] = "NotPresent";
79
+ StoreLinkStateValues[StoreLinkStateValues["RefNotPresent"] = 1] = "RefNotPresent";
80
+ StoreLinkStateValues[StoreLinkStateValues["RefPresent"] = 2] = "RefPresent";
81
+ StoreLinkStateValues[StoreLinkStateValues["Null"] = 3] = "Null";
82
+ StoreLinkStateValues[StoreLinkStateValues["Missing"] = 4] = "Missing";
83
+ StoreLinkStateValues[StoreLinkStateValues["Pending"] = 5] = "Pending";
84
+ })(StoreLinkStateValues || (StoreLinkStateValues = {}));
85
+ var FragmentReadResultState;
86
+ (function (FragmentReadResultState) {
87
+ FragmentReadResultState[FragmentReadResultState["Missing"] = 0] = "Missing";
88
+ FragmentReadResultState[FragmentReadResultState["Success"] = 1] = "Success";
89
+ FragmentReadResultState[FragmentReadResultState["Error"] = 2] = "Error";
90
+ })(FragmentReadResultState || (FragmentReadResultState = {}));
91
+ ({
92
+ state: FragmentReadResultState.Missing,
93
+ });
94
+
95
+ var ResourceParamType;
96
+ (function (ResourceParamType) {
97
+ ResourceParamType[ResourceParamType["UrlParameter"] = 0] = "UrlParameter";
98
+ ResourceParamType[ResourceParamType["QueryParameter"] = 1] = "QueryParameter";
99
+ ResourceParamType[ResourceParamType["Body"] = 2] = "Body";
100
+ ResourceParamType[ResourceParamType["Header"] = 3] = "Header";
101
+ })(ResourceParamType || (ResourceParamType = {}));
102
+ var TypeCheckShapes;
103
+ (function (TypeCheckShapes) {
104
+ TypeCheckShapes[TypeCheckShapes["String"] = 0] = "String";
105
+ TypeCheckShapes[TypeCheckShapes["Boolean"] = 1] = "Boolean";
106
+ TypeCheckShapes[TypeCheckShapes["Number"] = 2] = "Number";
107
+ TypeCheckShapes[TypeCheckShapes["Integer"] = 3] = "Integer";
108
+ TypeCheckShapes[TypeCheckShapes["Unsupported"] = 4] = "Unsupported";
109
+ })(TypeCheckShapes || (TypeCheckShapes = {}));
110
+ // engine version: 0.158.7-bafe2646
111
+
112
+ const DurableEnvironmentEventDiscriminator = 'durable';
113
+ function isDurableEnvironmentEvent(event) {
114
+ return (event.type === 'environment' && event.environment === DurableEnvironmentEventDiscriminator);
115
+ }
116
+
117
+ /**
118
+ * Copyright (c) 2022, Salesforce, Inc.,
119
+ * All rights reserved.
120
+ * For full license text, see the LICENSE.txt file
121
+ */
122
+
123
+
124
+ const { stringify: stringify$1 } = JSON;
125
+
126
+ function isPromise$1(value) {
127
+ // check for Thenable due to test frameworks using custom Promise impls
128
+ return value !== null && value !== undefined && typeof value.then === 'function';
129
+ }
130
+ function isErrorSnapshot(snapshot) {
131
+ return snapshot.state === 'Error';
132
+ }
133
+
134
+ function runAdapterWithReport(adapterName, adapter, adapterConfig, requestContext, onAdapterComplete) {
135
+ let adapterStart;
136
+ let adapterEnd;
137
+ let lookupStart;
138
+ let lookupEnd;
139
+ let cacheStart;
140
+ let cacheEnd;
141
+ let networkStart;
142
+ let networkEnd;
143
+ let staleLookup = false;
144
+ let cacheLookupState = undefined;
145
+ let result;
146
+ let snapshotState = '';
147
+ let error;
148
+ let exceptionMessage;
149
+ let completedNetworkRequests = [];
150
+ let collectedNetworkStartEvents = {};
151
+ let reviveStats = [];
152
+ let rawEvents = [];
153
+ const markEnd = (adapterEndedCallback) => {
154
+ if (adapterStart === undefined) {
155
+ throw Error('adapter has not been started yet');
156
+ }
157
+ if (adapterEnd) {
158
+ throw Error('adapter has already ended');
159
+ }
160
+ if (result === undefined) {
161
+ throw Error('no result type set');
162
+ }
163
+ adapterEnd = Date.now();
164
+ const executionTime = adapterEnd - adapterStart;
165
+ const cacheLookupTime = cacheEnd - cacheStart;
166
+ const lookupTime = lookupEnd - lookupStart;
167
+ const networkLookupTime = networkEnd - networkStart;
168
+ const config = stringify$1(adapterConfig);
169
+ switch (result) {
170
+ case 'l1-hit':
171
+ if (staleLookup === true) {
172
+ adapterEndedCallback({
173
+ result: 'l1-hit',
174
+ adapterName,
175
+ rawEvents,
176
+ config,
177
+ stale: true,
178
+ executionTime,
179
+ cacheLookupTime,
180
+ lookupTime,
181
+ snapshotState,
182
+ });
183
+ }
184
+ else {
185
+ adapterEndedCallback({
186
+ result: 'l1-hit',
187
+ adapterName,
188
+ rawEvents,
189
+ config,
190
+ stale: false,
191
+ executionTime,
192
+ cacheLookupTime,
193
+ lookupTime,
194
+ snapshotState,
195
+ });
196
+ }
197
+ break;
198
+ case 'l2-hit':
199
+ if (staleLookup === true) {
200
+ adapterEndedCallback({
201
+ result: 'l2-hit',
202
+ adapterName,
203
+ rawEvents,
204
+ config,
205
+ stale: true,
206
+ executionTime,
207
+ cacheLookupTime,
208
+ lookupTime,
209
+ snapshotState,
210
+ revives: reviveStats,
211
+ });
212
+ }
213
+ else {
214
+ adapterEndedCallback({
215
+ result: 'l2-hit',
216
+ adapterName,
217
+ rawEvents,
218
+ config,
219
+ stale: false,
220
+ executionTime,
221
+ cacheLookupTime,
222
+ lookupTime,
223
+ snapshotState,
224
+ revives: reviveStats,
225
+ });
226
+ }
227
+ break;
228
+ case 'cache-miss':
229
+ adapterEndedCallback({
230
+ result: 'cache-miss',
231
+ adapterName,
232
+ rawEvents,
233
+ config,
234
+ executionTime,
235
+ cacheLookupTime,
236
+ lookupTime,
237
+ networkLookupTime,
238
+ completedNetworkRequests,
239
+ snapshotState,
240
+ revives: reviveStats,
241
+ });
242
+ break;
243
+ case 'invalid-config':
244
+ adapterEndedCallback({
245
+ result: 'invalid-config',
246
+ rawEvents,
247
+ config,
248
+ adapterName,
249
+ executionTime,
250
+ });
251
+ break;
252
+ case 'error':
253
+ adapterEndedCallback({
254
+ result: 'error',
255
+ rawEvents,
256
+ config,
257
+ error,
258
+ adapterName,
259
+ executionTime,
260
+ cacheLookupTime,
261
+ lookupTime,
262
+ networkLookupTime,
263
+ snapshotState,
264
+ });
265
+ break;
266
+ case 'exception':
267
+ adapterEndedCallback({
268
+ result: 'exception',
269
+ rawEvents,
270
+ config,
271
+ exceptionMessage,
272
+ adapterName,
273
+ executionTime,
274
+ });
275
+ break;
276
+ }
277
+ };
278
+ const markException = (error) => {
279
+ let message = 'Unknown Error';
280
+ if (error instanceof Error)
281
+ message = error.message;
282
+ exceptionMessage = message;
283
+ result = 'exception';
284
+ };
285
+ const metricsEventObserver = {
286
+ onAdapterEvent: (ev) => {
287
+ rawEvents.push(ev);
288
+ switch (ev.type) {
289
+ case 'adapter-lookup-start':
290
+ lookupStart = Date.now();
291
+ break;
292
+ case 'adapter-lookup-end':
293
+ lookupEnd = Date.now();
294
+ break;
295
+ case 'cache-lookup-start':
296
+ cacheStart = Date.now();
297
+ break;
298
+ case 'cache-lookup-end':
299
+ cacheEnd = Date.now();
300
+ if (ev.wasResultAsync === false) {
301
+ // L1 cache hit
302
+ result = 'l1-hit';
303
+ }
304
+ else {
305
+ // L2 cache hit
306
+ result = 'l2-hit';
307
+ }
308
+ cacheLookupState = ev.snapshotState;
309
+ if (ev.snapshotState === 'Stale') {
310
+ staleLookup = true;
311
+ }
312
+ break;
313
+ case 'network-lookup-start':
314
+ // if the lookup is stale or fulfilled it wasnt a cache miss
315
+ // but refreshed due to explicitly being asked (cache-and-network) or because it was stale
316
+ if (cacheLookupState &&
317
+ cacheLookupState !== 'Stale' &&
318
+ cacheLookupState !== 'Fulfilled') {
319
+ result = 'cache-miss';
320
+ networkStart = Date.now();
321
+ }
322
+ break;
323
+ case 'network-lookup-end':
324
+ if (staleLookup === false) {
325
+ networkEnd = Date.now();
326
+ }
327
+ break;
328
+ case 'network-request-start':
329
+ collectedNetworkStartEvents[ev.uuid] = ev;
330
+ break;
331
+ case 'network-request-end': {
332
+ const startEvent = collectedNetworkStartEvents[ev.uuid];
333
+ if (startEvent === undefined || startEvent.type !== 'network-request-start') {
334
+ if (process.env.NODE_ENV !== 'production') {
335
+ throw Error('no matching network start event emmited');
336
+ }
337
+ return;
338
+ }
339
+ completedNetworkRequests.push({
340
+ request: startEvent.request,
341
+ response: ev.response,
342
+ duration: ev.timestamp - startEvent.timestamp,
343
+ });
344
+ break;
345
+ }
346
+ }
347
+ },
348
+ onEnvironmentEvent: (ev) => {
349
+ rawEvents.push(ev);
350
+ if (isDurableEnvironmentEvent(ev)) {
351
+ if (ev.data.type === 'l2-revive-end') {
352
+ let missingKeys;
353
+ const snapshot = ev.data.snapshot;
354
+ if (snapshot.state === 'Unfulfilled') {
355
+ missingKeys = snapshot.missingLinks.keysAsArray();
356
+ if (snapshot.missingLinks.size() === 0) {
357
+ missingKeys.push(snapshot.recordId);
358
+ }
359
+ }
360
+ reviveStats.push({
361
+ resultState: ev.data.snapshot.state,
362
+ missingKeys,
363
+ l2Trips: ev.data.l2Trips,
364
+ duration: ev.data.duration,
365
+ });
366
+ }
367
+ }
368
+ },
369
+ onCustomAdapterEvent: (ev) => {
370
+ rawEvents.push(ev);
371
+ },
372
+ };
373
+ const bindObserverToAdapterRequestContext = (requestContext) => {
374
+ let requestContextWithInstrumentationObserver = { ...requestContext };
375
+ if (requestContextWithInstrumentationObserver.eventObservers === undefined) {
376
+ requestContextWithInstrumentationObserver.eventObservers = [];
377
+ }
378
+ requestContextWithInstrumentationObserver.eventObservers.push(metricsEventObserver);
379
+ return requestContextWithInstrumentationObserver;
380
+ };
381
+ adapterStart = Date.now();
382
+ try {
383
+ const normalizedRequestContext = bindObserverToAdapterRequestContext(requestContext);
384
+ const adapterResult = adapter(adapterConfig, normalizedRequestContext);
385
+ if (isPromise$1(adapterResult)) {
386
+ adapterResult
387
+ .then((snapshot) => {
388
+ snapshotState = snapshot.state;
389
+ if (isErrorSnapshot(snapshot)) {
390
+ result = 'error';
391
+ error = stringify$1(snapshot.error);
392
+ }
393
+ markEnd(onAdapterComplete);
394
+ })
395
+ .catch((e) => {
396
+ // async error
397
+ markException(e);
398
+ markEnd(onAdapterComplete);
399
+ });
400
+ }
401
+ else {
402
+ if (adapterResult === null) {
403
+ // invalid config
404
+ result = 'invalid-config';
405
+ }
406
+ else if (isErrorSnapshot(adapterResult)) {
407
+ snapshotState = 'Error';
408
+ result = 'error';
409
+ error = stringify$1(adapterResult.error);
410
+ }
411
+ markEnd(onAdapterComplete);
412
+ }
413
+ return adapterResult;
414
+ }
415
+ catch (error) {
416
+ // synchronous error (lookup exception)
417
+ markException(error);
418
+ markEnd(onAdapterComplete);
419
+ throw error;
420
+ }
421
+ }
422
+
423
+ const ADAPTER_CACHE_HIT_COUNT_METRIC_NAME = 'cache-hit-count';
424
+ const ADAPTER_CACHE_HIT_DURATION_METRIC_NAME = 'cache-hit-duration';
425
+ const ADAPTER_CACHE_HIT_L2_COUNT_METRIC_NAME = 'cache-hit-l2-count';
426
+ const ADAPTER_CACHE_HIT_L2_DURATION_METRIC_NAME = 'cache-hit-l2-duration';
427
+ const ADAPTER_CACHE_MISS_COUNT_METRIC_NAME = 'cache-miss-count';
428
+ const ADAPTER_CACHE_MISS_DURATION_METRIC_NAME = 'cache-miss-duration';
429
+ const ADAPTER_CACHE_MISS_OUT_OF_TTL_COUNT_METRIC_NAME = 'cache-miss-out-of-ttl-count';
430
+ const ADAPTER_CACHE_MISS_OUT_OF_TTL_DURATION_METRIC_NAME = 'cache-miss-out-of-ttl-duration';
431
+ const REPRESENTATION_CACHE_MISS_OUT_OF_TTL_DATA_CHANGED_COUNT_METRIC_NAME = 'cache-miss-out-of-ttl-data-changed-count';
432
+ const REPRESENTATION_CACHE_MISS_OUT_OF_TTL_DATA_UNCHANGED_COUNT_METRIC_NAME = 'cache-miss-out-of-ttl-data-unchanged-count';
433
+ const REPRESENTATION_CACHE_MISS_OUT_OF_TTL_DATA_CHANGED_DURATION_METRIC_NAME = 'cache-miss-out-of-ttl-data-changed-duration';
434
+ const REPRESENTATION_CACHE_MISS_OUT_OF_TTL_DATA_UNCHANGED_DURATION_METRIC_NAME = 'cache-miss-out-of-ttl-data-unchanged-duration';
435
+ /**
436
+ * W-8121791
437
+ * Number of subqueries used when aggregateUi is invoked for getRecord
438
+ */
439
+ const AGGREGATE_UI_CHUNK_COUNT = 'aggregate-ui-chunk-count';
440
+ /**
441
+ * W-6981216
442
+ * Counter for overall LDS cache hits.
443
+ * Note: This is also being recorded in AILTN logging.
444
+ */
445
+ const CACHE_HIT_COUNT = ADAPTER_CACHE_HIT_COUNT_METRIC_NAME;
446
+ /**
447
+ * W-6981216
448
+ * Counter for overall LDS cache hits.
449
+ * Note: This is also being recorded in AILTN logging.
450
+ */
451
+ const CACHE_MISS_COUNT = ADAPTER_CACHE_MISS_COUNT_METRIC_NAME;
452
+ /**
453
+ * W-9949353
454
+ * Used to track how often we dedupe HTTP requests
455
+ * Invoked when an HTTP request is deduped against an already in-flight request
456
+ */
457
+ const DUPLICATE_REQUEST_COUNT = 'duplicate-request-count';
458
+ /**
459
+ * W-7667066
460
+ * This count represents the number of times getRecord() was invoked, but not including
461
+ * executeAggregateUi calls. It can be represented as the sum of the Aura Action invocations
462
+ * GetRecordWithLayouts and GetRecordWithFields.
463
+ */
464
+ const GET_RECORD_NORMAL_INVOKE_COUNT = 'get-record-normal-invoke-count';
465
+ /**
466
+ * W-7667066
467
+ * This count represents the number of times getRecord() was invoked, with a large enough payload
468
+ * that executeAggregateUi was used.
469
+ */
470
+ const GET_RECORD_AGGREGATE_INVOKE_COUNT = 'get-record-aggregate-invoke-count';
471
+ /**
472
+ * W-7301684
473
+ * Counter for when getRecordNotifyChange api calls are allowed through.
474
+ */
475
+ const GET_RECORD_NOTIFY_CHANGE_ALLOW_COUNT = 'get-record-notify-change-allow-count';
476
+ /**
477
+ * W-7301684
478
+ * Counter for when getRecordNotifyChange api calls are dropped/throttled.
479
+ */
480
+ const GET_RECORD_NOTIFY_CHANGE_DROP_COUNT = 'get-record-notify-change-drop-count';
481
+ /**
482
+ * W-11118785
483
+ * Counter for when notifyRecordUpdateAvailable api calls are allowed through.
484
+ */
485
+ const NOTIFY_RECORD_UPDATE_AVAILABLE_ALLOW_COUNT = 'notify-record-update-available-allow-count';
486
+ /**
487
+ * W-11118785
488
+ * Counter for when notifyRecordUpdateAvailable api calls are dropped/throttled.
489
+ */
490
+ const NOTIFY_RECORD_UPDATE_AVAILABLE_DROP_COUNT = 'notify-record-update-available-drop-count';
491
+ /**
492
+ * W-8278006
493
+ * Counter for rate limiting telemetry. Is updated whenever the network adapter hits the specified limit.
494
+ */
495
+ const NETWORK_RATE_LIMIT_EXCEEDED_COUNT = 'network-rate-limit-exceeded-count';
496
+ /**
497
+ * W-6981216
498
+ * Timer to measure performance for Luvio.storeBroadcast() method.
499
+ */
500
+ const STORE_BROADCAST_DURATION = 'store-broadcast-duration';
501
+ /**
502
+ * W-6981216
503
+ * Timer to measure performance for Luvio.storeIngest() method.
504
+ */
505
+ const STORE_INGEST_DURATION = 'store-ingest-duration';
506
+ /**
507
+ * W-6981216
508
+ * Timer to measure performance for Luvio.storeLookup() method.
509
+ */
510
+ const STORE_LOOKUP_DURATION = 'store-lookup-duration';
511
+ /**
512
+ * W-9805009
513
+ * Timer to measure performance for Luvio.storeSetTTLOverride() method.
514
+ */
515
+ const STORE_SET_TTL_OVERRIDE_DURATION = 'store-set-ttl-override-duration';
516
+ /**
517
+ * W-9805009
518
+ * Timer to measure performance for Luvio.storeSetDefaultTTLOverride() method.
519
+ */
520
+ const STORE_SET_DEFAULT_TTL_OVERRIDE_DURATION = 'store-set-default-ttl-override-duration';
521
+ /**
522
+ * W-11118785
523
+ * Timer to measure performance for Luvio.notifyStoreUpdateAvailable() method.
524
+ */
525
+ const NOTIFY_STORE_UPDATE_AVAILABLE_DURATION = 'notify-store-update-available-duration';
526
+ /**
527
+ * W-6981216
528
+ * Counter for number of records in LDS store. Is updated by periodicLogger invocations.
529
+ * Note: This is also being recorded in AILTN logging.
530
+ */
531
+ const STORE_SIZE_COUNT = 'store-size-count';
532
+ /**
533
+ * W-6981216
534
+ * Counter for number of LDS snapshot subscription. Is updated by periodicLogger invocations.
535
+ * Note: This is also being recorded in AILTN logging.
536
+ */
537
+ const STORE_SNAPSHOT_SUBSCRIPTIONS_COUNT = 'store-snapshot-subscriptions-count';
538
+ /**
539
+ * W-6981216
540
+ * Counter for number of LDS watch subscriptions. Is updated by periodicLogger invocations.
541
+ * Note: This is also being recorded in AILTN logging.
542
+ */
543
+ const STORE_WATCH_SUBSCRIPTIONS_COUNT = 'store-watch-subscriptions-count';
544
+ /**
545
+ * W-9131128
546
+ * Counter for graphQL get adapter response with mixed bag of both data and error in response
547
+ */
548
+ const GET_GRAPHQL_RESPONSE_MIXED = 'get-graphql-response-mixed-count';
549
+ /**
550
+ * W-9537401
551
+ * Counter for Luvio store trim task invocation
552
+ */
553
+ const STORE_TRIM_TASK_COUNT = 'store-trim-task-count';
554
+ /**
555
+ * W-9537401
556
+ * Timer to measure performance for Luvio store trim task
557
+ */
558
+ const STORE_TRIM_TASK_DURATION = 'store-trim-task-duration';
559
+ /**
560
+ * W-9804037
561
+ * Counters for Luvio cache policy usage
562
+ * Note: Undefined cache policy defaults to different cache policies based on runtime
563
+ */
564
+ const CACHE_POLICY_COUNTERS = {
565
+ 'cache-and-network': 'cache-policy-cache-and-network',
566
+ 'cache-then-network': 'cache-policy-cache-then-network',
567
+ 'no-cache': 'cache-policy-no-cache',
568
+ 'only-if-cached': 'cache-policy-only-if-cached',
569
+ 'stale-while-revalidate': 'cache-policy-stale-while-revalidate',
570
+ 'valid-at': 'cache-policy-valid-at',
571
+ };
572
+ const CACHE_POLICY_UNDEFINED_COUNTER = 'cache-policy-undefined';
573
+ const STALE_TAG = 'stale';
574
+ /**
575
+ * W-9804037
576
+ * Durable Store health metric
577
+ * Counter to track Durable Store read, write and error rates
578
+ */
579
+ const DURABLE_STORE_COUNT = 'durable-store-count';
580
+ /**
581
+ * W-10490363
582
+ * GraphQL Eval health metric
583
+ * Counter to track Success and Error Rate on Eval
584
+ */
585
+ const GRAPHQL_ADAPTER_COUNT = 'graphql-adapter-count';
586
+ /**
587
+ * Counter for tracking invalid record type IDs
588
+ */
589
+ const RECORD_TYPE_ID_IS_NULL_COUNT = 'record-type-id-is-null-count';
590
+ /**
591
+ * W-12293528
592
+ * GraphQL health metric
593
+ * Counter to track size of the top-level GraphQL object
594
+ */
595
+ const STORE_GRAPHQL_SIZE_COUNT = 'store-graphql-size-count';
596
+ /**
597
+ * W-12293528
598
+ * GraphQL health metric
599
+ * Counter to track validation errors in query
600
+ */
601
+ const GRAPHQL_QUERY_VALIDATION_ERROR_COUNT = 'graphql-query-validation-error-count';
602
+ /**
603
+ * W-12293528
604
+ * GraphQL health metric
605
+ * Counter to track syntax errors in query
606
+ */
607
+ const GRAPHQL_QUERY_SYNTAX_ERROR_COUNT = 'graphql-query-syntax-error-count';
608
+ /**
609
+ * W-12293528
610
+ * GraphQL health metric
611
+ * Counter to track miscellaneous errors in query
612
+ */
613
+ const GRAPHQL_QUERY_OTHER_ERROR_COUNT = 'graphql-query-other-error-count';
614
+ /**
615
+ * W-12025795
616
+ * GraphQL metric
617
+ * Counter to track usage of legacy adapter
618
+ */
619
+ const GRAPHQL_LEGACY_ADAPTER_USAGE_COUNT = 'graphql-legacy-adapter-usage-count';
620
+ /**
621
+ * W-12025795
622
+ * GraphQL metric
623
+ * Counter to track errors in usage of legacy adapter
624
+ */
625
+ const GRAPHQL_LEGACY_ADAPTER_ERRORS_IN_RESPONSE_COUNT = 'graphql-legacy-adapter-errors-in-response-count';
626
+ /**
627
+ * W-15022402
628
+ * Predictive Data loading predict() Activity Tracking
629
+ * Activity name to track duration, errors for predictive data loading's predict() function
630
+ */
631
+ const PREDICTIVE_DATA_LOADING_PREDICT = 'predictive-data-loading-predict';
632
+ /**
633
+ * W-15022402
634
+ * Predictive Data loading saveRequest() Activity Tracking
635
+ * Activity name to track duration, errors for predictive data loading's saveRequest() function
636
+ */
637
+ const PREDICTIVE_DATA_LOADING_SAVE_REQUEST = 'predictive-data-loading-save-request';
638
+ /**
639
+ * W-18777099
640
+ * Prefetch Using State Managers Activity Tracking
641
+ * Activity name to track errors from prefetch data for top entities using state managers
642
+ */
643
+ const PREFETCH_USING_STATE_MANAGERS = 'prefetch-using-state-managers';
644
+ /**
645
+ * W-18812516
646
+ * State Manager metric
647
+ * Counter to track number of state managers created
648
+ */
649
+ const STATE_CREATED_COUNT = 'state-created-count';
650
+
651
+ var metricKeys = /*#__PURE__*/Object.freeze({
652
+ __proto__: null,
653
+ ADAPTER_CACHE_HIT_COUNT_METRIC_NAME: ADAPTER_CACHE_HIT_COUNT_METRIC_NAME,
654
+ ADAPTER_CACHE_HIT_DURATION_METRIC_NAME: ADAPTER_CACHE_HIT_DURATION_METRIC_NAME,
655
+ ADAPTER_CACHE_HIT_L2_COUNT_METRIC_NAME: ADAPTER_CACHE_HIT_L2_COUNT_METRIC_NAME,
656
+ ADAPTER_CACHE_HIT_L2_DURATION_METRIC_NAME: ADAPTER_CACHE_HIT_L2_DURATION_METRIC_NAME,
657
+ ADAPTER_CACHE_MISS_COUNT_METRIC_NAME: ADAPTER_CACHE_MISS_COUNT_METRIC_NAME,
658
+ ADAPTER_CACHE_MISS_DURATION_METRIC_NAME: ADAPTER_CACHE_MISS_DURATION_METRIC_NAME,
659
+ ADAPTER_CACHE_MISS_OUT_OF_TTL_COUNT_METRIC_NAME: ADAPTER_CACHE_MISS_OUT_OF_TTL_COUNT_METRIC_NAME,
660
+ ADAPTER_CACHE_MISS_OUT_OF_TTL_DURATION_METRIC_NAME: ADAPTER_CACHE_MISS_OUT_OF_TTL_DURATION_METRIC_NAME,
661
+ AGGREGATE_UI_CHUNK_COUNT: AGGREGATE_UI_CHUNK_COUNT,
662
+ CACHE_HIT_COUNT: CACHE_HIT_COUNT,
663
+ CACHE_MISS_COUNT: CACHE_MISS_COUNT,
664
+ CACHE_POLICY_COUNTERS: CACHE_POLICY_COUNTERS,
665
+ CACHE_POLICY_UNDEFINED_COUNTER: CACHE_POLICY_UNDEFINED_COUNTER,
666
+ DUPLICATE_REQUEST_COUNT: DUPLICATE_REQUEST_COUNT,
667
+ DURABLE_STORE_COUNT: DURABLE_STORE_COUNT,
668
+ GET_GRAPHQL_RESPONSE_MIXED: GET_GRAPHQL_RESPONSE_MIXED,
669
+ GET_RECORD_AGGREGATE_INVOKE_COUNT: GET_RECORD_AGGREGATE_INVOKE_COUNT,
670
+ GET_RECORD_NORMAL_INVOKE_COUNT: GET_RECORD_NORMAL_INVOKE_COUNT,
671
+ GET_RECORD_NOTIFY_CHANGE_ALLOW_COUNT: GET_RECORD_NOTIFY_CHANGE_ALLOW_COUNT,
672
+ GET_RECORD_NOTIFY_CHANGE_DROP_COUNT: GET_RECORD_NOTIFY_CHANGE_DROP_COUNT,
673
+ GRAPHQL_ADAPTER_COUNT: GRAPHQL_ADAPTER_COUNT,
674
+ GRAPHQL_LEGACY_ADAPTER_ERRORS_IN_RESPONSE_COUNT: GRAPHQL_LEGACY_ADAPTER_ERRORS_IN_RESPONSE_COUNT,
675
+ GRAPHQL_LEGACY_ADAPTER_USAGE_COUNT: GRAPHQL_LEGACY_ADAPTER_USAGE_COUNT,
676
+ GRAPHQL_QUERY_OTHER_ERROR_COUNT: GRAPHQL_QUERY_OTHER_ERROR_COUNT,
677
+ GRAPHQL_QUERY_SYNTAX_ERROR_COUNT: GRAPHQL_QUERY_SYNTAX_ERROR_COUNT,
678
+ GRAPHQL_QUERY_VALIDATION_ERROR_COUNT: GRAPHQL_QUERY_VALIDATION_ERROR_COUNT,
679
+ NETWORK_RATE_LIMIT_EXCEEDED_COUNT: NETWORK_RATE_LIMIT_EXCEEDED_COUNT,
680
+ NOTIFY_RECORD_UPDATE_AVAILABLE_ALLOW_COUNT: NOTIFY_RECORD_UPDATE_AVAILABLE_ALLOW_COUNT,
681
+ NOTIFY_RECORD_UPDATE_AVAILABLE_DROP_COUNT: NOTIFY_RECORD_UPDATE_AVAILABLE_DROP_COUNT,
682
+ NOTIFY_STORE_UPDATE_AVAILABLE_DURATION: NOTIFY_STORE_UPDATE_AVAILABLE_DURATION,
683
+ PREDICTIVE_DATA_LOADING_PREDICT: PREDICTIVE_DATA_LOADING_PREDICT,
684
+ PREDICTIVE_DATA_LOADING_SAVE_REQUEST: PREDICTIVE_DATA_LOADING_SAVE_REQUEST,
685
+ PREFETCH_USING_STATE_MANAGERS: PREFETCH_USING_STATE_MANAGERS,
686
+ RECORD_TYPE_ID_IS_NULL_COUNT: RECORD_TYPE_ID_IS_NULL_COUNT,
687
+ REPRESENTATION_CACHE_MISS_OUT_OF_TTL_DATA_CHANGED_COUNT_METRIC_NAME: REPRESENTATION_CACHE_MISS_OUT_OF_TTL_DATA_CHANGED_COUNT_METRIC_NAME,
688
+ REPRESENTATION_CACHE_MISS_OUT_OF_TTL_DATA_CHANGED_DURATION_METRIC_NAME: REPRESENTATION_CACHE_MISS_OUT_OF_TTL_DATA_CHANGED_DURATION_METRIC_NAME,
689
+ REPRESENTATION_CACHE_MISS_OUT_OF_TTL_DATA_UNCHANGED_COUNT_METRIC_NAME: REPRESENTATION_CACHE_MISS_OUT_OF_TTL_DATA_UNCHANGED_COUNT_METRIC_NAME,
690
+ REPRESENTATION_CACHE_MISS_OUT_OF_TTL_DATA_UNCHANGED_DURATION_METRIC_NAME: REPRESENTATION_CACHE_MISS_OUT_OF_TTL_DATA_UNCHANGED_DURATION_METRIC_NAME,
691
+ STALE_TAG: STALE_TAG,
692
+ STATE_CREATED_COUNT: STATE_CREATED_COUNT,
693
+ STORE_BROADCAST_DURATION: STORE_BROADCAST_DURATION,
694
+ STORE_GRAPHQL_SIZE_COUNT: STORE_GRAPHQL_SIZE_COUNT,
695
+ STORE_INGEST_DURATION: STORE_INGEST_DURATION,
696
+ STORE_LOOKUP_DURATION: STORE_LOOKUP_DURATION,
697
+ STORE_SET_DEFAULT_TTL_OVERRIDE_DURATION: STORE_SET_DEFAULT_TTL_OVERRIDE_DURATION,
698
+ STORE_SET_TTL_OVERRIDE_DURATION: STORE_SET_TTL_OVERRIDE_DURATION,
699
+ STORE_SIZE_COUNT: STORE_SIZE_COUNT,
700
+ STORE_SNAPSHOT_SUBSCRIPTIONS_COUNT: STORE_SNAPSHOT_SUBSCRIPTIONS_COUNT,
701
+ STORE_TRIM_TASK_COUNT: STORE_TRIM_TASK_COUNT,
702
+ STORE_TRIM_TASK_DURATION: STORE_TRIM_TASK_DURATION,
703
+ STORE_WATCH_SUBSCRIPTIONS_COUNT: STORE_WATCH_SUBSCRIPTIONS_COUNT
704
+ });
705
+
706
+ /**
707
+ * Observability / Critical Availability Program (230+)
708
+ *
709
+ * This file is intended to be used as a consolidated place for all definitions, functions,
710
+ * and helpers related to "M1"[1].
711
+ *
712
+ * Below are the R.E.A.D.S. metrics for the Lightning Data Service, defined here[2].
713
+ *
714
+ * [1] Search "[M1] Lightning Data Service Design Spike" in Quip
715
+ * [2] Search "Lightning Data Service R.E.A.D.S. Metrics" in Quip
716
+ */
717
+ const OBSERVABILITY_NAMESPACE = 'LIGHTNING.lds.service';
718
+ const ADAPTER_INVOCATION_COUNT_METRIC_NAME = 'request';
719
+ const ADAPTER_ERROR_COUNT_METRIC_NAME = 'error';
720
+ /**
721
+ * W-8828410
722
+ * Counter for the number of UnfulfilledSnapshotErrors the luvio engine has.
723
+ */
724
+ const TOTAL_ADAPTER_ERROR_COUNT = ADAPTER_ERROR_COUNT_METRIC_NAME;
725
+ /**
726
+ * W-8828410
727
+ * Counter for the number of invocations made into LDS by a wire adapter.
728
+ */
729
+ const TOTAL_ADAPTER_REQUEST_SUCCESS_COUNT = ADAPTER_INVOCATION_COUNT_METRIC_NAME;
730
+
731
+ const { create, keys } = Object;
732
+ const { isArray } = Array;
733
+ const { parse, stringify } = JSON;
734
+
735
+ /**
736
+ * Inspired by https://www.npmjs.com/package/hashlru
737
+ */
738
+ class LRUCache {
739
+ constructor(limit) {
740
+ this.oldCache = new Map();
741
+ this.newCache = new Map();
742
+ this.size = 0;
743
+ this.limit = limit;
744
+ }
745
+ checkSize() {
746
+ if (this.size >= this.limit) {
747
+ this.size = 0;
748
+ this.oldCache = this.newCache;
749
+ this.newCache = new Map();
750
+ }
751
+ }
752
+ get(key) {
753
+ if (this.newCache.has(key)) {
754
+ return this.newCache.get(key);
755
+ }
756
+ else if (this.oldCache.has(key)) {
757
+ const value = this.oldCache.get(key);
758
+ this.oldCache.delete(key);
759
+ this.newCache.set(key, value);
760
+ this.size += 1;
761
+ this.checkSize();
762
+ return value;
763
+ }
764
+ return undefined;
765
+ }
766
+ set(key, value) {
767
+ if (this.newCache.has(key)) {
768
+ this.newCache.set(key, value);
769
+ }
770
+ else {
771
+ this.newCache.set(key, value);
772
+ this.size += 1;
773
+ this.checkSize();
774
+ }
775
+ }
776
+ delete(key) {
777
+ if (this.newCache.has(key)) {
778
+ this.newCache.delete(key);
779
+ this.size -= 1;
780
+ }
781
+ else if (this.oldCache.has(key)) {
782
+ this.oldCache.delete(key);
783
+ }
784
+ }
785
+ }
786
+
787
+ /**
788
+ * A deterministic JSON stringify implementation. Heavily adapted from https://github.com/epoberezkin/fast-json-stable-stringify.
789
+ * This is needed because insertion order for JSON.stringify(object) affects output:
790
+ * JSON.stringify({a: 1, b: 2})
791
+ * "{"a":1,"b":2}"
792
+ * JSON.stringify({b: 2, a: 1})
793
+ * "{"b":2,"a":1}"
794
+ * Modified from the apex implementation to sort arrays non-destructively.
795
+ * @param data Data to be JSON-stringified.
796
+ * @returns JSON.stringified value with consistent ordering of keys.
797
+ */
798
+ function stableJSONStringify(node) {
799
+ // This is for Date values.
800
+ if (node && node.toJSON && typeof node.toJSON === 'function') {
801
+ // eslint-disable-next-line no-param-reassign
802
+ node = node.toJSON();
803
+ }
804
+ if (node === undefined) {
805
+ return;
806
+ }
807
+ if (typeof node === 'number') {
808
+ return isFinite(node) ? '' + node : 'null';
809
+ }
810
+ if (typeof node !== 'object') {
811
+ return stringify(node);
812
+ }
813
+ let i;
814
+ let out;
815
+ if (isArray(node)) {
816
+ // copy any array before sorting so we don't mutate the object.
817
+ // eslint-disable-next-line no-param-reassign
818
+ node = node.slice(0).sort();
819
+ out = '[';
820
+ for (i = 0; i < node.length; i++) {
821
+ if (i) {
822
+ out += ',';
823
+ }
824
+ out += stableJSONStringify(node[i]) || 'null';
825
+ }
826
+ return out + ']';
827
+ }
828
+ if (node === null) {
829
+ return 'null';
830
+ }
831
+ const keys$1 = keys(node).sort();
832
+ out = '';
833
+ for (i = 0; i < keys$1.length; i++) {
834
+ const key = keys$1[i];
835
+ const value = stableJSONStringify(node[key]);
836
+ if (!value) {
837
+ continue;
838
+ }
839
+ if (out) {
840
+ out += ',';
841
+ }
842
+ out += stringify(key) + ':' + value;
843
+ }
844
+ return '{' + out + '}';
845
+ }
846
+ function isPromise(value) {
847
+ // check for Thenable due to test frameworks using custom Promise impls
848
+ return value !== null && value !== undefined && typeof value.then === 'function';
849
+ }
850
+ function isAdapterError(error) {
851
+ if (typeof error !== 'string') {
852
+ return false;
853
+ }
854
+ const parsedError = parse(error);
855
+ return parsedError.errorType !== undefined && parsedError.errorType === 'adapterError';
856
+ }
857
+ function throttle(callback, ms) {
858
+ let waiting = false;
859
+ return () => {
860
+ if (!waiting) {
861
+ callback();
862
+ waiting = true;
863
+ setTimeout(() => (waiting = false), ms);
864
+ }
865
+ };
866
+ }
867
+
868
+ const NAMESPACE = 'lds';
869
+ const APEX_ADAPTER_NAME = 'getApex';
870
+ const STATE_MANAGERS_NAMESPACE = 'state-managers';
871
+ const NORMALIZED_APEX_ADAPTER_NAME = createMetricsKey('Apex', APEX_ADAPTER_NAME);
872
+ const GRAPHQL_ADAPTER_NAME = 'graphQL';
873
+ const GRAPHQL_RECORDS_KEY = 'GraphQL::graphql__uiapi__query';
874
+ const ldsInstrumentation = getInstrumentation(NAMESPACE);
875
+ const observabilityInstrumentation = getInstrumentation(OBSERVABILITY_NAMESPACE);
876
+ const stateManagersInstrumentation = getInstrumentation(STATE_MANAGERS_NAMESPACE);
877
+ class Instrumentation {
878
+ /**
879
+ * Injected to LDS for Luvio specific instrumentation.
880
+ *
881
+ * @param context The transaction context.
882
+ */
883
+ instrumentLuvio(_context) {
884
+ // TODO [W-9783151]: refactor luvio.instrument to not require this class
885
+ }
886
+ }
887
+ /**
888
+ * Provide this method for the instrument option for a Luvio instance.
889
+ * @param context The transaction context.
890
+ */
891
+ function instrumentLuvio(context) {
892
+ if (isAdapterUnfulfilledError(context)) {
893
+ // We are consolidating all apex adapter instrumentation calls under a single key
894
+ const normalizedContext = {
895
+ ...context,
896
+ adapterName: normalizeAdapterName(context.adapterName),
897
+ };
898
+ incrementAdapterRequestErrorCount(normalizedContext);
899
+ logAdapterRequestError(normalizedContext);
900
+ }
901
+ }
902
+ /**
903
+ * Returns whether or not this is an AdapterUnfulfilledError.
904
+ * @param context The transaction context.
905
+ * @returns Whether or not this is an AdapterUnfulfilledError.
906
+ */
907
+ function isAdapterUnfulfilledError(context) {
908
+ return context[ADAPTER_UNFULFILLED_ERROR] === true;
909
+ }
910
+ /**
911
+ * W-8620679
912
+ * Increment the counter for an UnfulfilledSnapshotError coming from luvio
913
+ *
914
+ * @param context The transaction context.
915
+ */
916
+ function incrementAdapterRequestErrorCount(context) {
917
+ const adapterRequestErrorCounter = createMetricsKey(ADAPTER_ERROR_COUNT_METRIC_NAME, context.adapterName);
918
+ observabilityInstrumentation.incrementCounter(adapterRequestErrorCounter);
919
+ observabilityInstrumentation.incrementCounter(TOTAL_ADAPTER_ERROR_COUNT);
920
+ }
921
+ /**
922
+ * W-10495632
923
+ * Logs the missing paths and/or links associated with the UnfulfilledSnapshotError.
924
+ *
925
+ * @param context The transaction context.
926
+ */
927
+ function logAdapterRequestError(context) {
928
+ ldsInstrumentation.error(ADAPTER_UNFULFILLED_ERROR, adapterUnfulfilledErrorSchema, {
929
+ adapter: context.adapterName,
930
+ missing_paths: keys(context.missingPaths),
931
+ missing_links: keys(context.missingLinks),
932
+ });
933
+ }
934
+ /**
935
+ * W-13639107
936
+ * Logs when object info has changed
937
+ */
938
+ function logObjectInfoChanged() {
939
+ ldsInstrumentation.log('objectInfoChanged');
940
+ }
941
+ /**
942
+ * Increment the counter based on the cache policy type for an adapter call
943
+ *
944
+ * @param requestContext Adapter request context that includes cache policy
945
+ */
946
+ function incrementAdapterCachePolicyType(requestContext) {
947
+ const cachePolicy = requestContext && requestContext.cachePolicy && requestContext.cachePolicy.type;
948
+ if (cachePolicy !== undefined) {
949
+ ldsInstrumentation.incrementCounter(CACHE_POLICY_COUNTERS[cachePolicy], 1);
950
+ return;
951
+ }
952
+ ldsInstrumentation.incrementCounter(CACHE_POLICY_UNDEFINED_COUNTER, 1);
953
+ }
954
+ /**
955
+ * Increment the counter based on missing recordTypeId in a record ingest
956
+ *
957
+ * @param apiName incoming API name for bad data
958
+ */
959
+ function incrementRecordTypeIdIsNullCount(apiName) {
960
+ const adapterRequestErrorCounter = createMetricsKey(RECORD_TYPE_ID_IS_NULL_COUNT, apiName);
961
+ observabilityInstrumentation.incrementCounter(adapterRequestErrorCounter);
962
+ }
963
+ /**
964
+ * 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.
965
+ * Backed by an LRU Cache implementation to prevent too many record entries from being stored in-memory.
966
+ * @param name The wire adapter name.
967
+ * @param config The config passed into wire adapter.
968
+ * @param currentCacheMissTimestamp Timestamp for when the request was made.
969
+ * @param ttl TTL for the wire adapter.
970
+ * @param durationMetricName Name for duration metric.
971
+ * @param counterMetricName Name for counter metric.
972
+ */
973
+ const adapterCacheMisses = new LRUCache(250);
974
+ function logAdapterCacheMissOutOfTtlDuration(name, config, currentCacheMissTimestamp, ttl, counterMetricName, durationMetricName) {
975
+ const configKey = `${name}:${stableJSONStringify(config)}`;
976
+ const existingCacheMissTimestamp = adapterCacheMisses.get(configKey);
977
+ adapterCacheMisses.set(configKey, currentCacheMissTimestamp);
978
+ if (existingCacheMissTimestamp !== undefined) {
979
+ const duration = currentCacheMissTimestamp - existingCacheMissTimestamp;
980
+ if (duration > ttl) {
981
+ ldsInstrumentation.incrementCounter(counterMetricName, 1);
982
+ ldsInstrumentation.trackValue(durationMetricName, duration);
983
+ }
984
+ }
985
+ }
986
+ /**
987
+ * Starts an o11y Activity using the supplied o11y Instrumention and adapterName.
988
+ * If a requestContext is supplied, we check for the existence of a requestCorrelator containing an ObservabilityContext.
989
+ * If an ObservabilityContext is defined, we build an object that conforms to the ApiOptions interface required by the startActivity API
990
+ */
991
+ function startAdapterActivity(instrumentation, adapterName, requestContext) {
992
+ if (requestContext === undefined ||
993
+ requestContext.requestCorrelator === undefined ||
994
+ getObservabilityContext(requestContext) === undefined) {
995
+ return instrumentation.startActivity(adapterName);
996
+ }
997
+ const { traceId, ...rest } = requestContext.requestCorrelator
998
+ .observabilityContext;
999
+ const apiOptions = {
1000
+ instrumentationContext: rest,
1001
+ };
1002
+ if (traceId !== undefined) {
1003
+ apiOptions.instrumentationContext.parentId = traceId;
1004
+ }
1005
+ return instrumentation.startActivity(adapterName, apiOptions);
1006
+ }
1007
+ const executeAsyncActivityDefaultOptions = {
1008
+ LOG_ERROR_ONLY: false,
1009
+ };
1010
+ /**
1011
+ * Starts an async o11y Activity using ldsInstrumentation.
1012
+ *
1013
+ * Heavily borrowed from o11y asyncActivity, but actually swallows the error instead of rethrowing.
1014
+ *
1015
+ */
1016
+ async function executeAsyncActivity(name, execute, options) {
1017
+ const normalizedOptions = Object.assign({}, executeAsyncActivityDefaultOptions, options);
1018
+ const act = ldsInstrumentation.startActivity(name, options);
1019
+ let isError = false;
1020
+ try {
1021
+ return await execute(act);
1022
+ }
1023
+ catch (err) {
1024
+ if (normalizedOptions.errorPayload?.schema) {
1025
+ act.error(err, normalizedOptions.errorPayload.schema, normalizedOptions.errorPayload?.payload);
1026
+ }
1027
+ else {
1028
+ act.error(err, normalizedOptions.ERROR_SCOPE);
1029
+ }
1030
+ isError = true;
1031
+ }
1032
+ finally {
1033
+ if (normalizedOptions.LOG_ERROR_ONLY && !isError) {
1034
+ act.discard();
1035
+ }
1036
+ else {
1037
+ act.stop(normalizedOptions?.stopPayload?.schema, normalizedOptions?.stopPayload?.payload);
1038
+ }
1039
+ }
1040
+ }
1041
+ function instrumentAdapter(adapter, metadata, adapterInstrumentationOptions) {
1042
+ const { apiFamily, name, ttl } = metadata;
1043
+ let trackL1Hits = false;
1044
+ let trackL2Hits = false;
1045
+ let trackCacheMisses = false;
1046
+ let reportObserver = undefined;
1047
+ if (adapterInstrumentationOptions !== undefined) {
1048
+ ({ trackL1Hits, trackL2Hits, trackCacheMisses, reportObserver } =
1049
+ adapterInstrumentationOptions);
1050
+ }
1051
+ const adapterName = normalizeAdapterName(name, apiFamily);
1052
+ /**
1053
+ * W-8076905
1054
+ * Dynamically generated metric. Simple counter for all requests made by this adapter.
1055
+ */
1056
+ const wireAdapterRequestMetric = createMetricsKey(ADAPTER_INVOCATION_COUNT_METRIC_NAME, adapterName);
1057
+ /**
1058
+ * W-6981216
1059
+ * Dynamically generated metric. Simple counter for cache hits by adapter name.
1060
+ */
1061
+ const cacheHitCountByAdapterMetric = createMetricsKey(ADAPTER_CACHE_HIT_COUNT_METRIC_NAME, adapterName);
1062
+ /**
1063
+ * W-10490326
1064
+ * Dynamically generated metric. Simple counter for L2 cache hits by adapter name.
1065
+ */
1066
+ const l2CacheHitCountByAdapterMetric = createMetricsKey(ADAPTER_CACHE_HIT_L2_COUNT_METRIC_NAME, adapterName);
1067
+ /**
1068
+ * W-7404607
1069
+ * Dynamically generated metric. Timer for cache hits by adapter name.
1070
+ */
1071
+ const cacheHitDurationByAdapterMetric = createMetricsKey(ADAPTER_CACHE_HIT_DURATION_METRIC_NAME, adapterName);
1072
+ /**
1073
+ * W-10490326
1074
+ * Dynamically generated metric. Timer for L2 cache hits by adapter name.
1075
+ */
1076
+ const l2CacheHitDurationByAdapterMetric = createMetricsKey(ADAPTER_CACHE_HIT_L2_DURATION_METRIC_NAME, adapterName);
1077
+ /**
1078
+ * W-6981216
1079
+ * Dynamically generated metric. Simple counter for cache misses by adapter name.
1080
+ */
1081
+ const cacheMissCountByAdapterMetric = createMetricsKey(ADAPTER_CACHE_MISS_COUNT_METRIC_NAME, adapterName);
1082
+ /**
1083
+ * W-7404607
1084
+ * Dynamically generated metric. Timer for cache hits by adapter name.
1085
+ */
1086
+ const cacheMissDurationByAdapterMetric = createMetricsKey(ADAPTER_CACHE_MISS_DURATION_METRIC_NAME, adapterName);
1087
+ /**
1088
+ * W-7376275
1089
+ * Dynamically generated metric. Measures the amount of time it takes for LDS to get another cache miss on
1090
+ * a request we've made in the past.
1091
+ * Request Record 1 -> Record 2 -> Back to Record 1 outside of TTL is an example of when this metric will fire.
1092
+ */
1093
+ const cacheMissOutOfTtlDurationByAdapterMetric = createMetricsKey(ADAPTER_CACHE_MISS_OUT_OF_TTL_DURATION_METRIC_NAME, adapterName);
1094
+ const cacheMissOutOfTtlCountByAdapterMetric = createMetricsKey(ADAPTER_CACHE_MISS_OUT_OF_TTL_COUNT_METRIC_NAME, adapterName);
1095
+ const instrumentedAdapter = (config, requestContext) => {
1096
+ // increment adapter request metrics
1097
+ observabilityInstrumentation.incrementCounter(wireAdapterRequestMetric, 1);
1098
+ observabilityInstrumentation.incrementCounter(TOTAL_ADAPTER_REQUEST_SUCCESS_COUNT, 1);
1099
+ // increment cache policy metrics
1100
+ incrementAdapterCachePolicyType(requestContext);
1101
+ // start collecting
1102
+ const activity = startAdapterActivity(ldsInstrumentation, adapterName, requestContext);
1103
+ // swap in activity's Id if observabilityContext exists
1104
+ updateRequestContext(activity, requestContext);
1105
+ return runAdapterWithReport(metadata.name, adapter, config, requestContext || {}, (report) => {
1106
+ const { executionTime } = report;
1107
+ switch (report.result) {
1108
+ case 'l1-hit': {
1109
+ ldsInstrumentation.incrementCounter(ADAPTER_CACHE_HIT_COUNT_METRIC_NAME, 1);
1110
+ ldsInstrumentation.incrementCounter(cacheHitCountByAdapterMetric, 1);
1111
+ ldsInstrumentation.trackValue(cacheHitDurationByAdapterMetric, executionTime);
1112
+ if (trackL1Hits) {
1113
+ activity.stop('l1-hit');
1114
+ }
1115
+ else {
1116
+ activity.discard();
1117
+ }
1118
+ break;
1119
+ }
1120
+ case 'l2-hit': {
1121
+ let tags = undefined;
1122
+ if (report.stale === true) {
1123
+ tags = { [STALE_TAG]: true };
1124
+ }
1125
+ ldsInstrumentation.incrementCounter(ADAPTER_CACHE_HIT_L2_COUNT_METRIC_NAME, 1, undefined, tags);
1126
+ ldsInstrumentation.incrementCounter(l2CacheHitCountByAdapterMetric, 1, undefined, tags);
1127
+ ldsInstrumentation.trackValue(l2CacheHitDurationByAdapterMetric, executionTime, undefined, tags);
1128
+ if (trackL2Hits) {
1129
+ activity.stop('l2-hit');
1130
+ }
1131
+ else {
1132
+ activity.discard();
1133
+ }
1134
+ break;
1135
+ }
1136
+ case 'cache-miss':
1137
+ {
1138
+ ldsInstrumentation.trackValue(cacheMissDurationByAdapterMetric, executionTime);
1139
+ ldsInstrumentation.incrementCounter(ADAPTER_CACHE_MISS_COUNT_METRIC_NAME, 1);
1140
+ ldsInstrumentation.incrementCounter(cacheMissCountByAdapterMetric, 1);
1141
+ if (trackCacheMisses) {
1142
+ activity.stop('cache-miss');
1143
+ }
1144
+ else {
1145
+ activity.discard();
1146
+ }
1147
+ if (ttl !== undefined) {
1148
+ logAdapterCacheMissOutOfTtlDuration(adapterName, config, Date.now(), ttl, cacheMissOutOfTtlCountByAdapterMetric, cacheMissOutOfTtlDurationByAdapterMetric);
1149
+ }
1150
+ }
1151
+ break;
1152
+ case 'error':
1153
+ {
1154
+ const error = report.error;
1155
+ // We are capturing userland transient errors through here, and
1156
+ // there is a chance that these errors could contain PII, as seen in W-12224448.
1157
+ // Log AdapterErrors only
1158
+ if (isAdapterError(error)) {
1159
+ activity.error(error);
1160
+ }
1161
+ activity.stop('error');
1162
+ }
1163
+ break;
1164
+ case 'exception':
1165
+ case 'invalid-config':
1166
+ {
1167
+ activity.discard();
1168
+ }
1169
+ break;
1170
+ default: {
1171
+ if ('production' !== process.env.NODE_ENV) {
1172
+ throw new Error(`unsupported adapter result`);
1173
+ }
1174
+ }
1175
+ }
1176
+ if (reportObserver !== undefined) {
1177
+ reportObserver(report);
1178
+ }
1179
+ });
1180
+ };
1181
+ // Set the name property on the function for debugging purposes.
1182
+ Object.defineProperty(instrumentedAdapter, 'name', {
1183
+ value: name + '__instrumented',
1184
+ });
1185
+ return isGraphqlAdapter(name) === true
1186
+ ? instrumentGraphqlAdapter(instrumentedAdapter)
1187
+ : instrumentedAdapter;
1188
+ }
1189
+ /**
1190
+ * Replaces observabilityContext's traceId with parentActivity's Id since traceId
1191
+ * is treated as parentId when observabilityContext is passed to native as part of
1192
+ * the network request.
1193
+ * @param parentActivity
1194
+ * @param requestContext
1195
+ */
1196
+ function updateRequestContext(parentActivity, requestContext) {
1197
+ if (requestContext !== undefined) {
1198
+ const observabilityContext = getObservabilityContext(requestContext);
1199
+ if (observabilityContext !== undefined) {
1200
+ observabilityContext.traceId = parentActivity.getId();
1201
+ }
1202
+ }
1203
+ }
1204
+ function getObservabilityContext(requestContext) {
1205
+ return requestContext.requestCorrelator !== undefined
1206
+ ? requestContext.requestCorrelator.observabilityContext
1207
+ : undefined;
1208
+ }
1209
+ /**
1210
+ * Any graphql get adapter specific instrumentation that we need to log
1211
+ * @param snapshot from either in-memory or built after a network hit
1212
+ */
1213
+ function logGraphqlMetrics(snapshot, config) {
1214
+ // We have both data and error in the returned response
1215
+ const { data: snapshotData } = snapshot;
1216
+ if (snapshotData &&
1217
+ snapshotData.data &&
1218
+ keys(snapshotData.data).length > 0 &&
1219
+ snapshotData.errors &&
1220
+ snapshotData.errors.length > 0) {
1221
+ ldsInstrumentation.incrementCounter(GET_GRAPHQL_RESPONSE_MIXED);
1222
+ }
1223
+ if (config && 'useUiApiAdapter' in config && config.useUiApiAdapter === false) {
1224
+ // using legacy adapter
1225
+ ldsInstrumentation.incrementCounter(GRAPHQL_LEGACY_ADAPTER_USAGE_COUNT);
1226
+ if (snapshotData && snapshotData.errors && snapshotData.errors.length > 0) {
1227
+ // track errors in legacy adapter responses
1228
+ ldsInstrumentation.incrementCounter(GRAPHQL_LEGACY_ADAPTER_ERRORS_IN_RESPONSE_COUNT);
1229
+ }
1230
+ }
1231
+ if (snapshotData.errors && snapshotData.errors.length > 0) {
1232
+ // W-12293528 GraphQL metrics
1233
+ // log counts of returned errors
1234
+ let validationErrorCount = 0;
1235
+ let syntaxErrorCount = 0;
1236
+ let otherErrorCount = 0;
1237
+ // conslidate instrumentation calls with # of occurrences
1238
+ snapshotData.errors.forEach((e) => {
1239
+ if (e.message && e.message.startsWith('Validation error')) {
1240
+ validationErrorCount++;
1241
+ }
1242
+ else if (e.message && e.message.startsWith('Invalid Syntax')) {
1243
+ syntaxErrorCount++;
1244
+ }
1245
+ else {
1246
+ otherErrorCount++;
1247
+ }
1248
+ });
1249
+ if (validationErrorCount > 0) {
1250
+ ldsInstrumentation.incrementCounter(GRAPHQL_QUERY_VALIDATION_ERROR_COUNT, validationErrorCount);
1251
+ }
1252
+ if (syntaxErrorCount > 0) {
1253
+ ldsInstrumentation.incrementCounter(GRAPHQL_QUERY_SYNTAX_ERROR_COUNT, syntaxErrorCount);
1254
+ }
1255
+ if (otherErrorCount > 0) {
1256
+ ldsInstrumentation.incrementCounter(GRAPHQL_QUERY_OTHER_ERROR_COUNT, otherErrorCount);
1257
+ }
1258
+ }
1259
+ }
1260
+ /**
1261
+ * Wraps methods to collect runtime performance using o11y's trackValue API
1262
+ * @param obj Object instance containing the methods to instrument
1263
+ * @param methods array containing objects with keys for the method name and the metric key to use in o11y
1264
+ */
1265
+ function instrumentMethods(obj, methods) {
1266
+ for (let i = 0, len = methods.length; i < len; i++) {
1267
+ const { methodName, metricKey } = methods[i];
1268
+ const originalMethod = obj[methodName];
1269
+ obj[methodName] = function (...args) {
1270
+ const startTime = Date.now();
1271
+ try {
1272
+ const res = originalMethod.call(this, ...args);
1273
+ const executionTime = Date.now() - startTime;
1274
+ // handle async resolved/rejected
1275
+ if (isPromise(res)) {
1276
+ res.then(() => {
1277
+ ldsInstrumentation.trackValue(metricKey, Date.now() - startTime);
1278
+ }).catch((_error) => {
1279
+ ldsInstrumentation.trackValue(metricKey, Date.now() - startTime, true);
1280
+ });
1281
+ }
1282
+ else {
1283
+ // handle synchronous success
1284
+ ldsInstrumentation.trackValue(metricKey, executionTime);
1285
+ }
1286
+ return res;
1287
+ }
1288
+ catch (error) {
1289
+ // handle synchronous throw
1290
+ ldsInstrumentation.trackValue(metricKey, Date.now() - startTime, true);
1291
+ // rethrow error
1292
+ throw error;
1293
+ }
1294
+ };
1295
+ }
1296
+ }
1297
+ function createMetricsKey(name, unit) {
1298
+ let metricName = name;
1299
+ if (unit) {
1300
+ metricName = metricName + '.' + unit;
1301
+ }
1302
+ return metricName;
1303
+ }
1304
+ /**
1305
+ * Returns whether adapter is an Apex one or not.
1306
+ * @param adapterName The name of the adapter.
1307
+ */
1308
+ function isApexAdapter(adapterName) {
1309
+ return adapterName.indexOf(APEX_ADAPTER_NAME) > -1;
1310
+ }
1311
+ /**
1312
+ * Returns boolean whether adapter is a graphQL one or not.
1313
+ * @param adapterName The name of the adapter.
1314
+ */
1315
+ function isGraphqlAdapter(adapterName) {
1316
+ return adapterName === GRAPHQL_ADAPTER_NAME;
1317
+ }
1318
+ /**
1319
+ * Normalizes getApex adapter names to `Apex.getApex`. Non-Apex adapters will be prefixed with
1320
+ * API family, if supplied. Example: `UiApi.getRecord`.
1321
+ *
1322
+ * Note: If you are adding additional logging that can come from getApex adapter contexts that provide
1323
+ * the full getApex adapter name (i.e. getApex_[namespace]_[class]_[function]_[continuation]),
1324
+ * ensure to call this method to normalize all logging to 'getApex'. This
1325
+ * is because Argus has a 50k key cardinality limit. More context: W-8379680.
1326
+ *
1327
+ * @param adapterName The name of the adapter.
1328
+ * @param apiFamily The API family of the adapter.
1329
+ */
1330
+ function normalizeAdapterName(adapterName, apiFamily) {
1331
+ if (isApexAdapter(adapterName)) {
1332
+ return NORMALIZED_APEX_ADAPTER_NAME;
1333
+ }
1334
+ return apiFamily ? `${apiFamily}.${adapterName}` : adapterName;
1335
+ }
1336
+ /**
1337
+ * Logs an error to Splunk using o11y
1338
+ */
1339
+ function logError(err) {
1340
+ ldsInstrumentation.error(err);
1341
+ }
1342
+ /**
1343
+ * Calls instrumentation/service telemetry counter
1344
+ * @param name Name of the metric
1345
+ * @param value number to increment by, if undefined increment by 1
1346
+ */
1347
+ function incrementCounterMetric(name, number) {
1348
+ ldsInstrumentation.incrementCounter(name, number);
1349
+ }
1350
+ /**
1351
+ * Calls instrumentation/service telemetry percentileHistogram
1352
+ * @param name Name of the metric
1353
+ * @param value number used to update the percentileHistogram
1354
+ */
1355
+ function updatePercentileHistogramMetric(name, value) {
1356
+ ldsInstrumentation.trackValue(name, value);
1357
+ }
1358
+ function setAggregateUiChunkCountMetric(chunkCount) {
1359
+ updatePercentileHistogramMetric(AGGREGATE_UI_CHUNK_COUNT, chunkCount);
1360
+ }
1361
+ function incrementGetRecordNormalInvokeCount() {
1362
+ incrementCounterMetric(GET_RECORD_NORMAL_INVOKE_COUNT);
1363
+ }
1364
+ function incrementGetRecordAggregateInvokeCount() {
1365
+ incrementCounterMetric(GET_RECORD_AGGREGATE_INVOKE_COUNT);
1366
+ }
1367
+ function incrementGetRecordNotifyChangeAllowCount() {
1368
+ incrementCounterMetric(GET_RECORD_NOTIFY_CHANGE_ALLOW_COUNT);
1369
+ }
1370
+ function incrementGetRecordNotifyChangeDropCount() {
1371
+ incrementCounterMetric(GET_RECORD_NOTIFY_CHANGE_DROP_COUNT);
1372
+ }
1373
+ function incrementNotifyRecordUpdateAvailableAllowCount() {
1374
+ incrementCounterMetric(NOTIFY_RECORD_UPDATE_AVAILABLE_ALLOW_COUNT);
1375
+ }
1376
+ function incrementNotifyRecordUpdateAvailableDropCount() {
1377
+ incrementCounterMetric(NOTIFY_RECORD_UPDATE_AVAILABLE_DROP_COUNT);
1378
+ }
1379
+ function incrementNetworkRateLimitExceededCount() {
1380
+ incrementCounterMetric(NETWORK_RATE_LIMIT_EXCEEDED_COUNT);
1381
+ }
1382
+ function incrementStateCreatedCount() {
1383
+ stateManagersInstrumentation.incrementCounter(STATE_CREATED_COUNT);
1384
+ }
1385
+ function instrumentStoreTrimTask(callback) {
1386
+ return () => {
1387
+ ldsInstrumentation.incrementCounter(STORE_TRIM_TASK_COUNT);
1388
+ const startTime = Date.now();
1389
+ const res = callback();
1390
+ ldsInstrumentation.trackValue(STORE_TRIM_TASK_DURATION, Date.now() - startTime);
1391
+ // TODO [W-10060579]: replace record count per trim task with metric
1392
+ return res;
1393
+ };
1394
+ }
1395
+ function setStoreScheduler(store) {
1396
+ const originalScheduler = store.scheduler;
1397
+ store.scheduler = (callback, done) => {
1398
+ originalScheduler(instrumentStoreTrimTask(callback), done);
1399
+ };
1400
+ }
1401
+ function instrumentStoreStatsCallback(store) {
1402
+ return () => {
1403
+ const { snapshotSubscriptions, watchSubscriptions } = store;
1404
+ const records = store.fallbackStringKeyInMemoryStore.records;
1405
+ updatePercentileHistogramMetric(STORE_SIZE_COUNT, keys(records).length);
1406
+ if (GRAPHQL_RECORDS_KEY in records) {
1407
+ const graphQLRecordSize = keys(records[GRAPHQL_RECORDS_KEY]).length;
1408
+ updatePercentileHistogramMetric(STORE_GRAPHQL_SIZE_COUNT, graphQLRecordSize);
1409
+ }
1410
+ updatePercentileHistogramMetric(STORE_SNAPSHOT_SUBSCRIPTIONS_COUNT, keys(snapshotSubscriptions).length);
1411
+ updatePercentileHistogramMetric(STORE_WATCH_SUBSCRIPTIONS_COUNT, keys(watchSubscriptions).length);
1412
+ };
1413
+ }
1414
+ /**
1415
+ * Collects additional store statistics by tying its periodic,
1416
+ * point-in-time data collection with a luvio method
1417
+ * @param luvio
1418
+ * @param store
1419
+ */
1420
+ function setupStoreStatsCollection(luvio, callback) {
1421
+ const wrapMethod = 'storeBroadcast';
1422
+ const originalMethod = luvio[wrapMethod];
1423
+ const throttledCallback = throttle(callback, 200);
1424
+ luvio[wrapMethod] = function (...args) {
1425
+ throttledCallback();
1426
+ return originalMethod.call(this, ...args);
1427
+ };
1428
+ }
1429
+ /**
1430
+ * @param instrumentedAdapter
1431
+ * @returns instrumentedGraphqlAdapter, which logs additional metrics for get graphQL adapter
1432
+ */
1433
+ function instrumentGraphqlAdapter(instrumentedAdapter) {
1434
+ const instrumentedGraphqlAdapter = (config, requestContext) => {
1435
+ const result = instrumentedAdapter(config, requestContext);
1436
+ if (result === null) {
1437
+ return result;
1438
+ }
1439
+ if (isPromise(result)) {
1440
+ result.then((_snapshot) => {
1441
+ logGraphqlMetrics(_snapshot, config);
1442
+ });
1443
+ }
1444
+ else {
1445
+ logGraphqlMetrics(result, config);
1446
+ }
1447
+ return result;
1448
+ };
1449
+ return instrumentedGraphqlAdapter;
1450
+ }
1451
+ /**
1452
+ * Sets up instrumentation for @salesforce/lds-adapters-uiapi
1453
+ */
1454
+ function setLdsAdaptersUiapiInstrumentation(uiapiRegistration) {
1455
+ uiapiRegistration.instrument({
1456
+ recordConflictsResolved: (serverRequestCount) => {
1457
+ // Ignore 0 values which can originate from ADS bridge
1458
+ if (serverRequestCount > 0) {
1459
+ updatePercentileHistogramMetric('record-conflicts-resolved', serverRequestCount);
1460
+ }
1461
+ },
1462
+ nullDisplayValueConflict: ({ fieldType, areValuesEqual }) => {
1463
+ const metricName = `merge-null-dv-count.${fieldType}`;
1464
+ if (fieldType === 'scalar') {
1465
+ incrementCounterMetric(`${metricName}.${areValuesEqual}`);
1466
+ }
1467
+ else {
1468
+ incrementCounterMetric(metricName);
1469
+ }
1470
+ },
1471
+ getRecordNotifyChangeAllowed: incrementGetRecordNotifyChangeAllowCount,
1472
+ getRecordNotifyChangeDropped: incrementGetRecordNotifyChangeDropCount,
1473
+ notifyRecordUpdateAvailableAllowed: incrementNotifyRecordUpdateAvailableAllowCount,
1474
+ notifyRecordUpdateAvailableDropped: incrementNotifyRecordUpdateAvailableDropCount,
1475
+ recordTypeIdIsNull: incrementRecordTypeIdIsNullCount,
1476
+ });
1477
+ }
1478
+ /**
1479
+ * Sets up instrumentation for @salesforce/lds-network-adapter
1480
+ */
1481
+ function setLdsNetworkAdapterInstrumentation(networkAdapterRegistration) {
1482
+ networkAdapterRegistration.instrument({
1483
+ aggregateUiChunkCount: (cb) => setAggregateUiChunkCountMetric(cb()),
1484
+ duplicateRequest: () => incrementCounterMetric(DUPLICATE_REQUEST_COUNT),
1485
+ getRecordAggregateInvoke: incrementGetRecordAggregateInvokeCount,
1486
+ getRecordNormalInvoke: incrementGetRecordNormalInvokeCount,
1487
+ networkRateLimitExceeded: incrementNetworkRateLimitExceededCount,
1488
+ });
1489
+ }
1490
+ /**
1491
+ * Provides concrete implementations using o11y/client for instrumentation hooks
1492
+ */
1493
+ function setInstrumentationHooks() {
1494
+ instrument({
1495
+ instrumentAdapter: instrumentAdapter,
1496
+ });
1497
+ }
1498
+ /**
1499
+ * Initialize the instrumentation and instrument the LDS instance and the InMemoryStore.
1500
+ *
1501
+ * @param luvio The Luvio instance to instrument.
1502
+ * @param store The InMemoryStore to instrument.
1503
+ */
1504
+ function setupInstrumentation(luvio, store) {
1505
+ setInstrumentationHooks();
1506
+ instrumentStoreMethods(luvio);
1507
+ setupStoreStatsCollection(luvio, instrumentStoreStatsCallback(store));
1508
+ setStoreScheduler(store);
1509
+ setStoreEventObservers(store);
1510
+ }
1511
+ function instrumentStoreMethods(luvio, _store) {
1512
+ instrumentMethods(luvio, [
1513
+ { methodName: 'storeBroadcast', metricKey: STORE_BROADCAST_DURATION },
1514
+ { methodName: 'storeIngest', metricKey: STORE_INGEST_DURATION },
1515
+ { methodName: 'storeLookup', metricKey: STORE_LOOKUP_DURATION },
1516
+ { methodName: 'storeSetTTLOverride', metricKey: STORE_SET_TTL_OVERRIDE_DURATION },
1517
+ {
1518
+ methodName: 'storeSetDefaultTTLOverride',
1519
+ metricKey: STORE_SET_DEFAULT_TTL_OVERRIDE_DURATION,
1520
+ },
1521
+ {
1522
+ methodName: 'notifyStoreUpdateAvailable',
1523
+ metricKey: NOTIFY_STORE_UPDATE_AVAILABLE_DURATION,
1524
+ },
1525
+ ]);
1526
+ }
1527
+ function handleIngestedNewData(event) {
1528
+ if (event.type === 'cache-miss-out-of-ttl') {
1529
+ const { recordMetadata, oldSnapshot, newSnapshot } = event;
1530
+ const lastExpiredDurationEntry = cacheMissOutOfTtlDurations.get(event.recordId);
1531
+ if (lastExpiredDurationEntry !== undefined) {
1532
+ const representationName = `${recordMetadata.namespace}__${recordMetadata.representationName}`;
1533
+ let durationMetricName;
1534
+ let countMetricName;
1535
+ if (oldSnapshot !== newSnapshot) {
1536
+ durationMetricName =
1537
+ REPRESENTATION_CACHE_MISS_OUT_OF_TTL_DATA_CHANGED_DURATION_METRIC_NAME;
1538
+ countMetricName =
1539
+ REPRESENTATION_CACHE_MISS_OUT_OF_TTL_DATA_CHANGED_COUNT_METRIC_NAME;
1540
+ }
1541
+ else {
1542
+ durationMetricName =
1543
+ REPRESENTATION_CACHE_MISS_OUT_OF_TTL_DATA_UNCHANGED_DURATION_METRIC_NAME;
1544
+ countMetricName =
1545
+ REPRESENTATION_CACHE_MISS_OUT_OF_TTL_DATA_UNCHANGED_COUNT_METRIC_NAME;
1546
+ }
1547
+ const metricTags = {
1548
+ state: lastExpiredDurationEntry.storeResolveResultState,
1549
+ representationName: representationName,
1550
+ };
1551
+ ldsInstrumentation.trackValue(durationMetricName, lastExpiredDurationEntry.value, undefined, metricTags);
1552
+ ldsInstrumentation.incrementCounter(countMetricName, 1, undefined, metricTags);
1553
+ cacheMissOutOfTtlDurations.delete(event.recordId);
1554
+ }
1555
+ }
1556
+ }
1557
+ const cacheMissOutOfTtlDurations = new LRUCache(250);
1558
+ function handleOnDataOutOfTtlDurationUpdate(event) {
1559
+ cacheMissOutOfTtlDurations.set(event.recordId, {
1560
+ value: event.lastExpiredDuration,
1561
+ storeResolveResultState: event.storeResolveResultState,
1562
+ });
1563
+ }
1564
+ function setStoreEventObservers(store) {
1565
+ const cacheMissOutOfTtlEventObserver = {
1566
+ onCacheMissOutOfTtl: handleIngestedNewData,
1567
+ };
1568
+ const cacheMissOutOfTtlDurationUpdateEventObserver = {
1569
+ onDataOutOfTtlDurationUpdate: handleOnDataOutOfTtlDurationUpdate,
1570
+ };
1571
+ store.addStoreEventObserver(cacheMissOutOfTtlEventObserver);
1572
+ store.addStoreEventObserver(cacheMissOutOfTtlDurationUpdateEventObserver);
1573
+ }
1574
+ function onIdleDetected(callback) {
1575
+ idleDetector.requestIdleDetectedCallback((timestamp) => {
1576
+ callback(timestamp);
1577
+ });
1578
+ }
1579
+ const instrumentation = new Instrumentation();
1580
+
1581
+ export { Instrumentation, LRUCache, metricKeys as METRIC_KEYS, executeAsyncActivity, handleIngestedNewData, handleOnDataOutOfTtlDurationUpdate, incrementCounterMetric, incrementGetRecordNormalInvokeCount, incrementGetRecordNotifyChangeAllowCount, incrementGetRecordNotifyChangeDropCount, incrementNotifyRecordUpdateAvailableAllowCount, incrementNotifyRecordUpdateAvailableDropCount, incrementStateCreatedCount, instrumentAdapter, instrumentLuvio, instrumentMethods, instrumentStoreMethods, instrumentation, logError, logObjectInfoChanged, onIdleDetected, setInstrumentationHooks, setLdsAdaptersUiapiInstrumentation, setLdsNetworkAdapterInstrumentation, setStoreEventObservers, setupInstrumentation, startAdapterActivity, updatePercentileHistogramMetric };
1582
+ // version: 0.1.0-dev1-54c03dd38c