@3sixty/telemetry-connector-entity-storage 0.20.0-next.2 → 0.20.0-next.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.
@@ -1,8 +1,9 @@
1
1
  // Copyright 2024 IOTA Stiftung.
2
+ // Copyright 2026 3Sixty Trade Ltd.
2
3
  // SPDX-License-Identifier: Apache-2.0.
3
4
  import { TaskStatus } from "@3sixty/background-task-models";
4
5
  import { ContextIdKeys, ContextIdStore } from "@3sixty/context";
5
- import { AlreadyExistsError, BaseError, Coerce, ComponentFactory, Converter, GeneralError, Guards, Is, LfuCache, NotFoundError, RandomHelper } from "@3sixty/core";
6
+ import { AlreadyExistsError, BaseError, Coerce, ComponentFactory, Converter, DurationConstants, GeneralError, Guards, Is, LfuCache, NotFoundError, RandomHelper } from "@3sixty/core";
6
7
  import { ComparisonOperator, LogicalOperator, SortDirection } from "@3sixty/entity";
7
8
  import { EntityStorageConnectorFactory } from "@3sixty/entity-storage-models";
8
9
  import { MetricCounterOperation, MetricType } from "@3sixty/telemetry-models";
@@ -24,7 +25,7 @@ export class EntityStorageTelemetryConnector {
24
25
  /**
25
26
  * Default time in milliseconds to wait for the background thread to confirm a flush.
26
27
  */
27
- static DEFAULT_FLUSH_TIMEOUT_MS = 30_000;
28
+ static DEFAULT_FLUSH_TIMEOUT_MS = 30 * DurationConstants.MsPerSecond;
28
29
  /**
29
30
  * Maximum number of metric definitions to hold in the in-memory definition cache.
30
31
  */
@@ -34,7 +35,7 @@ export class EntityStorageTelemetryConnector {
34
35
  * Metric definitions are immutable once registered; a long TTI keeps active
35
36
  * metrics cached without permanent references.
36
37
  */
37
- static DEFAULT_METRIC_DEFINITION_CACHE_TTI_MS = 3_600_000;
38
+ static DEFAULT_METRIC_DEFINITION_CACHE_TTI_MS = DurationConstants.MsPerHour;
38
39
  /**
39
40
  * Default time in milliseconds values are held so several share a single background task.
40
41
  * Keeps the task write off the caller's path, which matters most for the metrics recorded
@@ -45,7 +46,7 @@ export class EntityStorageTelemetryConnector {
45
46
  * the task queue is on a database, so the window is set an order of magnitude below that:
46
47
  * one task per second whatever the metric rate.
47
48
  */
48
- static DEFAULT_TASK_COALESCE_MS = 1000;
49
+ static DEFAULT_TASK_COALESCE_MS = DurationConstants.MsPerSecond;
49
50
  /**
50
51
  * Maximum number of values held while coalescing before a task is created regardless of
51
52
  * how much of the window is left.
@@ -58,7 +59,7 @@ export class EntityStorageTelemetryConnector {
58
59
  * Default time in milliseconds with tasks outstanding and none of them completing before
59
60
  * the background thread is treated as stalled.
60
61
  */
61
- static DEFAULT_TASK_STALL_TIMEOUT_MS = 60_000;
62
+ static DEFAULT_TASK_STALL_TIMEOUT_MS = DurationConstants.MsPerMinute;
62
63
  /**
63
64
  * Task type identifier for the metric value background task.
64
65
  * @internal
@@ -1 +1 @@
1
- {"version":3,"file":"entityStorageTelemetryConnector.js","sources":["../../src/entityStorageTelemetryConnector.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EAGN,UAAU,EACV,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,cAAc,EAAoB,MAAM,iBAAiB,CAAC;AAClF,OAAO,EACN,kBAAkB,EAClB,SAAS,EACT,MAAM,EACN,gBAAgB,EAChB,SAAS,EACT,YAAY,EACZ,MAAM,EACN,EAAE,EACF,QAAQ,EACR,aAAa,EACb,YAAY,EACZ,MAAM,cAAc,CAAC;AACtB,OAAO,EACN,kBAAkB,EAElB,eAAe,EACf,aAAa,EACb,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACN,6BAA6B,EAE7B,MAAM,+BAA+B,CAAC;AAGvC,OAAO,EAKN,sBAAsB,EACtB,UAAU,EACV,MAAM,0BAA0B,CAAC;AAQlC;;;;;GAKG;AACH,MAAM,OAAO,+BAA+B;IAC3C;;OAEG;IACI,MAAM,CAAU,SAAS,GAAW,gBAAgB,CAAC;IAE5D;;OAEG;IACI,MAAM,CAAU,UAAU,GAAW,iCAAiC,CAAC;IAE9E;;OAEG;IACI,MAAM,CAAU,wBAAwB,GAAW,MAAM,CAAC;IAEjE;;OAEG;IACI,MAAM,CAAU,wCAAwC,GAAW,IAAI,CAAC;IAE/E;;;;OAIG;IACI,MAAM,CAAU,sCAAsC,GAAW,SAAS,CAAC;IAElF;;;;;;;;;OASG;IACI,MAAM,CAAU,wBAAwB,GAAW,IAAI,CAAC;IAE/D;;;;;;OAMG;IACI,MAAM,CAAU,qBAAqB,GAAW,MAAM,CAAC;IAE9D;;;OAGG;IACI,MAAM,CAAU,6BAA6B,GAAW,MAAM,CAAC;IAEtE;;;OAGG;IACK,MAAM,CAAU,uBAAuB,GAAW,8BAA8B,CAAC;IAEzF;;;OAGG;IACK,MAAM,CAAU,6BAA6B,GAAW,CAAC,CAAC;IAElE;;;OAGG;IACc,cAAc,CAA2C;IAE1E;;;OAGG;IACc,mBAAmB,CAAgD;IAEpF;;;OAGG;IACc,QAAQ,CAAqB;IAE9C;;;;;OAKG;IACc,wBAAwB,CAA4B;IAErE;;;OAGG;IACc,uBAAuB,CAAS;IAEjD;;;OAGG;IACc,aAAa,CAAoC;IAElE;;;OAGG;IACc,eAAe,CAAS;IAEzC;;;;;OAKG;IACK,aAAa,CAA4C;IAEjE;;;OAGG;IACc,cAAc,CAG7B;IAEF;;;;OAIG;IACc,eAAe,CAAS;IAEzC;;;OAGG;IACc,UAAU,CAAiC;IAE5D;;;OAGG;IACK,cAAc,CAAiC;IAEvD;;;;OAIG;IACc,mBAAmB,CAAS;IAE7C;;;;OAIG;IACc,mBAAmB,CAAc;IAElD;;;;;OAKG;IACK,iBAAiB,CAAS;IAElC;;;OAGG;IACK,WAAW,CAAiC;IAEpD;;;OAGG;IACK,kBAAkB,CAAU;IAEpC;;;;OAIG;IACK,kBAAkB,CAAS;IAEnC;;;OAGG;IACK,aAAa,CAAS;IAE9B;;;OAGG;IACK,cAAc,CAAS;IAE/B;;;;OAIG;IACK,WAAW,CAAgB;IAEnC;;;OAGG;IACK,QAAQ,CAAU;IAE1B;;;OAGG;IACc,8BAA8B,CAAS;IAExD;;;OAGG;IACc,2BAA2B,CAAS;IAErD;;;;OAIG;IACK,sBAAsB,CAA4B;IAE1D;;;OAGG;IACH,YAAY,OAA4D;QACvE,IAAI,CAAC,cAAc,GAAG,6BAA6B,CAAC,GAAG,CACtD,OAAO,EAAE,mCAAmC,IAAI,kBAAkB,CAClE,CAAC;QACF,IAAI,CAAC,mBAAmB,GAAG,6BAA6B,CAAC,GAAG,CAC3D,OAAO,EAAE,wCAAwC,IAAI,wBAAwB,CAC7E,CAAC;QAEF,IAAI,CAAC,QAAQ,GAAG,gBAAgB,CAAC,WAAW,CAAC,OAAO,EAAE,oBAAoB,CAAC,CAAC;QAE5E,2FAA2F;QAC3F,qFAAqF;QACrF,IAAI,CAAC,wBAAwB,GAAG,gBAAgB,CAAC,WAAW,CAC3D,OAAO,EAAE,2BAA2B,IAAI,iBAAiB,CACzD,CAAC;QAEF,IAAI,CAAC,uBAAuB;YAC3B,OAAO,EAAE,MAAM,EAAE,8BAA8B;gBAC/C,IAAI,GAAG,CAAC,+BAA+B,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;QAEhE,IAAI,CAAC,aAAa,GAAG;YACpB,wCAAwC,EAAE,OAAO,EAAE,wCAAwC;YAC3F,oBAAoB,EAAE,OAAO,EAAE,oBAAoB;YACnD,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS;YACrC,eAAe,EAAE,OAAO,EAAE,MAAM,EAAE,eAAe;YACjD,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY;YAC3C,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,cAAc;YAC/C,eAAe,EAAE,OAAO,EAAE,MAAM,EAAE,eAAe;SACjD,CAAC;QAEF,MAAM,iBAAiB,GACtB,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,cAAc,CAAC;YAC/C,+BAA+B,CAAC,wBAAwB,CAAC;QAC1D,IAAI,CAAC,eAAe;YACnB,iBAAiB,GAAG,CAAC;gBACpB,CAAC,CAAC,iBAAiB;gBACnB,CAAC,CAAC,+BAA+B,CAAC,wBAAwB,CAAC;QAE7D,MAAM,aAAa,GAClB,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,cAAc,CAAC;YAC/C,+BAA+B,CAAC,wBAAwB,CAAC;QAC1D,IAAI,CAAC,eAAe,GAAG,aAAa,GAAG,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;QAE7D,MAAM,qBAAqB,GAC1B,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,kBAAkB,CAAC;YACnD,+BAA+B,CAAC,6BAA6B,CAAC;QAC/D,IAAI,CAAC,mBAAmB,GAAG,qBAAqB,GAAG,CAAC,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,CAAC;QAEjF,IAAI,CAAC,cAAc,GAAG,IAAI,GAAG,EAAE,CAAC;QAChC,IAAI,CAAC,UAAU,GAAG,EAAE,CAAC;QACrB,IAAI,CAAC,mBAAmB,GAAG,IAAI,GAAG,EAAE,CAAC;QACrC,IAAI,CAAC,iBAAiB,GAAG,CAAC,CAAC;QAC3B,IAAI,CAAC,kBAAkB,GAAG,KAAK,CAAC;QAChC,IAAI,CAAC,kBAAkB,GAAG,CAAC,CAAC;QAC5B,IAAI,CAAC,aAAa,GAAG,CAAC,CAAC;QACvB,IAAI,CAAC,cAAc,GAAG,CAAC,CAAC;QACxB,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;QACrC,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;QACtB,IAAI,CAAC,8BAA8B;YAClC,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,6BAA6B,CAAC;gBAC9D,+BAA+B,CAAC,wCAAwC,CAAC;QAC1E,IAAI,CAAC,2BAA2B;YAC/B,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,0BAA0B,CAAC;gBAC3D,+BAA+B,CAAC,sCAAsC,CAAC;QACxE,IAAI,CAAC,sBAAsB,GAAG,IAAI,QAAQ,CAAC;YAC1C,QAAQ,EAAE,IAAI,CAAC,8BAA8B;YAC7C,KAAK,EAAE,IAAI,CAAC,2BAA2B;SACvC,CAAC,CAAC;IACJ,CAAC;IAED;;;OAGG;IACI,SAAS;QACf,OAAO,+BAA+B,CAAC,UAAU,CAAC;IACnD,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,KAAK;QACjB,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACpB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;YACrB,IAAI,CAAC,sBAAsB,GAAG,IAAI,QAAQ,CAAC;gBAC1C,QAAQ,EAAE,IAAI,CAAC,8BAA8B;gBAC7C,KAAK,EAAE,IAAI,CAAC,2BAA2B;aACvC,CAAC,CAAC;YAEH,MAAM,IAAI,CAAC,0BAA0B,EAAE,CAAC;QACzC,CAAC;IACF,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,IAAI;QAChB,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAEzB,oFAAoF;QACpF,uFAAuF;QACvF,oFAAoF;QACpF,IAAI,CAAC;YACJ,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;QACpB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,MAAM,IAAI,CAAC,kBAAkB,CAAC;gBAC7B,MAAM,EAAE,+BAA+B,CAAC,UAAU;gBAClD,OAAO,EAAE,iBAAiB;gBAC1B,KAAK,EAAE,OAAO;gBACd,KAAK,EAAE,SAAS,CAAC,SAAS,CAAC,GAAG,CAAC;aAC/B,CAAC,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;QACtB,IAAI,CAAC,cAAc,EAAE,CAAC;QACtB,IAAI,CAAC,mBAAmB,CAAC,KAAK,EAAE,CAAC;QACjC,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC;QAE/B,sFAAsF;QACtF,sEAAsE;QACtE,MAAM,IAAI,CAAC,wBAAwB,EAAE,iBAAiB,CACrD,+BAA+B,CAAC,uBAAuB,CACvD,CAAC;QAEF,KAAK,MAAM,YAAY,IAAI,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,EAAE,CAAC;YACzD,UAAU,CAAC,YAAY,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;YAC5C,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAC7B,CAAC;QACD,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC;QAE5B,IAAI,CAAC,sBAAsB,CAAC,OAAO,EAAE,CAAC;IACvC,CAAC;IAED;;;;;;;;OAQG;IACI,KAAK,CAAC,YAAY,CAAC,MAA6C;QACtE,MAAM,OAAO,GAAG,EAAE,CAAC,KAAK,CAAmB,MAAM,CAAC,CAAC;QACnD,MAAM,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QAE5C,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;QAC5B,CAAC;QAED,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO;QACR,CAAC;QAED,MAAM,gBAAgB,GAAsB,EAAE,CAAC;QAE/C,IAAI,OAAO,EAAE,CAAC;YACb,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,KAAK,CAC/C;gBACC,QAAQ,EAAE,IAAI;gBACd,UAAU,EAAE,kBAAkB,CAAC,EAAE;gBACjC,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;aACrC,EACD,SAAS,EACT,CAAC,IAAI,CAAC,EACN,SAAS,EACT,OAAO,CAAC,MAAM,CACd,CAAC;YACF,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,EAAY,CAAC,CAAC,CAAC;YAElF,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;gBAC7B,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;oBAChC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC,CAAC;gBACtD,CAAC;YACF,CAAC;QACF,CAAC;aAAM,CAAC;YACP,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACpE,IAAI,EAAE,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;gBACjC,MAAM,IAAI,kBAAkB,CAC3B,+BAA+B,CAAC,UAAU,EAC1C,qBAAqB,EACrB,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CACb,CAAC;YACH,CAAC;YACD,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3D,CAAC;QAED,IAAI,gBAAgB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACnC,OAAO;QACR,CAAC;QAED,IAAI,gBAAgB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACnC,MAAM,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;QACpD,CAAC;aAAM,CAAC;YACP,MAAM,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC;QACtD,CAAC;QAED,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAClD,KAAK,MAAM,eAAe,IAAI,gBAAgB,EAAE,CAAC;YAChD,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAC9B,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,eAAe,CAAC,EAAE,CAAC,EAC/C,eAAe,CACf,CAAC;QACH,CAAC;QAED,IAAI,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC;gBACxB,MAAM,EAAE,+BAA+B,CAAC,UAAU;gBAClD,OAAO,EAAE,gBAAgB;gBACzB,KAAK,EAAE,MAAM;gBACb,IAAI,EAAE,EAAE,KAAK,EAAE,gBAAgB,CAAC,MAAM,EAAE;aACxC,CAAC,CAAC;QACJ,CAAC;aAAM,CAAC;YACP,MAAM,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC;gBACxB,MAAM,EAAE,+BAA+B,CAAC,UAAU;gBAClD,OAAO,EAAE,eAAe;gBACxB,KAAK,EAAE,MAAM;gBACb,IAAI,EAAE;oBACL,EAAE,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC,EAAE;oBAC1B,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC,IAAI;oBAC9B,KAAK,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC,KAAK;iBAChC;aACD,CAAC,CAAC;QACJ,CAAC;IACF,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,SAAS,CAAC,EAAU;QAIhC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC;QAC/E,OAAO;YACN,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;SAC1B,CAAC;IACH,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,YAAY,CAAC,MAAsC;QAC/D,MAAM,CAAC,MAAM,CACZ,+BAA+B,CAAC,UAAU,EAC1C,QAAQ,EACR,MAAM,CACN,CAAC;QACF,MAAM,CAAC,WAAW,CAAC,+BAA+B,CAAC,UAAU,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;QACvF,MAAM,CAAC,WAAW,CACjB,+BAA+B,CAAC,UAAU,EAC1C,cAAc,EACd,MAAM,CAAC,KAAK,CACZ,CAAC;QACF,IAAI,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC;YACrC,MAAM,CAAC,MAAM,CACZ,+BAA+B,CAAC,UAAU,EAC1C,oBAAoB,EACpB,MAAM,CAAC,WAAW,CAClB,CAAC;QACH,CAAC;QACD,IAAI,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9B,MAAM,CAAC,MAAM,CAAC,+BAA+B,CAAC,UAAU,EAAE,aAAa,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;QACvF,CAAC;QACD,IAAI,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;YACpC,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,MAAM,CAAC,UAAU,IAAI,CAAC,EAAE,CAAC;gBAC9D,MAAM,IAAI,YAAY,CACrB,+BAA+B,CAAC,UAAU,EAC1C,iCAAiC,CACjC,CAAC;YACH,CAAC;QACF,CAAC;QAED,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAChE,IAAI,EAAE,CAAC,SAAS,CAAC,cAAc,CAAC,EAAE,CAAC;YAClC,MAAM,IAAI,aAAa,CACtB,+BAA+B,CAAC,UAAU,EAC1C,gBAAgB,EAChB,MAAM,CAAC,EAAE,CACT,CAAC;QACH,CAAC;QAED,MAAM,eAAe,GAAoB;YACxC,EAAE,EAAE,MAAM,CAAC,EAAE;YACb,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,IAAI,EAAE,cAAc,CAAC,IAAI;YACzB,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,cAAc,CAAC,IAAI;YACxC,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,cAAc,CAAC,WAAW;YAC7D,UAAU,EAAE,MAAM,CAAC,UAAU,IAAI,cAAc,CAAC,UAAU;SAC1D,CAAC;QAEF,MAAM,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;QAC/C,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,IAAI,CAAC,iBAAiB,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;QAC7E,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC;QAE5D,MAAM,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC;YACxB,MAAM,EAAE,+BAA+B,CAAC,UAAU;YAClD,OAAO,EAAE,eAAe;YACxB,KAAK,EAAE,MAAM;YACb,IAAI,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE;SAC/D,CAAC,CAAC;IACJ,CAAC;IAED;;;;;;;;OAQG;IACI,KAAK,CAAC,cAAc,CAC1B,EAAU,EACV,KAAsC,EACtC,UAAuC;QAEvC,MAAM,CAAC,WAAW,CAAC,+BAA+B,CAAC,UAAU,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;QAEzE,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,EAAE,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC;QAEvE,MAAM,IAAI,CAAC,iBAAiB,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC;QAE5C,MAAM,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC;YACxB,MAAM,EAAE,+BAA+B,CAAC,UAAU;YAClD,OAAO,EAAE,oBAAoB;YAC7B,KAAK,EAAE,MAAM;YACb,IAAI,EAAE,EAAE,EAAE,EAAE;SACZ,CAAC,CAAC;QAEH,OAAO,WAAW,CAAC,OAAO,CAAC;IAC5B,CAAC;IAED;;;;;OAKG;IACI,KAAK,CAAC,eAAe,CAAC,MAAoC;QAChE,MAAM,CAAC,KAAK,CACX,+BAA+B,CAAC,UAAU,EAC1C,QAAQ,EACR,MAAM,CACN,CAAC;QAEF,MAAM,YAAY,GAAmC,EAAE,CAAC;QAExD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC5B,MAAM,CAAC,WAAW,CAAC,+BAA+B,CAAC,UAAU,EAAE,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;YACrF,YAAY,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;QACzF,CAAC;QAED,MAAM,IAAI,CAAC,iBAAiB,CAAC,YAAY,CAAC,CAAC;QAE3C,MAAM,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC;YACxB,MAAM,EAAE,+BAA+B,CAAC,UAAU;YAClD,OAAO,EAAE,qBAAqB;YAC9B,KAAK,EAAE,MAAM;YACb,IAAI,EAAE,EAAE,KAAK,EAAE,YAAY,CAAC,MAAM,EAAE;SACpC,CAAC,CAAC;QAEH,OAAO,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IAC7D,CAAC;IAED;;;;;OAKG;IACI,KAAK,CAAC,cAAc,CAAC,EAAU,EAAE,OAAe;QACtD,MAAM,CAAC,WAAW,CAAC,+BAA+B,CAAC,UAAU,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;QACzE,MAAM,CAAC,WAAW,CAAC,+BAA+B,CAAC,UAAU,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;QAEnF,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;QAEnB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,mBAAmB,CAAC,KAAK,CACnD;YACC,UAAU,EAAE;gBACX,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,kBAAkB,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE;gBAC1E,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,kBAAkB,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE;aACzE;YACD,eAAe,EAAE,eAAe,CAAC,GAAG;SACpC,EACD,SAAS,EACT,SAAS,EACT,SAAS,EACT,CAAC,CACD,CAAC;QAEF,IAAI,OAAO,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACnC,MAAM,IAAI,aAAa,CACtB,+BAA+B,CAAC,UAAU,EAC1C,qBAAqB,EACrB,OAAO,CACP,CAAC;QACH,CAAC;QAED,MAAM,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAyB,CAAC;QAChE,OAAO;YACN,EAAE,EAAE,WAAW,CAAC,EAAE;YAClB,EAAE,EAAE,WAAW,CAAC,EAAE;YAClB,KAAK,EAAE,WAAW,CAAC,KAAK;YACxB,UAAU,EAAE,WAAW,CAAC,UAAU;SAClC,CAAC;IACH,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,YAAY,CAAC,EAAU;QACnC,MAAM,CAAC,WAAW,CAAC,+BAA+B,CAAC,UAAU,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;QAEzE,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACzD,IAAI,EAAE,CAAC,SAAS,CAAC,cAAc,CAAC,EAAE,CAAC;YAClC,MAAM,IAAI,aAAa,CAAC,+BAA+B,CAAC,UAAU,EAAE,gBAAgB,EAAE,EAAE,CAAC,CAAC;QAC3F,CAAC;QAED,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;QAEnB,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACrC,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,IAAI,CAAC,iBAAiB,EAAE,EAAE,EAAE,CAAC,CAAC;QACtE,IAAI,CAAC,sBAAsB,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAE9C,IAAI,kBAAsC,CAAC;QAC3C,MAAM,gBAAgB,GAAa,EAAE,CAAC;QACtC,GAAG,CAAC;YACH,MAAM,0BAA0B,GAAG,MAAM,IAAI,CAAC,mBAAmB,CAAC,KAAK,CACtE;gBACC,QAAQ,EAAE,UAAU;gBACpB,UAAU,EAAE,kBAAkB,CAAC,MAAM;gBACrC,KAAK,EAAE,EAAE;aACT,EACD,SAAS,EACT,CAAC,IAAI,CAAC,EACN,kBAAkB,CAClB,CAAC;YACF,gBAAgB,CAAC,IAAI,CAAC,GAAG,0BAA0B,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAY,CAAC,CAAC,CAAC;YACvF,kBAAkB,GAAG,0BAA0B,CAAC,MAAM,CAAC;QACxD,CAAC,QAAQ,EAAE,CAAC,WAAW,CAAC,kBAAkB,CAAC,EAAE;QAC7C,IAAI,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACjC,MAAM,IAAI,CAAC,mBAAmB,CAAC,WAAW,CAAC,gBAAgB,CAAC,CAAC;QAC9D,CAAC;QAED,MAAM,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC;YACxB,MAAM,EAAE,+BAA+B,CAAC,UAAU;YAClD,OAAO,EAAE,eAAe;YACxB,KAAK,EAAE,MAAM;YACb,IAAI,EAAE,EAAE,EAAE,EAAE;SACZ,CAAC,CAAC;IACJ,CAAC;IAED;;;;;;;;OAQG;IACI,KAAK,CAAC,KAAK,CACjB,IAAiB,EACjB,MAAe,EACf,KAAc;QAYd,MAAM,SAAS,GAAqC;YACnD,UAAU,EAAE,EAAE;YACd,eAAe,EAAE,eAAe,CAAC,GAAG;SACpC,CAAC;QAEF,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC;YACpD,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC;gBACzB,QAAQ,EAAE,MAAM;gBAChB,UAAU,EAAE,kBAAkB,CAAC,MAAM;gBACrC,KAAK,EAAE,IAAI;aACX,CAAC,CAAC;QACJ,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,KAAK,CAC7C,SAAS,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,EACvD;YACC;gBACC,QAAQ,EAAE,OAAO;gBACjB,aAAa,EAAE,aAAa,CAAC,SAAS;aACtC;SACD,EACD,SAAS,EACT,MAAM,EACN,KAAK,CACL,CAAC;QAEF,OAAO;YACN,QAAQ,EAAE,MAAM,CAAC,QAA8B;YAC/C,MAAM,EAAE,MAAM,CAAC,MAAM;SACrB,CAAC;IACH,CAAC;IAED;;;;;;;;;OASG;IACI,KAAK,CAAC,WAAW,CACvB,EAAU,EACV,SAAkB,EAClB,OAAgB,EAChB,MAAe,EACf,KAAc;QAiBd,MAAM,CAAC,WAAW,CAAC,+BAA+B,CAAC,UAAU,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;QAEzE,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;QAEnB,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACzD,IAAI,EAAE,CAAC,SAAS,CAAC,cAAc,CAAC,EAAE,CAAC;YAClC,MAAM,IAAI,aAAa,CAAC,+BAA+B,CAAC,UAAU,EAAE,gBAAgB,EAAE,EAAE,CAAC,CAAC;QAC3F,CAAC;QAED,MAAM,SAAS,GAA0C;YACxD,UAAU,EAAE,EAAE;YACd,eAAe,EAAE,eAAe,CAAC,GAAG;SACpC,CAAC;QAEF,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC;YACzB,QAAQ,EAAE,UAAU;YACpB,UAAU,EAAE,kBAAkB,CAAC,MAAM;YACrC,KAAK,EAAE,EAAE;SACT,CAAC,CAAC;QAEH,IAAI,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;YAC1B,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC;gBACzB,QAAQ,EAAE,IAAI;gBACd,UAAU,EAAE,kBAAkB,CAAC,kBAAkB;gBACjD,KAAK,EAAE,SAAS;aAChB,CAAC,CAAC;QACJ,CAAC;QAED,IAAI,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;YACxB,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC;gBACzB,QAAQ,EAAE,IAAI;gBACd,UAAU,EAAE,kBAAkB,CAAC,eAAe;gBAC9C,KAAK,EAAE,OAAO;aACd,CAAC,CAAC;QACJ,CAAC;QAED,0FAA0F;QAC1F,uFAAuF;QACvF,0FAA0F;QAC1F,sFAAsF;QACtF,MAAM,MAAM,GAA6E;YACxF,EAAE,QAAQ,EAAE,IAAI,EAAE,aAAa,EAAE,aAAa,CAAC,UAAU,EAAE;SAC3D,CAAC;QAEF,IAAI,cAAc,CAAC,IAAI,KAAK,UAAU,CAAC,OAAO,EAAE,CAAC;YAChD,MAAM,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,aAAa,EAAE,aAAa,CAAC,UAAU,EAAE,CAAC,CAAC;QAC7E,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAClD,SAAS,EACT,MAAM,EACN,SAAS,EACT,MAAM,EACN,KAAK,CACL,CAAC;QAEF,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAmC,CAAC;QAE5D,OAAO;YACN,MAAM,EAAE,cAAkC;YAC1C,QAAQ;YACR,MAAM,EAAE,MAAM,CAAC,MAAM;SACrB,CAAC;IACH,CAAC;IAED;;;;;;OAMG;IACK,KAAK,CAAC,0BAA0B;QACvC,MAAM,IAAI,CAAC,wBAAwB,EAAE,eAAe,CAInD,+BAA+B,CAAC,uBAAuB,EACvD,IAAI,CAAC,uBAAuB,EAC5B,0BAA0B,EAC1B,KAAK,EAAC,IAAI,EAAC,EAAE,CAAC,IAAI,CAAC,2BAA2B,CAAC,IAAI,CAAC,EACpD;YACC,kFAAkF;YAClF,+DAA+D;YAC/D,cAAc,EAAE,CAAC;YACjB,mBAAmB,EAAE,CAAC,CAAC;YACvB,gBAAgB,EAAE,+BAA+B;YACjD,4EAA4E;YAC5E,eAAe;YACf,sBAAsB,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC;YACxD,cAAc,EAAE,6BAA6B;SAC7C,CACD,CAAC;IACH,CAAC;IAED;;;;;;OAMG;IACK,KAAK,CAAC,KAAK;QAClB,IACC,CAAC,IAAI,CAAC,QAAQ;YACd,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,wBAAwB,CAAC;YACvC,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,aAAa,KAAK,IAAI,CAAC,cAAc,CAAC,EAC3E,CAAC;YACF,OAAO;QACR,CAAC;QAED,0FAA0F;QAC1F,wFAAwF;QACxF,sFAAsF;QACtF,kFAAkF;QAClF,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC;QAClC,IAAI,OAAiC,CAAC;QACtC,IAAI,CAAC,WAAW,GAAG,IAAI,OAAO,CAAO,OAAO,CAAC,EAAE;YAC9C,OAAO,GAAG,OAAO,CAAC;QACnB,CAAC,CAAC,CAAC;QAEH,MAAM,QAAQ,CAAC;QAEf,IAAI,CAAC;YACJ,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC;QAC3B,CAAC;gBAAS,CAAC;YACV,OAAO,EAAE,EAAE,CAAC;QACb,CAAC;IACF,CAAC;IAED;;;;;;OAMG;IACK,KAAK,CAAC,YAAY;QACzB,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACpB,OAAO;QACR,CAAC;QAED,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAEzB,8FAA8F;QAC9F,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAEzC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,aAAa,KAAK,IAAI,CAAC,cAAc,EAAE,CAAC;YACvE,OAAO;QACR,CAAC;QAED,sFAAsF;QACtF,wFAAwF;QACxF,sFAAsF;QACtF,+DAA+D;QAC/D,MAAM,YAAY,GAAG,IAAI,CAAC,aAAa,CAAC;QACxC,IACC,MAAM,CAAC,MAAM,KAAK,CAAC;YACnB,CAAC,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC;YACvB,YAAY,CAAC,YAAY,IAAI,IAAI,CAAC,aAAa,EAC9C,CAAC;YACF,IAAI,MAAM,IAAI,CAAC,WAAW,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC;gBACjD,IAAI,CAAC,cAAc,GAAG,YAAY,CAAC,YAAY,CAAC;YACjD,CAAC;YACD,OAAO;QACR,CAAC;QAED,IAAI,MAAc,CAAC;QACnB,IAAI,CAAC;YACJ,MAAM,GAAG,MAAM,IAAI,CAAC,qBAAqB,CACxC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAC7D,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,8EAA8E;YAC9E,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,MAAM,CAAC,CAAC;YACnC,MAAM,GAAG,CAAC;QACX,CAAC;QAED,IAAI,CAAC,aAAa,IAAI,MAAM,CAAC,MAAM,CAAC;QAEpC,qFAAqF;QACrF,6EAA6E;QAC7E,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC;QAClC,IAAI,CAAC,aAAa,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC;QAEtD,wFAAwF;QACxF,oFAAoF;QACpF,gFAAgF;QAChF,IAAI,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC;YACpC,IAAI,CAAC,cAAc,GAAG,MAAM,CAAC;QAC9B,CAAC;IACF,CAAC;IAED;;;;;;;;;OASG;IACK,KAAK,CAAC,gBAAgB,CAC7B,EAAU,EACV,KAAsC,EACtC,UAAuC;QAEvC,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAClD,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;QAElD,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,sBAAsB,CAAC,QAAQ,CAAC,SAAS,EAAE,KAAK,IAAI,EAAE;YACvF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACjD,IAAI,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC1B,MAAM,IAAI,aAAa,CAAC,+BAA+B,CAAC,UAAU,EAAE,gBAAgB,EAAE,EAAE,CAAC,CAAC;YAC3F,CAAC;YACD,OAAO,MAAM,CAAC;QACf,CAAC,CAAC,CAAC;QAEH,MAAM,UAAU,GAAG,cAAc,CAAC,IAAkB,CAAC;QACrD,IAAI,CAAC,iBAAiB,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QAE1C,OAAO;YACN,OAAO,EAAE,SAAS,CAAC,UAAU,CAAC,YAAY,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;YACxD,QAAQ,EAAE,EAAE;YACZ,UAAU;YACV,SAAS,EAAE,KAAK;YAChB,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE;YACd,UAAU,EAAE,cAAc,CAAC,UAAU;YACrC,UAAU;YACV,UAAU;SACV,CAAC;IACH,CAAC;IAED;;;;;;OAMG;IACK,KAAK,CAAC,iBAAiB,CAAC,YAA4C;QAC3E,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC/B,OAAO;QACR,CAAC;QAED,IAAI,IAAI,CAAC,eAAe,KAAK,CAAC,EAAE,CAAC;YAChC,MAAM,IAAI,CAAC,qBAAqB,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC,CAAC;YAC3D,IAAI,CAAC,aAAa,IAAI,YAAY,CAAC,MAAM,CAAC;YAC1C,OAAO;QACR,CAAC;QAED,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,CAAC;QAEtC,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,IAAI,+BAA+B,CAAC,qBAAqB,EAAE,CAAC;YACrF,MAAM,IAAI,CAAC,aAAa,EAAE,CAAC;QAC5B,CAAC;aAAM,CAAC;YACP,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACzB,CAAC;IACF,CAAC;IAED;;;OAGG;IACK,gBAAgB;QACvB,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC;YACpC,OAAO;QACR,CAAC;QAED,IAAI,CAAC,cAAc,GAAG,UAAU,CAAC,UAAU,CAAC,KAAK,IAAI,EAAE;YACtD,IAAI,CAAC,cAAc,GAAG,SAAS,CAAC;YAChC,IAAI,CAAC;gBACJ,MAAM,IAAI,CAAC,aAAa,EAAE,CAAC;YAC5B,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACd,MAAM,IAAI,CAAC,kBAAkB,CAAC;oBAC7B,MAAM,EAAE,+BAA+B,CAAC,UAAU;oBAClD,OAAO,EAAE,uBAAuB;oBAChC,KAAK,EAAE,OAAO;oBACd,IAAI,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;oBACpB,KAAK,EAAE,SAAS,CAAC,SAAS,CAAC,GAAG,CAAC;iBAC/B,CAAC,CAAC;gBAEH,2EAA2E;gBAC3E,4EAA4E;gBAC5E,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACzB,CAAC;QACF,CAAC,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;IAC1B,CAAC;IAED;;;;OAIG;IACK,KAAK,CAAC,aAAa;QAC1B,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAEzB,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAClC,OAAO;QACR,CAAC;QAED,wFAAwF;QACxF,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAEzC,IAAI,CAAC;YACJ,MAAM,IAAI,CAAC,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;YAC7C,IAAI,CAAC,aAAa,IAAI,MAAM,CAAC,MAAM,CAAC;QACrC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,qEAAqE;YACrE,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,MAAM,CAAC,CAAC;YACnC,MAAM,GAAG,CAAC;QACX,CAAC;IACF,CAAC;IAED;;;OAGG;IACK,iBAAiB;QACxB,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC;YACpC,UAAU,CAAC,YAAY,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YAC7C,IAAI,CAAC,cAAc,GAAG,SAAS,CAAC;QACjC,CAAC;IACF,CAAC;IAED;;;;;;OAMG;IACK,KAAK,CAAC,qBAAqB,CAAC,OAAyC;QAC5E,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,wBAAwB,CAAC,EAAE,CAAC;YAC7C,MAAM,IAAI,YAAY,CACrB,+BAA+B,CAAC,UAAU,EAC1C,gCAAgC,CAChC,CAAC;QACH,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,wBAAwB,CAAC,MAAM,CACxD,+BAA+B,CAAC,uBAAuB,EACvD,OAAO,CACP,CAAC;QAEF,IAAI,IAAI,CAAC,mBAAmB,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YACzC,oFAAoF;YACpF,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACrC,CAAC;QACD,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACrC,IAAI,CAAC,aAAa,EAAE,CAAC;QAErB,OAAO,MAAM,CAAC;IACf,CAAC;IAED;;;;;OAKG;IACK,KAAK,CAAC,WAAW,CAAC,MAAc;QACvC,OAAO,IAAI,OAAO,CAAU,OAAO,CAAC,EAAE;YACrC,MAAM,KAAK,GAAG,UAAU,CAAC,UAAU,CAAC,KAAK,IAAI,EAAE;gBAC9C,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;gBACnC,MAAM,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC;oBACxB,MAAM,EAAE,+BAA+B,CAAC,UAAU;oBAClD,OAAO,EAAE,cAAc;oBACvB,KAAK,EAAE,MAAM;oBACb,IAAI,EAAE;wBACL,MAAM;wBACN,SAAS,EAAE,IAAI,CAAC,eAAe;wBAC/B,SAAS,EAAE,IAAI,CAAC,mBAAmB,CAAC,IAAI;qBACxC;iBACD,CAAC,CAAC;gBACH,OAAO,CAAC,KAAK,CAAC,CAAC;YAChB,CAAC,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;YAEzB,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;QACrD,CAAC,CAAC,CAAC;IACJ,CAAC;IAED;;;;;OAKG;IACK,WAAW;QAClB,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAC1B,CAAC,IAAI,IAAI,CAAC,kBAAkB,EAC5B,+BAA+B,CAAC,6BAA6B,CAC7D,CAAC;QACF,OAAO,IAAI,CAAC,mBAAmB,GAAG,UAAU,CAAC;IAC9C,CAAC;IAED;;;;OAIG;IACK,aAAa;QACpB,IACC,IAAI,CAAC,mBAAmB,KAAK,CAAC;YAC9B,CAAC,IAAI,CAAC,QAAQ;YACd,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC;YAC3B,IAAI,CAAC,kBAAkB,EACtB,CAAC;YACF,OAAO;QACR,CAAC;QAED,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC,CAAC;QAEtF,IAAI,CAAC,WAAW,GAAG,UAAU,CAAC,UAAU,CAAC,KAAK,IAAI,EAAE;YACnD,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC;YAC7B,qFAAqF;YACrF,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC;YAC/B,IAAI,CAAC;gBACJ,MAAM,IAAI,CAAC,qBAAqB,EAAE,CAAC;YACpC,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACd,+EAA+E;gBAC/E,0CAA0C;gBAC1C,MAAM,IAAI,CAAC,kBAAkB,CAAC;oBAC7B,MAAM,EAAE,+BAA+B,CAAC,UAAU;oBAClD,OAAO,EAAE,8BAA8B;oBACvC,KAAK,EAAE,OAAO;oBACd,KAAK,EAAE,SAAS,CAAC,SAAS,CAAC,GAAG,CAAC;iBAC/B,CAAC,CAAC;gBAEH,iFAAiF;gBACjF,+EAA+E;gBAC/E,2CAA2C;gBAC3C,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC;YAChC,CAAC;oBAAS,CAAC;gBACV,IAAI,CAAC,kBAAkB,GAAG,KAAK,CAAC;gBAChC,IAAI,IAAI,CAAC,mBAAmB,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;oBACvC,IAAI,CAAC,aAAa,EAAE,CAAC;gBACtB,CAAC;YACF,CAAC;QACF,CAAC,EAAE,KAAK,CAAC,CAAC;IACX,CAAC;IAED;;;OAGG;IACK,cAAc;QACrB,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;YACjC,UAAU,CAAC,YAAY,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YAC1C,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC;QAC9B,CAAC;IACF,CAAC;IAED;;;;;OAKG;IACK,KAAK,CAAC,qBAAqB;QAClC,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,mBAAmB,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YAC3D,OAAO;QACR,CAAC;QAED,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,iBAAiB,CAAC;QAEzD,IAAI,YAAY,GAAG,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;YACvC,mFAAmF;YACnF,OAAO;QACR,CAAC;QAED,yFAAyF;QACzF,mFAAmF;QACnF,sFAAsF;QACtF,+EAA+E;QAC/E,MAAM,aAAa,GAAG,IAAI,CAAC,mBAAmB,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,KAAe,CAAC;QAC/E,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,wBAAwB,EAAE,GAAG,CAAC,aAAa,CAAC,CAAC;QAE5E,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,CAAC;YAClD,mFAAmF;YACnF,mFAAmF;YACnF,OAAO;QACR,CAAC;QAED,IACC,EAAE,CAAC,KAAK,CAAC,WAAW,CAAC;YACrB,CAAC,WAAW,CAAC,MAAM,KAAK,UAAU,CAAC,OAAO,IAAI,WAAW,CAAC,MAAM,KAAK,UAAU,CAAC,UAAU,CAAC,EAC1F,CAAC;YACF,MAAM,IAAI,CAAC,kBAAkB,CAAC;gBAC7B,MAAM,EAAE,+BAA+B,CAAC,UAAU;gBAClD,OAAO,EAAE,4BAA4B;gBACrC,KAAK,EAAE,MAAM;gBACb,IAAI,EAAE,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE;aAC5D,CAAC,CAAC;YACH,IAAI,CAAC,mBAAmB,CAAC,KAAK,EAAE,CAAC;YACjC,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC;YAC/B,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACpC,IAAI,CAAC,kBAAkB,GAAG,CAAC,CAAC;YAC5B,OAAO;QACR,CAAC;QAED,IAAI,WAAW,CAAC,MAAM,KAAK,UAAU,CAAC,OAAO,EAAE,CAAC;YAC/C,kFAAkF;YAClF,MAAM,eAAe,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;YACzE,MAAM,IAAI,CAAC,kBAAkB,CAAC;gBAC7B,MAAM,EAAE,+BAA+B,CAAC,UAAU;gBAClD,OAAO,EAAE,6BAA6B;gBACtC,KAAK,EAAE,MAAM;gBACb,IAAI,EAAE;oBACL,MAAM,EAAE,aAAa;oBACrB,YAAY,EAAE,eAAe;oBAC7B,SAAS,EAAE,IAAI,CAAC,mBAAmB,CAAC,IAAI;iBACxC;aACD,CAAC,CAAC;YACH,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACpC,OAAO;QACR,CAAC;QAED,MAAM,IAAI,CAAC,wBAAwB,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC;IAClE,CAAC;IAED;;;;;;;;;;;;OAYG;IACK,KAAK,CAAC,wBAAwB,CACrC,YAAoB,EACpB,aAAqB;QAErB,MAAM,IAAI,CAAC,kBAAkB,CAAC;YAC7B,MAAM,EAAE,+BAA+B,CAAC,UAAU;YAClD,OAAO,EAAE,0BAA0B;YACnC,KAAK,EAAE,OAAO;YACd,IAAI,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,SAAS,EAAE,IAAI,CAAC,mBAAmB,CAAC,IAAI,EAAE;SACvF,CAAC,CAAC;QAEH,oFAAoF;QACpF,kFAAkF;QAClF,2EAA2E;QAC3E,wFAAwF;QACxF,oFAAoF;QACpF,IAAI,CAAC,mBAAmB,CAAC,KAAK,EAAE,CAAC;QACjC,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC;QAC/B,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACpC,IAAI,CAAC,kBAAkB,IAAI,CAAC,CAAC;QAE7B,IAAI,CAAC;YACJ,MAAM,IAAI,CAAC,wBAAwB,EAAE,iBAAiB,CACrD,+BAA+B,CAAC,uBAAuB,CACvD,CAAC;YACF,MAAM,IAAI,CAAC,0BAA0B,EAAE,CAAC;YAExC,MAAM,IAAI,CAAC,kBAAkB,CAAC;gBAC7B,MAAM,EAAE,+BAA+B,CAAC,UAAU;gBAClD,OAAO,EAAE,4BAA4B;gBACrC,KAAK,EAAE,MAAM;aACb,CAAC,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,MAAM,IAAI,CAAC,kBAAkB,CAAC;gBAC7B,MAAM,EAAE,+BAA+B,CAAC,UAAU;gBAClD,OAAO,EAAE,gCAAgC;gBACzC,KAAK,EAAE,OAAO;gBACd,KAAK,EAAE,SAAS,CAAC,SAAS,CAAC,GAAG,CAAC;aAC/B,CAAC,CAAC;QACJ,CAAC;IACF,CAAC;IAED;;;;;;;OAOG;IACK,KAAK,CAAC,kBAAkB,CAAC,QAAmB;QACnD,IAAI,CAAC;YACJ,MAAM,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC;QACpC,CAAC;QAAC,MAAM,CAAC;YACR,sFAAsF;YACtF,6DAA6D;QAC9D,CAAC;IACF,CAAC;IAED;;;;;OAKG;IACK,KAAK,CAAC,2BAA2B,CACxC,IAAkE;QAElE,IACC,IAAI,CAAC,MAAM,KAAK,UAAU,CAAC,OAAO;YAClC,IAAI,CAAC,MAAM,KAAK,UAAU,CAAC,MAAM;YACjC,IAAI,CAAC,MAAM,KAAK,UAAU,CAAC,SAAS,EACnC,CAAC;YACF,OAAO;QACR,CAAC;QAED,qFAAqF;QACrF,uFAAuF;QACvF,qEAAqE;QACrE,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACzC,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACpC,IAAI,CAAC,kBAAkB,GAAG,CAAC,CAAC;QAE5B,IAAI,IAAI,CAAC,aAAa,EAAE,MAAM,KAAK,IAAI,CAAC,EAAE,EAAE,CAAC;YAC5C,sFAAsF;YACtF,iFAAiF;YACjF,iDAAiD;YACjD,IAAI,IAAI,CAAC,MAAM,KAAK,UAAU,CAAC,OAAO,EAAE,CAAC;gBACxC,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC;YACtF,CAAC;YACD,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC;QAChC,CAAC;QAED,qFAAqF;QACrF,2DAA2D;QAC3D,IAAI,CAAC,cAAc,EAAE,CAAC;QACtB,IAAI,IAAI,CAAC,mBAAmB,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;YACvC,IAAI,CAAC,aAAa,EAAE,CAAC;QACtB,CAAC;QAED,MAAM,YAAY,GAAG,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACtD,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,CAAC;YAC7B,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACpC,UAAU,CAAC,YAAY,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;YAC5C,kFAAkF;YAClF,wDAAwD;YACxD,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,KAAK,UAAU,CAAC,OAAO,CAAC,CAAC;QAC1D,CAAC;QAED,IAAI,IAAI,CAAC,MAAM,KAAK,UAAU,CAAC,MAAM,EAAE,CAAC;YACvC,MAAM,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC;gBACxB,MAAM,EAAE,+BAA+B,CAAC,UAAU;gBAClD,OAAO,EAAE,uBAAuB;gBAChC,KAAK,EAAE,OAAO;gBACd,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE;gBACzB,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS;aAC1E,CAAC,CAAC;QACJ,CAAC;IACF,CAAC;IAED;;;;;OAKG;IACK,iBAAiB,CAAC,MAAwB;QACjD,OAAO;YACN,EAAE,EAAE,MAAM,CAAC,EAAE;YACb,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,EAAE;YACvB,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,EAAE;YACrC,UAAU,EAAE,MAAM,CAAC,UAAU;SAC7B,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACK,cAAc,CAAC,MAAwB;QAC9C,MAAM,CAAC,MAAM,CACZ,+BAA+B,CAAC,UAAU,EAC1C,QAAQ,EACR,MAAM,CACN,CAAC;QACF,MAAM,CAAC,WAAW,CAAC,+BAA+B,CAAC,UAAU,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;QACvF,MAAM,CAAC,WAAW,CACjB,+BAA+B,CAAC,UAAU,EAC1C,cAAc,EACd,MAAM,CAAC,KAAK,CACZ,CAAC;QACF,MAAM,CAAC,UAAU,CAChB,+BAA+B,CAAC,UAAU,EAC1C,aAAa,EACb,MAAM,CAAC,IAAI,EACX,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CACzB,CAAC;QAEF,IAAI,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC;YACrC,MAAM,CAAC,MAAM,CACZ,+BAA+B,CAAC,UAAU,EAC1C,oBAAoB,EACpB,MAAM,CAAC,WAAW,CAClB,CAAC;QACH,CAAC;QACD,IAAI,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9B,MAAM,CAAC,MAAM,CAAC,+BAA+B,CAAC,UAAU,EAAE,aAAa,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;QACvF,CAAC;QACD,IAAI,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;YACpC,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,MAAM,CAAC,UAAU,IAAI,CAAC,EAAE,CAAC;gBAC9D,MAAM,IAAI,YAAY,CACrB,+BAA+B,CAAC,UAAU,EAC1C,iCAAiC,CACjC,CAAC;YACH,CAAC;QACF,CAAC;IACF,CAAC;IAED;;;;;;;OAOG;IACK,iBAAiB,CAAC,UAAsB,EAAE,KAAsC;QACvF,IAAI,UAAU,KAAK,UAAU,CAAC,OAAO,EAAE,CAAC;YACvC,IAAI,KAAK,KAAK,sBAAsB,CAAC,SAAS,IAAI,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC,EAAE,CAAC;gBACrF,MAAM,IAAI,YAAY,CAAC,+BAA+B,CAAC,UAAU,EAAE,gBAAgB,CAAC,CAAC;YACtF,CAAC;QACF,CAAC;aAAM,IAAI,UAAU,KAAK,UAAU,CAAC,aAAa,EAAE,CAAC;YACpD,IACC,KAAK,KAAK,sBAAsB,CAAC,SAAS;gBAC1C,KAAK,KAAK,sBAAsB,CAAC,SAAS;gBAC1C,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,EACjB,CAAC;gBACF,MAAM,IAAI,YAAY,CACrB,+BAA+B,CAAC,UAAU,EAC1C,2BAA2B,CAC3B,CAAC;YACH,CAAC;QACF,CAAC;aAAM,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;YAC9B,MAAM,IAAI,YAAY,CAAC,+BAA+B,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC;QACrF,CAAC;IACF,CAAC;IAED;;;;;;;;OAQG;IACK,UAAU,CAAC,UAAuB,EAAE,QAAiB;QAC5D,MAAM,GAAG,GAAG,GAAG,UAAU,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,UAAU,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;QAChG,IAAI,QAAQ,EAAE,CAAC;YACd,OAAO,GAAG,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC7B,CAAC;QACD,OAAO,GAAG,CAAC;IACZ,CAAC;IAED;;;;;;OAMG;IACK,KAAK,CAAC,iBAAiB;QAC9B,MAAM,UAAU,GAAG,MAAM,cAAc,CAAC,aAAa,EAAE,CAAC;QACxD,OAAO;YACN,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE;YAC5D,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,EAAE;SAChE,CAAC;IACH,CAAC;CACD","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport {\n\ttype IBackgroundTask,\n\ttype IBackgroundTaskComponent,\n\tTaskStatus\n} from \"@3sixty/background-task-models\";\nimport { ContextIdKeys, ContextIdStore, type IContextIds } from \"@3sixty/context\";\nimport {\n\tAlreadyExistsError,\n\tBaseError,\n\tCoerce,\n\tComponentFactory,\n\tConverter,\n\tGeneralError,\n\tGuards,\n\tIs,\n\tLfuCache,\n\tNotFoundError,\n\tRandomHelper\n} from \"@3sixty/core\";\nimport {\n\tComparisonOperator,\n\ttype EntityCondition,\n\tLogicalOperator,\n\tSortDirection\n} from \"@3sixty/entity\";\nimport {\n\tEntityStorageConnectorFactory,\n\ttype IEntityStorageConnector\n} from \"@3sixty/entity-storage-models\";\nimport type { ILogEntry, ILoggingComponent } from \"@3sixty/logging-models\";\n\nimport {\n\ttype ITelemetryConnector,\n\ttype ITelemetryMetric,\n\ttype ITelemetryMetricValue,\n\ttype ITelemetryMetricValueEntry,\n\tMetricCounterOperation,\n\tMetricType\n} from \"@3sixty/telemetry-models\";\nimport type { TelemetryMetric } from \"./entities/telemetryMetric.js\";\nimport type { TelemetryMetricValue } from \"./entities/telemetryMetricValue.js\";\nimport type { IEntityStorageTelemetryConnectorConstructorOptions } from \"./models/IEntityStorageTelemetryConnectorConstructorOptions.js\";\nimport type { ITelemetryMetricValuePayload } from \"./models/ITelemetryMetricValuePayload.js\";\nimport type { ITelemetryMetricValueTaskPayload } from \"./models/ITelemetryMetricValueTaskPayload.js\";\nimport type { ITelemetryMetricValueWriterConfig } from \"./models/ITelemetryMetricValueWriterConfig.js\";\n\n/**\n * Class for performing telemetry operations in entity storage.\n * The metric values are written by a long running background thread, so nothing about a value\n * is held in memory here; only the metric definitions, which are immutable once registered,\n * are cached.\n */\nexport class EntityStorageTelemetryConnector implements ITelemetryConnector {\n\t/**\n\t * The namespace supported by the telemetry connector.\n\t */\n\tpublic static readonly NAMESPACE: string = \"entity-storage\";\n\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = \"EntityStorageTelemetryConnector\";\n\n\t/**\n\t * Default time in milliseconds to wait for the background thread to confirm a flush.\n\t */\n\tpublic static readonly DEFAULT_FLUSH_TIMEOUT_MS: number = 30_000;\n\n\t/**\n\t * Maximum number of metric definitions to hold in the in-memory definition cache.\n\t */\n\tpublic static readonly DEFAULT_METRIC_DEFINITION_CACHE_CAPACITY: number = 1000;\n\n\t/**\n\t * Time-to-idle in milliseconds for cached metric definitions.\n\t * Metric definitions are immutable once registered; a long TTI keeps active\n\t * metrics cached without permanent references.\n\t */\n\tpublic static readonly DEFAULT_METRIC_DEFINITION_CACHE_TTI_MS: number = 3_600_000;\n\n\t/**\n\t * Default time in milliseconds values are held so several share a single background task.\n\t * Keeps the task write off the caller's path, which matters most for the metrics recorded\n\t * on every REST request.\n\t * The window is also what limits how many tasks the connector can produce, as one task\n\t * carries a whole window regardless of how many values it holds. The scheduler moves a\n\t * single task per cycle for a task type, which is of the order of a few per second once\n\t * the task queue is on a database, so the window is set an order of magnitude below that:\n\t * one task per second whatever the metric rate.\n\t */\n\tpublic static readonly DEFAULT_TASK_COALESCE_MS: number = 1000;\n\n\t/**\n\t * Maximum number of values held while coalescing before a task is created regardless of\n\t * how much of the window is left.\n\t * This is a bound on the values held for the window rather than a throughput control; a\n\t * limit low enough to be reached by ordinary traffic would raise the task rate above what\n\t * the scheduler can drain.\n\t */\n\tpublic static readonly DEFAULT_COALESCE_SIZE: number = 10_000;\n\n\t/**\n\t * Default time in milliseconds with tasks outstanding and none of them completing before\n\t * the background thread is treated as stalled.\n\t */\n\tpublic static readonly DEFAULT_TASK_STALL_TIMEOUT_MS: number = 60_000;\n\n\t/**\n\t * Task type identifier for the metric value background task.\n\t * @internal\n\t */\n\tprivate static readonly _METRIC_VALUE_TASK_TYPE: string = \"telemetry-metric-value-write\";\n\n\t/**\n\t * Cap on how many times the stall wait can double.\n\t * @internal\n\t */\n\tprivate static readonly _STALL_BACKOFF_MAX_MULTIPLIER: number = 8;\n\n\t/**\n\t * The entity storage for the telemetry metrics.\n\t * @internal\n\t */\n\tprivate readonly _metricStorage: IEntityStorageConnector<TelemetryMetric>;\n\n\t/**\n\t * The entity storage for the telemetry metric values, used for the read operations.\n\t * @internal\n\t */\n\tprivate readonly _metricValueStorage: IEntityStorageConnector<TelemetryMetricValue>;\n\n\t/**\n\t * The component for logging.\n\t * @internal\n\t */\n\tprivate readonly _logging?: ILoggingComponent;\n\n\t/**\n\t * The background task component used to run the metric value writes on a background thread.\n\t * Absent when the connector is created inside an engine clone, which builds it only to\n\t * register the metric storage and never writes values through it.\n\t * @internal\n\t */\n\tprivate readonly _backgroundTaskComponent?: IBackgroundTaskComponent;\n\n\t/**\n\t * The URL of the module the background thread runs the metric value writes from.\n\t * @internal\n\t */\n\tprivate readonly _metricValueTaskHandler: string;\n\n\t/**\n\t * The writer settings passed to the background thread with each task payload.\n\t * @internal\n\t */\n\tprivate readonly _writerConfig: ITelemetryMetricValueWriterConfig;\n\n\t/**\n\t * How long in milliseconds to wait for the background thread to confirm a flush.\n\t * @internal\n\t */\n\tprivate readonly _flushTimeoutMs: number;\n\n\t/**\n\t * The flush task the background thread has been given which has not yet reached a final\n\t * state, with the value count it will have written once it completes. Present so a read\n\t * waits for a flush already in the queue rather than adding another to it.\n\t * @internal\n\t */\n\tprivate _pendingFlush?: { taskId: string; coveredCount: number };\n\n\t/**\n\t * Write requests waiting for their background task to complete, keyed by task id.\n\t * @internal\n\t */\n\tprivate readonly _pendingWrites: Map<\n\t\tstring,\n\t\t{ resolve: (isWritten: boolean) => void; timer: ReturnType<typeof setTimeout> }\n\t>;\n\n\t/**\n\t * How long in milliseconds to hold values before handing them to the background thread as a\n\t * single task; 0 creates a task per value.\n\t * @internal\n\t */\n\tprivate readonly _taskCoalesceMs: number;\n\n\t/**\n\t * Values held for the coalesce window, waiting to be handed to the background thread.\n\t * @internal\n\t */\n\tprivate readonly _coalesced: ITelemetryMetricValuePayload[];\n\n\t/**\n\t * Handle for the coalesce window timer.\n\t * @internal\n\t */\n\tprivate _coalesceTimer?: ReturnType<typeof setTimeout>;\n\n\t/**\n\t * How long in milliseconds with tasks outstanding and none of them completing before the\n\t * background thread is treated as stalled; 0 disables the check.\n\t * @internal\n\t */\n\tprivate readonly _taskStallTimeoutMs: number;\n\n\t/**\n\t * Ids of the tasks handed to the background thread which have not yet reported a final\n\t * state, oldest first. Ids only; the values they carry belong to the thread.\n\t * @internal\n\t */\n\tprivate readonly _outstandingTaskIds: Set<string>;\n\n\t/**\n\t * The time a task last reached a final state, the outstanding tasks were created, or a task\n\t * was last reported still waiting for a worker. A thread which has stopped answering is only\n\t * visible as the absence of this moving on.\n\t * @internal\n\t */\n\tprivate _lastTaskProgress: number;\n\n\t/**\n\t * Handle for the stall check timer, present only while tasks are outstanding.\n\t * @internal\n\t */\n\tprivate _stallTimer?: ReturnType<typeof setTimeout>;\n\n\t/**\n\t * True while a stall check, or the replacement it starts, is running.\n\t * @internal\n\t */\n\tprivate _stallCheckRunning: boolean;\n\n\t/**\n\t * Number of consecutive replacements with no completion between them; reset once a task\n\t * completes. Used to back off the stall wait.\n\t * @internal\n\t */\n\tprivate _stallRestartCount: number;\n\n\t/**\n\t * Number of values handed to the background thread, counted once their task is queued.\n\t * @internal\n\t */\n\tprivate _valuesQueued: number;\n\n\t/**\n\t * The value count as of the last write the background thread confirmed.\n\t * @internal\n\t */\n\tprivate _valuesWritten: number;\n\n\t/**\n\t * Serialises the write requests. A caller cannot return on a write already in progress, as\n\t * its own values may have been queued behind that write's task.\n\t * @internal\n\t */\n\tprivate _writeChain: Promise<void>;\n\n\t/**\n\t * Is the service running.\n\t * @internal\n\t */\n\tprivate _started: boolean;\n\n\t/**\n\t * Capacity used when constructing the metric definition cache.\n\t * @internal\n\t */\n\tprivate readonly _metricDefinitionCacheCapacity: number;\n\n\t/**\n\t * Time-to-idle in milliseconds used when constructing the metric definition cache.\n\t * @internal\n\t */\n\tprivate readonly _metricDefinitionCacheTtiMs: number;\n\n\t/**\n\t * In-memory cache of metric definitions, keyed by partition context + metric id.\n\t * Avoids a storage read on every addMetricValue call.\n\t * @internal\n\t */\n\tprivate _metricDefinitionCache: LfuCache<TelemetryMetric>;\n\n\t/**\n\t * Create a new instance of EntityStorageTelemetryConnector.\n\t * @param options The options for the connector.\n\t */\n\tconstructor(options?: IEntityStorageTelemetryConnectorConstructorOptions) {\n\t\tthis._metricStorage = EntityStorageConnectorFactory.get(\n\t\t\toptions?.telemetryMetricStorageConnectorType ?? \"telemetry-metric\"\n\t\t);\n\t\tthis._metricValueStorage = EntityStorageConnectorFactory.get(\n\t\t\toptions?.telemetryMetricValueStorageConnectorType ?? \"telemetry-metric-value\"\n\t\t);\n\n\t\tthis._logging = ComponentFactory.getIfExists(options?.loggingComponentType);\n\n\t\t// The background task component is used to run metric value writes on a background thread.\n\t\t// but if this component is instantiated from the background thread it is not needed.\n\t\tthis._backgroundTaskComponent = ComponentFactory.getIfExists<IBackgroundTaskComponent>(\n\t\t\toptions?.backgroundTaskComponentType ?? \"background-task\"\n\t\t);\n\n\t\tthis._metricValueTaskHandler =\n\t\t\toptions?.config?.overrideMetricValueTaskHandler ??\n\t\t\tnew URL(\"./telemetryMetricValueTask.js\", import.meta.url).href;\n\n\t\tthis._writerConfig = {\n\t\t\ttelemetryMetricValueStorageConnectorType: options?.telemetryMetricValueStorageConnectorType,\n\t\t\tloggingComponentType: options?.loggingComponentType,\n\t\t\tbatchSize: options?.config?.batchSize,\n\t\t\tbatchIntervalMs: options?.config?.batchIntervalMs,\n\t\t\tmaxCacheSize: options?.config?.maxCacheSize,\n\t\t\ttrimIntervalMs: options?.config?.trimIntervalMs,\n\t\t\ttrimRemoveLimit: options?.config?.trimRemoveLimit\n\t\t};\n\n\t\tconst cfgFlushTimeoutMs =\n\t\t\tCoerce.integer(options?.config?.flushTimeoutMs) ??\n\t\t\tEntityStorageTelemetryConnector.DEFAULT_FLUSH_TIMEOUT_MS;\n\t\tthis._flushTimeoutMs =\n\t\t\tcfgFlushTimeoutMs > 0\n\t\t\t\t? cfgFlushTimeoutMs\n\t\t\t\t: EntityStorageTelemetryConnector.DEFAULT_FLUSH_TIMEOUT_MS;\n\n\t\tconst cfgCoalesceMs =\n\t\t\tCoerce.integer(options?.config?.taskCoalesceMs) ??\n\t\t\tEntityStorageTelemetryConnector.DEFAULT_TASK_COALESCE_MS;\n\t\tthis._taskCoalesceMs = cfgCoalesceMs > 0 ? cfgCoalesceMs : 0;\n\n\t\tconst cfgTaskStallTimeoutMs =\n\t\t\tCoerce.integer(options?.config?.taskStallTimeoutMs) ??\n\t\t\tEntityStorageTelemetryConnector.DEFAULT_TASK_STALL_TIMEOUT_MS;\n\t\tthis._taskStallTimeoutMs = cfgTaskStallTimeoutMs > 0 ? cfgTaskStallTimeoutMs : 0;\n\n\t\tthis._pendingWrites = new Map();\n\t\tthis._coalesced = [];\n\t\tthis._outstandingTaskIds = new Set();\n\t\tthis._lastTaskProgress = 0;\n\t\tthis._stallCheckRunning = false;\n\t\tthis._stallRestartCount = 0;\n\t\tthis._valuesQueued = 0;\n\t\tthis._valuesWritten = 0;\n\t\tthis._writeChain = Promise.resolve();\n\t\tthis._started = false;\n\t\tthis._metricDefinitionCacheCapacity =\n\t\t\tCoerce.integer(options?.config?.metricDefinitionCacheCapacity) ??\n\t\t\tEntityStorageTelemetryConnector.DEFAULT_METRIC_DEFINITION_CACHE_CAPACITY;\n\t\tthis._metricDefinitionCacheTtiMs =\n\t\t\tCoerce.integer(options?.config?.metricDefinitionCacheTtiMs) ??\n\t\t\tEntityStorageTelemetryConnector.DEFAULT_METRIC_DEFINITION_CACHE_TTI_MS;\n\t\tthis._metricDefinitionCache = new LfuCache({\n\t\t\tcapacity: this._metricDefinitionCacheCapacity,\n\t\t\tttiMs: this._metricDefinitionCacheTtiMs\n\t\t});\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn EntityStorageTelemetryConnector.CLASS_NAME;\n\t}\n\n\t/**\n\t * Start the connector; registers the long running background thread which writes the\n\t * metric values.\n\t * @returns A promise that resolves when the connector is ready to accept metric values.\n\t */\n\tpublic async start(): Promise<void> {\n\t\tif (!this._started) {\n\t\t\tthis._started = true;\n\t\t\tthis._metricDefinitionCache = new LfuCache({\n\t\t\t\tcapacity: this._metricDefinitionCacheCapacity,\n\t\t\t\tttiMs: this._metricDefinitionCacheTtiMs\n\t\t\t});\n\n\t\t\tawait this.registerMetricValueHandler();\n\t\t}\n\t}\n\n\t/**\n\t * Stop the connector; writes any values still pending and releases the background thread.\n\t * @returns A promise that resolves when the final write completes and the thread is released.\n\t */\n\tpublic async stop(): Promise<void> {\n\t\tthis.stopCoalesceTimer();\n\n\t\t// Flush before clearing the started flag, as the background thread is only asked to\n\t\t// write while the connector is running. A failed final write must not abandon the rest\n\t\t// of the teardown, which is what releases the thread and the callers waiting on it.\n\t\ttry {\n\t\t\tawait this.flush();\n\t\t} catch (err) {\n\t\t\tawait this.logWithoutThrowing({\n\t\t\t\tsource: EntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\tmessage: \"stopFlushFailed\",\n\t\t\t\tlevel: \"error\",\n\t\t\t\terror: BaseError.fromError(err)\n\t\t\t});\n\t\t}\n\n\t\tthis._started = false;\n\t\tthis.stopStallTimer();\n\t\tthis._outstandingTaskIds.clear();\n\t\tthis._pendingFlush = undefined;\n\n\t\t// unregisterHandler terminates the worker without calling its shutdown method, so the\n\t\t// flush above is what persists anything the thread still had pending.\n\t\tawait this._backgroundTaskComponent?.unregisterHandler(\n\t\t\tEntityStorageTelemetryConnector._METRIC_VALUE_TASK_TYPE\n\t\t);\n\n\t\tfor (const pendingWrite of this._pendingWrites.values()) {\n\t\t\tglobalThis.clearTimeout(pendingWrite.timer);\n\t\t\tpendingWrite.resolve(false);\n\t\t}\n\t\tthis._pendingWrites.clear();\n\n\t\tthis._metricDefinitionCache.destroy();\n\t}\n\n\t/**\n\t * Create one or more metrics.\n\t * A single metric fails if it already exists; an array declares the set that should exist,\n\t * creating the ones that are missing and leaving the rest untouched. The array form resolves\n\t * what already exists with one query and writes the rest in one batch.\n\t * @param metric The metric details, or the details of several metrics.\n\t * @returns A promise that resolves when the metrics have been created.\n\t * @throws AlreadyExistsError if a single metric already exists.\n\t */\n\tpublic async createMetric(metric: ITelemetryMetric | ITelemetryMetric[]): Promise<void> {\n\t\tconst isBatch = Is.array<ITelemetryMetric>(metric);\n\t\tconst metrics = isBatch ? metric : [metric];\n\n\t\tfor (const entry of metrics) {\n\t\t\tthis.validateMetric(entry);\n\t\t}\n\n\t\tif (metrics.length === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst telemetryMetrics: TelemetryMetric[] = [];\n\n\t\tif (isBatch) {\n\t\t\tconst existing = await this._metricStorage.query(\n\t\t\t\t{\n\t\t\t\t\tproperty: \"id\",\n\t\t\t\t\tcomparison: ComparisonOperator.In,\n\t\t\t\t\tvalue: metrics.map(entry => entry.id)\n\t\t\t\t},\n\t\t\t\tundefined,\n\t\t\t\t[\"id\"],\n\t\t\t\tundefined,\n\t\t\t\tmetrics.length\n\t\t\t);\n\t\t\tconst existingIds = new Set(existing.entities.map(entity => entity.id as string));\n\n\t\t\tfor (const entry of metrics) {\n\t\t\t\tif (!existingIds.has(entry.id)) {\n\t\t\t\t\ttelemetryMetrics.push(this.toTelemetryMetric(entry));\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tconst existingMetric = await this._metricStorage.get(metrics[0].id);\n\t\t\tif (Is.notEmpty(existingMetric)) {\n\t\t\t\tthrow new AlreadyExistsError(\n\t\t\t\t\tEntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\t\t\"metricAlreadyExists\",\n\t\t\t\t\tmetrics[0].id\n\t\t\t\t);\n\t\t\t}\n\t\t\ttelemetryMetrics.push(this.toTelemetryMetric(metrics[0]));\n\t\t}\n\n\t\tif (telemetryMetrics.length === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (telemetryMetrics.length === 1) {\n\t\t\tawait this._metricStorage.set(telemetryMetrics[0]);\n\t\t} else {\n\t\t\tawait this._metricStorage.setBatch(telemetryMetrics);\n\t\t}\n\n\t\tconst contextIds = await this.captureContextIds();\n\t\tfor (const telemetryMetric of telemetryMetrics) {\n\t\t\tthis._metricDefinitionCache.set(\n\t\t\t\tthis.contextKey(contextIds, telemetryMetric.id),\n\t\t\t\ttelemetryMetric\n\t\t\t);\n\t\t}\n\n\t\tif (isBatch) {\n\t\t\tawait this._logging?.log({\n\t\t\t\tsource: EntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\tmessage: \"metricsCreated\",\n\t\t\t\tlevel: \"info\",\n\t\t\t\tdata: { count: telemetryMetrics.length }\n\t\t\t});\n\t\t} else {\n\t\t\tawait this._logging?.log({\n\t\t\t\tsource: EntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\tmessage: \"metricCreated\",\n\t\t\t\tlevel: \"info\",\n\t\t\t\tdata: {\n\t\t\t\t\tid: telemetryMetrics[0].id,\n\t\t\t\t\ttype: telemetryMetrics[0].type,\n\t\t\t\t\tlabel: telemetryMetrics[0].label\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Get the metric details and it's most recent value.\n\t * @param id The metric id.\n\t * @returns The metric details and it's most recent value.\n\t */\n\tpublic async getMetric(id: string): Promise<{\n\t\tmetric: ITelemetryMetric;\n\t\tvalue?: ITelemetryMetricValue;\n\t}> {\n\t\tconst metrics = await this.queryValues(id, undefined, undefined, undefined, 1);\n\t\treturn {\n\t\t\tmetric: metrics.metric,\n\t\t\tvalue: metrics.entities[0]\n\t\t};\n\t}\n\n\t/**\n\t * Update metric.\n\t * @param metric The metric details.\n\t * @returns A promise that resolves when the metric has been updated.\n\t */\n\tpublic async updateMetric(metric: Omit<ITelemetryMetric, \"type\">): Promise<void> {\n\t\tGuards.object<ITelemetryMetric>(\n\t\t\tEntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\"metric\",\n\t\t\tmetric\n\t\t);\n\t\tGuards.stringValue(EntityStorageTelemetryConnector.CLASS_NAME, \"metric.id\", metric.id);\n\t\tGuards.stringValue(\n\t\t\tEntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\"metric.label\",\n\t\t\tmetric.label\n\t\t);\n\t\tif (Is.notEmpty(metric.description)) {\n\t\t\tGuards.string(\n\t\t\t\tEntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\t\"metric.description\",\n\t\t\t\tmetric.description\n\t\t\t);\n\t\t}\n\t\tif (Is.notEmpty(metric.unit)) {\n\t\t\tGuards.string(EntityStorageTelemetryConnector.CLASS_NAME, \"metric.unit\", metric.unit);\n\t\t}\n\t\tif (Is.notEmpty(metric.maxHistory)) {\n\t\t\tif (!Is.integer(metric.maxHistory) || metric.maxHistory <= 0) {\n\t\t\t\tthrow new GeneralError(\n\t\t\t\t\tEntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\t\t\"maxHistoryMustBePositiveInteger\"\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tconst existingMetric = await this._metricStorage.get(metric.id);\n\t\tif (Is.undefined(existingMetric)) {\n\t\t\tthrow new NotFoundError(\n\t\t\t\tEntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\t\"metricNotFound\",\n\t\t\t\tmetric.id\n\t\t\t);\n\t\t}\n\n\t\tconst telemetryMetric: TelemetryMetric = {\n\t\t\tid: metric.id,\n\t\t\tlabel: metric.label,\n\t\t\ttype: existingMetric.type,\n\t\t\tunit: metric.unit ?? existingMetric.unit,\n\t\t\tdescription: metric.description ?? existingMetric.description,\n\t\t\tmaxHistory: metric.maxHistory ?? existingMetric.maxHistory\n\t\t};\n\n\t\tawait this._metricStorage.set(telemetryMetric);\n\t\tconst metricKey = this.contextKey(await this.captureContextIds(), metric.id);\n\t\tthis._metricDefinitionCache.set(metricKey, telemetryMetric);\n\n\t\tawait this._logging?.log({\n\t\t\tsource: EntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\tmessage: \"metricUpdated\",\n\t\t\tlevel: \"info\",\n\t\t\tdata: { id: metric.id, type: metric.type, label: metric.label }\n\t\t});\n\t}\n\n\t/**\n\t * Add a metric value.\n\t * The value is handed to the background thread which computes it from the value currently\n\t * in storage and performs the write, so it is not visible until the thread has flushed.\n\t * @param id The id of the metric.\n\t * @param value The value for the add operation.\n\t * @param customData The custom data for the metric value.\n\t * @returns The id of the newly created metric value entry.\n\t */\n\tpublic async addMetricValue(\n\t\tid: string,\n\t\tvalue: MetricCounterOperation | number,\n\t\tcustomData?: { [key: string]: unknown }\n\t): Promise<string> {\n\t\tGuards.stringValue(EntityStorageTelemetryConnector.CLASS_NAME, \"id\", id);\n\n\t\tconst metricValue = await this.buildMetricValue(id, value, customData);\n\n\t\tawait this.queueMetricValues([metricValue]);\n\n\t\tawait this._logging?.log({\n\t\t\tsource: EntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\tmessage: \"metricValueCreated\",\n\t\t\tlevel: \"info\",\n\t\t\tdata: { id }\n\t\t});\n\n\t\treturn metricValue.valueId;\n\t}\n\n\t/**\n\t * Add multiple metric values, handing the whole set to the background thread as a single\n\t * task rather than one per value.\n\t * @param values The metric values to add.\n\t * @returns The ids of the newly created metric value entries, in the order supplied.\n\t */\n\tpublic async addMetricValues(values: ITelemetryMetricValueEntry[]): Promise<string[]> {\n\t\tGuards.array<ITelemetryMetricValueEntry>(\n\t\t\tEntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\"values\",\n\t\t\tvalues\n\t\t);\n\n\t\tconst metricValues: ITelemetryMetricValuePayload[] = [];\n\n\t\tfor (const entry of values) {\n\t\t\tGuards.stringValue(EntityStorageTelemetryConnector.CLASS_NAME, \"entry.id\", entry.id);\n\t\t\tmetricValues.push(await this.buildMetricValue(entry.id, entry.value, entry.customData));\n\t\t}\n\n\t\tawait this.queueMetricValues(metricValues);\n\n\t\tawait this._logging?.log({\n\t\t\tsource: EntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\tmessage: \"metricValuesCreated\",\n\t\t\tlevel: \"info\",\n\t\t\tdata: { count: metricValues.length }\n\t\t});\n\n\t\treturn metricValues.map(metricValue => metricValue.valueId);\n\t}\n\n\t/**\n\t * Get a specific metric value by its id.\n\t * @param id The id of the metric.\n\t * @param valueId The id of the metric value.\n\t * @returns The metric value.\n\t */\n\tpublic async getMetricValue(id: string, valueId: string): Promise<ITelemetryMetricValue> {\n\t\tGuards.stringValue(EntityStorageTelemetryConnector.CLASS_NAME, \"id\", id);\n\t\tGuards.stringValue(EntityStorageTelemetryConnector.CLASS_NAME, \"valueId\", valueId);\n\n\t\tawait this.flush();\n\n\t\tconst results = await this._metricValueStorage.query(\n\t\t\t{\n\t\t\t\tconditions: [\n\t\t\t\t\t{ property: \"metricId\", comparison: ComparisonOperator.Equals, value: id },\n\t\t\t\t\t{ property: \"id\", comparison: ComparisonOperator.Equals, value: valueId }\n\t\t\t\t],\n\t\t\t\tlogicalOperator: LogicalOperator.And\n\t\t\t},\n\t\t\tundefined,\n\t\t\tundefined,\n\t\t\tundefined,\n\t\t\t1\n\t\t);\n\n\t\tif (results.entities.length === 0) {\n\t\t\tthrow new NotFoundError(\n\t\t\t\tEntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\t\"metricValueNotFound\",\n\t\t\t\tvalueId\n\t\t\t);\n\t\t}\n\n\t\tconst metricValue = results.entities[0] as TelemetryMetricValue;\n\t\treturn {\n\t\t\tid: metricValue.id,\n\t\t\tts: metricValue.ts,\n\t\t\tvalue: metricValue.value,\n\t\t\tcustomData: metricValue.customData\n\t\t};\n\t}\n\n\t/**\n\t * Remove metric.\n\t * @param id The id of the metric.\n\t * @returns A promise that resolves when the metric and all its values have been removed.\n\t */\n\tpublic async removeMetric(id: string): Promise<void> {\n\t\tGuards.stringValue(EntityStorageTelemetryConnector.CLASS_NAME, \"id\", id);\n\n\t\tconst existingMetric = await this._metricStorage.get(id);\n\t\tif (Is.undefined(existingMetric)) {\n\t\t\tthrow new NotFoundError(EntityStorageTelemetryConnector.CLASS_NAME, \"metricNotFound\", id);\n\t\t}\n\n\t\tawait this.flush();\n\n\t\tawait this._metricStorage.remove(id);\n\t\tconst metricKey = this.contextKey(await this.captureContextIds(), id);\n\t\tthis._metricDefinitionCache.delete(metricKey);\n\n\t\tlet removeValuesCursor: string | undefined;\n\t\tconst valueIdsToRemove: string[] = [];\n\t\tdo {\n\t\t\tconst existingMetricValuesResult = await this._metricValueStorage.query(\n\t\t\t\t{\n\t\t\t\t\tproperty: \"metricId\",\n\t\t\t\t\tcomparison: ComparisonOperator.Equals,\n\t\t\t\t\tvalue: id\n\t\t\t\t},\n\t\t\t\tundefined,\n\t\t\t\t[\"id\"],\n\t\t\t\tremoveValuesCursor\n\t\t\t);\n\t\t\tvalueIdsToRemove.push(...existingMetricValuesResult.entities.map(e => e.id as string));\n\t\t\tremoveValuesCursor = existingMetricValuesResult.cursor;\n\t\t} while (Is.stringValue(removeValuesCursor));\n\t\tif (valueIdsToRemove.length > 0) {\n\t\t\tawait this._metricValueStorage.removeBatch(valueIdsToRemove);\n\t\t}\n\n\t\tawait this._logging?.log({\n\t\t\tsource: EntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\tmessage: \"metricRemoved\",\n\t\t\tlevel: \"info\",\n\t\t\tdata: { id }\n\t\t});\n\t}\n\n\t/**\n\t * Query the metrics.\n\t * @param type The type of the metric.\n\t * @param cursor The cursor to request the next chunk of entities.\n\t * @param limit Limit the number of entities to return.\n\t * @returns All the entities for the storage matching the conditions,\n\t * and a cursor which can be used to request more entities.\n\t * @throws NotImplementedError if the implementation does not support retrieval.\n\t */\n\tpublic async query(\n\t\ttype?: MetricType,\n\t\tcursor?: string,\n\t\tlimit?: number\n\t): Promise<{\n\t\t/**\n\t\t * The metrics.\n\t\t */\n\t\tentities: ITelemetryMetric[];\n\n\t\t/**\n\t\t * An optional cursor, when defined can be used to call find to get more values.\n\t\t */\n\t\tcursor?: string;\n\t}> {\n\t\tconst condition: EntityCondition<TelemetryMetric> = {\n\t\t\tconditions: [],\n\t\t\tlogicalOperator: LogicalOperator.And\n\t\t};\n\n\t\tif (Is.arrayOneOf(type, Object.values(MetricType))) {\n\t\t\tcondition.conditions.push({\n\t\t\t\tproperty: \"type\",\n\t\t\t\tcomparison: ComparisonOperator.Equals,\n\t\t\t\tvalue: type\n\t\t\t});\n\t\t}\n\n\t\tconst result = await this._metricStorage.query(\n\t\t\tcondition.conditions.length > 0 ? condition : undefined,\n\t\t\t[\n\t\t\t\t{\n\t\t\t\t\tproperty: \"label\",\n\t\t\t\t\tsortDirection: SortDirection.Ascending\n\t\t\t\t}\n\t\t\t],\n\t\t\tundefined,\n\t\t\tcursor,\n\t\t\tlimit\n\t\t);\n\n\t\treturn {\n\t\t\tentities: result.entities as ITelemetryMetric[],\n\t\t\tcursor: result.cursor\n\t\t};\n\t}\n\n\t/**\n\t * Query the metric values.\n\t * @param id The id of the metric.\n\t * @param timeStart The inclusive time as the start of the metric entries.\n\t * @param timeEnd The inclusive time as the end of the metric entries.\n\t * @param cursor The cursor to request the next chunk of entities.\n\t * @param limit Limit the number of entities to return.\n\t * @returns All the entities for the storage matching the conditions,\n\t * and a cursor which can be used to request more entities.\n\t */\n\tpublic async queryValues(\n\t\tid: string,\n\t\ttimeStart?: number,\n\t\ttimeEnd?: number,\n\t\tcursor?: string,\n\t\tlimit?: number\n\t): Promise<{\n\t\t/**\n\t\t * The metric details.\n\t\t */\n\t\tmetric: ITelemetryMetric;\n\n\t\t/**\n\t\t * The values for the metric.\n\t\t */\n\t\tentities: ITelemetryMetricValue[];\n\n\t\t/**\n\t\t * An optional cursor, when defined can be used to call find to get more values.\n\t\t */\n\t\tcursor?: string;\n\t}> {\n\t\tGuards.stringValue(EntityStorageTelemetryConnector.CLASS_NAME, \"id\", id);\n\n\t\tawait this.flush();\n\n\t\tconst existingMetric = await this._metricStorage.get(id);\n\t\tif (Is.undefined(existingMetric)) {\n\t\t\tthrow new NotFoundError(EntityStorageTelemetryConnector.CLASS_NAME, \"metricNotFound\", id);\n\t\t}\n\n\t\tconst condition: EntityCondition<TelemetryMetricValue> = {\n\t\t\tconditions: [],\n\t\t\tlogicalOperator: LogicalOperator.And\n\t\t};\n\n\t\tcondition.conditions.push({\n\t\t\tproperty: \"metricId\",\n\t\t\tcomparison: ComparisonOperator.Equals,\n\t\t\tvalue: id\n\t\t});\n\n\t\tif (Is.number(timeStart)) {\n\t\t\tcondition.conditions.push({\n\t\t\t\tproperty: \"ts\",\n\t\t\t\tcomparison: ComparisonOperator.GreaterThanOrEqual,\n\t\t\t\tvalue: timeStart\n\t\t\t});\n\t\t}\n\n\t\tif (Is.number(timeEnd)) {\n\t\t\tcondition.conditions.push({\n\t\t\t\tproperty: \"ts\",\n\t\t\t\tcomparison: ComparisonOperator.LessThanOrEqual,\n\t\t\t\tvalue: timeEnd\n\t\t\t});\n\t\t}\n\n\t\t// Newest first, with the value breaking a tie for a counter. Leading on the value instead\n\t\t// meant sorting the whole history by a column no index on the metric can serve, and it\n\t\t// gave the same sequence anyway: the writer only ever moves a counter up, and forces each\n\t\t// timestamp past the one it read, so a larger value always carries a later timestamp.\n\t\tconst sortBy: { property: keyof TelemetryMetricValue; sortDirection: SortDirection }[] = [\n\t\t\t{ property: \"ts\", sortDirection: SortDirection.Descending }\n\t\t];\n\n\t\tif (existingMetric.type === MetricType.Counter) {\n\t\t\tsortBy.push({ property: \"value\", sortDirection: SortDirection.Descending });\n\t\t}\n\n\t\tconst result = await this._metricValueStorage.query(\n\t\t\tcondition,\n\t\t\tsortBy,\n\t\t\tundefined,\n\t\t\tcursor,\n\t\t\tlimit\n\t\t);\n\n\t\tconst entities = result.entities as ITelemetryMetricValue[];\n\n\t\treturn {\n\t\t\tmetric: existingMetric as ITelemetryMetric,\n\t\t\tentities,\n\t\t\tcursor: result.cursor\n\t\t};\n\t}\n\n\t/**\n\t * Register the handler which runs the metric value writes on the background thread.\n\t * Also used to replace a thread which has stopped answering, so the registration is kept in\n\t * one place.\n\t * @returns A promise that resolves when the handler is registered.\n\t * @internal\n\t */\n\tprivate async registerMetricValueHandler(): Promise<void> {\n\t\tawait this._backgroundTaskComponent?.registerHandler<\n\t\t\tITelemetryMetricValueTaskPayload,\n\t\t\tundefined\n\t\t>(\n\t\t\tEntityStorageTelemetryConnector._METRIC_VALUE_TASK_TYPE,\n\t\t\tthis._metricValueTaskHandler,\n\t\t\t\"telemetryMetricValueTask\",\n\t\t\tasync task => this.metricValueTaskStateChanged(task),\n\t\t\t{\n\t\t\t\t// A single long running worker, kept alive by the negative idle shutdown timeout,\n\t\t\t\t// so the values it has batched are never split across threads.\n\t\t\t\tmaxWorkerCount: 1,\n\t\t\t\tidleShutdownTimeout: -1,\n\t\t\t\tinitialiseMethod: \"telemetryMetricValueTaskStart\",\n\t\t\t\t// The writer settings are sent once when the worker starts rather than with\n\t\t\t\t// every value.\n\t\t\t\tinitialiseMethodParams: async () => [this._writerConfig],\n\t\t\t\tshutdownMethod: \"telemetryMetricValueTaskEnd\"\n\t\t\t}\n\t\t);\n\t}\n\n\t/**\n\t * Ask the background thread to write everything it currently has pending, so a read sees\n\t * the values which have been added but not yet persisted.\n\t * @returns A promise that resolves when the background thread confirms the write, or when\n\t * the flush timeout elapses.\n\t * @internal\n\t */\n\tprivate async flush(): Promise<void> {\n\t\tif (\n\t\t\t!this._started ||\n\t\t\tIs.empty(this._backgroundTaskComponent) ||\n\t\t\t(this._coalesced.length === 0 && this._valuesQueued === this._valuesWritten)\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\t// A caller has to take its turn behind any write already running rather than join it: the\n\t\t// values it added may have been queued after that write's task, so joining would let it\n\t\t// read storage before its own values were written. The handed on promise is only ever\n\t\t// resolved, never rejected, so a failed write does not stop the caller behind it.\n\t\tconst previous = this._writeChain;\n\t\tlet release: (() => void) | undefined;\n\t\tthis._writeChain = new Promise<void>(resolve => {\n\t\t\trelease = resolve;\n\t\t});\n\n\t\tawait previous;\n\n\t\ttry {\n\t\t\tawait this.requestWrite();\n\t\t} finally {\n\t\t\trelease?.();\n\t\t}\n\t}\n\n\t/**\n\t * Ask the background thread to write, handing over any values still inside the coalesce\n\t * window with the same task so a read costs one task rather than two.\n\t * @returns A promise that resolves when the background thread confirms the write, or when the\n\t * write timeout elapses.\n\t * @internal\n\t */\n\tprivate async requestWrite(): Promise<void> {\n\t\tif (!this._started) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.stopCoalesceTimer();\n\n\t\t// splice is synchronous, so there is no yield point where a value could be handed over twice.\n\t\tconst values = this._coalesced.splice(0);\n\n\t\tif (values.length === 0 && this._valuesQueued === this._valuesWritten) {\n\t\t\treturn;\n\t\t}\n\n\t\t// A flush the thread has already been given which covers everything queued will write\n\t\t// what this caller needs, so it waits for that one. Creating another would lengthen the\n\t\t// very queue the caller is waiting on, which is how a backend slow enough to miss one\n\t\t// flush timeout turns into a queue that grows with every read.\n\t\tconst pendingFlush = this._pendingFlush;\n\t\tif (\n\t\t\tvalues.length === 0 &&\n\t\t\t!Is.empty(pendingFlush) &&\n\t\t\tpendingFlush.coveredCount >= this._valuesQueued\n\t\t) {\n\t\t\tif (await this.waitForTask(pendingFlush.taskId)) {\n\t\t\t\tthis._valuesWritten = pendingFlush.coveredCount;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tlet taskId: string;\n\t\ttry {\n\t\t\ttaskId = await this.createMetricValueTask(\n\t\t\t\tvalues.length > 0 ? { values, flush: true } : { flush: true }\n\t\t\t);\n\t\t} catch (err) {\n\t\t\t// Nothing was handed over, so put them back at the head for the next attempt.\n\t\t\tthis._coalesced.unshift(...values);\n\t\t\tthrow err;\n\t\t}\n\n\t\tthis._valuesQueued += values.length;\n\n\t\t// Read before waiting, so every value it counts belongs to a task the thread already\n\t\t// has; anything queued afterwards leaves the counts apart for the next read.\n\t\tconst queued = this._valuesQueued;\n\t\tthis._pendingFlush = { taskId, coveredCount: queued };\n\n\t\t// A task which does not confirm stays in the queue rather than being sent again, as the\n\t\t// thread may already have its values. The counts are only closed when it completes,\n\t\t// which for a caller that gave up first happens in metricValueTaskStateChanged.\n\t\tif (await this.waitForTask(taskId)) {\n\t\t\tthis._valuesWritten = queued;\n\t\t}\n\t}\n\n\t/**\n\t * Resolve the metric definition, check the operation against it and build the payload the\n\t * background thread needs to write the value.\n\t * @param id The id of the metric.\n\t * @param value The value for the add operation.\n\t * @param customData The custom data for the metric value.\n\t * @returns The payload for the background thread.\n\t * @throws NotFoundError if the metric does not exist.\n\t * @internal\n\t */\n\tprivate async buildMetricValue(\n\t\tid: string,\n\t\tvalue: MetricCounterOperation | number,\n\t\tcustomData?: { [key: string]: unknown }\n\t): Promise<ITelemetryMetricValuePayload> {\n\t\tconst contextIds = await this.captureContextIds();\n\t\tconst metricKey = this.contextKey(contextIds, id);\n\n\t\tconst existingMetric = await this._metricDefinitionCache.getOrSet(metricKey, async () => {\n\t\t\tconst stored = await this._metricStorage.get(id);\n\t\t\tif (Is.undefined(stored)) {\n\t\t\t\tthrow new NotFoundError(EntityStorageTelemetryConnector.CLASS_NAME, \"metricNotFound\", id);\n\t\t\t}\n\t\t\treturn stored;\n\t\t});\n\n\t\tconst metricType = existingMetric.type as MetricType;\n\t\tthis.validateOperation(metricType, value);\n\n\t\treturn {\n\t\t\tvalueId: Converter.bytesToHex(RandomHelper.generate(16)),\n\t\t\tmetricId: id,\n\t\t\tmetricType,\n\t\t\toperation: value,\n\t\t\tts: Date.now(),\n\t\t\tmaxHistory: existingMetric.maxHistory,\n\t\t\tcustomData,\n\t\t\tcontextIds\n\t\t};\n\t}\n\n\t/**\n\t * Hand values to the background thread, holding them for the coalesce window when one is\n\t * configured so several share a single task.\n\t * @param metricValues The values to hand over.\n\t * @returns A promise that resolves when the values have been queued or buffered.\n\t * @internal\n\t */\n\tprivate async queueMetricValues(metricValues: ITelemetryMetricValuePayload[]): Promise<void> {\n\t\tif (metricValues.length === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (this._taskCoalesceMs === 0) {\n\t\t\tawait this.createMetricValueTask({ values: metricValues });\n\t\t\tthis._valuesQueued += metricValues.length;\n\t\t\treturn;\n\t\t}\n\n\t\tthis._coalesced.push(...metricValues);\n\n\t\tif (this._coalesced.length >= EntityStorageTelemetryConnector.DEFAULT_COALESCE_SIZE) {\n\t\t\tawait this.sendCoalesced();\n\t\t} else {\n\t\t\tthis.armCoalesceTimer();\n\t\t}\n\t}\n\n\t/**\n\t * Arm the timer which hands the values held for the coalesce window over.\n\t * @internal\n\t */\n\tprivate armCoalesceTimer(): void {\n\t\tif (!Is.empty(this._coalesceTimer)) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis._coalesceTimer = globalThis.setTimeout(async () => {\n\t\t\tthis._coalesceTimer = undefined;\n\t\t\ttry {\n\t\t\t\tawait this.sendCoalesced();\n\t\t\t} catch (err) {\n\t\t\t\tawait this.logWithoutThrowing({\n\t\t\t\t\tsource: EntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\t\tmessage: \"metricValueTaskFailed\",\n\t\t\t\t\tlevel: \"error\",\n\t\t\t\t\tdata: { taskId: \"\" },\n\t\t\t\t\terror: BaseError.fromError(err)\n\t\t\t\t});\n\n\t\t\t\t// The values are back at the head of the window, so the window has to keep\n\t\t\t\t// running: nothing else would hand them over until the next value is added.\n\t\t\t\tthis.armCoalesceTimer();\n\t\t\t}\n\t\t}, this._taskCoalesceMs);\n\t}\n\n\t/**\n\t * Hand any values held for the coalesce window to the background thread as a single task.\n\t * @returns A promise that resolves when the values have been queued.\n\t * @internal\n\t */\n\tprivate async sendCoalesced(): Promise<void> {\n\t\tthis.stopCoalesceTimer();\n\n\t\tif (this._coalesced.length === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\t// splice is synchronous, so there is no yield point where a value could be added twice.\n\t\tconst values = this._coalesced.splice(0);\n\n\t\ttry {\n\t\t\tawait this.createMetricValueTask({ values });\n\t\t\tthis._valuesQueued += values.length;\n\t\t} catch (err) {\n\t\t\t// Put them back at the head so the next attempt keeps them in order.\n\t\t\tthis._coalesced.unshift(...values);\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\t/**\n\t * Stop the coalesce window timer if it is running.\n\t * @internal\n\t */\n\tprivate stopCoalesceTimer(): void {\n\t\tif (!Is.empty(this._coalesceTimer)) {\n\t\t\tglobalThis.clearTimeout(this._coalesceTimer);\n\t\t\tthis._coalesceTimer = undefined;\n\t\t}\n\t}\n\n\t/**\n\t * Queue a task for the background thread.\n\t * @param payload The task payload.\n\t * @returns The id of the created task.\n\t * @throws GeneralError if no background task component is available to run the write.\n\t * @internal\n\t */\n\tprivate async createMetricValueTask(payload: ITelemetryMetricValueTaskPayload): Promise<string> {\n\t\tif (Is.empty(this._backgroundTaskComponent)) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tEntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\t\"backgroundTaskComponentMissing\"\n\t\t\t);\n\t\t}\n\n\t\tconst taskId = await this._backgroundTaskComponent.create<ITelemetryMetricValueTaskPayload>(\n\t\t\tEntityStorageTelemetryConnector._METRIC_VALUE_TASK_TYPE,\n\t\t\tpayload\n\t\t);\n\n\t\tif (this._outstandingTaskIds.size === 0) {\n\t\t\t// Nothing was outstanding, so the wait for the thread to show progress starts here.\n\t\t\tthis._lastTaskProgress = Date.now();\n\t\t}\n\t\tthis._outstandingTaskIds.add(taskId);\n\t\tthis.armStallTimer();\n\n\t\treturn taskId;\n\t}\n\n\t/**\n\t * Wait for the background thread to report a task complete.\n\t * @param taskId The id of the task to wait for.\n\t * @returns True when the task completed successfully.\n\t * @internal\n\t */\n\tprivate async waitForTask(taskId: string): Promise<boolean> {\n\t\treturn new Promise<boolean>(resolve => {\n\t\t\tconst timer = globalThis.setTimeout(async () => {\n\t\t\t\tthis._pendingWrites.delete(taskId);\n\t\t\t\tawait this._logging?.log({\n\t\t\t\t\tsource: EntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\t\tmessage: \"flushTimeout\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\tdata: {\n\t\t\t\t\t\ttaskId,\n\t\t\t\t\t\ttimeoutMs: this._flushTimeoutMs,\n\t\t\t\t\t\ttaskCount: this._outstandingTaskIds.size\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tresolve(false);\n\t\t\t}, this._flushTimeoutMs);\n\n\t\t\tthis._pendingWrites.set(taskId, { resolve, timer });\n\t\t});\n\t}\n\n\t/**\n\t * The delay before the next stall check: the configured timeout, doubled per consecutive\n\t * replacement not followed by a completion, capped at _STALL_BACKOFF_MAX_MULTIPLIER times.\n\t * @returns The delay in milliseconds.\n\t * @internal\n\t */\n\tprivate stallWaitMs(): number {\n\t\tconst multiplier = Math.min(\n\t\t\t2 ** this._stallRestartCount,\n\t\t\tEntityStorageTelemetryConnector._STALL_BACKOFF_MAX_MULTIPLIER\n\t\t);\n\t\treturn this._taskStallTimeoutMs * multiplier;\n\t}\n\n\t/**\n\t * Arm the timer which checks whether the background thread has stopped answering, for\n\t * whenever the stall wait will have elapsed with no task having completed.\n\t * @internal\n\t */\n\tprivate armStallTimer(): void {\n\t\tif (\n\t\t\tthis._taskStallTimeoutMs === 0 ||\n\t\t\t!this._started ||\n\t\t\t!Is.empty(this._stallTimer) ||\n\t\t\tthis._stallCheckRunning\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst delay = Math.max(this.stallWaitMs() - (Date.now() - this._lastTaskProgress), 0);\n\n\t\tthis._stallTimer = globalThis.setTimeout(async () => {\n\t\t\tthis._stallTimer = undefined;\n\t\t\t// Blocks a second check from arming while this one, and any replacement, is running.\n\t\t\tthis._stallCheckRunning = true;\n\t\t\ttry {\n\t\t\t\tawait this.checkForStalledThread();\n\t\t\t} catch (err) {\n\t\t\t\t// Supervision has to survive its own failures, and an unhandled rejection from\n\t\t\t\t// a timer would take the process with it.\n\t\t\t\tawait this.logWithoutThrowing({\n\t\t\t\t\tsource: EntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\t\tmessage: \"metricValueThreadCheckFailed\",\n\t\t\t\t\tlevel: \"error\",\n\t\t\t\t\terror: BaseError.fromError(err)\n\t\t\t\t});\n\n\t\t\t\t// A check which could not read the task cannot vouch for the flush being tracked\n\t\t\t\t// either, and the reads waiting for it have no other way out, so it is dropped\n\t\t\t\t// and the next read asks the thread again.\n\t\t\t\tthis._pendingFlush = undefined;\n\t\t\t} finally {\n\t\t\t\tthis._stallCheckRunning = false;\n\t\t\t\tif (this._outstandingTaskIds.size > 0) {\n\t\t\t\t\tthis.armStallTimer();\n\t\t\t\t}\n\t\t\t}\n\t\t}, delay);\n\t}\n\n\t/**\n\t * Stop the stall check timer if it is running.\n\t * @internal\n\t */\n\tprivate stopStallTimer(): void {\n\t\tif (!Is.empty(this._stallTimer)) {\n\t\t\tglobalThis.clearTimeout(this._stallTimer);\n\t\t\tthis._stallTimer = undefined;\n\t\t}\n\t}\n\n\t/**\n\t * Check the oldest outstanding task once none has completed for the stall wait, and act on\n\t * its status: missed, still waiting for a worker, or actually stalled.\n\t * @returns A promise that resolves when the check, and any action it takes, is complete.\n\t * @internal\n\t */\n\tprivate async checkForStalledThread(): Promise<void> {\n\t\tif (!this._started || this._outstandingTaskIds.size === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst stalledForMs = Date.now() - this._lastTaskProgress;\n\n\t\tif (stalledForMs < this.stallWaitMs()) {\n\t\t\t// A task completed while the timer was armed; the caller re-arms for what remains.\n\t\t\treturn;\n\t\t}\n\n\t\t// No task has completed since the oldest one outstanding was created, so that is the one\n\t\t// the thread would be stuck on. Replacing the thread costs whatever the writer has\n\t\t// batched, so the task is checked before it is done: a task which is no longer in the\n\t\t// queue means a state change was missed rather than the thread having stopped.\n\t\tconst stalledTaskId = this._outstandingTaskIds.values().next().value as string;\n\t\tconst stalledTask = await this._backgroundTaskComponent?.get(stalledTaskId);\n\n\t\tif (!this._outstandingTaskIds.has(stalledTaskId)) {\n\t\t\t// Completed, or the connector stopped, while the read above was in flight; already\n\t\t\t// handled by metricValueTaskStateChanged or stop(), so the stale snapshot is moot.\n\t\t\treturn;\n\t\t}\n\n\t\tif (\n\t\t\tIs.empty(stalledTask) ||\n\t\t\t(stalledTask.status !== TaskStatus.Pending && stalledTask.status !== TaskStatus.Processing)\n\t\t) {\n\t\t\tawait this.logWithoutThrowing({\n\t\t\t\tsource: EntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\tmessage: \"metricValueTaskStateMissed\",\n\t\t\t\tlevel: \"warn\",\n\t\t\t\tdata: { taskId: stalledTaskId, status: stalledTask?.status }\n\t\t\t});\n\t\t\tthis._outstandingTaskIds.clear();\n\t\t\tthis._pendingFlush = undefined;\n\t\t\tthis._lastTaskProgress = Date.now();\n\t\t\tthis._stallRestartCount = 0;\n\t\t\treturn;\n\t\t}\n\n\t\tif (stalledTask.status === TaskStatus.Pending) {\n\t\t\t// Not yet scheduled, so replacing the thread would not help; report and leave it.\n\t\t\tconst scheduledWaitMs = Date.now() - Date.parse(stalledTask.dateCreated);\n\t\t\tawait this.logWithoutThrowing({\n\t\t\t\tsource: EntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\tmessage: \"metricValueTaskNotScheduled\",\n\t\t\t\tlevel: \"warn\",\n\t\t\t\tdata: {\n\t\t\t\t\ttaskId: stalledTaskId,\n\t\t\t\t\tstalledForMs: scheduledWaitMs,\n\t\t\t\t\ttaskCount: this._outstandingTaskIds.size\n\t\t\t\t}\n\t\t\t});\n\t\t\tthis._lastTaskProgress = Date.now();\n\t\t\treturn;\n\t\t}\n\n\t\tawait this.restartMetricValueThread(stalledForMs, stalledTaskId);\n\t}\n\n\t/**\n\t * Replace the background thread after it has stopped completing the tasks handed to it.\n\t * Nothing else recovers from this: the handler has no execution timeout, so the scheduler\n\t * goes on waiting for the one worker it believes is busy and never dispatches for this task\n\t * type again. Re-registering the handler terminates that worker, which releases the\n\t * scheduler's claim on the task it was given, so the tasks in the queue are dispatched to\n\t * the replacement rather than left waiting on a thread that will never answer. Counts toward\n\t * the backoff in stallWaitMs() when not followed by a completion.\n\t * @param stalledForMs How long the thread has had tasks outstanding without completing one.\n\t * @param stalledTaskId The id of the task the thread has not completed.\n\t * @returns A promise that resolves when the handler has been re-registered.\n\t * @internal\n\t */\n\tprivate async restartMetricValueThread(\n\t\tstalledForMs: number,\n\t\tstalledTaskId: string\n\t): Promise<void> {\n\t\tawait this.logWithoutThrowing({\n\t\t\tsource: EntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\tmessage: \"metricValueThreadStalled\",\n\t\t\tlevel: \"error\",\n\t\t\tdata: { stalledForMs, taskId: stalledTaskId, taskCount: this._outstandingTaskIds.size }\n\t\t});\n\n\t\t// The tasks themselves are left in the queue, so the values already handed over are\n\t\t// written by the replacement rather than lost. Anything waiting on one of them is\n\t\t// released by its own flush timeout, or when the replacement completes it.\n\t\t// The replacement reports the tasks it completes, but the flush being tracked was given\n\t\t// to the thread that has gone, so it is dropped and the next read asks the new one.\n\t\tthis._outstandingTaskIds.clear();\n\t\tthis._pendingFlush = undefined;\n\t\tthis._lastTaskProgress = Date.now();\n\t\tthis._stallRestartCount += 1;\n\n\t\ttry {\n\t\t\tawait this._backgroundTaskComponent?.unregisterHandler(\n\t\t\t\tEntityStorageTelemetryConnector._METRIC_VALUE_TASK_TYPE\n\t\t\t);\n\t\t\tawait this.registerMetricValueHandler();\n\n\t\t\tawait this.logWithoutThrowing({\n\t\t\t\tsource: EntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\tmessage: \"metricValueThreadRestarted\",\n\t\t\t\tlevel: \"warn\"\n\t\t\t});\n\t\t} catch (err) {\n\t\t\tawait this.logWithoutThrowing({\n\t\t\t\tsource: EntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\tmessage: \"metricValueThreadRestartFailed\",\n\t\t\t\tlevel: \"error\",\n\t\t\t\terror: BaseError.fromError(err)\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Log on one of the paths which has no caller to report to, without the logging itself\n\t * becoming the failure. An unhandled rejection from a timer callback would end the\n\t * supervision it came from, and take the process with it.\n\t * @param logEntry The entry to log.\n\t * @returns A promise that resolves when the entry has been logged, or given up on.\n\t * @internal\n\t */\n\tprivate async logWithoutThrowing(logEntry: ILogEntry): Promise<void> {\n\t\ttry {\n\t\t\tawait this._logging?.log(logEntry);\n\t\t} catch {\n\t\t\t// The logging component is the only sink available, and it writes to storage which is\n\t\t\t// what tends to be failing when this path is reached at all.\n\t\t}\n\t}\n\n\t/**\n\t * Handle a state change reported for a metric value task.\n\t * @param task The task which changed state.\n\t * @returns A promise that resolves when any waiting flush has been released.\n\t * @internal\n\t */\n\tprivate async metricValueTaskStateChanged(\n\t\ttask: IBackgroundTask<ITelemetryMetricValueTaskPayload, undefined>\n\t): Promise<void> {\n\t\tif (\n\t\t\ttask.status !== TaskStatus.Success &&\n\t\t\ttask.status !== TaskStatus.Failed &&\n\t\t\ttask.status !== TaskStatus.Cancelled\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\t// A final state is the only evidence the thread is still working through what it has\n\t\t// been given. Only this connector's own tasks are tracked, so a state reported for one\n\t\t// belonging to another connector in the process is simply not found.\n\t\tthis._outstandingTaskIds.delete(task.id);\n\t\tthis._lastTaskProgress = Date.now();\n\t\tthis._stallRestartCount = 0;\n\n\t\tif (this._pendingFlush?.taskId === task.id) {\n\t\t\t// A caller which gave up on this task is long gone, but the values it was waiting for\n\t\t\t// are written all the same, so the counts are closed here rather than being left\n\t\t\t// apart for every later read to ask about again.\n\t\t\tif (task.status === TaskStatus.Success) {\n\t\t\t\tthis._valuesWritten = Math.max(this._valuesWritten, this._pendingFlush.coveredCount);\n\t\t\t}\n\t\t\tthis._pendingFlush = undefined;\n\t\t}\n\n\t\t// The reset can shorten stallWaitMs() below what the current timer expects, so it is\n\t\t// cleared and, if anything is still outstanding, re-armed.\n\t\tthis.stopStallTimer();\n\t\tif (this._outstandingTaskIds.size > 0) {\n\t\t\tthis.armStallTimer();\n\t\t}\n\n\t\tconst pendingWrite = this._pendingWrites.get(task.id);\n\t\tif (!Is.empty(pendingWrite)) {\n\t\t\tthis._pendingWrites.delete(task.id);\n\t\t\tglobalThis.clearTimeout(pendingWrite.timer);\n\t\t\t// Only a successful task means the thread wrote what it was holding, so a failure\n\t\t\t// leaves the counts apart and the next read asks again.\n\t\t\tpendingWrite.resolve(task.status === TaskStatus.Success);\n\t\t}\n\n\t\tif (task.status === TaskStatus.Failed) {\n\t\t\tawait this._logging?.log({\n\t\t\t\tsource: EntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\tmessage: \"metricValueTaskFailed\",\n\t\t\t\tlevel: \"error\",\n\t\t\t\tdata: { taskId: task.id },\n\t\t\t\terror: Is.object(task.error) ? BaseError.fromError(task.error) : undefined\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Build the stored entity for a metric definition.\n\t * @param metric The metric details.\n\t * @returns The entity to store.\n\t * @internal\n\t */\n\tprivate toTelemetryMetric(metric: ITelemetryMetric): TelemetryMetric {\n\t\treturn {\n\t\t\tid: metric.id,\n\t\t\tlabel: metric.label,\n\t\t\ttype: metric.type,\n\t\t\tunit: metric.unit ?? \"\",\n\t\t\tdescription: metric.description ?? \"\",\n\t\t\tmaxHistory: metric.maxHistory\n\t\t};\n\t}\n\n\t/**\n\t * Check the metric details are complete and consistent before it is stored.\n\t * @param metric The metric details.\n\t * @throws GeneralError if the maxHistory is not a positive integer.\n\t * @internal\n\t */\n\tprivate validateMetric(metric: ITelemetryMetric): void {\n\t\tGuards.object<ITelemetryMetric>(\n\t\t\tEntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\"metric\",\n\t\t\tmetric\n\t\t);\n\t\tGuards.stringValue(EntityStorageTelemetryConnector.CLASS_NAME, \"metric.id\", metric.id);\n\t\tGuards.stringValue(\n\t\t\tEntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\"metric.label\",\n\t\t\tmetric.label\n\t\t);\n\t\tGuards.arrayOneOf(\n\t\t\tEntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\"metric.type\",\n\t\t\tmetric.type,\n\t\t\tObject.values(MetricType)\n\t\t);\n\n\t\tif (Is.notEmpty(metric.description)) {\n\t\t\tGuards.string(\n\t\t\t\tEntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\t\"metric.description\",\n\t\t\t\tmetric.description\n\t\t\t);\n\t\t}\n\t\tif (Is.notEmpty(metric.unit)) {\n\t\t\tGuards.string(EntityStorageTelemetryConnector.CLASS_NAME, \"metric.unit\", metric.unit);\n\t\t}\n\t\tif (Is.notEmpty(metric.maxHistory)) {\n\t\t\tif (!Is.integer(metric.maxHistory) || metric.maxHistory <= 0) {\n\t\t\t\tthrow new GeneralError(\n\t\t\t\t\tEntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\t\t\"maxHistoryMustBePositiveInteger\"\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Check the operation is valid for the metric type before it is handed to the background\n\t * thread, so the caller still sees an invalid operation as an error.\n\t * @param metricType The type of the metric.\n\t * @param value The value for the add operation.\n\t * @throws GeneralError if the operation cannot be applied to the metric type.\n\t * @internal\n\t */\n\tprivate validateOperation(metricType: MetricType, value: MetricCounterOperation | number): void {\n\t\tif (metricType === MetricType.Counter) {\n\t\t\tif (value !== MetricCounterOperation.Increment && !(Is.integer(value) && value > 0)) {\n\t\t\t\tthrow new GeneralError(EntityStorageTelemetryConnector.CLASS_NAME, \"counterIncOnly\");\n\t\t\t}\n\t\t} else if (metricType === MetricType.IncDecCounter) {\n\t\t\tif (\n\t\t\t\tvalue !== MetricCounterOperation.Increment &&\n\t\t\t\tvalue !== MetricCounterOperation.Decrement &&\n\t\t\t\t!Is.integer(value)\n\t\t\t) {\n\t\t\t\tthrow new GeneralError(\n\t\t\t\t\tEntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\t\t\"upDownCounterIncOrDecOnly\"\n\t\t\t\t);\n\t\t\t}\n\t\t} else if (!Is.number(value)) {\n\t\t\tthrow new GeneralError(EntityStorageTelemetryConnector.CLASS_NAME, \"gaugeNoIncDec\");\n\t\t}\n\t}\n\n\t/**\n\t * Build the partition key used for the metric definition cache.\n\t * Only the node and tenant ids partition the storage, so the key is built from those two\n\t * directly rather than canonicalising the whole context object on every metric write.\n\t * @param contextIds The context ids to derive the key from.\n\t * @param metricId The metric id to include in the partition key.\n\t * @returns The partition key.\n\t * @internal\n\t */\n\tprivate contextKey(contextIds: IContextIds, metricId?: string): string {\n\t\tconst key = `${contextIds[ContextIdKeys.Node] ?? \"\"}|${contextIds[ContextIdKeys.Tenant] ?? \"\"}`;\n\t\tif (metricId) {\n\t\t\treturn `${key}|${metricId}`;\n\t\t}\n\t\treturn key;\n\t}\n\n\t/**\n\t * Capture the partition keys of the ambient context ids. Other keys (organization, user,\n\t * per-request ids) vary within a partition and would split the definition cache and the\n\t * background thread batch groups.\n\t * @returns The partition context ids from the ambient context.\n\t * @internal\n\t */\n\tprivate async captureContextIds(): Promise<IContextIds> {\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\treturn {\n\t\t\t[ContextIdKeys.Node]: contextIds?.[ContextIdKeys.Node] ?? \"\",\n\t\t\t[ContextIdKeys.Tenant]: contextIds?.[ContextIdKeys.Tenant] ?? \"\"\n\t\t};\n\t}\n}\n"]}
1
+ {"version":3,"file":"entityStorageTelemetryConnector.js","sources":["../../src/entityStorageTelemetryConnector.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// Copyright 2026 3Sixty Trade Ltd.\n// SPDX-License-Identifier: Apache-2.0.\nimport {\n\ttype IBackgroundTask,\n\ttype IBackgroundTaskComponent,\n\tTaskStatus\n} from \"@3sixty/background-task-models\";\nimport { ContextIdKeys, ContextIdStore, type IContextIds } from \"@3sixty/context\";\nimport {\n\tAlreadyExistsError,\n\tBaseError,\n\tCoerce,\n\tComponentFactory,\n\tConverter,\n\tDurationConstants,\n\tGeneralError,\n\tGuards,\n\tIs,\n\tLfuCache,\n\tNotFoundError,\n\tRandomHelper\n} from \"@3sixty/core\";\nimport {\n\tComparisonOperator,\n\ttype EntityCondition,\n\tLogicalOperator,\n\tSortDirection\n} from \"@3sixty/entity\";\nimport {\n\tEntityStorageConnectorFactory,\n\ttype IEntityStorageConnector\n} from \"@3sixty/entity-storage-models\";\nimport type { ILogEntry, ILoggingComponent } from \"@3sixty/logging-models\";\n\nimport {\n\ttype ITelemetryConnector,\n\ttype ITelemetryMetric,\n\ttype ITelemetryMetricValue,\n\ttype ITelemetryMetricValueEntry,\n\tMetricCounterOperation,\n\tMetricType\n} from \"@3sixty/telemetry-models\";\nimport type { TelemetryMetric } from \"./entities/telemetryMetric.js\";\nimport type { TelemetryMetricValue } from \"./entities/telemetryMetricValue.js\";\nimport type { IEntityStorageTelemetryConnectorConstructorOptions } from \"./models/IEntityStorageTelemetryConnectorConstructorOptions.js\";\nimport type { ITelemetryMetricValuePayload } from \"./models/ITelemetryMetricValuePayload.js\";\nimport type { ITelemetryMetricValueTaskPayload } from \"./models/ITelemetryMetricValueTaskPayload.js\";\nimport type { ITelemetryMetricValueWriterConfig } from \"./models/ITelemetryMetricValueWriterConfig.js\";\n\n/**\n * Class for performing telemetry operations in entity storage.\n * The metric values are written by a long running background thread, so nothing about a value\n * is held in memory here; only the metric definitions, which are immutable once registered,\n * are cached.\n */\nexport class EntityStorageTelemetryConnector implements ITelemetryConnector {\n\t/**\n\t * The namespace supported by the telemetry connector.\n\t */\n\tpublic static readonly NAMESPACE: string = \"entity-storage\";\n\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = \"EntityStorageTelemetryConnector\";\n\n\t/**\n\t * Default time in milliseconds to wait for the background thread to confirm a flush.\n\t */\n\tpublic static readonly DEFAULT_FLUSH_TIMEOUT_MS: number = 30 * DurationConstants.MsPerSecond;\n\n\t/**\n\t * Maximum number of metric definitions to hold in the in-memory definition cache.\n\t */\n\tpublic static readonly DEFAULT_METRIC_DEFINITION_CACHE_CAPACITY: number = 1000;\n\n\t/**\n\t * Time-to-idle in milliseconds for cached metric definitions.\n\t * Metric definitions are immutable once registered; a long TTI keeps active\n\t * metrics cached without permanent references.\n\t */\n\tpublic static readonly DEFAULT_METRIC_DEFINITION_CACHE_TTI_MS: number =\n\t\tDurationConstants.MsPerHour;\n\n\t/**\n\t * Default time in milliseconds values are held so several share a single background task.\n\t * Keeps the task write off the caller's path, which matters most for the metrics recorded\n\t * on every REST request.\n\t * The window is also what limits how many tasks the connector can produce, as one task\n\t * carries a whole window regardless of how many values it holds. The scheduler moves a\n\t * single task per cycle for a task type, which is of the order of a few per second once\n\t * the task queue is on a database, so the window is set an order of magnitude below that:\n\t * one task per second whatever the metric rate.\n\t */\n\tpublic static readonly DEFAULT_TASK_COALESCE_MS: number = DurationConstants.MsPerSecond;\n\n\t/**\n\t * Maximum number of values held while coalescing before a task is created regardless of\n\t * how much of the window is left.\n\t * This is a bound on the values held for the window rather than a throughput control; a\n\t * limit low enough to be reached by ordinary traffic would raise the task rate above what\n\t * the scheduler can drain.\n\t */\n\tpublic static readonly DEFAULT_COALESCE_SIZE: number = 10_000;\n\n\t/**\n\t * Default time in milliseconds with tasks outstanding and none of them completing before\n\t * the background thread is treated as stalled.\n\t */\n\tpublic static readonly DEFAULT_TASK_STALL_TIMEOUT_MS: number = DurationConstants.MsPerMinute;\n\n\t/**\n\t * Task type identifier for the metric value background task.\n\t * @internal\n\t */\n\tprivate static readonly _METRIC_VALUE_TASK_TYPE: string = \"telemetry-metric-value-write\";\n\n\t/**\n\t * Cap on how many times the stall wait can double.\n\t * @internal\n\t */\n\tprivate static readonly _STALL_BACKOFF_MAX_MULTIPLIER: number = 8;\n\n\t/**\n\t * The entity storage for the telemetry metrics.\n\t * @internal\n\t */\n\tprivate readonly _metricStorage: IEntityStorageConnector<TelemetryMetric>;\n\n\t/**\n\t * The entity storage for the telemetry metric values, used for the read operations.\n\t * @internal\n\t */\n\tprivate readonly _metricValueStorage: IEntityStorageConnector<TelemetryMetricValue>;\n\n\t/**\n\t * The component for logging.\n\t * @internal\n\t */\n\tprivate readonly _logging?: ILoggingComponent;\n\n\t/**\n\t * The background task component used to run the metric value writes on a background thread.\n\t * Absent when the connector is created inside an engine clone, which builds it only to\n\t * register the metric storage and never writes values through it.\n\t * @internal\n\t */\n\tprivate readonly _backgroundTaskComponent?: IBackgroundTaskComponent;\n\n\t/**\n\t * The URL of the module the background thread runs the metric value writes from.\n\t * @internal\n\t */\n\tprivate readonly _metricValueTaskHandler: string;\n\n\t/**\n\t * The writer settings passed to the background thread with each task payload.\n\t * @internal\n\t */\n\tprivate readonly _writerConfig: ITelemetryMetricValueWriterConfig;\n\n\t/**\n\t * How long in milliseconds to wait for the background thread to confirm a flush.\n\t * @internal\n\t */\n\tprivate readonly _flushTimeoutMs: number;\n\n\t/**\n\t * The flush task the background thread has been given which has not yet reached a final\n\t * state, with the value count it will have written once it completes. Present so a read\n\t * waits for a flush already in the queue rather than adding another to it.\n\t * @internal\n\t */\n\tprivate _pendingFlush?: { taskId: string; coveredCount: number };\n\n\t/**\n\t * Write requests waiting for their background task to complete, keyed by task id.\n\t * @internal\n\t */\n\tprivate readonly _pendingWrites: Map<\n\t\tstring,\n\t\t{ resolve: (isWritten: boolean) => void; timer: ReturnType<typeof setTimeout> }\n\t>;\n\n\t/**\n\t * How long in milliseconds to hold values before handing them to the background thread as a\n\t * single task; 0 creates a task per value.\n\t * @internal\n\t */\n\tprivate readonly _taskCoalesceMs: number;\n\n\t/**\n\t * Values held for the coalesce window, waiting to be handed to the background thread.\n\t * @internal\n\t */\n\tprivate readonly _coalesced: ITelemetryMetricValuePayload[];\n\n\t/**\n\t * Handle for the coalesce window timer.\n\t * @internal\n\t */\n\tprivate _coalesceTimer?: ReturnType<typeof setTimeout>;\n\n\t/**\n\t * How long in milliseconds with tasks outstanding and none of them completing before the\n\t * background thread is treated as stalled; 0 disables the check.\n\t * @internal\n\t */\n\tprivate readonly _taskStallTimeoutMs: number;\n\n\t/**\n\t * Ids of the tasks handed to the background thread which have not yet reported a final\n\t * state, oldest first. Ids only; the values they carry belong to the thread.\n\t * @internal\n\t */\n\tprivate readonly _outstandingTaskIds: Set<string>;\n\n\t/**\n\t * The time a task last reached a final state, the outstanding tasks were created, or a task\n\t * was last reported still waiting for a worker. A thread which has stopped answering is only\n\t * visible as the absence of this moving on.\n\t * @internal\n\t */\n\tprivate _lastTaskProgress: number;\n\n\t/**\n\t * Handle for the stall check timer, present only while tasks are outstanding.\n\t * @internal\n\t */\n\tprivate _stallTimer?: ReturnType<typeof setTimeout>;\n\n\t/**\n\t * True while a stall check, or the replacement it starts, is running.\n\t * @internal\n\t */\n\tprivate _stallCheckRunning: boolean;\n\n\t/**\n\t * Number of consecutive replacements with no completion between them; reset once a task\n\t * completes. Used to back off the stall wait.\n\t * @internal\n\t */\n\tprivate _stallRestartCount: number;\n\n\t/**\n\t * Number of values handed to the background thread, counted once their task is queued.\n\t * @internal\n\t */\n\tprivate _valuesQueued: number;\n\n\t/**\n\t * The value count as of the last write the background thread confirmed.\n\t * @internal\n\t */\n\tprivate _valuesWritten: number;\n\n\t/**\n\t * Serialises the write requests. A caller cannot return on a write already in progress, as\n\t * its own values may have been queued behind that write's task.\n\t * @internal\n\t */\n\tprivate _writeChain: Promise<void>;\n\n\t/**\n\t * Is the service running.\n\t * @internal\n\t */\n\tprivate _started: boolean;\n\n\t/**\n\t * Capacity used when constructing the metric definition cache.\n\t * @internal\n\t */\n\tprivate readonly _metricDefinitionCacheCapacity: number;\n\n\t/**\n\t * Time-to-idle in milliseconds used when constructing the metric definition cache.\n\t * @internal\n\t */\n\tprivate readonly _metricDefinitionCacheTtiMs: number;\n\n\t/**\n\t * In-memory cache of metric definitions, keyed by partition context + metric id.\n\t * Avoids a storage read on every addMetricValue call.\n\t * @internal\n\t */\n\tprivate _metricDefinitionCache: LfuCache<TelemetryMetric>;\n\n\t/**\n\t * Create a new instance of EntityStorageTelemetryConnector.\n\t * @param options The options for the connector.\n\t */\n\tconstructor(options?: IEntityStorageTelemetryConnectorConstructorOptions) {\n\t\tthis._metricStorage = EntityStorageConnectorFactory.get(\n\t\t\toptions?.telemetryMetricStorageConnectorType ?? \"telemetry-metric\"\n\t\t);\n\t\tthis._metricValueStorage = EntityStorageConnectorFactory.get(\n\t\t\toptions?.telemetryMetricValueStorageConnectorType ?? \"telemetry-metric-value\"\n\t\t);\n\n\t\tthis._logging = ComponentFactory.getIfExists(options?.loggingComponentType);\n\n\t\t// The background task component is used to run metric value writes on a background thread.\n\t\t// but if this component is instantiated from the background thread it is not needed.\n\t\tthis._backgroundTaskComponent = ComponentFactory.getIfExists<IBackgroundTaskComponent>(\n\t\t\toptions?.backgroundTaskComponentType ?? \"background-task\"\n\t\t);\n\n\t\tthis._metricValueTaskHandler =\n\t\t\toptions?.config?.overrideMetricValueTaskHandler ??\n\t\t\tnew URL(\"./telemetryMetricValueTask.js\", import.meta.url).href;\n\n\t\tthis._writerConfig = {\n\t\t\ttelemetryMetricValueStorageConnectorType: options?.telemetryMetricValueStorageConnectorType,\n\t\t\tloggingComponentType: options?.loggingComponentType,\n\t\t\tbatchSize: options?.config?.batchSize,\n\t\t\tbatchIntervalMs: options?.config?.batchIntervalMs,\n\t\t\tmaxCacheSize: options?.config?.maxCacheSize,\n\t\t\ttrimIntervalMs: options?.config?.trimIntervalMs,\n\t\t\ttrimRemoveLimit: options?.config?.trimRemoveLimit\n\t\t};\n\n\t\tconst cfgFlushTimeoutMs =\n\t\t\tCoerce.integer(options?.config?.flushTimeoutMs) ??\n\t\t\tEntityStorageTelemetryConnector.DEFAULT_FLUSH_TIMEOUT_MS;\n\t\tthis._flushTimeoutMs =\n\t\t\tcfgFlushTimeoutMs > 0\n\t\t\t\t? cfgFlushTimeoutMs\n\t\t\t\t: EntityStorageTelemetryConnector.DEFAULT_FLUSH_TIMEOUT_MS;\n\n\t\tconst cfgCoalesceMs =\n\t\t\tCoerce.integer(options?.config?.taskCoalesceMs) ??\n\t\t\tEntityStorageTelemetryConnector.DEFAULT_TASK_COALESCE_MS;\n\t\tthis._taskCoalesceMs = cfgCoalesceMs > 0 ? cfgCoalesceMs : 0;\n\n\t\tconst cfgTaskStallTimeoutMs =\n\t\t\tCoerce.integer(options?.config?.taskStallTimeoutMs) ??\n\t\t\tEntityStorageTelemetryConnector.DEFAULT_TASK_STALL_TIMEOUT_MS;\n\t\tthis._taskStallTimeoutMs = cfgTaskStallTimeoutMs > 0 ? cfgTaskStallTimeoutMs : 0;\n\n\t\tthis._pendingWrites = new Map();\n\t\tthis._coalesced = [];\n\t\tthis._outstandingTaskIds = new Set();\n\t\tthis._lastTaskProgress = 0;\n\t\tthis._stallCheckRunning = false;\n\t\tthis._stallRestartCount = 0;\n\t\tthis._valuesQueued = 0;\n\t\tthis._valuesWritten = 0;\n\t\tthis._writeChain = Promise.resolve();\n\t\tthis._started = false;\n\t\tthis._metricDefinitionCacheCapacity =\n\t\t\tCoerce.integer(options?.config?.metricDefinitionCacheCapacity) ??\n\t\t\tEntityStorageTelemetryConnector.DEFAULT_METRIC_DEFINITION_CACHE_CAPACITY;\n\t\tthis._metricDefinitionCacheTtiMs =\n\t\t\tCoerce.integer(options?.config?.metricDefinitionCacheTtiMs) ??\n\t\t\tEntityStorageTelemetryConnector.DEFAULT_METRIC_DEFINITION_CACHE_TTI_MS;\n\t\tthis._metricDefinitionCache = new LfuCache({\n\t\t\tcapacity: this._metricDefinitionCacheCapacity,\n\t\t\tttiMs: this._metricDefinitionCacheTtiMs\n\t\t});\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn EntityStorageTelemetryConnector.CLASS_NAME;\n\t}\n\n\t/**\n\t * Start the connector; registers the long running background thread which writes the\n\t * metric values.\n\t * @returns A promise that resolves when the connector is ready to accept metric values.\n\t */\n\tpublic async start(): Promise<void> {\n\t\tif (!this._started) {\n\t\t\tthis._started = true;\n\t\t\tthis._metricDefinitionCache = new LfuCache({\n\t\t\t\tcapacity: this._metricDefinitionCacheCapacity,\n\t\t\t\tttiMs: this._metricDefinitionCacheTtiMs\n\t\t\t});\n\n\t\t\tawait this.registerMetricValueHandler();\n\t\t}\n\t}\n\n\t/**\n\t * Stop the connector; writes any values still pending and releases the background thread.\n\t * @returns A promise that resolves when the final write completes and the thread is released.\n\t */\n\tpublic async stop(): Promise<void> {\n\t\tthis.stopCoalesceTimer();\n\n\t\t// Flush before clearing the started flag, as the background thread is only asked to\n\t\t// write while the connector is running. A failed final write must not abandon the rest\n\t\t// of the teardown, which is what releases the thread and the callers waiting on it.\n\t\ttry {\n\t\t\tawait this.flush();\n\t\t} catch (err) {\n\t\t\tawait this.logWithoutThrowing({\n\t\t\t\tsource: EntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\tmessage: \"stopFlushFailed\",\n\t\t\t\tlevel: \"error\",\n\t\t\t\terror: BaseError.fromError(err)\n\t\t\t});\n\t\t}\n\n\t\tthis._started = false;\n\t\tthis.stopStallTimer();\n\t\tthis._outstandingTaskIds.clear();\n\t\tthis._pendingFlush = undefined;\n\n\t\t// unregisterHandler terminates the worker without calling its shutdown method, so the\n\t\t// flush above is what persists anything the thread still had pending.\n\t\tawait this._backgroundTaskComponent?.unregisterHandler(\n\t\t\tEntityStorageTelemetryConnector._METRIC_VALUE_TASK_TYPE\n\t\t);\n\n\t\tfor (const pendingWrite of this._pendingWrites.values()) {\n\t\t\tglobalThis.clearTimeout(pendingWrite.timer);\n\t\t\tpendingWrite.resolve(false);\n\t\t}\n\t\tthis._pendingWrites.clear();\n\n\t\tthis._metricDefinitionCache.destroy();\n\t}\n\n\t/**\n\t * Create one or more metrics.\n\t * A single metric fails if it already exists; an array declares the set that should exist,\n\t * creating the ones that are missing and leaving the rest untouched. The array form resolves\n\t * what already exists with one query and writes the rest in one batch.\n\t * @param metric The metric details, or the details of several metrics.\n\t * @returns A promise that resolves when the metrics have been created.\n\t * @throws AlreadyExistsError if a single metric already exists.\n\t */\n\tpublic async createMetric(metric: ITelemetryMetric | ITelemetryMetric[]): Promise<void> {\n\t\tconst isBatch = Is.array<ITelemetryMetric>(metric);\n\t\tconst metrics = isBatch ? metric : [metric];\n\n\t\tfor (const entry of metrics) {\n\t\t\tthis.validateMetric(entry);\n\t\t}\n\n\t\tif (metrics.length === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst telemetryMetrics: TelemetryMetric[] = [];\n\n\t\tif (isBatch) {\n\t\t\tconst existing = await this._metricStorage.query(\n\t\t\t\t{\n\t\t\t\t\tproperty: \"id\",\n\t\t\t\t\tcomparison: ComparisonOperator.In,\n\t\t\t\t\tvalue: metrics.map(entry => entry.id)\n\t\t\t\t},\n\t\t\t\tundefined,\n\t\t\t\t[\"id\"],\n\t\t\t\tundefined,\n\t\t\t\tmetrics.length\n\t\t\t);\n\t\t\tconst existingIds = new Set(existing.entities.map(entity => entity.id as string));\n\n\t\t\tfor (const entry of metrics) {\n\t\t\t\tif (!existingIds.has(entry.id)) {\n\t\t\t\t\ttelemetryMetrics.push(this.toTelemetryMetric(entry));\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tconst existingMetric = await this._metricStorage.get(metrics[0].id);\n\t\t\tif (Is.notEmpty(existingMetric)) {\n\t\t\t\tthrow new AlreadyExistsError(\n\t\t\t\t\tEntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\t\t\"metricAlreadyExists\",\n\t\t\t\t\tmetrics[0].id\n\t\t\t\t);\n\t\t\t}\n\t\t\ttelemetryMetrics.push(this.toTelemetryMetric(metrics[0]));\n\t\t}\n\n\t\tif (telemetryMetrics.length === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (telemetryMetrics.length === 1) {\n\t\t\tawait this._metricStorage.set(telemetryMetrics[0]);\n\t\t} else {\n\t\t\tawait this._metricStorage.setBatch(telemetryMetrics);\n\t\t}\n\n\t\tconst contextIds = await this.captureContextIds();\n\t\tfor (const telemetryMetric of telemetryMetrics) {\n\t\t\tthis._metricDefinitionCache.set(\n\t\t\t\tthis.contextKey(contextIds, telemetryMetric.id),\n\t\t\t\ttelemetryMetric\n\t\t\t);\n\t\t}\n\n\t\tif (isBatch) {\n\t\t\tawait this._logging?.log({\n\t\t\t\tsource: EntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\tmessage: \"metricsCreated\",\n\t\t\t\tlevel: \"info\",\n\t\t\t\tdata: { count: telemetryMetrics.length }\n\t\t\t});\n\t\t} else {\n\t\t\tawait this._logging?.log({\n\t\t\t\tsource: EntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\tmessage: \"metricCreated\",\n\t\t\t\tlevel: \"info\",\n\t\t\t\tdata: {\n\t\t\t\t\tid: telemetryMetrics[0].id,\n\t\t\t\t\ttype: telemetryMetrics[0].type,\n\t\t\t\t\tlabel: telemetryMetrics[0].label\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Get the metric details and it's most recent value.\n\t * @param id The metric id.\n\t * @returns The metric details and it's most recent value.\n\t */\n\tpublic async getMetric(id: string): Promise<{\n\t\tmetric: ITelemetryMetric;\n\t\tvalue?: ITelemetryMetricValue;\n\t}> {\n\t\tconst metrics = await this.queryValues(id, undefined, undefined, undefined, 1);\n\t\treturn {\n\t\t\tmetric: metrics.metric,\n\t\t\tvalue: metrics.entities[0]\n\t\t};\n\t}\n\n\t/**\n\t * Update metric.\n\t * @param metric The metric details.\n\t * @returns A promise that resolves when the metric has been updated.\n\t */\n\tpublic async updateMetric(metric: Omit<ITelemetryMetric, \"type\">): Promise<void> {\n\t\tGuards.object<ITelemetryMetric>(\n\t\t\tEntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\"metric\",\n\t\t\tmetric\n\t\t);\n\t\tGuards.stringValue(EntityStorageTelemetryConnector.CLASS_NAME, \"metric.id\", metric.id);\n\t\tGuards.stringValue(\n\t\t\tEntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\"metric.label\",\n\t\t\tmetric.label\n\t\t);\n\t\tif (Is.notEmpty(metric.description)) {\n\t\t\tGuards.string(\n\t\t\t\tEntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\t\"metric.description\",\n\t\t\t\tmetric.description\n\t\t\t);\n\t\t}\n\t\tif (Is.notEmpty(metric.unit)) {\n\t\t\tGuards.string(EntityStorageTelemetryConnector.CLASS_NAME, \"metric.unit\", metric.unit);\n\t\t}\n\t\tif (Is.notEmpty(metric.maxHistory)) {\n\t\t\tif (!Is.integer(metric.maxHistory) || metric.maxHistory <= 0) {\n\t\t\t\tthrow new GeneralError(\n\t\t\t\t\tEntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\t\t\"maxHistoryMustBePositiveInteger\"\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tconst existingMetric = await this._metricStorage.get(metric.id);\n\t\tif (Is.undefined(existingMetric)) {\n\t\t\tthrow new NotFoundError(\n\t\t\t\tEntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\t\"metricNotFound\",\n\t\t\t\tmetric.id\n\t\t\t);\n\t\t}\n\n\t\tconst telemetryMetric: TelemetryMetric = {\n\t\t\tid: metric.id,\n\t\t\tlabel: metric.label,\n\t\t\ttype: existingMetric.type,\n\t\t\tunit: metric.unit ?? existingMetric.unit,\n\t\t\tdescription: metric.description ?? existingMetric.description,\n\t\t\tmaxHistory: metric.maxHistory ?? existingMetric.maxHistory\n\t\t};\n\n\t\tawait this._metricStorage.set(telemetryMetric);\n\t\tconst metricKey = this.contextKey(await this.captureContextIds(), metric.id);\n\t\tthis._metricDefinitionCache.set(metricKey, telemetryMetric);\n\n\t\tawait this._logging?.log({\n\t\t\tsource: EntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\tmessage: \"metricUpdated\",\n\t\t\tlevel: \"info\",\n\t\t\tdata: { id: metric.id, type: metric.type, label: metric.label }\n\t\t});\n\t}\n\n\t/**\n\t * Add a metric value.\n\t * The value is handed to the background thread which computes it from the value currently\n\t * in storage and performs the write, so it is not visible until the thread has flushed.\n\t * @param id The id of the metric.\n\t * @param value The value for the add operation.\n\t * @param customData The custom data for the metric value.\n\t * @returns The id of the newly created metric value entry.\n\t */\n\tpublic async addMetricValue(\n\t\tid: string,\n\t\tvalue: MetricCounterOperation | number,\n\t\tcustomData?: { [key: string]: unknown }\n\t): Promise<string> {\n\t\tGuards.stringValue(EntityStorageTelemetryConnector.CLASS_NAME, \"id\", id);\n\n\t\tconst metricValue = await this.buildMetricValue(id, value, customData);\n\n\t\tawait this.queueMetricValues([metricValue]);\n\n\t\tawait this._logging?.log({\n\t\t\tsource: EntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\tmessage: \"metricValueCreated\",\n\t\t\tlevel: \"info\",\n\t\t\tdata: { id }\n\t\t});\n\n\t\treturn metricValue.valueId;\n\t}\n\n\t/**\n\t * Add multiple metric values, handing the whole set to the background thread as a single\n\t * task rather than one per value.\n\t * @param values The metric values to add.\n\t * @returns The ids of the newly created metric value entries, in the order supplied.\n\t */\n\tpublic async addMetricValues(values: ITelemetryMetricValueEntry[]): Promise<string[]> {\n\t\tGuards.array<ITelemetryMetricValueEntry>(\n\t\t\tEntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\"values\",\n\t\t\tvalues\n\t\t);\n\n\t\tconst metricValues: ITelemetryMetricValuePayload[] = [];\n\n\t\tfor (const entry of values) {\n\t\t\tGuards.stringValue(EntityStorageTelemetryConnector.CLASS_NAME, \"entry.id\", entry.id);\n\t\t\tmetricValues.push(await this.buildMetricValue(entry.id, entry.value, entry.customData));\n\t\t}\n\n\t\tawait this.queueMetricValues(metricValues);\n\n\t\tawait this._logging?.log({\n\t\t\tsource: EntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\tmessage: \"metricValuesCreated\",\n\t\t\tlevel: \"info\",\n\t\t\tdata: { count: metricValues.length }\n\t\t});\n\n\t\treturn metricValues.map(metricValue => metricValue.valueId);\n\t}\n\n\t/**\n\t * Get a specific metric value by its id.\n\t * @param id The id of the metric.\n\t * @param valueId The id of the metric value.\n\t * @returns The metric value.\n\t */\n\tpublic async getMetricValue(id: string, valueId: string): Promise<ITelemetryMetricValue> {\n\t\tGuards.stringValue(EntityStorageTelemetryConnector.CLASS_NAME, \"id\", id);\n\t\tGuards.stringValue(EntityStorageTelemetryConnector.CLASS_NAME, \"valueId\", valueId);\n\n\t\tawait this.flush();\n\n\t\tconst results = await this._metricValueStorage.query(\n\t\t\t{\n\t\t\t\tconditions: [\n\t\t\t\t\t{ property: \"metricId\", comparison: ComparisonOperator.Equals, value: id },\n\t\t\t\t\t{ property: \"id\", comparison: ComparisonOperator.Equals, value: valueId }\n\t\t\t\t],\n\t\t\t\tlogicalOperator: LogicalOperator.And\n\t\t\t},\n\t\t\tundefined,\n\t\t\tundefined,\n\t\t\tundefined,\n\t\t\t1\n\t\t);\n\n\t\tif (results.entities.length === 0) {\n\t\t\tthrow new NotFoundError(\n\t\t\t\tEntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\t\"metricValueNotFound\",\n\t\t\t\tvalueId\n\t\t\t);\n\t\t}\n\n\t\tconst metricValue = results.entities[0] as TelemetryMetricValue;\n\t\treturn {\n\t\t\tid: metricValue.id,\n\t\t\tts: metricValue.ts,\n\t\t\tvalue: metricValue.value,\n\t\t\tcustomData: metricValue.customData\n\t\t};\n\t}\n\n\t/**\n\t * Remove metric.\n\t * @param id The id of the metric.\n\t * @returns A promise that resolves when the metric and all its values have been removed.\n\t */\n\tpublic async removeMetric(id: string): Promise<void> {\n\t\tGuards.stringValue(EntityStorageTelemetryConnector.CLASS_NAME, \"id\", id);\n\n\t\tconst existingMetric = await this._metricStorage.get(id);\n\t\tif (Is.undefined(existingMetric)) {\n\t\t\tthrow new NotFoundError(EntityStorageTelemetryConnector.CLASS_NAME, \"metricNotFound\", id);\n\t\t}\n\n\t\tawait this.flush();\n\n\t\tawait this._metricStorage.remove(id);\n\t\tconst metricKey = this.contextKey(await this.captureContextIds(), id);\n\t\tthis._metricDefinitionCache.delete(metricKey);\n\n\t\tlet removeValuesCursor: string | undefined;\n\t\tconst valueIdsToRemove: string[] = [];\n\t\tdo {\n\t\t\tconst existingMetricValuesResult = await this._metricValueStorage.query(\n\t\t\t\t{\n\t\t\t\t\tproperty: \"metricId\",\n\t\t\t\t\tcomparison: ComparisonOperator.Equals,\n\t\t\t\t\tvalue: id\n\t\t\t\t},\n\t\t\t\tundefined,\n\t\t\t\t[\"id\"],\n\t\t\t\tremoveValuesCursor\n\t\t\t);\n\t\t\tvalueIdsToRemove.push(...existingMetricValuesResult.entities.map(e => e.id as string));\n\t\t\tremoveValuesCursor = existingMetricValuesResult.cursor;\n\t\t} while (Is.stringValue(removeValuesCursor));\n\t\tif (valueIdsToRemove.length > 0) {\n\t\t\tawait this._metricValueStorage.removeBatch(valueIdsToRemove);\n\t\t}\n\n\t\tawait this._logging?.log({\n\t\t\tsource: EntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\tmessage: \"metricRemoved\",\n\t\t\tlevel: \"info\",\n\t\t\tdata: { id }\n\t\t});\n\t}\n\n\t/**\n\t * Query the metrics.\n\t * @param type The type of the metric.\n\t * @param cursor The cursor to request the next chunk of entities.\n\t * @param limit Limit the number of entities to return.\n\t * @returns All the entities for the storage matching the conditions,\n\t * and a cursor which can be used to request more entities.\n\t * @throws NotImplementedError if the implementation does not support retrieval.\n\t */\n\tpublic async query(\n\t\ttype?: MetricType,\n\t\tcursor?: string,\n\t\tlimit?: number\n\t): Promise<{\n\t\t/**\n\t\t * The metrics.\n\t\t */\n\t\tentities: ITelemetryMetric[];\n\n\t\t/**\n\t\t * An optional cursor, when defined can be used to call find to get more values.\n\t\t */\n\t\tcursor?: string;\n\t}> {\n\t\tconst condition: EntityCondition<TelemetryMetric> = {\n\t\t\tconditions: [],\n\t\t\tlogicalOperator: LogicalOperator.And\n\t\t};\n\n\t\tif (Is.arrayOneOf(type, Object.values(MetricType))) {\n\t\t\tcondition.conditions.push({\n\t\t\t\tproperty: \"type\",\n\t\t\t\tcomparison: ComparisonOperator.Equals,\n\t\t\t\tvalue: type\n\t\t\t});\n\t\t}\n\n\t\tconst result = await this._metricStorage.query(\n\t\t\tcondition.conditions.length > 0 ? condition : undefined,\n\t\t\t[\n\t\t\t\t{\n\t\t\t\t\tproperty: \"label\",\n\t\t\t\t\tsortDirection: SortDirection.Ascending\n\t\t\t\t}\n\t\t\t],\n\t\t\tundefined,\n\t\t\tcursor,\n\t\t\tlimit\n\t\t);\n\n\t\treturn {\n\t\t\tentities: result.entities as ITelemetryMetric[],\n\t\t\tcursor: result.cursor\n\t\t};\n\t}\n\n\t/**\n\t * Query the metric values.\n\t * @param id The id of the metric.\n\t * @param timeStart The inclusive time as the start of the metric entries.\n\t * @param timeEnd The inclusive time as the end of the metric entries.\n\t * @param cursor The cursor to request the next chunk of entities.\n\t * @param limit Limit the number of entities to return.\n\t * @returns All the entities for the storage matching the conditions,\n\t * and a cursor which can be used to request more entities.\n\t */\n\tpublic async queryValues(\n\t\tid: string,\n\t\ttimeStart?: number,\n\t\ttimeEnd?: number,\n\t\tcursor?: string,\n\t\tlimit?: number\n\t): Promise<{\n\t\t/**\n\t\t * The metric details.\n\t\t */\n\t\tmetric: ITelemetryMetric;\n\n\t\t/**\n\t\t * The values for the metric.\n\t\t */\n\t\tentities: ITelemetryMetricValue[];\n\n\t\t/**\n\t\t * An optional cursor, when defined can be used to call find to get more values.\n\t\t */\n\t\tcursor?: string;\n\t}> {\n\t\tGuards.stringValue(EntityStorageTelemetryConnector.CLASS_NAME, \"id\", id);\n\n\t\tawait this.flush();\n\n\t\tconst existingMetric = await this._metricStorage.get(id);\n\t\tif (Is.undefined(existingMetric)) {\n\t\t\tthrow new NotFoundError(EntityStorageTelemetryConnector.CLASS_NAME, \"metricNotFound\", id);\n\t\t}\n\n\t\tconst condition: EntityCondition<TelemetryMetricValue> = {\n\t\t\tconditions: [],\n\t\t\tlogicalOperator: LogicalOperator.And\n\t\t};\n\n\t\tcondition.conditions.push({\n\t\t\tproperty: \"metricId\",\n\t\t\tcomparison: ComparisonOperator.Equals,\n\t\t\tvalue: id\n\t\t});\n\n\t\tif (Is.number(timeStart)) {\n\t\t\tcondition.conditions.push({\n\t\t\t\tproperty: \"ts\",\n\t\t\t\tcomparison: ComparisonOperator.GreaterThanOrEqual,\n\t\t\t\tvalue: timeStart\n\t\t\t});\n\t\t}\n\n\t\tif (Is.number(timeEnd)) {\n\t\t\tcondition.conditions.push({\n\t\t\t\tproperty: \"ts\",\n\t\t\t\tcomparison: ComparisonOperator.LessThanOrEqual,\n\t\t\t\tvalue: timeEnd\n\t\t\t});\n\t\t}\n\n\t\t// Newest first, with the value breaking a tie for a counter. Leading on the value instead\n\t\t// meant sorting the whole history by a column no index on the metric can serve, and it\n\t\t// gave the same sequence anyway: the writer only ever moves a counter up, and forces each\n\t\t// timestamp past the one it read, so a larger value always carries a later timestamp.\n\t\tconst sortBy: { property: keyof TelemetryMetricValue; sortDirection: SortDirection }[] = [\n\t\t\t{ property: \"ts\", sortDirection: SortDirection.Descending }\n\t\t];\n\n\t\tif (existingMetric.type === MetricType.Counter) {\n\t\t\tsortBy.push({ property: \"value\", sortDirection: SortDirection.Descending });\n\t\t}\n\n\t\tconst result = await this._metricValueStorage.query(\n\t\t\tcondition,\n\t\t\tsortBy,\n\t\t\tundefined,\n\t\t\tcursor,\n\t\t\tlimit\n\t\t);\n\n\t\tconst entities = result.entities as ITelemetryMetricValue[];\n\n\t\treturn {\n\t\t\tmetric: existingMetric as ITelemetryMetric,\n\t\t\tentities,\n\t\t\tcursor: result.cursor\n\t\t};\n\t}\n\n\t/**\n\t * Register the handler which runs the metric value writes on the background thread.\n\t * Also used to replace a thread which has stopped answering, so the registration is kept in\n\t * one place.\n\t * @returns A promise that resolves when the handler is registered.\n\t * @internal\n\t */\n\tprivate async registerMetricValueHandler(): Promise<void> {\n\t\tawait this._backgroundTaskComponent?.registerHandler<\n\t\t\tITelemetryMetricValueTaskPayload,\n\t\t\tundefined\n\t\t>(\n\t\t\tEntityStorageTelemetryConnector._METRIC_VALUE_TASK_TYPE,\n\t\t\tthis._metricValueTaskHandler,\n\t\t\t\"telemetryMetricValueTask\",\n\t\t\tasync task => this.metricValueTaskStateChanged(task),\n\t\t\t{\n\t\t\t\t// A single long running worker, kept alive by the negative idle shutdown timeout,\n\t\t\t\t// so the values it has batched are never split across threads.\n\t\t\t\tmaxWorkerCount: 1,\n\t\t\t\tidleShutdownTimeout: -1,\n\t\t\t\tinitialiseMethod: \"telemetryMetricValueTaskStart\",\n\t\t\t\t// The writer settings are sent once when the worker starts rather than with\n\t\t\t\t// every value.\n\t\t\t\tinitialiseMethodParams: async () => [this._writerConfig],\n\t\t\t\tshutdownMethod: \"telemetryMetricValueTaskEnd\"\n\t\t\t}\n\t\t);\n\t}\n\n\t/**\n\t * Ask the background thread to write everything it currently has pending, so a read sees\n\t * the values which have been added but not yet persisted.\n\t * @returns A promise that resolves when the background thread confirms the write, or when\n\t * the flush timeout elapses.\n\t * @internal\n\t */\n\tprivate async flush(): Promise<void> {\n\t\tif (\n\t\t\t!this._started ||\n\t\t\tIs.empty(this._backgroundTaskComponent) ||\n\t\t\t(this._coalesced.length === 0 && this._valuesQueued === this._valuesWritten)\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\t// A caller has to take its turn behind any write already running rather than join it: the\n\t\t// values it added may have been queued after that write's task, so joining would let it\n\t\t// read storage before its own values were written. The handed on promise is only ever\n\t\t// resolved, never rejected, so a failed write does not stop the caller behind it.\n\t\tconst previous = this._writeChain;\n\t\tlet release: (() => void) | undefined;\n\t\tthis._writeChain = new Promise<void>(resolve => {\n\t\t\trelease = resolve;\n\t\t});\n\n\t\tawait previous;\n\n\t\ttry {\n\t\t\tawait this.requestWrite();\n\t\t} finally {\n\t\t\trelease?.();\n\t\t}\n\t}\n\n\t/**\n\t * Ask the background thread to write, handing over any values still inside the coalesce\n\t * window with the same task so a read costs one task rather than two.\n\t * @returns A promise that resolves when the background thread confirms the write, or when the\n\t * write timeout elapses.\n\t * @internal\n\t */\n\tprivate async requestWrite(): Promise<void> {\n\t\tif (!this._started) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.stopCoalesceTimer();\n\n\t\t// splice is synchronous, so there is no yield point where a value could be handed over twice.\n\t\tconst values = this._coalesced.splice(0);\n\n\t\tif (values.length === 0 && this._valuesQueued === this._valuesWritten) {\n\t\t\treturn;\n\t\t}\n\n\t\t// A flush the thread has already been given which covers everything queued will write\n\t\t// what this caller needs, so it waits for that one. Creating another would lengthen the\n\t\t// very queue the caller is waiting on, which is how a backend slow enough to miss one\n\t\t// flush timeout turns into a queue that grows with every read.\n\t\tconst pendingFlush = this._pendingFlush;\n\t\tif (\n\t\t\tvalues.length === 0 &&\n\t\t\t!Is.empty(pendingFlush) &&\n\t\t\tpendingFlush.coveredCount >= this._valuesQueued\n\t\t) {\n\t\t\tif (await this.waitForTask(pendingFlush.taskId)) {\n\t\t\t\tthis._valuesWritten = pendingFlush.coveredCount;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tlet taskId: string;\n\t\ttry {\n\t\t\ttaskId = await this.createMetricValueTask(\n\t\t\t\tvalues.length > 0 ? { values, flush: true } : { flush: true }\n\t\t\t);\n\t\t} catch (err) {\n\t\t\t// Nothing was handed over, so put them back at the head for the next attempt.\n\t\t\tthis._coalesced.unshift(...values);\n\t\t\tthrow err;\n\t\t}\n\n\t\tthis._valuesQueued += values.length;\n\n\t\t// Read before waiting, so every value it counts belongs to a task the thread already\n\t\t// has; anything queued afterwards leaves the counts apart for the next read.\n\t\tconst queued = this._valuesQueued;\n\t\tthis._pendingFlush = { taskId, coveredCount: queued };\n\n\t\t// A task which does not confirm stays in the queue rather than being sent again, as the\n\t\t// thread may already have its values. The counts are only closed when it completes,\n\t\t// which for a caller that gave up first happens in metricValueTaskStateChanged.\n\t\tif (await this.waitForTask(taskId)) {\n\t\t\tthis._valuesWritten = queued;\n\t\t}\n\t}\n\n\t/**\n\t * Resolve the metric definition, check the operation against it and build the payload the\n\t * background thread needs to write the value.\n\t * @param id The id of the metric.\n\t * @param value The value for the add operation.\n\t * @param customData The custom data for the metric value.\n\t * @returns The payload for the background thread.\n\t * @throws NotFoundError if the metric does not exist.\n\t * @internal\n\t */\n\tprivate async buildMetricValue(\n\t\tid: string,\n\t\tvalue: MetricCounterOperation | number,\n\t\tcustomData?: { [key: string]: unknown }\n\t): Promise<ITelemetryMetricValuePayload> {\n\t\tconst contextIds = await this.captureContextIds();\n\t\tconst metricKey = this.contextKey(contextIds, id);\n\n\t\tconst existingMetric = await this._metricDefinitionCache.getOrSet(metricKey, async () => {\n\t\t\tconst stored = await this._metricStorage.get(id);\n\t\t\tif (Is.undefined(stored)) {\n\t\t\t\tthrow new NotFoundError(EntityStorageTelemetryConnector.CLASS_NAME, \"metricNotFound\", id);\n\t\t\t}\n\t\t\treturn stored;\n\t\t});\n\n\t\tconst metricType = existingMetric.type as MetricType;\n\t\tthis.validateOperation(metricType, value);\n\n\t\treturn {\n\t\t\tvalueId: Converter.bytesToHex(RandomHelper.generate(16)),\n\t\t\tmetricId: id,\n\t\t\tmetricType,\n\t\t\toperation: value,\n\t\t\tts: Date.now(),\n\t\t\tmaxHistory: existingMetric.maxHistory,\n\t\t\tcustomData,\n\t\t\tcontextIds\n\t\t};\n\t}\n\n\t/**\n\t * Hand values to the background thread, holding them for the coalesce window when one is\n\t * configured so several share a single task.\n\t * @param metricValues The values to hand over.\n\t * @returns A promise that resolves when the values have been queued or buffered.\n\t * @internal\n\t */\n\tprivate async queueMetricValues(metricValues: ITelemetryMetricValuePayload[]): Promise<void> {\n\t\tif (metricValues.length === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (this._taskCoalesceMs === 0) {\n\t\t\tawait this.createMetricValueTask({ values: metricValues });\n\t\t\tthis._valuesQueued += metricValues.length;\n\t\t\treturn;\n\t\t}\n\n\t\tthis._coalesced.push(...metricValues);\n\n\t\tif (this._coalesced.length >= EntityStorageTelemetryConnector.DEFAULT_COALESCE_SIZE) {\n\t\t\tawait this.sendCoalesced();\n\t\t} else {\n\t\t\tthis.armCoalesceTimer();\n\t\t}\n\t}\n\n\t/**\n\t * Arm the timer which hands the values held for the coalesce window over.\n\t * @internal\n\t */\n\tprivate armCoalesceTimer(): void {\n\t\tif (!Is.empty(this._coalesceTimer)) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis._coalesceTimer = globalThis.setTimeout(async () => {\n\t\t\tthis._coalesceTimer = undefined;\n\t\t\ttry {\n\t\t\t\tawait this.sendCoalesced();\n\t\t\t} catch (err) {\n\t\t\t\tawait this.logWithoutThrowing({\n\t\t\t\t\tsource: EntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\t\tmessage: \"metricValueTaskFailed\",\n\t\t\t\t\tlevel: \"error\",\n\t\t\t\t\tdata: { taskId: \"\" },\n\t\t\t\t\terror: BaseError.fromError(err)\n\t\t\t\t});\n\n\t\t\t\t// The values are back at the head of the window, so the window has to keep\n\t\t\t\t// running: nothing else would hand them over until the next value is added.\n\t\t\t\tthis.armCoalesceTimer();\n\t\t\t}\n\t\t}, this._taskCoalesceMs);\n\t}\n\n\t/**\n\t * Hand any values held for the coalesce window to the background thread as a single task.\n\t * @returns A promise that resolves when the values have been queued.\n\t * @internal\n\t */\n\tprivate async sendCoalesced(): Promise<void> {\n\t\tthis.stopCoalesceTimer();\n\n\t\tif (this._coalesced.length === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\t// splice is synchronous, so there is no yield point where a value could be added twice.\n\t\tconst values = this._coalesced.splice(0);\n\n\t\ttry {\n\t\t\tawait this.createMetricValueTask({ values });\n\t\t\tthis._valuesQueued += values.length;\n\t\t} catch (err) {\n\t\t\t// Put them back at the head so the next attempt keeps them in order.\n\t\t\tthis._coalesced.unshift(...values);\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\t/**\n\t * Stop the coalesce window timer if it is running.\n\t * @internal\n\t */\n\tprivate stopCoalesceTimer(): void {\n\t\tif (!Is.empty(this._coalesceTimer)) {\n\t\t\tglobalThis.clearTimeout(this._coalesceTimer);\n\t\t\tthis._coalesceTimer = undefined;\n\t\t}\n\t}\n\n\t/**\n\t * Queue a task for the background thread.\n\t * @param payload The task payload.\n\t * @returns The id of the created task.\n\t * @throws GeneralError if no background task component is available to run the write.\n\t * @internal\n\t */\n\tprivate async createMetricValueTask(payload: ITelemetryMetricValueTaskPayload): Promise<string> {\n\t\tif (Is.empty(this._backgroundTaskComponent)) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tEntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\t\"backgroundTaskComponentMissing\"\n\t\t\t);\n\t\t}\n\n\t\tconst taskId = await this._backgroundTaskComponent.create<ITelemetryMetricValueTaskPayload>(\n\t\t\tEntityStorageTelemetryConnector._METRIC_VALUE_TASK_TYPE,\n\t\t\tpayload\n\t\t);\n\n\t\tif (this._outstandingTaskIds.size === 0) {\n\t\t\t// Nothing was outstanding, so the wait for the thread to show progress starts here.\n\t\t\tthis._lastTaskProgress = Date.now();\n\t\t}\n\t\tthis._outstandingTaskIds.add(taskId);\n\t\tthis.armStallTimer();\n\n\t\treturn taskId;\n\t}\n\n\t/**\n\t * Wait for the background thread to report a task complete.\n\t * @param taskId The id of the task to wait for.\n\t * @returns True when the task completed successfully.\n\t * @internal\n\t */\n\tprivate async waitForTask(taskId: string): Promise<boolean> {\n\t\treturn new Promise<boolean>(resolve => {\n\t\t\tconst timer = globalThis.setTimeout(async () => {\n\t\t\t\tthis._pendingWrites.delete(taskId);\n\t\t\t\tawait this._logging?.log({\n\t\t\t\t\tsource: EntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\t\tmessage: \"flushTimeout\",\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\tdata: {\n\t\t\t\t\t\ttaskId,\n\t\t\t\t\t\ttimeoutMs: this._flushTimeoutMs,\n\t\t\t\t\t\ttaskCount: this._outstandingTaskIds.size\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tresolve(false);\n\t\t\t}, this._flushTimeoutMs);\n\n\t\t\tthis._pendingWrites.set(taskId, { resolve, timer });\n\t\t});\n\t}\n\n\t/**\n\t * The delay before the next stall check: the configured timeout, doubled per consecutive\n\t * replacement not followed by a completion, capped at _STALL_BACKOFF_MAX_MULTIPLIER times.\n\t * @returns The delay in milliseconds.\n\t * @internal\n\t */\n\tprivate stallWaitMs(): number {\n\t\tconst multiplier = Math.min(\n\t\t\t2 ** this._stallRestartCount,\n\t\t\tEntityStorageTelemetryConnector._STALL_BACKOFF_MAX_MULTIPLIER\n\t\t);\n\t\treturn this._taskStallTimeoutMs * multiplier;\n\t}\n\n\t/**\n\t * Arm the timer which checks whether the background thread has stopped answering, for\n\t * whenever the stall wait will have elapsed with no task having completed.\n\t * @internal\n\t */\n\tprivate armStallTimer(): void {\n\t\tif (\n\t\t\tthis._taskStallTimeoutMs === 0 ||\n\t\t\t!this._started ||\n\t\t\t!Is.empty(this._stallTimer) ||\n\t\t\tthis._stallCheckRunning\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst delay = Math.max(this.stallWaitMs() - (Date.now() - this._lastTaskProgress), 0);\n\n\t\tthis._stallTimer = globalThis.setTimeout(async () => {\n\t\t\tthis._stallTimer = undefined;\n\t\t\t// Blocks a second check from arming while this one, and any replacement, is running.\n\t\t\tthis._stallCheckRunning = true;\n\t\t\ttry {\n\t\t\t\tawait this.checkForStalledThread();\n\t\t\t} catch (err) {\n\t\t\t\t// Supervision has to survive its own failures, and an unhandled rejection from\n\t\t\t\t// a timer would take the process with it.\n\t\t\t\tawait this.logWithoutThrowing({\n\t\t\t\t\tsource: EntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\t\tmessage: \"metricValueThreadCheckFailed\",\n\t\t\t\t\tlevel: \"error\",\n\t\t\t\t\terror: BaseError.fromError(err)\n\t\t\t\t});\n\n\t\t\t\t// A check which could not read the task cannot vouch for the flush being tracked\n\t\t\t\t// either, and the reads waiting for it have no other way out, so it is dropped\n\t\t\t\t// and the next read asks the thread again.\n\t\t\t\tthis._pendingFlush = undefined;\n\t\t\t} finally {\n\t\t\t\tthis._stallCheckRunning = false;\n\t\t\t\tif (this._outstandingTaskIds.size > 0) {\n\t\t\t\t\tthis.armStallTimer();\n\t\t\t\t}\n\t\t\t}\n\t\t}, delay);\n\t}\n\n\t/**\n\t * Stop the stall check timer if it is running.\n\t * @internal\n\t */\n\tprivate stopStallTimer(): void {\n\t\tif (!Is.empty(this._stallTimer)) {\n\t\t\tglobalThis.clearTimeout(this._stallTimer);\n\t\t\tthis._stallTimer = undefined;\n\t\t}\n\t}\n\n\t/**\n\t * Check the oldest outstanding task once none has completed for the stall wait, and act on\n\t * its status: missed, still waiting for a worker, or actually stalled.\n\t * @returns A promise that resolves when the check, and any action it takes, is complete.\n\t * @internal\n\t */\n\tprivate async checkForStalledThread(): Promise<void> {\n\t\tif (!this._started || this._outstandingTaskIds.size === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst stalledForMs = Date.now() - this._lastTaskProgress;\n\n\t\tif (stalledForMs < this.stallWaitMs()) {\n\t\t\t// A task completed while the timer was armed; the caller re-arms for what remains.\n\t\t\treturn;\n\t\t}\n\n\t\t// No task has completed since the oldest one outstanding was created, so that is the one\n\t\t// the thread would be stuck on. Replacing the thread costs whatever the writer has\n\t\t// batched, so the task is checked before it is done: a task which is no longer in the\n\t\t// queue means a state change was missed rather than the thread having stopped.\n\t\tconst stalledTaskId = this._outstandingTaskIds.values().next().value as string;\n\t\tconst stalledTask = await this._backgroundTaskComponent?.get(stalledTaskId);\n\n\t\tif (!this._outstandingTaskIds.has(stalledTaskId)) {\n\t\t\t// Completed, or the connector stopped, while the read above was in flight; already\n\t\t\t// handled by metricValueTaskStateChanged or stop(), so the stale snapshot is moot.\n\t\t\treturn;\n\t\t}\n\n\t\tif (\n\t\t\tIs.empty(stalledTask) ||\n\t\t\t(stalledTask.status !== TaskStatus.Pending && stalledTask.status !== TaskStatus.Processing)\n\t\t) {\n\t\t\tawait this.logWithoutThrowing({\n\t\t\t\tsource: EntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\tmessage: \"metricValueTaskStateMissed\",\n\t\t\t\tlevel: \"warn\",\n\t\t\t\tdata: { taskId: stalledTaskId, status: stalledTask?.status }\n\t\t\t});\n\t\t\tthis._outstandingTaskIds.clear();\n\t\t\tthis._pendingFlush = undefined;\n\t\t\tthis._lastTaskProgress = Date.now();\n\t\t\tthis._stallRestartCount = 0;\n\t\t\treturn;\n\t\t}\n\n\t\tif (stalledTask.status === TaskStatus.Pending) {\n\t\t\t// Not yet scheduled, so replacing the thread would not help; report and leave it.\n\t\t\tconst scheduledWaitMs = Date.now() - Date.parse(stalledTask.dateCreated);\n\t\t\tawait this.logWithoutThrowing({\n\t\t\t\tsource: EntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\tmessage: \"metricValueTaskNotScheduled\",\n\t\t\t\tlevel: \"warn\",\n\t\t\t\tdata: {\n\t\t\t\t\ttaskId: stalledTaskId,\n\t\t\t\t\tstalledForMs: scheduledWaitMs,\n\t\t\t\t\ttaskCount: this._outstandingTaskIds.size\n\t\t\t\t}\n\t\t\t});\n\t\t\tthis._lastTaskProgress = Date.now();\n\t\t\treturn;\n\t\t}\n\n\t\tawait this.restartMetricValueThread(stalledForMs, stalledTaskId);\n\t}\n\n\t/**\n\t * Replace the background thread after it has stopped completing the tasks handed to it.\n\t * Nothing else recovers from this: the handler has no execution timeout, so the scheduler\n\t * goes on waiting for the one worker it believes is busy and never dispatches for this task\n\t * type again. Re-registering the handler terminates that worker, which releases the\n\t * scheduler's claim on the task it was given, so the tasks in the queue are dispatched to\n\t * the replacement rather than left waiting on a thread that will never answer. Counts toward\n\t * the backoff in stallWaitMs() when not followed by a completion.\n\t * @param stalledForMs How long the thread has had tasks outstanding without completing one.\n\t * @param stalledTaskId The id of the task the thread has not completed.\n\t * @returns A promise that resolves when the handler has been re-registered.\n\t * @internal\n\t */\n\tprivate async restartMetricValueThread(\n\t\tstalledForMs: number,\n\t\tstalledTaskId: string\n\t): Promise<void> {\n\t\tawait this.logWithoutThrowing({\n\t\t\tsource: EntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\tmessage: \"metricValueThreadStalled\",\n\t\t\tlevel: \"error\",\n\t\t\tdata: { stalledForMs, taskId: stalledTaskId, taskCount: this._outstandingTaskIds.size }\n\t\t});\n\n\t\t// The tasks themselves are left in the queue, so the values already handed over are\n\t\t// written by the replacement rather than lost. Anything waiting on one of them is\n\t\t// released by its own flush timeout, or when the replacement completes it.\n\t\t// The replacement reports the tasks it completes, but the flush being tracked was given\n\t\t// to the thread that has gone, so it is dropped and the next read asks the new one.\n\t\tthis._outstandingTaskIds.clear();\n\t\tthis._pendingFlush = undefined;\n\t\tthis._lastTaskProgress = Date.now();\n\t\tthis._stallRestartCount += 1;\n\n\t\ttry {\n\t\t\tawait this._backgroundTaskComponent?.unregisterHandler(\n\t\t\t\tEntityStorageTelemetryConnector._METRIC_VALUE_TASK_TYPE\n\t\t\t);\n\t\t\tawait this.registerMetricValueHandler();\n\n\t\t\tawait this.logWithoutThrowing({\n\t\t\t\tsource: EntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\tmessage: \"metricValueThreadRestarted\",\n\t\t\t\tlevel: \"warn\"\n\t\t\t});\n\t\t} catch (err) {\n\t\t\tawait this.logWithoutThrowing({\n\t\t\t\tsource: EntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\tmessage: \"metricValueThreadRestartFailed\",\n\t\t\t\tlevel: \"error\",\n\t\t\t\terror: BaseError.fromError(err)\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Log on one of the paths which has no caller to report to, without the logging itself\n\t * becoming the failure. An unhandled rejection from a timer callback would end the\n\t * supervision it came from, and take the process with it.\n\t * @param logEntry The entry to log.\n\t * @returns A promise that resolves when the entry has been logged, or given up on.\n\t * @internal\n\t */\n\tprivate async logWithoutThrowing(logEntry: ILogEntry): Promise<void> {\n\t\ttry {\n\t\t\tawait this._logging?.log(logEntry);\n\t\t} catch {\n\t\t\t// The logging component is the only sink available, and it writes to storage which is\n\t\t\t// what tends to be failing when this path is reached at all.\n\t\t}\n\t}\n\n\t/**\n\t * Handle a state change reported for a metric value task.\n\t * @param task The task which changed state.\n\t * @returns A promise that resolves when any waiting flush has been released.\n\t * @internal\n\t */\n\tprivate async metricValueTaskStateChanged(\n\t\ttask: IBackgroundTask<ITelemetryMetricValueTaskPayload, undefined>\n\t): Promise<void> {\n\t\tif (\n\t\t\ttask.status !== TaskStatus.Success &&\n\t\t\ttask.status !== TaskStatus.Failed &&\n\t\t\ttask.status !== TaskStatus.Cancelled\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\t// A final state is the only evidence the thread is still working through what it has\n\t\t// been given. Only this connector's own tasks are tracked, so a state reported for one\n\t\t// belonging to another connector in the process is simply not found.\n\t\tthis._outstandingTaskIds.delete(task.id);\n\t\tthis._lastTaskProgress = Date.now();\n\t\tthis._stallRestartCount = 0;\n\n\t\tif (this._pendingFlush?.taskId === task.id) {\n\t\t\t// A caller which gave up on this task is long gone, but the values it was waiting for\n\t\t\t// are written all the same, so the counts are closed here rather than being left\n\t\t\t// apart for every later read to ask about again.\n\t\t\tif (task.status === TaskStatus.Success) {\n\t\t\t\tthis._valuesWritten = Math.max(this._valuesWritten, this._pendingFlush.coveredCount);\n\t\t\t}\n\t\t\tthis._pendingFlush = undefined;\n\t\t}\n\n\t\t// The reset can shorten stallWaitMs() below what the current timer expects, so it is\n\t\t// cleared and, if anything is still outstanding, re-armed.\n\t\tthis.stopStallTimer();\n\t\tif (this._outstandingTaskIds.size > 0) {\n\t\t\tthis.armStallTimer();\n\t\t}\n\n\t\tconst pendingWrite = this._pendingWrites.get(task.id);\n\t\tif (!Is.empty(pendingWrite)) {\n\t\t\tthis._pendingWrites.delete(task.id);\n\t\t\tglobalThis.clearTimeout(pendingWrite.timer);\n\t\t\t// Only a successful task means the thread wrote what it was holding, so a failure\n\t\t\t// leaves the counts apart and the next read asks again.\n\t\t\tpendingWrite.resolve(task.status === TaskStatus.Success);\n\t\t}\n\n\t\tif (task.status === TaskStatus.Failed) {\n\t\t\tawait this._logging?.log({\n\t\t\t\tsource: EntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\tmessage: \"metricValueTaskFailed\",\n\t\t\t\tlevel: \"error\",\n\t\t\t\tdata: { taskId: task.id },\n\t\t\t\terror: Is.object(task.error) ? BaseError.fromError(task.error) : undefined\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Build the stored entity for a metric definition.\n\t * @param metric The metric details.\n\t * @returns The entity to store.\n\t * @internal\n\t */\n\tprivate toTelemetryMetric(metric: ITelemetryMetric): TelemetryMetric {\n\t\treturn {\n\t\t\tid: metric.id,\n\t\t\tlabel: metric.label,\n\t\t\ttype: metric.type,\n\t\t\tunit: metric.unit ?? \"\",\n\t\t\tdescription: metric.description ?? \"\",\n\t\t\tmaxHistory: metric.maxHistory\n\t\t};\n\t}\n\n\t/**\n\t * Check the metric details are complete and consistent before it is stored.\n\t * @param metric The metric details.\n\t * @throws GeneralError if the maxHistory is not a positive integer.\n\t * @internal\n\t */\n\tprivate validateMetric(metric: ITelemetryMetric): void {\n\t\tGuards.object<ITelemetryMetric>(\n\t\t\tEntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\"metric\",\n\t\t\tmetric\n\t\t);\n\t\tGuards.stringValue(EntityStorageTelemetryConnector.CLASS_NAME, \"metric.id\", metric.id);\n\t\tGuards.stringValue(\n\t\t\tEntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\"metric.label\",\n\t\t\tmetric.label\n\t\t);\n\t\tGuards.arrayOneOf(\n\t\t\tEntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\"metric.type\",\n\t\t\tmetric.type,\n\t\t\tObject.values(MetricType)\n\t\t);\n\n\t\tif (Is.notEmpty(metric.description)) {\n\t\t\tGuards.string(\n\t\t\t\tEntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\t\"metric.description\",\n\t\t\t\tmetric.description\n\t\t\t);\n\t\t}\n\t\tif (Is.notEmpty(metric.unit)) {\n\t\t\tGuards.string(EntityStorageTelemetryConnector.CLASS_NAME, \"metric.unit\", metric.unit);\n\t\t}\n\t\tif (Is.notEmpty(metric.maxHistory)) {\n\t\t\tif (!Is.integer(metric.maxHistory) || metric.maxHistory <= 0) {\n\t\t\t\tthrow new GeneralError(\n\t\t\t\t\tEntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\t\t\"maxHistoryMustBePositiveInteger\"\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Check the operation is valid for the metric type before it is handed to the background\n\t * thread, so the caller still sees an invalid operation as an error.\n\t * @param metricType The type of the metric.\n\t * @param value The value for the add operation.\n\t * @throws GeneralError if the operation cannot be applied to the metric type.\n\t * @internal\n\t */\n\tprivate validateOperation(metricType: MetricType, value: MetricCounterOperation | number): void {\n\t\tif (metricType === MetricType.Counter) {\n\t\t\tif (value !== MetricCounterOperation.Increment && !(Is.integer(value) && value > 0)) {\n\t\t\t\tthrow new GeneralError(EntityStorageTelemetryConnector.CLASS_NAME, \"counterIncOnly\");\n\t\t\t}\n\t\t} else if (metricType === MetricType.IncDecCounter) {\n\t\t\tif (\n\t\t\t\tvalue !== MetricCounterOperation.Increment &&\n\t\t\t\tvalue !== MetricCounterOperation.Decrement &&\n\t\t\t\t!Is.integer(value)\n\t\t\t) {\n\t\t\t\tthrow new GeneralError(\n\t\t\t\t\tEntityStorageTelemetryConnector.CLASS_NAME,\n\t\t\t\t\t\"upDownCounterIncOrDecOnly\"\n\t\t\t\t);\n\t\t\t}\n\t\t} else if (!Is.number(value)) {\n\t\t\tthrow new GeneralError(EntityStorageTelemetryConnector.CLASS_NAME, \"gaugeNoIncDec\");\n\t\t}\n\t}\n\n\t/**\n\t * Build the partition key used for the metric definition cache.\n\t * Only the node and tenant ids partition the storage, so the key is built from those two\n\t * directly rather than canonicalising the whole context object on every metric write.\n\t * @param contextIds The context ids to derive the key from.\n\t * @param metricId The metric id to include in the partition key.\n\t * @returns The partition key.\n\t * @internal\n\t */\n\tprivate contextKey(contextIds: IContextIds, metricId?: string): string {\n\t\tconst key = `${contextIds[ContextIdKeys.Node] ?? \"\"}|${contextIds[ContextIdKeys.Tenant] ?? \"\"}`;\n\t\tif (metricId) {\n\t\t\treturn `${key}|${metricId}`;\n\t\t}\n\t\treturn key;\n\t}\n\n\t/**\n\t * Capture the partition keys of the ambient context ids. Other keys (organization, user,\n\t * per-request ids) vary within a partition and would split the definition cache and the\n\t * background thread batch groups.\n\t * @returns The partition context ids from the ambient context.\n\t * @internal\n\t */\n\tprivate async captureContextIds(): Promise<IContextIds> {\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\treturn {\n\t\t\t[ContextIdKeys.Node]: contextIds?.[ContextIdKeys.Node] ?? \"\",\n\t\t\t[ContextIdKeys.Tenant]: contextIds?.[ContextIdKeys.Tenant] ?? \"\"\n\t\t};\n\t}\n}\n"]}
@@ -1,7 +1,8 @@
1
1
  // Copyright 2026 IOTA Stiftung.
2
+ // Copyright 2026 3Sixty Trade Ltd.
2
3
  // SPDX-License-Identifier: Apache-2.0.
3
4
  import { ContextIdKeys, ContextIdStore } from "@3sixty/context";
4
- import { BaseError, Coerce, ComponentFactory, Is } from "@3sixty/core";
5
+ import { BaseError, Coerce, ComponentFactory, DurationConstants, Is } from "@3sixty/core";
5
6
  import { ComparisonOperator, LogicalOperator, SortDirection } from "@3sixty/entity";
6
7
  import { EntityStorageConnectorFactory } from "@3sixty/entity-storage-models";
7
8
  import { MetricCounterOperation, MetricType } from "@3sixty/telemetry-models";
@@ -26,7 +27,7 @@ export class MetricValueWriter {
26
27
  /**
27
28
  * Default interval in milliseconds between automatic writes.
28
29
  */
29
- static DEFAULT_BATCH_INTERVAL_MS = 5000;
30
+ static DEFAULT_BATCH_INTERVAL_MS = 5 * DurationConstants.MsPerSecond;
30
31
  /**
31
32
  * Default maximum number of entries to hold when a write fails and the entries are re-queued.
32
33
  */
@@ -34,7 +35,7 @@ export class MetricValueWriter {
34
35
  /**
35
36
  * Default interval in milliseconds between trim passes.
36
37
  */
37
- static DEFAULT_TRIM_INTERVAL_MS = 60000;
38
+ static DEFAULT_TRIM_INTERVAL_MS = DurationConstants.MsPerMinute;
38
39
  /**
39
40
  * Default maximum number of values a single trim pass removes from one metric.
40
41
  */
@@ -1 +1 @@
1
- {"version":3,"file":"metricValueWriter.js","sources":["../../src/metricValueWriter.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { ContextIdKeys, ContextIdStore, type IContextIds } from \"@3sixty/context\";\nimport { BaseError, Coerce, ComponentFactory, Is } from \"@3sixty/core\";\nimport {\n\tComparisonOperator,\n\ttype EntityCondition,\n\tLogicalOperator,\n\tSortDirection\n} from \"@3sixty/entity\";\nimport {\n\tEntityStorageConnectorFactory,\n\ttype IEntityStorageConnector\n} from \"@3sixty/entity-storage-models\";\nimport type { ILoggingComponent } from \"@3sixty/logging-models\";\n\nimport { MetricCounterOperation, MetricType } from \"@3sixty/telemetry-models\";\nimport type { TelemetryMetricValue } from \"./entities/telemetryMetricValue.js\";\nimport type { ITelemetryMetricValuePayload } from \"./models/ITelemetryMetricValuePayload.js\";\nimport type { ITelemetryMetricValueWriterConfig } from \"./models/ITelemetryMetricValueWriterConfig.js\";\n\n/**\n * Accumulates metric values and persists them to entity storage.\n * Runs on the background thread started by the telemetry connector. The value a counter\n * builds on is always read from storage rather than cached, which keeps the chain correct\n * when several load balanced nodes write to the same storage, and that read is of the newest\n * value alone, so the cost of a write does not grow with the history it is appending to. A\n * gauge replaces the stored value rather than building on it, so it is written without any\n * read at all. The retention cap is applied by the trim pass on its own interval.\n */\nexport class MetricValueWriter {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = \"MetricValueWriter\";\n\n\t/**\n\t * Default number of entries to accumulate before writing.\n\t */\n\tpublic static readonly DEFAULT_BATCH_SIZE: number = 10;\n\n\t/**\n\t * Default interval in milliseconds between automatic writes.\n\t */\n\tpublic static readonly DEFAULT_BATCH_INTERVAL_MS: number = 5000;\n\n\t/**\n\t * Default maximum number of entries to hold when a write fails and the entries are re-queued.\n\t */\n\tpublic static readonly DEFAULT_MAX_CACHE_SIZE: number = 1000;\n\n\t/**\n\t * Default interval in milliseconds between trim passes.\n\t */\n\tpublic static readonly DEFAULT_TRIM_INTERVAL_MS: number = 60000;\n\n\t/**\n\t * Default maximum number of values a single trim pass removes from one metric.\n\t */\n\tpublic static readonly DEFAULT_TRIM_REMOVE_LIMIT: number = 10000;\n\n\t/**\n\t * Page size used when removing the values which fall outside the retention cap.\n\t * Large enough to clear most overflows in one or two pages without overwhelming storage.\n\t * @internal\n\t */\n\tprivate static readonly _TRIM_PAGE_SIZE = 1000;\n\n\t/**\n\t * Entries waiting to be written to storage.\n\t * @internal\n\t */\n\tprivate readonly _pending: ITelemetryMetricValuePayload[];\n\n\t/**\n\t * The capped metrics written since the last trim pass, keyed by partition and metric id.\n\t * A metric is only removed once its history is back inside the cap, so an overflow larger\n\t * than a single pass can remove is finished by the passes which follow it.\n\t * @internal\n\t */\n\tprivate readonly _toTrim: Map<\n\t\tstring,\n\t\t{ metricId: string; maxHistory: number; contextIds: IContextIds }\n\t>;\n\n\t/**\n\t * The entity storage for the telemetry metric values, resolved when the writer is started.\n\t * @internal\n\t */\n\tprivate _metricValueStorage?: IEntityStorageConnector<TelemetryMetricValue>;\n\n\t/**\n\t * The component for logging.\n\t * @internal\n\t */\n\tprivate _logging?: ILoggingComponent;\n\n\t/**\n\t * Write when the pending entries reach this size; undefined or &lt;= 1 disables size based writing.\n\t * @internal\n\t */\n\tprivate _batchSize?: number;\n\n\t/**\n\t * Write every this many milliseconds; undefined or &lt;= 0 disables timer based writing.\n\t * @internal\n\t */\n\tprivate _batchIntervalMs?: number;\n\n\t/**\n\t * Maximum entries to keep after a failed write re-queue; 0 means unlimited.\n\t * @internal\n\t */\n\tprivate _maxCacheSize: number;\n\n\t/**\n\t * Run a trim pass every this many milliseconds; undefined disables trimming.\n\t * @internal\n\t */\n\tprivate _trimIntervalMs?: number;\n\n\t/**\n\t * Maximum values a single trim pass removes from one metric.\n\t * @internal\n\t */\n\tprivate _trimRemoveLimit: number;\n\n\t/**\n\t * Handle for the interval timer, present only while the writer is running.\n\t * @internal\n\t */\n\tprivate _batchTimer?: ReturnType<typeof setTimeout>;\n\n\t/**\n\t * Handle for the trim timer, present only while the writer is running.\n\t * @internal\n\t */\n\tprivate _trimTimer?: ReturnType<typeof setTimeout>;\n\n\t/**\n\t * Is a trim pass in progress, so the timer does not start a second one over it.\n\t * @internal\n\t */\n\tprivate _trimRunning: boolean;\n\n\t/**\n\t * Is the writer running.\n\t * @internal\n\t */\n\tprivate _started: boolean;\n\n\t/**\n\t * Serialises the writes issued by this instance, as writePending is only ever entered from\n\t * flush and an entry added after a write has taken its snapshot would otherwise be skipped\n\t * by a caller which simply joined it.\n\t * @internal\n\t */\n\tprivate _writeChain: Promise<void>;\n\n\t/**\n\t * Create a new instance of MetricValueWriter.\n\t */\n\tconstructor() {\n\t\tthis._pending = [];\n\t\tthis._toTrim = new Map();\n\t\tthis._maxCacheSize = MetricValueWriter.DEFAULT_MAX_CACHE_SIZE;\n\t\tthis._trimRemoveLimit = MetricValueWriter.DEFAULT_TRIM_REMOVE_LIMIT;\n\t\tthis._started = false;\n\t\tthis._trimRunning = false;\n\t\tthis._writeChain = Promise.resolve();\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn MetricValueWriter.CLASS_NAME;\n\t}\n\n\t/**\n\t * Resolve the storage connector and start the interval timer.\n\t * @param config The configuration for the writer.\n\t * @returns A promise that resolves when the writer is ready to accept metric values.\n\t */\n\tpublic async start(config?: ITelemetryMetricValueWriterConfig): Promise<void> {\n\t\tif (this._started) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis._metricValueStorage = EntityStorageConnectorFactory.get(\n\t\t\tconfig?.telemetryMetricValueStorageConnectorType ?? \"telemetry-metric-value\"\n\t\t);\n\t\tthis._logging = ComponentFactory.getIfExists(config?.loggingComponentType);\n\n\t\tconst cfgBatchSize = Coerce.integer(config?.batchSize) ?? MetricValueWriter.DEFAULT_BATCH_SIZE;\n\t\tthis._batchSize = cfgBatchSize > 1 ? cfgBatchSize : undefined;\n\n\t\tconst cfgIntervalMs =\n\t\t\tCoerce.integer(config?.batchIntervalMs) ?? MetricValueWriter.DEFAULT_BATCH_INTERVAL_MS;\n\t\tthis._batchIntervalMs = cfgIntervalMs > 0 ? cfgIntervalMs : undefined;\n\n\t\tconst cfgMaxCacheSize =\n\t\t\tCoerce.integer(config?.maxCacheSize) ?? MetricValueWriter.DEFAULT_MAX_CACHE_SIZE;\n\t\tthis._maxCacheSize = cfgMaxCacheSize > 0 ? cfgMaxCacheSize : 0;\n\n\t\tconst cfgTrimIntervalMs =\n\t\t\tCoerce.integer(config?.trimIntervalMs) ?? MetricValueWriter.DEFAULT_TRIM_INTERVAL_MS;\n\t\tthis._trimIntervalMs = cfgTrimIntervalMs > 0 ? cfgTrimIntervalMs : undefined;\n\n\t\tconst cfgTrimRemoveLimit =\n\t\t\tCoerce.integer(config?.trimRemoveLimit) ?? MetricValueWriter.DEFAULT_TRIM_REMOVE_LIMIT;\n\t\tthis._trimRemoveLimit =\n\t\t\tcfgTrimRemoveLimit > 0 ? cfgTrimRemoveLimit : MetricValueWriter.DEFAULT_TRIM_REMOVE_LIMIT;\n\n\t\tthis._started = true;\n\t\tthis.startTimer();\n\t\tthis.startTrimTimer();\n\t}\n\n\t/**\n\t * Write any remaining entries to storage and clear the timer.\n\t * @returns A promise that resolves when the final write completes and the timer is cleared.\n\t */\n\tpublic async stop(): Promise<void> {\n\t\tif (this._started) {\n\t\t\tthis._started = false;\n\t\t\tthis.stopTimer();\n\t\t\tthis.stopTrimTimer();\n\t\t}\n\t\tawait this.flush();\n\t}\n\n\t/**\n\t * Queue metric values to be written to storage.\n\t * @param values The metric value details.\n\t * @returns A promise that resolves when the values have been queued, or written when batching is disabled.\n\t */\n\tpublic async add(values: ITelemetryMetricValuePayload[]): Promise<void> {\n\t\t// push is synchronous, so there are no yield points and no race with the flush splice.\n\t\tthis._pending.push(...values);\n\n\t\tconst isBatching = !Is.empty(this._batchSize) || !Is.empty(this._batchIntervalMs);\n\t\tconst isSizeReached = !Is.empty(this._batchSize) && this._pending.length >= this._batchSize;\n\n\t\tif (!isBatching || isSizeReached) {\n\t\t\tawait this.flush();\n\t\t}\n\t}\n\n\t/**\n\t * Write all pending entries to storage and clear them.\n\t * Overlapping calls queue behind each other, as entries added after a write has taken its\n\t * snapshot would otherwise be skipped by a caller that simply joined it.\n\t * @returns A promise that resolves when all pending entries have been written to storage.\n\t */\n\tpublic async flush(): Promise<void> {\n\t\t// Take a place in the queue before waiting, so callers run in the order they arrived.\n\t\t// The handed on promise is only ever resolved, never rejected, so a failed write does\n\t\t// not stop the caller behind it.\n\t\tconst previous = this._writeChain;\n\t\tlet release: (() => void) | undefined;\n\t\tthis._writeChain = new Promise<void>(resolve => {\n\t\t\trelease = resolve;\n\t\t});\n\n\t\tawait previous;\n\n\t\ttry {\n\t\t\tawait this.writePending();\n\t\t} finally {\n\t\t\trelease?.();\n\t\t}\n\t}\n\n\t/**\n\t * Apply the retention cap to every capped metric written since the last pass.\n\t * Runs on its own interval rather than on the write path, and removes at most\n\t * trimRemoveLimit values per metric, so a history far beyond its cap is brought back over\n\t * several passes instead of stalling one write.\n\t * @returns A promise that resolves when the pass is complete.\n\t */\n\tpublic async trim(): Promise<void> {\n\t\tif (this._trimRunning) {\n\t\t\treturn;\n\t\t}\n\t\tthis._trimRunning = true;\n\n\t\ttry {\n\t\t\t// Snapshotted because a write landing during the pass adds to the map, and those\n\t\t\t// metrics are picked up by the next pass rather than extending this one.\n\t\t\tfor (const [key, target] of [...this._toTrim]) {\n\t\t\t\ttry {\n\t\t\t\t\tconst isComplete = await ContextIdStore.run(target.contextIds, async () =>\n\t\t\t\t\t\tthis.trimMetric(target.metricId, target.maxHistory)\n\t\t\t\t\t);\n\n\t\t\t\t\t// An incomplete pass leaves the metric registered so the next one continues it,\n\t\t\t\t\t// as does a write which landed during the pass: that registration is a new\n\t\t\t\t\t// entry for the same key, and the values it added may be ones the boundary\n\t\t\t\t\t// read of this pass could not see.\n\t\t\t\t\tif (isComplete && this._toTrim.get(key) === target) {\n\t\t\t\t\t\tthis._toTrim.delete(key);\n\t\t\t\t\t}\n\t\t\t\t} catch (err) {\n\t\t\t\t\tawait this._logging?.log({\n\t\t\t\t\t\tsource: MetricValueWriter.CLASS_NAME,\n\t\t\t\t\t\tmessage: \"trimFailed\",\n\t\t\t\t\t\tlevel: \"error\",\n\t\t\t\t\t\tdata: { id: target.metricId },\n\t\t\t\t\t\terror: BaseError.fromError(err)\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tthis._trimRunning = false;\n\t\t}\n\t}\n\n\t/**\n\t * Write the entries currently pending to storage.\n\t * On a storage write failure the entries are returned to the head of the queue for the next attempt.\n\t * @returns A promise that resolves when the pending entries have been written to storage.\n\t * @internal\n\t */\n\tprivate async writePending(): Promise<void> {\n\t\tthis.stopTimer();\n\n\t\tif (this._pending.length === 0) {\n\t\t\tthis.startTimer();\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tconst entries = this._pending.splice(0);\n\n\t\t\tconst groups = new Map<string, ITelemetryMetricValuePayload[]>();\n\t\t\tfor (const entry of entries) {\n\t\t\t\tconst contextGroupKey = this.contextKey(entry.contextIds ?? {});\n\t\t\t\tlet group = groups.get(contextGroupKey);\n\t\t\t\tif (Is.undefined(group)) {\n\t\t\t\t\tgroup = [];\n\t\t\t\t\tgroups.set(contextGroupKey, group);\n\t\t\t\t}\n\t\t\t\tgroup.push(entry);\n\t\t\t}\n\n\t\t\tconst failedEntries: ITelemetryMetricValuePayload[] = [];\n\t\t\tconst errors: unknown[] = [];\n\t\t\tfor (const group of groups.values()) {\n\t\t\t\ttry {\n\t\t\t\t\tawait ContextIdStore.run(group[0].contextIds ?? {}, async () => {\n\t\t\t\t\t\tawait this.writeGroup(group);\n\t\t\t\t\t});\n\t\t\t\t} catch (err) {\n\t\t\t\t\tfailedEntries.push(...group);\n\t\t\t\t\terrors.push(err);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (Is.arrayValue(errors)) {\n\t\t\t\t// Re-queued before the failure is reported, as the reporting writes to storage\n\t\t\t\t// too: a failure in it would otherwise take the entries with it.\n\t\t\t\tthis._pending.unshift(...failedEntries);\n\t\t\t\tif (this._maxCacheSize > 0 && this._pending.length > this._maxCacheSize) {\n\t\t\t\t\tthis._pending.splice(0, this._pending.length - this._maxCacheSize);\n\t\t\t\t}\n\n\t\t\t\tawait this._logging?.log({\n\t\t\t\t\tsource: MetricValueWriter.CLASS_NAME,\n\t\t\t\t\tmessage: \"flushFailed\",\n\t\t\t\t\tlevel: \"error\",\n\t\t\t\t\tdata: { error: errors.length === 1 ? errors[0] : errors }\n\t\t\t\t});\n\t\t\t}\n\t\t} finally {\n\t\t\t// Re-armed here rather than after the block, so a failure inside it cannot leave the\n\t\t\t// writer without its interval for the life of the thread.\n\t\t\tthis.startTimer();\n\t\t}\n\t}\n\n\t/**\n\t * Build the partition key used to group the pending entries before writing.\n\t * Only the node and tenant ids partition the storage, so the key is built from those two.\n\t * @param contextIds The context ids to derive the key from.\n\t * @returns The partition key.\n\t * @internal\n\t */\n\tprivate contextKey(contextIds: IContextIds): string {\n\t\treturn `${contextIds[ContextIdKeys.Node] ?? \"\"}|${contextIds[ContextIdKeys.Tenant] ?? \"\"}`;\n\t}\n\n\t/**\n\t * Write one partition worth of entries, chaining the counter values from the value\n\t * currently held in storage.\n\t * @param group The entries for a single partition, in the order they were added.\n\t * @returns A promise that resolves when the entries have been written.\n\t * @internal\n\t */\n\tprivate async writeGroup(group: ITelemetryMetricValuePayload[]): Promise<void> {\n\t\tconst metricValueStorage = this._metricValueStorage;\n\t\tif (Is.empty(metricValueStorage)) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst byMetric = new Map<string, ITelemetryMetricValuePayload[]>();\n\t\tfor (const entry of group) {\n\t\t\tlet metricGroup = byMetric.get(entry.metricId);\n\t\t\tif (Is.undefined(metricGroup)) {\n\t\t\t\tmetricGroup = [];\n\t\t\t\tbyMetric.set(entry.metricId, metricGroup);\n\t\t\t}\n\t\t\tmetricGroup.push(entry);\n\t\t}\n\n\t\tconst entities: TelemetryMetricValue[] = [];\n\t\tconst trimTargets = new Map<\n\t\t\tstring,\n\t\t\t{ metricId: string; maxHistory: number; contextIds: IContextIds }\n\t\t>();\n\n\t\tfor (const [metricId, metricEntries] of byMetric) {\n\t\t\t// A metric updated mid batch is capped at the value in force at its last write.\n\t\t\tlet cap: number | undefined;\n\t\t\tfor (const entry of metricEntries) {\n\t\t\t\tif (Is.integer(entry.maxHistory) && entry.maxHistory > 0) {\n\t\t\t\t\tcap = entry.maxHistory;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst metricKey = `${this.contextKey(metricEntries[0].contextIds ?? {})}|${metricId}`;\n\n\t\t\t// Only the counter types build on the value already stored; a gauge replaces it, so\n\t\t\t// it has nothing to read.\n\t\t\tconst isChained = metricEntries.some(\n\t\t\t\tentry =>\n\t\t\t\t\tentry.metricType === MetricType.Counter || entry.metricType === MetricType.IncDecCounter\n\t\t\t);\n\n\t\t\tlet lastTs: number | undefined;\n\t\t\tlet lastValue: number | undefined;\n\n\t\t\tif (isChained) {\n\t\t\t\t// Only the newest row is read: it seeds the chain and keeps the timestamps\n\t\t\t\t// increasing, which is everything a chained write needs. The chain always restarts\n\t\t\t\t// from storage, as other nodes write to the same partition and any value held in\n\t\t\t\t// memory here would already be out of date. The retention cap is applied by the\n\t\t\t\t// trim pass instead, so the cost of a write does not grow with the history behind\n\t\t\t\t// it.\n\t\t\t\tconst existingMetricValues = await metricValueStorage.query(\n\t\t\t\t\t{ property: \"metricId\", comparison: ComparisonOperator.Equals, value: metricId },\n\t\t\t\t\t[{ property: \"ts\", sortDirection: SortDirection.Descending }],\n\t\t\t\t\t[\"ts\", \"value\"],\n\t\t\t\t\tundefined,\n\t\t\t\t\t1\n\t\t\t\t);\n\t\t\t\tconst newest = existingMetricValues.entities[0] as TelemetryMetricValue | undefined;\n\n\t\t\t\tlastTs = newest?.ts;\n\t\t\t\tlastValue = newest?.value;\n\t\t\t}\n\n\t\t\tfor (const entry of metricEntries) {\n\t\t\t\tconst newValue = this.applyOperation(entry, lastValue ?? 0);\n\n\t\t\t\tif (Is.undefined(newValue)) {\n\t\t\t\t\tawait this._logging?.log({\n\t\t\t\t\t\tsource: MetricValueWriter.CLASS_NAME,\n\t\t\t\t\t\tmessage: \"invalidMetricOperation\",\n\t\t\t\t\t\tlevel: \"error\",\n\t\t\t\t\t\tdata: { id: metricId, type: entry.metricType, operation: entry.operation }\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\t// A chained metric keeps its timestamps strictly increasing, so the order its\n\t\t\t\t\t// values were written in is never ambiguous. A gauge builds on nothing, so it\n\t\t\t\t\t// is stored with the timestamp it arrived with.\n\t\t\t\t\tconst ts = isChained && Is.notEmpty(lastTs) ? Math.max(entry.ts, lastTs + 1) : entry.ts;\n\n\t\t\t\t\tentities.push({\n\t\t\t\t\t\tid: entry.valueId,\n\t\t\t\t\t\tmetricId,\n\t\t\t\t\t\tts,\n\t\t\t\t\t\tvalue: newValue,\n\t\t\t\t\t\tcustomData: entry.customData\n\t\t\t\t\t});\n\n\t\t\t\t\tlastTs = ts;\n\t\t\t\t\tlastValue = newValue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (Is.integer(cap)) {\n\t\t\t\ttrimTargets.set(metricKey, {\n\t\t\t\t\tmetricId,\n\t\t\t\t\tmaxHistory: cap,\n\t\t\t\t\tcontextIds: metricEntries[0].contextIds ?? {}\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tif (entities.length > 0) {\n\t\t\tawait metricValueStorage.setBatch(entities);\n\t\t}\n\n\t\t// Registered only once the values are stored, as the trim pass runs on its own timer: a\n\t\t// metric registered before the write lands reads a history still inside its cap, retires\n\t\t// itself from the map, and leaves the values arriving behind it uncapped until another\n\t\t// write registers the metric again.\n\t\tfor (const [key, target] of trimTargets) {\n\t\t\tthis._toTrim.set(key, target);\n\t\t}\n\t}\n\n\t/**\n\t * Remove the values of a metric which fall outside its retention cap.\n\t * @param metricId The metric id.\n\t * @param maxHistory The maximum number of values to retain.\n\t * @returns True when the history is back inside the cap, false when the removal limit was\n\t * reached first and there is more to remove.\n\t * @internal\n\t */\n\tprivate async trimMetric(metricId: string, maxHistory: number): Promise<boolean> {\n\t\tconst metricValueStorage = this._metricValueStorage;\n\t\tif (Is.empty(metricValueStorage)) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// The newest maxHistory values are the ones to keep, so finding the boundary costs the\n\t\t// cap rather than the length of the history however far past the cap it has grown.\n\t\t// Ordering on the value as well as the timestamp gives the boundary a discriminator for\n\t\t// the values which share a timestamp, which a gauge produces whenever two are set in the\n\t\t// same millisecond. Both columns are indexed.\n\t\tconst retained = await metricValueStorage.query(\n\t\t\t{ property: \"metricId\", comparison: ComparisonOperator.Equals, value: metricId },\n\t\t\t[\n\t\t\t\t{ property: \"ts\", sortDirection: SortDirection.Descending },\n\t\t\t\t{ property: \"value\", sortDirection: SortDirection.Descending }\n\t\t\t],\n\t\t\t[\"ts\", \"value\"],\n\t\t\tundefined,\n\t\t\tmaxHistory\n\t\t);\n\n\t\tif (retained.entities.length < maxHistory) {\n\t\t\treturn true;\n\t\t}\n\n\t\tconst boundary = retained.entities[maxHistory - 1] as TelemetryMetricValue;\n\n\t\t// Everything the boundary read did not reach, expressed the same way it was ordered, so\n\t\t// the pages below are a range rather than a scan the retained values have to be sifted\n\t\t// out of. Values matching the boundary on both columns are kept, which leaves the history\n\t\t// over the cap by however many of those there are until a later write moves the boundary.\n\t\tconst beyondCap: EntityCondition<TelemetryMetricValue> = {\n\t\t\tconditions: [\n\t\t\t\t{ property: \"metricId\", comparison: ComparisonOperator.Equals, value: metricId },\n\t\t\t\t{\n\t\t\t\t\tconditions: [\n\t\t\t\t\t\t{ property: \"ts\", comparison: ComparisonOperator.LessThan, value: boundary.ts },\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tconditions: [\n\t\t\t\t\t\t\t\t{ property: \"ts\", comparison: ComparisonOperator.Equals, value: boundary.ts },\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tproperty: \"value\",\n\t\t\t\t\t\t\t\t\tcomparison: ComparisonOperator.LessThan,\n\t\t\t\t\t\t\t\t\tvalue: boundary.value\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\tlogicalOperator: LogicalOperator.And\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\tlogicalOperator: LogicalOperator.Or\n\t\t\t\t}\n\t\t\t],\n\t\t\tlogicalOperator: LogicalOperator.And\n\t\t};\n\n\t\tlet removed = 0;\n\t\twhile (removed < this._trimRemoveLimit) {\n\t\t\tconst pageSize = Math.min(MetricValueWriter._TRIM_PAGE_SIZE, this._trimRemoveLimit - removed);\n\n\t\t\t// No cursor is needed, as each page is removed before the next is read.\n\t\t\tconst page = await metricValueStorage.query(\n\t\t\t\tbeyondCap,\n\t\t\t\t[{ property: \"ts\", sortDirection: SortDirection.Ascending }],\n\t\t\t\t[\"id\"],\n\t\t\t\tundefined,\n\t\t\t\tpageSize\n\t\t\t);\n\n\t\t\tif (page.entities.length === 0) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tawait metricValueStorage.removeBatch(page.entities.map(entity => entity.id as string));\n\t\t\tremoved += page.entities.length;\n\n\t\t\tif (page.entities.length < pageSize) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/**\n\t * Apply a metric operation to the previous value.\n\t * The connector validates the operation against the metric type before queueing it, so an\n\t * unsupported combination only reaches here if the payload was built elsewhere.\n\t * @param entry The entry being written.\n\t * @param lastValue The previous value for the metric.\n\t * @returns The new value, or undefined when the operation is not valid for the metric type.\n\t * @internal\n\t */\n\tprivate applyOperation(\n\t\tentry: ITelemetryMetricValuePayload,\n\t\tlastValue: number\n\t): number | undefined {\n\t\tif (entry.metricType === MetricType.Counter) {\n\t\t\tif (entry.operation === MetricCounterOperation.Increment) {\n\t\t\t\treturn lastValue + 1;\n\t\t\t}\n\t\t\tif (Is.integer(entry.operation) && entry.operation > 0) {\n\t\t\t\treturn lastValue + entry.operation;\n\t\t\t}\n\t\t} else if (entry.metricType === MetricType.IncDecCounter) {\n\t\t\tif (entry.operation === MetricCounterOperation.Increment) {\n\t\t\t\treturn lastValue + 1;\n\t\t\t}\n\t\t\tif (entry.operation === MetricCounterOperation.Decrement) {\n\t\t\t\treturn lastValue - 1;\n\t\t\t}\n\t\t\tif (Is.integer(entry.operation)) {\n\t\t\t\treturn lastValue + entry.operation;\n\t\t\t}\n\t\t} else if (Is.number(entry.operation)) {\n\t\t\treturn entry.operation;\n\t\t}\n\t}\n\n\t/**\n\t * Start the interval timer if batchIntervalMs is configured and the writer is running.\n\t * @internal\n\t */\n\tprivate startTimer(): void {\n\t\tif (!Is.empty(this._batchIntervalMs) && Is.empty(this._batchTimer) && this._started) {\n\t\t\tthis._batchTimer = globalThis.setTimeout(async () => {\n\t\t\t\t// Nothing is waiting on the interval write, so a failure has to be reported and\n\t\t\t\t// dropped here. An unhandled rejection in a timer takes down the worker thread,\n\t\t\t\t// and a thread which has gone is indistinguishable to the scheduler from one\n\t\t\t\t// still busy with a task, so it would never dispatch for this task type again.\n\t\t\t\ttry {\n\t\t\t\t\tawait this.flush();\n\t\t\t\t} catch (err) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait this._logging?.log({\n\t\t\t\t\t\t\tsource: MetricValueWriter.CLASS_NAME,\n\t\t\t\t\t\t\tmessage: \"intervalWriteFailed\",\n\t\t\t\t\t\t\tlevel: \"error\",\n\t\t\t\t\t\t\terror: BaseError.fromError(err)\n\t\t\t\t\t\t});\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// need to make sure a logging exception doesn't kill the timer\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}, this._batchIntervalMs);\n\t\t}\n\t}\n\n\t/**\n\t * Stop the interval timer if it is running.\n\t * @internal\n\t */\n\tprivate stopTimer(): void {\n\t\tif (!Is.empty(this._batchTimer)) {\n\t\t\tglobalThis.clearTimeout(this._batchTimer);\n\t\t\tthis._batchTimer = undefined;\n\t\t}\n\t}\n\n\t/**\n\t * Start the trim timer if trimIntervalMs is configured and the writer is running.\n\t * @internal\n\t */\n\tprivate startTrimTimer(): void {\n\t\tif (!Is.empty(this._trimIntervalMs) && Is.empty(this._trimTimer) && this._started) {\n\t\t\tthis._trimTimer = globalThis.setTimeout(async () => {\n\t\t\t\tthis._trimTimer = undefined;\n\t\t\t\ttry {\n\t\t\t\t\t// Deliberately not serialised with the writes: a trim only removes values\n\t\t\t\t\t// older than the cap and a write only reads the newest one and appends, so\n\t\t\t\t\t// the two cannot contend, and chaining them would put the trim back on the\n\t\t\t\t\t// path of the reads waiting for a flush.\n\t\t\t\t\tawait this.trim();\n\t\t\t\t} catch (err) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait this._logging?.log({\n\t\t\t\t\t\t\tsource: MetricValueWriter.CLASS_NAME,\n\t\t\t\t\t\t\tmessage: \"trimPassFailed\",\n\t\t\t\t\t\t\tlevel: \"error\",\n\t\t\t\t\t\t\terror: BaseError.fromError(err)\n\t\t\t\t\t\t});\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// need to make sure a logging exception doesn't kill the timer\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tthis.startTrimTimer();\n\t\t\t}, this._trimIntervalMs);\n\t\t}\n\t}\n\n\t/**\n\t * Stop the trim timer if it is running.\n\t * @internal\n\t */\n\tprivate stopTrimTimer(): void {\n\t\tif (!Is.empty(this._trimTimer)) {\n\t\t\tglobalThis.clearTimeout(this._trimTimer);\n\t\t\tthis._trimTimer = undefined;\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"metricValueWriter.js","sources":["../../src/metricValueWriter.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// Copyright 2026 3Sixty Trade Ltd.\n// SPDX-License-Identifier: Apache-2.0.\nimport { ContextIdKeys, ContextIdStore, type IContextIds } from \"@3sixty/context\";\nimport { BaseError, Coerce, ComponentFactory, DurationConstants, Is } from \"@3sixty/core\";\nimport {\n\tComparisonOperator,\n\ttype EntityCondition,\n\tLogicalOperator,\n\tSortDirection\n} from \"@3sixty/entity\";\nimport {\n\tEntityStorageConnectorFactory,\n\ttype IEntityStorageConnector\n} from \"@3sixty/entity-storage-models\";\nimport type { ILoggingComponent } from \"@3sixty/logging-models\";\n\nimport { MetricCounterOperation, MetricType } from \"@3sixty/telemetry-models\";\nimport type { TelemetryMetricValue } from \"./entities/telemetryMetricValue.js\";\nimport type { ITelemetryMetricValuePayload } from \"./models/ITelemetryMetricValuePayload.js\";\nimport type { ITelemetryMetricValueWriterConfig } from \"./models/ITelemetryMetricValueWriterConfig.js\";\n\n/**\n * Accumulates metric values and persists them to entity storage.\n * Runs on the background thread started by the telemetry connector. The value a counter\n * builds on is always read from storage rather than cached, which keeps the chain correct\n * when several load balanced nodes write to the same storage, and that read is of the newest\n * value alone, so the cost of a write does not grow with the history it is appending to. A\n * gauge replaces the stored value rather than building on it, so it is written without any\n * read at all. The retention cap is applied by the trim pass on its own interval.\n */\nexport class MetricValueWriter {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = \"MetricValueWriter\";\n\n\t/**\n\t * Default number of entries to accumulate before writing.\n\t */\n\tpublic static readonly DEFAULT_BATCH_SIZE: number = 10;\n\n\t/**\n\t * Default interval in milliseconds between automatic writes.\n\t */\n\tpublic static readonly DEFAULT_BATCH_INTERVAL_MS: number = 5 * DurationConstants.MsPerSecond;\n\n\t/**\n\t * Default maximum number of entries to hold when a write fails and the entries are re-queued.\n\t */\n\tpublic static readonly DEFAULT_MAX_CACHE_SIZE: number = 1000;\n\n\t/**\n\t * Default interval in milliseconds between trim passes.\n\t */\n\tpublic static readonly DEFAULT_TRIM_INTERVAL_MS: number = DurationConstants.MsPerMinute;\n\n\t/**\n\t * Default maximum number of values a single trim pass removes from one metric.\n\t */\n\tpublic static readonly DEFAULT_TRIM_REMOVE_LIMIT: number = 10000;\n\n\t/**\n\t * Page size used when removing the values which fall outside the retention cap.\n\t * Large enough to clear most overflows in one or two pages without overwhelming storage.\n\t * @internal\n\t */\n\tprivate static readonly _TRIM_PAGE_SIZE = 1000;\n\n\t/**\n\t * Entries waiting to be written to storage.\n\t * @internal\n\t */\n\tprivate readonly _pending: ITelemetryMetricValuePayload[];\n\n\t/**\n\t * The capped metrics written since the last trim pass, keyed by partition and metric id.\n\t * A metric is only removed once its history is back inside the cap, so an overflow larger\n\t * than a single pass can remove is finished by the passes which follow it.\n\t * @internal\n\t */\n\tprivate readonly _toTrim: Map<\n\t\tstring,\n\t\t{ metricId: string; maxHistory: number; contextIds: IContextIds }\n\t>;\n\n\t/**\n\t * The entity storage for the telemetry metric values, resolved when the writer is started.\n\t * @internal\n\t */\n\tprivate _metricValueStorage?: IEntityStorageConnector<TelemetryMetricValue>;\n\n\t/**\n\t * The component for logging.\n\t * @internal\n\t */\n\tprivate _logging?: ILoggingComponent;\n\n\t/**\n\t * Write when the pending entries reach this size; undefined or &lt;= 1 disables size based writing.\n\t * @internal\n\t */\n\tprivate _batchSize?: number;\n\n\t/**\n\t * Write every this many milliseconds; undefined or &lt;= 0 disables timer based writing.\n\t * @internal\n\t */\n\tprivate _batchIntervalMs?: number;\n\n\t/**\n\t * Maximum entries to keep after a failed write re-queue; 0 means unlimited.\n\t * @internal\n\t */\n\tprivate _maxCacheSize: number;\n\n\t/**\n\t * Run a trim pass every this many milliseconds; undefined disables trimming.\n\t * @internal\n\t */\n\tprivate _trimIntervalMs?: number;\n\n\t/**\n\t * Maximum values a single trim pass removes from one metric.\n\t * @internal\n\t */\n\tprivate _trimRemoveLimit: number;\n\n\t/**\n\t * Handle for the interval timer, present only while the writer is running.\n\t * @internal\n\t */\n\tprivate _batchTimer?: ReturnType<typeof setTimeout>;\n\n\t/**\n\t * Handle for the trim timer, present only while the writer is running.\n\t * @internal\n\t */\n\tprivate _trimTimer?: ReturnType<typeof setTimeout>;\n\n\t/**\n\t * Is a trim pass in progress, so the timer does not start a second one over it.\n\t * @internal\n\t */\n\tprivate _trimRunning: boolean;\n\n\t/**\n\t * Is the writer running.\n\t * @internal\n\t */\n\tprivate _started: boolean;\n\n\t/**\n\t * Serialises the writes issued by this instance, as writePending is only ever entered from\n\t * flush and an entry added after a write has taken its snapshot would otherwise be skipped\n\t * by a caller which simply joined it.\n\t * @internal\n\t */\n\tprivate _writeChain: Promise<void>;\n\n\t/**\n\t * Create a new instance of MetricValueWriter.\n\t */\n\tconstructor() {\n\t\tthis._pending = [];\n\t\tthis._toTrim = new Map();\n\t\tthis._maxCacheSize = MetricValueWriter.DEFAULT_MAX_CACHE_SIZE;\n\t\tthis._trimRemoveLimit = MetricValueWriter.DEFAULT_TRIM_REMOVE_LIMIT;\n\t\tthis._started = false;\n\t\tthis._trimRunning = false;\n\t\tthis._writeChain = Promise.resolve();\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn MetricValueWriter.CLASS_NAME;\n\t}\n\n\t/**\n\t * Resolve the storage connector and start the interval timer.\n\t * @param config The configuration for the writer.\n\t * @returns A promise that resolves when the writer is ready to accept metric values.\n\t */\n\tpublic async start(config?: ITelemetryMetricValueWriterConfig): Promise<void> {\n\t\tif (this._started) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis._metricValueStorage = EntityStorageConnectorFactory.get(\n\t\t\tconfig?.telemetryMetricValueStorageConnectorType ?? \"telemetry-metric-value\"\n\t\t);\n\t\tthis._logging = ComponentFactory.getIfExists(config?.loggingComponentType);\n\n\t\tconst cfgBatchSize = Coerce.integer(config?.batchSize) ?? MetricValueWriter.DEFAULT_BATCH_SIZE;\n\t\tthis._batchSize = cfgBatchSize > 1 ? cfgBatchSize : undefined;\n\n\t\tconst cfgIntervalMs =\n\t\t\tCoerce.integer(config?.batchIntervalMs) ?? MetricValueWriter.DEFAULT_BATCH_INTERVAL_MS;\n\t\tthis._batchIntervalMs = cfgIntervalMs > 0 ? cfgIntervalMs : undefined;\n\n\t\tconst cfgMaxCacheSize =\n\t\t\tCoerce.integer(config?.maxCacheSize) ?? MetricValueWriter.DEFAULT_MAX_CACHE_SIZE;\n\t\tthis._maxCacheSize = cfgMaxCacheSize > 0 ? cfgMaxCacheSize : 0;\n\n\t\tconst cfgTrimIntervalMs =\n\t\t\tCoerce.integer(config?.trimIntervalMs) ?? MetricValueWriter.DEFAULT_TRIM_INTERVAL_MS;\n\t\tthis._trimIntervalMs = cfgTrimIntervalMs > 0 ? cfgTrimIntervalMs : undefined;\n\n\t\tconst cfgTrimRemoveLimit =\n\t\t\tCoerce.integer(config?.trimRemoveLimit) ?? MetricValueWriter.DEFAULT_TRIM_REMOVE_LIMIT;\n\t\tthis._trimRemoveLimit =\n\t\t\tcfgTrimRemoveLimit > 0 ? cfgTrimRemoveLimit : MetricValueWriter.DEFAULT_TRIM_REMOVE_LIMIT;\n\n\t\tthis._started = true;\n\t\tthis.startTimer();\n\t\tthis.startTrimTimer();\n\t}\n\n\t/**\n\t * Write any remaining entries to storage and clear the timer.\n\t * @returns A promise that resolves when the final write completes and the timer is cleared.\n\t */\n\tpublic async stop(): Promise<void> {\n\t\tif (this._started) {\n\t\t\tthis._started = false;\n\t\t\tthis.stopTimer();\n\t\t\tthis.stopTrimTimer();\n\t\t}\n\t\tawait this.flush();\n\t}\n\n\t/**\n\t * Queue metric values to be written to storage.\n\t * @param values The metric value details.\n\t * @returns A promise that resolves when the values have been queued, or written when batching is disabled.\n\t */\n\tpublic async add(values: ITelemetryMetricValuePayload[]): Promise<void> {\n\t\t// push is synchronous, so there are no yield points and no race with the flush splice.\n\t\tthis._pending.push(...values);\n\n\t\tconst isBatching = !Is.empty(this._batchSize) || !Is.empty(this._batchIntervalMs);\n\t\tconst isSizeReached = !Is.empty(this._batchSize) && this._pending.length >= this._batchSize;\n\n\t\tif (!isBatching || isSizeReached) {\n\t\t\tawait this.flush();\n\t\t}\n\t}\n\n\t/**\n\t * Write all pending entries to storage and clear them.\n\t * Overlapping calls queue behind each other, as entries added after a write has taken its\n\t * snapshot would otherwise be skipped by a caller that simply joined it.\n\t * @returns A promise that resolves when all pending entries have been written to storage.\n\t */\n\tpublic async flush(): Promise<void> {\n\t\t// Take a place in the queue before waiting, so callers run in the order they arrived.\n\t\t// The handed on promise is only ever resolved, never rejected, so a failed write does\n\t\t// not stop the caller behind it.\n\t\tconst previous = this._writeChain;\n\t\tlet release: (() => void) | undefined;\n\t\tthis._writeChain = new Promise<void>(resolve => {\n\t\t\trelease = resolve;\n\t\t});\n\n\t\tawait previous;\n\n\t\ttry {\n\t\t\tawait this.writePending();\n\t\t} finally {\n\t\t\trelease?.();\n\t\t}\n\t}\n\n\t/**\n\t * Apply the retention cap to every capped metric written since the last pass.\n\t * Runs on its own interval rather than on the write path, and removes at most\n\t * trimRemoveLimit values per metric, so a history far beyond its cap is brought back over\n\t * several passes instead of stalling one write.\n\t * @returns A promise that resolves when the pass is complete.\n\t */\n\tpublic async trim(): Promise<void> {\n\t\tif (this._trimRunning) {\n\t\t\treturn;\n\t\t}\n\t\tthis._trimRunning = true;\n\n\t\ttry {\n\t\t\t// Snapshotted because a write landing during the pass adds to the map, and those\n\t\t\t// metrics are picked up by the next pass rather than extending this one.\n\t\t\tfor (const [key, target] of [...this._toTrim]) {\n\t\t\t\ttry {\n\t\t\t\t\tconst isComplete = await ContextIdStore.run(target.contextIds, async () =>\n\t\t\t\t\t\tthis.trimMetric(target.metricId, target.maxHistory)\n\t\t\t\t\t);\n\n\t\t\t\t\t// An incomplete pass leaves the metric registered so the next one continues it,\n\t\t\t\t\t// as does a write which landed during the pass: that registration is a new\n\t\t\t\t\t// entry for the same key, and the values it added may be ones the boundary\n\t\t\t\t\t// read of this pass could not see.\n\t\t\t\t\tif (isComplete && this._toTrim.get(key) === target) {\n\t\t\t\t\t\tthis._toTrim.delete(key);\n\t\t\t\t\t}\n\t\t\t\t} catch (err) {\n\t\t\t\t\tawait this._logging?.log({\n\t\t\t\t\t\tsource: MetricValueWriter.CLASS_NAME,\n\t\t\t\t\t\tmessage: \"trimFailed\",\n\t\t\t\t\t\tlevel: \"error\",\n\t\t\t\t\t\tdata: { id: target.metricId },\n\t\t\t\t\t\terror: BaseError.fromError(err)\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tthis._trimRunning = false;\n\t\t}\n\t}\n\n\t/**\n\t * Write the entries currently pending to storage.\n\t * On a storage write failure the entries are returned to the head of the queue for the next attempt.\n\t * @returns A promise that resolves when the pending entries have been written to storage.\n\t * @internal\n\t */\n\tprivate async writePending(): Promise<void> {\n\t\tthis.stopTimer();\n\n\t\tif (this._pending.length === 0) {\n\t\t\tthis.startTimer();\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tconst entries = this._pending.splice(0);\n\n\t\t\tconst groups = new Map<string, ITelemetryMetricValuePayload[]>();\n\t\t\tfor (const entry of entries) {\n\t\t\t\tconst contextGroupKey = this.contextKey(entry.contextIds ?? {});\n\t\t\t\tlet group = groups.get(contextGroupKey);\n\t\t\t\tif (Is.undefined(group)) {\n\t\t\t\t\tgroup = [];\n\t\t\t\t\tgroups.set(contextGroupKey, group);\n\t\t\t\t}\n\t\t\t\tgroup.push(entry);\n\t\t\t}\n\n\t\t\tconst failedEntries: ITelemetryMetricValuePayload[] = [];\n\t\t\tconst errors: unknown[] = [];\n\t\t\tfor (const group of groups.values()) {\n\t\t\t\ttry {\n\t\t\t\t\tawait ContextIdStore.run(group[0].contextIds ?? {}, async () => {\n\t\t\t\t\t\tawait this.writeGroup(group);\n\t\t\t\t\t});\n\t\t\t\t} catch (err) {\n\t\t\t\t\tfailedEntries.push(...group);\n\t\t\t\t\terrors.push(err);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (Is.arrayValue(errors)) {\n\t\t\t\t// Re-queued before the failure is reported, as the reporting writes to storage\n\t\t\t\t// too: a failure in it would otherwise take the entries with it.\n\t\t\t\tthis._pending.unshift(...failedEntries);\n\t\t\t\tif (this._maxCacheSize > 0 && this._pending.length > this._maxCacheSize) {\n\t\t\t\t\tthis._pending.splice(0, this._pending.length - this._maxCacheSize);\n\t\t\t\t}\n\n\t\t\t\tawait this._logging?.log({\n\t\t\t\t\tsource: MetricValueWriter.CLASS_NAME,\n\t\t\t\t\tmessage: \"flushFailed\",\n\t\t\t\t\tlevel: \"error\",\n\t\t\t\t\tdata: { error: errors.length === 1 ? errors[0] : errors }\n\t\t\t\t});\n\t\t\t}\n\t\t} finally {\n\t\t\t// Re-armed here rather than after the block, so a failure inside it cannot leave the\n\t\t\t// writer without its interval for the life of the thread.\n\t\t\tthis.startTimer();\n\t\t}\n\t}\n\n\t/**\n\t * Build the partition key used to group the pending entries before writing.\n\t * Only the node and tenant ids partition the storage, so the key is built from those two.\n\t * @param contextIds The context ids to derive the key from.\n\t * @returns The partition key.\n\t * @internal\n\t */\n\tprivate contextKey(contextIds: IContextIds): string {\n\t\treturn `${contextIds[ContextIdKeys.Node] ?? \"\"}|${contextIds[ContextIdKeys.Tenant] ?? \"\"}`;\n\t}\n\n\t/**\n\t * Write one partition worth of entries, chaining the counter values from the value\n\t * currently held in storage.\n\t * @param group The entries for a single partition, in the order they were added.\n\t * @returns A promise that resolves when the entries have been written.\n\t * @internal\n\t */\n\tprivate async writeGroup(group: ITelemetryMetricValuePayload[]): Promise<void> {\n\t\tconst metricValueStorage = this._metricValueStorage;\n\t\tif (Is.empty(metricValueStorage)) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst byMetric = new Map<string, ITelemetryMetricValuePayload[]>();\n\t\tfor (const entry of group) {\n\t\t\tlet metricGroup = byMetric.get(entry.metricId);\n\t\t\tif (Is.undefined(metricGroup)) {\n\t\t\t\tmetricGroup = [];\n\t\t\t\tbyMetric.set(entry.metricId, metricGroup);\n\t\t\t}\n\t\t\tmetricGroup.push(entry);\n\t\t}\n\n\t\tconst entities: TelemetryMetricValue[] = [];\n\t\tconst trimTargets = new Map<\n\t\t\tstring,\n\t\t\t{ metricId: string; maxHistory: number; contextIds: IContextIds }\n\t\t>();\n\n\t\tfor (const [metricId, metricEntries] of byMetric) {\n\t\t\t// A metric updated mid batch is capped at the value in force at its last write.\n\t\t\tlet cap: number | undefined;\n\t\t\tfor (const entry of metricEntries) {\n\t\t\t\tif (Is.integer(entry.maxHistory) && entry.maxHistory > 0) {\n\t\t\t\t\tcap = entry.maxHistory;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst metricKey = `${this.contextKey(metricEntries[0].contextIds ?? {})}|${metricId}`;\n\n\t\t\t// Only the counter types build on the value already stored; a gauge replaces it, so\n\t\t\t// it has nothing to read.\n\t\t\tconst isChained = metricEntries.some(\n\t\t\t\tentry =>\n\t\t\t\t\tentry.metricType === MetricType.Counter || entry.metricType === MetricType.IncDecCounter\n\t\t\t);\n\n\t\t\tlet lastTs: number | undefined;\n\t\t\tlet lastValue: number | undefined;\n\n\t\t\tif (isChained) {\n\t\t\t\t// Only the newest row is read: it seeds the chain and keeps the timestamps\n\t\t\t\t// increasing, which is everything a chained write needs. The chain always restarts\n\t\t\t\t// from storage, as other nodes write to the same partition and any value held in\n\t\t\t\t// memory here would already be out of date. The retention cap is applied by the\n\t\t\t\t// trim pass instead, so the cost of a write does not grow with the history behind\n\t\t\t\t// it.\n\t\t\t\tconst existingMetricValues = await metricValueStorage.query(\n\t\t\t\t\t{ property: \"metricId\", comparison: ComparisonOperator.Equals, value: metricId },\n\t\t\t\t\t[{ property: \"ts\", sortDirection: SortDirection.Descending }],\n\t\t\t\t\t[\"ts\", \"value\"],\n\t\t\t\t\tundefined,\n\t\t\t\t\t1\n\t\t\t\t);\n\t\t\t\tconst newest = existingMetricValues.entities[0] as TelemetryMetricValue | undefined;\n\n\t\t\t\tlastTs = newest?.ts;\n\t\t\t\tlastValue = newest?.value;\n\t\t\t}\n\n\t\t\tfor (const entry of metricEntries) {\n\t\t\t\tconst newValue = this.applyOperation(entry, lastValue ?? 0);\n\n\t\t\t\tif (Is.undefined(newValue)) {\n\t\t\t\t\tawait this._logging?.log({\n\t\t\t\t\t\tsource: MetricValueWriter.CLASS_NAME,\n\t\t\t\t\t\tmessage: \"invalidMetricOperation\",\n\t\t\t\t\t\tlevel: \"error\",\n\t\t\t\t\t\tdata: { id: metricId, type: entry.metricType, operation: entry.operation }\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\t// A chained metric keeps its timestamps strictly increasing, so the order its\n\t\t\t\t\t// values were written in is never ambiguous. A gauge builds on nothing, so it\n\t\t\t\t\t// is stored with the timestamp it arrived with.\n\t\t\t\t\tconst ts = isChained && Is.notEmpty(lastTs) ? Math.max(entry.ts, lastTs + 1) : entry.ts;\n\n\t\t\t\t\tentities.push({\n\t\t\t\t\t\tid: entry.valueId,\n\t\t\t\t\t\tmetricId,\n\t\t\t\t\t\tts,\n\t\t\t\t\t\tvalue: newValue,\n\t\t\t\t\t\tcustomData: entry.customData\n\t\t\t\t\t});\n\n\t\t\t\t\tlastTs = ts;\n\t\t\t\t\tlastValue = newValue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (Is.integer(cap)) {\n\t\t\t\ttrimTargets.set(metricKey, {\n\t\t\t\t\tmetricId,\n\t\t\t\t\tmaxHistory: cap,\n\t\t\t\t\tcontextIds: metricEntries[0].contextIds ?? {}\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tif (entities.length > 0) {\n\t\t\tawait metricValueStorage.setBatch(entities);\n\t\t}\n\n\t\t// Registered only once the values are stored, as the trim pass runs on its own timer: a\n\t\t// metric registered before the write lands reads a history still inside its cap, retires\n\t\t// itself from the map, and leaves the values arriving behind it uncapped until another\n\t\t// write registers the metric again.\n\t\tfor (const [key, target] of trimTargets) {\n\t\t\tthis._toTrim.set(key, target);\n\t\t}\n\t}\n\n\t/**\n\t * Remove the values of a metric which fall outside its retention cap.\n\t * @param metricId The metric id.\n\t * @param maxHistory The maximum number of values to retain.\n\t * @returns True when the history is back inside the cap, false when the removal limit was\n\t * reached first and there is more to remove.\n\t * @internal\n\t */\n\tprivate async trimMetric(metricId: string, maxHistory: number): Promise<boolean> {\n\t\tconst metricValueStorage = this._metricValueStorage;\n\t\tif (Is.empty(metricValueStorage)) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// The newest maxHistory values are the ones to keep, so finding the boundary costs the\n\t\t// cap rather than the length of the history however far past the cap it has grown.\n\t\t// Ordering on the value as well as the timestamp gives the boundary a discriminator for\n\t\t// the values which share a timestamp, which a gauge produces whenever two are set in the\n\t\t// same millisecond. Both columns are indexed.\n\t\tconst retained = await metricValueStorage.query(\n\t\t\t{ property: \"metricId\", comparison: ComparisonOperator.Equals, value: metricId },\n\t\t\t[\n\t\t\t\t{ property: \"ts\", sortDirection: SortDirection.Descending },\n\t\t\t\t{ property: \"value\", sortDirection: SortDirection.Descending }\n\t\t\t],\n\t\t\t[\"ts\", \"value\"],\n\t\t\tundefined,\n\t\t\tmaxHistory\n\t\t);\n\n\t\tif (retained.entities.length < maxHistory) {\n\t\t\treturn true;\n\t\t}\n\n\t\tconst boundary = retained.entities[maxHistory - 1] as TelemetryMetricValue;\n\n\t\t// Everything the boundary read did not reach, expressed the same way it was ordered, so\n\t\t// the pages below are a range rather than a scan the retained values have to be sifted\n\t\t// out of. Values matching the boundary on both columns are kept, which leaves the history\n\t\t// over the cap by however many of those there are until a later write moves the boundary.\n\t\tconst beyondCap: EntityCondition<TelemetryMetricValue> = {\n\t\t\tconditions: [\n\t\t\t\t{ property: \"metricId\", comparison: ComparisonOperator.Equals, value: metricId },\n\t\t\t\t{\n\t\t\t\t\tconditions: [\n\t\t\t\t\t\t{ property: \"ts\", comparison: ComparisonOperator.LessThan, value: boundary.ts },\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tconditions: [\n\t\t\t\t\t\t\t\t{ property: \"ts\", comparison: ComparisonOperator.Equals, value: boundary.ts },\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tproperty: \"value\",\n\t\t\t\t\t\t\t\t\tcomparison: ComparisonOperator.LessThan,\n\t\t\t\t\t\t\t\t\tvalue: boundary.value\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\tlogicalOperator: LogicalOperator.And\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\tlogicalOperator: LogicalOperator.Or\n\t\t\t\t}\n\t\t\t],\n\t\t\tlogicalOperator: LogicalOperator.And\n\t\t};\n\n\t\tlet removed = 0;\n\t\twhile (removed < this._trimRemoveLimit) {\n\t\t\tconst pageSize = Math.min(MetricValueWriter._TRIM_PAGE_SIZE, this._trimRemoveLimit - removed);\n\n\t\t\t// No cursor is needed, as each page is removed before the next is read.\n\t\t\tconst page = await metricValueStorage.query(\n\t\t\t\tbeyondCap,\n\t\t\t\t[{ property: \"ts\", sortDirection: SortDirection.Ascending }],\n\t\t\t\t[\"id\"],\n\t\t\t\tundefined,\n\t\t\t\tpageSize\n\t\t\t);\n\n\t\t\tif (page.entities.length === 0) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tawait metricValueStorage.removeBatch(page.entities.map(entity => entity.id as string));\n\t\t\tremoved += page.entities.length;\n\n\t\t\tif (page.entities.length < pageSize) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/**\n\t * Apply a metric operation to the previous value.\n\t * The connector validates the operation against the metric type before queueing it, so an\n\t * unsupported combination only reaches here if the payload was built elsewhere.\n\t * @param entry The entry being written.\n\t * @param lastValue The previous value for the metric.\n\t * @returns The new value, or undefined when the operation is not valid for the metric type.\n\t * @internal\n\t */\n\tprivate applyOperation(\n\t\tentry: ITelemetryMetricValuePayload,\n\t\tlastValue: number\n\t): number | undefined {\n\t\tif (entry.metricType === MetricType.Counter) {\n\t\t\tif (entry.operation === MetricCounterOperation.Increment) {\n\t\t\t\treturn lastValue + 1;\n\t\t\t}\n\t\t\tif (Is.integer(entry.operation) && entry.operation > 0) {\n\t\t\t\treturn lastValue + entry.operation;\n\t\t\t}\n\t\t} else if (entry.metricType === MetricType.IncDecCounter) {\n\t\t\tif (entry.operation === MetricCounterOperation.Increment) {\n\t\t\t\treturn lastValue + 1;\n\t\t\t}\n\t\t\tif (entry.operation === MetricCounterOperation.Decrement) {\n\t\t\t\treturn lastValue - 1;\n\t\t\t}\n\t\t\tif (Is.integer(entry.operation)) {\n\t\t\t\treturn lastValue + entry.operation;\n\t\t\t}\n\t\t} else if (Is.number(entry.operation)) {\n\t\t\treturn entry.operation;\n\t\t}\n\t}\n\n\t/**\n\t * Start the interval timer if batchIntervalMs is configured and the writer is running.\n\t * @internal\n\t */\n\tprivate startTimer(): void {\n\t\tif (!Is.empty(this._batchIntervalMs) && Is.empty(this._batchTimer) && this._started) {\n\t\t\tthis._batchTimer = globalThis.setTimeout(async () => {\n\t\t\t\t// Nothing is waiting on the interval write, so a failure has to be reported and\n\t\t\t\t// dropped here. An unhandled rejection in a timer takes down the worker thread,\n\t\t\t\t// and a thread which has gone is indistinguishable to the scheduler from one\n\t\t\t\t// still busy with a task, so it would never dispatch for this task type again.\n\t\t\t\ttry {\n\t\t\t\t\tawait this.flush();\n\t\t\t\t} catch (err) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait this._logging?.log({\n\t\t\t\t\t\t\tsource: MetricValueWriter.CLASS_NAME,\n\t\t\t\t\t\t\tmessage: \"intervalWriteFailed\",\n\t\t\t\t\t\t\tlevel: \"error\",\n\t\t\t\t\t\t\terror: BaseError.fromError(err)\n\t\t\t\t\t\t});\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// need to make sure a logging exception doesn't kill the timer\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}, this._batchIntervalMs);\n\t\t}\n\t}\n\n\t/**\n\t * Stop the interval timer if it is running.\n\t * @internal\n\t */\n\tprivate stopTimer(): void {\n\t\tif (!Is.empty(this._batchTimer)) {\n\t\t\tglobalThis.clearTimeout(this._batchTimer);\n\t\t\tthis._batchTimer = undefined;\n\t\t}\n\t}\n\n\t/**\n\t * Start the trim timer if trimIntervalMs is configured and the writer is running.\n\t * @internal\n\t */\n\tprivate startTrimTimer(): void {\n\t\tif (!Is.empty(this._trimIntervalMs) && Is.empty(this._trimTimer) && this._started) {\n\t\t\tthis._trimTimer = globalThis.setTimeout(async () => {\n\t\t\t\tthis._trimTimer = undefined;\n\t\t\t\ttry {\n\t\t\t\t\t// Deliberately not serialised with the writes: a trim only removes values\n\t\t\t\t\t// older than the cap and a write only reads the newest one and appends, so\n\t\t\t\t\t// the two cannot contend, and chaining them would put the trim back on the\n\t\t\t\t\t// path of the reads waiting for a flush.\n\t\t\t\t\tawait this.trim();\n\t\t\t\t} catch (err) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait this._logging?.log({\n\t\t\t\t\t\t\tsource: MetricValueWriter.CLASS_NAME,\n\t\t\t\t\t\t\tmessage: \"trimPassFailed\",\n\t\t\t\t\t\t\tlevel: \"error\",\n\t\t\t\t\t\t\terror: BaseError.fromError(err)\n\t\t\t\t\t\t});\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// need to make sure a logging exception doesn't kill the timer\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tthis.startTrimTimer();\n\t\t\t}, this._trimIntervalMs);\n\t\t}\n\t}\n\n\t/**\n\t * Stop the trim timer if it is running.\n\t * @internal\n\t */\n\tprivate stopTrimTimer(): void {\n\t\tif (!Is.empty(this._trimTimer)) {\n\t\t\tglobalThis.clearTimeout(this._trimTimer);\n\t\t\tthis._trimTimer = undefined;\n\t\t}\n\t}\n}\n"]}
package/docs/changelog.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.20.0-next.3](https://github.com/3sixtyglobal/telemetry/compare/telemetry-connector-entity-storage-v0.20.0-next.2...telemetry-connector-entity-storage-v0.20.0-next.3) (2026-11-03)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * require the telemetry component in the metrics collector service ([#25](https://github.com/3sixtyglobal/telemetry/issues/25)) ([2fd5170](https://github.com/3sixtyglobal/telemetry/commit/2fd5170fa0c9fb7a958c49edc0eee123bb2806e3))
9
+
10
+
11
+ ### Dependencies
12
+
13
+ * The following workspace dependencies were updated
14
+ * dependencies
15
+ * @3sixty/telemetry-models bumped from 0.20.0-next.2 to 0.20.0-next.3
16
+
3
17
  ## [0.20.0-next.2](https://github.com/3sixtyglobal/telemetry/compare/telemetry-connector-entity-storage-v0.20.0-next.1...telemetry-connector-entity-storage-v0.20.0-next.2) (2026-10-26)
4
18
 
5
19
 
@@ -49,7 +49,7 @@ Runtime name for the class.
49
49
 
50
50
  ### DEFAULT\_FLUSH\_TIMEOUT\_MS {#default_flush_timeout_ms}
51
51
 
52
- > `readonly` `static` **DEFAULT\_FLUSH\_TIMEOUT\_MS**: `number` = `30_000`
52
+ > `readonly` `static` **DEFAULT\_FLUSH\_TIMEOUT\_MS**: `number`
53
53
 
54
54
  Default time in milliseconds to wait for the background thread to confirm a flush.
55
55
 
@@ -65,7 +65,7 @@ Maximum number of metric definitions to hold in the in-memory definition cache.
65
65
 
66
66
  ### DEFAULT\_METRIC\_DEFINITION\_CACHE\_TTI\_MS {#default_metric_definition_cache_tti_ms}
67
67
 
68
- > `readonly` `static` **DEFAULT\_METRIC\_DEFINITION\_CACHE\_TTI\_MS**: `number` = `3_600_000`
68
+ > `readonly` `static` **DEFAULT\_METRIC\_DEFINITION\_CACHE\_TTI\_MS**: `number` = `DurationConstants.MsPerHour`
69
69
 
70
70
  Time-to-idle in milliseconds for cached metric definitions.
71
71
  Metric definitions are immutable once registered; a long TTI keeps active
@@ -75,7 +75,7 @@ metrics cached without permanent references.
75
75
 
76
76
  ### DEFAULT\_TASK\_COALESCE\_MS {#default_task_coalesce_ms}
77
77
 
78
- > `readonly` `static` **DEFAULT\_TASK\_COALESCE\_MS**: `number` = `1000`
78
+ > `readonly` `static` **DEFAULT\_TASK\_COALESCE\_MS**: `number` = `DurationConstants.MsPerSecond`
79
79
 
80
80
  Default time in milliseconds values are held so several share a single background task.
81
81
  Keeps the task write off the caller's path, which matters most for the metrics recorded
@@ -102,7 +102,7 @@ the scheduler can drain.
102
102
 
103
103
  ### DEFAULT\_TASK\_STALL\_TIMEOUT\_MS {#default_task_stall_timeout_ms}
104
104
 
105
- > `readonly` `static` **DEFAULT\_TASK\_STALL\_TIMEOUT\_MS**: `number` = `60_000`
105
+ > `readonly` `static` **DEFAULT\_TASK\_STALL\_TIMEOUT\_MS**: `number` = `DurationConstants.MsPerMinute`
106
106
 
107
107
  Default time in milliseconds with tasks outstanding and none of them completing before
108
108
  the background thread is treated as stalled.
@@ -40,7 +40,7 @@ Default number of entries to accumulate before writing.
40
40
 
41
41
  ### DEFAULT\_BATCH\_INTERVAL\_MS {#default_batch_interval_ms}
42
42
 
43
- > `readonly` `static` **DEFAULT\_BATCH\_INTERVAL\_MS**: `number` = `5000`
43
+ > `readonly` `static` **DEFAULT\_BATCH\_INTERVAL\_MS**: `number`
44
44
 
45
45
  Default interval in milliseconds between automatic writes.
46
46
 
@@ -56,7 +56,7 @@ Default maximum number of entries to hold when a write fails and the entries are
56
56
 
57
57
  ### DEFAULT\_TRIM\_INTERVAL\_MS {#default_trim_interval_ms}
58
58
 
59
- > `readonly` `static` **DEFAULT\_TRIM\_INTERVAL\_MS**: `number` = `60000`
59
+ > `readonly` `static` **DEFAULT\_TRIM\_INTERVAL\_MS**: `number` = `DurationConstants.MsPerMinute`
60
60
 
61
61
  Default interval in milliseconds between trim passes.
62
62
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@3sixty/telemetry-connector-entity-storage",
3
- "version": "0.20.0-next.2",
3
+ "version": "0.20.0-next.3",
4
4
  "description": "Entity storage connector for persisting telemetry metrics.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -23,7 +23,7 @@
23
23
  "@3sixty/logging-models": "next",
24
24
  "@3sixty/modules": "next",
25
25
  "@3sixty/nameof": "next",
26
- "@3sixty/telemetry-models": "0.20.0-next.2"
26
+ "@3sixty/telemetry-models": "0.20.0-next.3"
27
27
  },
28
28
  "main": "./dist/es/index.js",
29
29
  "types": "./dist/types/index.d.ts",