@oneuptime/common 11.5.13 → 11.6.1

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.
Files changed (156) hide show
  1. package/Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel.ts +41 -24
  2. package/Models/AnalyticsModels/MetricBaselineHourly.ts +2 -0
  3. package/Models/AnalyticsModels/MetricItemAggMV1m.ts +5 -2
  4. package/Models/AnalyticsModels/MetricItemAggMV1mByContainer.ts +5 -2
  5. package/Models/AnalyticsModels/MetricItemAggMV1mByHostV2.ts +5 -2
  6. package/Models/AnalyticsModels/MetricItemAggMV1mByK8sCluster.ts +5 -2
  7. package/Models/AnalyticsModels/MetricItemAggMV1mByService.ts +5 -2
  8. package/Models/DatabaseModels/EnterpriseLicense.ts +21 -0
  9. package/Models/DatabaseModels/EnterpriseLicenseInstance.ts +25 -0
  10. package/Models/DatabaseModels/GlobalConfig.ts +76 -0
  11. package/Server/API/EnterpriseLicenseAPI.ts +57 -0
  12. package/Server/API/GlobalConfigAPI.ts +62 -1
  13. package/Server/EnvironmentConfig.ts +22 -0
  14. package/Server/Infrastructure/Postgres/SchemaMigrations/1784659816363-AddInstanceVersionAndLatestReleaseColumns.ts +38 -0
  15. package/Server/Infrastructure/Postgres/SchemaMigrations/1784705487674-AddEnterpriseLicenseEvaluationColumns.ts +25 -0
  16. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
  17. package/Server/Services/AnalyticsDatabaseService.ts +61 -46
  18. package/Server/Services/DatabaseService.ts +66 -1
  19. package/Server/Services/WorkflowService.ts +100 -39
  20. package/Server/Types/Workflow/Components/Schedule.ts +57 -32
  21. package/Server/Utils/AnalyticsDatabase/InsertDedupContext.ts +59 -0
  22. package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +41 -4
  23. package/Server/Utils/Express.ts +13 -0
  24. package/Server/Utils/Telemetry/TelemetryFanInWriter.ts +799 -0
  25. package/Server/Utils/Telemetry/TelemetryWriterClient.ts +263 -0
  26. package/Server/Utils/Telemetry/TelemetryWriterServer.ts +240 -0
  27. package/Server/Utils/Telemetry/TelemetryWriterShedMetrics.ts +93 -0
  28. package/Tests/Models/AnalyticsModels/AnalyticsBaseModel.test.ts +122 -0
  29. package/Tests/Server/Services/DatabaseServiceSanitizeUpdateData.test.ts +232 -0
  30. package/Tests/Server/Services/UpdateOneByIdKeepsRowId.test.ts +107 -0
  31. package/Tests/Server/Services/WorkflowService.test.ts +308 -0
  32. package/Tests/Server/Utils/AnalyticsDatabase/ClusterAwareSchema.test.ts +164 -3
  33. package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +99 -0
  34. package/Tests/Server/Utils/Attribution.test.ts +203 -0
  35. package/Tests/Server/Utils/Marketing/ConversionUploadProvider.test.ts +183 -0
  36. package/Tests/Server/Utils/Marketing/GoogleAds.test.ts +340 -0
  37. package/Tests/Server/Utils/Monitor/Criteria/ExceptionMonitorCriteria.test.ts +87 -0
  38. package/Tests/Server/Utils/Monitor/Criteria/LogMonitorCriteria.test.ts +165 -0
  39. package/Tests/Server/Utils/Monitor/Criteria/TraceMonitorCriteria.test.ts +86 -0
  40. package/Tests/Server/Utils/Telemetry/TelemetryFanInWriter.test.ts +1565 -0
  41. package/Tests/Server/Utils/Telemetry/TelemetryWriterClient.test.ts +313 -0
  42. package/Tests/Server/Utils/Telemetry/TelemetryWriterServer.test.ts +270 -0
  43. package/Tests/Server/Utils/Telemetry/TelemetryWriterShedMetrics.test.ts +127 -0
  44. package/Tests/Types/AI/CodeFixTaskType.test.ts +3 -0
  45. package/Tests/Types/HashCode.test.ts +41 -0
  46. package/Tests/Types/Log/LogQueryParser.test.ts +186 -0
  47. package/Tests/Types/Monitor/MonitorCriteriaMetricVariables.test.ts +228 -0
  48. package/Tests/Types/Monitor/MonitorStepDefaultTelemetryConfig.test.ts +169 -0
  49. package/Tests/Types/Monitor/MonitorStepExceptionMonitor.test.ts +289 -0
  50. package/Tests/Types/Monitor/MonitorStepLogMonitor.test.ts +231 -0
  51. package/Tests/Types/Monitor/MonitorStepMetricViewConfigUtil.test.ts +245 -0
  52. package/Tests/Types/Monitor/MonitorStepNetworkDeviceMonitor.test.ts +155 -0
  53. package/Tests/Types/Monitor/MonitorStepTelemetrySerialization.test.ts +141 -0
  54. package/Tests/Types/Monitor/MonitorStepTraceMonitor.test.ts +137 -0
  55. package/Tests/Types/Workspace/NotificationRules/NotificationRuleCondition.test.ts +311 -0
  56. package/Tests/UI/Utils/Breadcrumb/BreadcrumbTrailResolver.test.ts +461 -0
  57. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +2852 -0
  58. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +758 -0
  59. package/Tests/UI/Utils/NotificationMethodUtil.test.ts +564 -0
  60. package/Tests/Utils/Array.test.ts +156 -0
  61. package/Tests/Utils/CronTab.test.ts +199 -0
  62. package/Tests/Utils/Dashboard/VariableInterpolation.test.ts +197 -0
  63. package/Tests/Utils/ModelImportExport.test.ts +101 -0
  64. package/Tests/Utils/Number.test.ts +179 -0
  65. package/Tests/Utils/ValueFormatter.test.ts +305 -0
  66. package/Tests/Utils/VersionUtil.test.ts +348 -0
  67. package/Tests/jest.setup.ts +1 -0
  68. package/Types/AnalyticsDatabase/TableColumn.ts +23 -0
  69. package/Types/EnterpriseLicense/EnterpriseLicenseInstanceSummary.ts +6 -0
  70. package/Types/Monitor/MonitorStep.ts +24 -4
  71. package/Types/Monitor/MonitorStepMetricViewConfigUtil.ts +105 -0
  72. package/UI/Components/EditionLabel/EditionLabel.tsx +445 -12
  73. package/UI/Components/Workflow/ArgumentsForm.tsx +68 -30
  74. package/UI/Components/Workflow/CronScheduleField.tsx +411 -0
  75. package/UI/Utils/Breadcrumb/BreadcrumbTrailResolver.ts +176 -0
  76. package/UI/Utils/NotificationMethodUtil.ts +310 -0
  77. package/Utils/CronTab.ts +823 -0
  78. package/Utils/ModelImportExport.ts +21 -6
  79. package/Utils/VersionUtil.ts +260 -0
  80. package/build/dist/Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel.js +25 -20
  81. package/build/dist/Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel.js.map +1 -1
  82. package/build/dist/Models/AnalyticsModels/MetricBaselineHourly.js +2 -0
  83. package/build/dist/Models/AnalyticsModels/MetricBaselineHourly.js.map +1 -1
  84. package/build/dist/Models/AnalyticsModels/MetricItemAggMV1m.js +5 -2
  85. package/build/dist/Models/AnalyticsModels/MetricItemAggMV1m.js.map +1 -1
  86. package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByContainer.js +5 -2
  87. package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByContainer.js.map +1 -1
  88. package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByHostV2.js +5 -2
  89. package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByHostV2.js.map +1 -1
  90. package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByK8sCluster.js +5 -2
  91. package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByK8sCluster.js.map +1 -1
  92. package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByService.js +5 -2
  93. package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByService.js.map +1 -1
  94. package/build/dist/Models/DatabaseModels/EnterpriseLicense.js +22 -0
  95. package/build/dist/Models/DatabaseModels/EnterpriseLicense.js.map +1 -1
  96. package/build/dist/Models/DatabaseModels/EnterpriseLicenseInstance.js +26 -0
  97. package/build/dist/Models/DatabaseModels/EnterpriseLicenseInstance.js.map +1 -1
  98. package/build/dist/Models/DatabaseModels/GlobalConfig.js +81 -0
  99. package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
  100. package/build/dist/Server/API/EnterpriseLicenseAPI.js +47 -0
  101. package/build/dist/Server/API/EnterpriseLicenseAPI.js.map +1 -1
  102. package/build/dist/Server/API/GlobalConfigAPI.js +52 -1
  103. package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
  104. package/build/dist/Server/EnvironmentConfig.js +17 -0
  105. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  106. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784659816363-AddInstanceVersionAndLatestReleaseColumns.js +18 -0
  107. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784659816363-AddInstanceVersionAndLatestReleaseColumns.js.map +1 -0
  108. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784705487674-AddEnterpriseLicenseEvaluationColumns.js +14 -0
  109. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784705487674-AddEnterpriseLicenseEvaluationColumns.js.map +1 -0
  110. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
  111. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  112. package/build/dist/Server/Services/AnalyticsDatabaseService.js +53 -17
  113. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  114. package/build/dist/Server/Services/DatabaseService.js +53 -1
  115. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  116. package/build/dist/Server/Services/WorkflowService.js +80 -30
  117. package/build/dist/Server/Services/WorkflowService.js.map +1 -1
  118. package/build/dist/Server/Types/Workflow/Components/Schedule.js +44 -19
  119. package/build/dist/Server/Types/Workflow/Components/Schedule.js.map +1 -1
  120. package/build/dist/Server/Utils/AnalyticsDatabase/InsertDedupContext.js +24 -0
  121. package/build/dist/Server/Utils/AnalyticsDatabase/InsertDedupContext.js.map +1 -0
  122. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +29 -4
  123. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
  124. package/build/dist/Server/Utils/Express.js +12 -0
  125. package/build/dist/Server/Utils/Express.js.map +1 -1
  126. package/build/dist/Server/Utils/Telemetry/TelemetryFanInWriter.js +496 -0
  127. package/build/dist/Server/Utils/Telemetry/TelemetryFanInWriter.js.map +1 -0
  128. package/build/dist/Server/Utils/Telemetry/TelemetryWriterClient.js +165 -0
  129. package/build/dist/Server/Utils/Telemetry/TelemetryWriterClient.js.map +1 -0
  130. package/build/dist/Server/Utils/Telemetry/TelemetryWriterServer.js +136 -0
  131. package/build/dist/Server/Utils/Telemetry/TelemetryWriterServer.js.map +1 -0
  132. package/build/dist/Server/Utils/Telemetry/TelemetryWriterShedMetrics.js +76 -0
  133. package/build/dist/Server/Utils/Telemetry/TelemetryWriterShedMetrics.js.map +1 -0
  134. package/build/dist/Types/AnalyticsDatabase/TableColumn.js +7 -0
  135. package/build/dist/Types/AnalyticsDatabase/TableColumn.js.map +1 -1
  136. package/build/dist/Types/Monitor/MonitorStep.js +20 -4
  137. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  138. package/build/dist/Types/Monitor/MonitorStepMetricViewConfigUtil.js +73 -0
  139. package/build/dist/Types/Monitor/MonitorStepMetricViewConfigUtil.js.map +1 -0
  140. package/build/dist/UI/Components/EditionLabel/EditionLabel.js +261 -13
  141. package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
  142. package/build/dist/UI/Components/Workflow/ArgumentsForm.js +30 -6
  143. package/build/dist/UI/Components/Workflow/ArgumentsForm.js.map +1 -1
  144. package/build/dist/UI/Components/Workflow/CronScheduleField.js +209 -0
  145. package/build/dist/UI/Components/Workflow/CronScheduleField.js.map +1 -0
  146. package/build/dist/UI/Utils/Breadcrumb/BreadcrumbTrailResolver.js +129 -0
  147. package/build/dist/UI/Utils/Breadcrumb/BreadcrumbTrailResolver.js.map +1 -0
  148. package/build/dist/UI/Utils/NotificationMethodUtil.js +189 -0
  149. package/build/dist/UI/Utils/NotificationMethodUtil.js.map +1 -0
  150. package/build/dist/Utils/CronTab.js +609 -0
  151. package/build/dist/Utils/CronTab.js.map +1 -0
  152. package/build/dist/Utils/ModelImportExport.js +20 -6
  153. package/build/dist/Utils/ModelImportExport.js.map +1 -1
  154. package/build/dist/Utils/VersionUtil.js +193 -0
  155. package/build/dist/Utils/VersionUtil.js.map +1 -0
  156. package/package.json +1 -1
@@ -15,6 +15,7 @@ import ComponentID from "../../../../Types/Workflow/ComponentID";
15
15
  import ScheduleComponents from "../../../../Types/Workflow/Components/Schedule";
16
16
  import Workflow from "../../../../Models/DatabaseModels/Workflow";
17
17
  import CaptureSpan from "../../../Utils/Telemetry/CaptureSpan";
18
+ import logger from "../../../Utils/Logger";
18
19
 
19
20
  export default class WebhookTrigger extends TriggerCode {
20
21
  public constructor() {
@@ -52,24 +53,37 @@ export default class WebhookTrigger extends TriggerCode {
52
53
 
53
54
  // query all workflows.
54
55
  for (const workflow of workflows) {
55
- const executeWorkflow: ExecuteWorkflowType = {
56
- workflowId: new ObjectID(workflow._id!),
57
- returnValues: {},
58
- };
59
-
60
- if (
61
- workflow.triggerArguments &&
62
- workflow.triggerArguments["schedule"] &&
63
- workflow.isEnabled
64
- ) {
65
- await props.scheduleWorkflow(
66
- executeWorkflow,
67
- workflow.triggerArguments["schedule"] as string,
56
+ /*
57
+ * Isolate each workflow: a single workflow whose schedule can't be
58
+ * registered (e.g. an invalid cron, or a transient queue error) must not
59
+ * abort the startup scan and leave every other scheduled workflow
60
+ * unregistered.
61
+ */
62
+ try {
63
+ const executeWorkflow: ExecuteWorkflowType = {
64
+ workflowId: new ObjectID(workflow._id!),
65
+ returnValues: {},
66
+ };
67
+
68
+ if (
69
+ workflow.triggerArguments &&
70
+ workflow.triggerArguments["schedule"] &&
71
+ workflow.isEnabled
72
+ ) {
73
+ await props.scheduleWorkflow(
74
+ executeWorkflow,
75
+ workflow.triggerArguments["schedule"] as string,
76
+ );
77
+ }
78
+
79
+ if (!workflow.isEnabled) {
80
+ await props.removeWorkflow(workflow.id!);
81
+ }
82
+ } catch (err) {
83
+ logger.error(
84
+ `Failed to register schedule for workflow ${workflow._id}`,
68
85
  );
69
- }
70
-
71
- if (!workflow.isEnabled) {
72
- await props.removeWorkflow(workflow.id!);
86
+ logger.error(err);
73
87
  }
74
88
  }
75
89
  }
@@ -128,23 +142,34 @@ export default class WebhookTrigger extends TriggerCode {
128
142
  returnValues: {},
129
143
  };
130
144
 
131
- if (
132
- workflow.triggerArguments &&
133
- workflow.triggerArguments["schedule"] &&
134
- workflow.isEnabled
135
- ) {
136
- await this.scheduleWorkflow(
137
- executeWorkflow,
138
- workflow.triggerArguments["schedule"] as string,
139
- );
140
- }
145
+ /*
146
+ * Guard the (re)registration so a bad schedule can't fail the workflow
147
+ * save that triggered this update. Invalid crons are already surfaced as a
148
+ * workflow log by the queue layer; this catch is a backstop for unexpected
149
+ * errors (e.g. a transient queue error).
150
+ */
151
+ try {
152
+ if (
153
+ workflow.triggerArguments &&
154
+ workflow.triggerArguments["schedule"] &&
155
+ workflow.isEnabled
156
+ ) {
157
+ await this.scheduleWorkflow(
158
+ executeWorkflow,
159
+ workflow.triggerArguments["schedule"] as string,
160
+ );
161
+ }
141
162
 
142
- if (!this.removeWorkflow) {
143
- return;
144
- }
163
+ if (!this.removeWorkflow) {
164
+ return;
165
+ }
145
166
 
146
- if (!workflow.isEnabled) {
147
- await this.removeWorkflow(workflow.id!);
167
+ if (!workflow.isEnabled) {
168
+ await this.removeWorkflow(workflow.id!);
169
+ }
170
+ } catch (err) {
171
+ logger.error(`Failed to update schedule for workflow ${workflow._id}`);
172
+ logger.error(err);
148
173
  }
149
174
  }
150
175
  }
@@ -0,0 +1,59 @@
1
+ import { AsyncLocalStorage } from "async_hooks";
2
+
3
+ /**
4
+ * Ambient context that makes ClickHouse inserts idempotent across queue
5
+ * retries. The telemetry queue worker wraps each job in
6
+ * `runWithInsertDedup(jobId, ...)`; token consumers then derive
7
+ * `insert_deduplication_token = "<tokenBase>:<table>:<chunkIndex>"`, so a
8
+ * stalled-job recovery or attempts-based retry that re-processes the same
9
+ * payload re-issues byte-identical tokens and ClickHouse drops the duplicate
10
+ * blocks (on replicated tables; on plain MergeTree the token is ignored
11
+ * unless non_replicated_deduplication_window is set — no harm either way).
12
+ * The chunk counter is per table because one job inserts into several tables
13
+ * (e.g. Span + ExceptionInstance) in a deterministic order.
14
+ *
15
+ * Two consumers exist:
16
+ * - AnalyticsDatabaseService.insertJsonRows stamps direct inserts that run
17
+ * inside the context without an explicit token.
18
+ * - TelemetryFanInWriter captures a token per submission AT SUBMIT TIME
19
+ * (still inside the job's context) so cross-job batching downstream can
20
+ * preserve per-job retry idempotence.
21
+ */
22
+ export interface InsertDedupContextStore {
23
+ tokenBase: string;
24
+ chunkIndexByTable: Map<string, number>;
25
+ }
26
+
27
+ const insertDedupContext: AsyncLocalStorage<InsertDedupContextStore> =
28
+ new AsyncLocalStorage<InsertDedupContextStore>();
29
+
30
+ export function runWithInsertDedup<T>(
31
+ tokenBase: string,
32
+ fn: () => Promise<T>,
33
+ ): Promise<T> {
34
+ return insertDedupContext.run(
35
+ { tokenBase, chunkIndexByTable: new Map<string, number>() },
36
+ fn,
37
+ );
38
+ }
39
+
40
+ /**
41
+ * Consume the next deterministic dedup token for `tableName` from the
42
+ * ambient context, or undefined when called outside a runWithInsertDedup
43
+ * scope. Consuming advances the per-table chunk counter, so calls must
44
+ * happen in a deterministic order relative to the payload — true for the
45
+ * ingest services, whose chunking is a pure function of the (byte-identical
46
+ * across retries) job body.
47
+ */
48
+ export function nextInsertDedupToken(tableName: string): string | undefined {
49
+ const store: InsertDedupContextStore | undefined =
50
+ insertDedupContext.getStore();
51
+
52
+ if (!store) {
53
+ return undefined;
54
+ }
55
+
56
+ const chunkIndex: number = store.chunkIndexByTable.get(tableName) ?? 0;
57
+ store.chunkIndexByTable.set(tableName, chunkIndex + 1);
58
+ return `${store.tokenBase}:${tableName}:${chunkIndex}`;
59
+ }
@@ -69,6 +69,8 @@ export interface EntityScopeQueryValue {
69
69
  attributeValue: string;
70
70
  }
71
71
 
72
+ const SIMPLE_AGGREGATE_FUNCTION_NAME_REGEX: RegExp = /^[A-Za-z_][A-Za-z0-9_]*$/;
73
+
72
74
  export default class StatementGenerator<TBaseModel extends AnalyticsBaseModel> {
73
75
  public model!: TBaseModel;
74
76
  public modelType!: { new (): TBaseModel };
@@ -1361,6 +1363,32 @@ export default class StatementGenerator<TBaseModel extends AnalyticsBaseModel> {
1361
1363
  * column. Scalar types ignore the rest of the column.
1362
1364
  */
1363
1365
  public toColumnType(column: AnalyticsTableColumn): Statement {
1366
+ const simpleAggregateFunction: string | undefined =
1367
+ column.simpleAggregateFunction;
1368
+
1369
+ if (simpleAggregateFunction !== undefined) {
1370
+ if (column.type === TableColumnType.AggregateFunction) {
1371
+ throw new BadDataException(
1372
+ `Column ${column.key} cannot declare both AggregateFunction and SimpleAggregateFunction.`,
1373
+ );
1374
+ }
1375
+
1376
+ /*
1377
+ * The function name is emitted as raw ClickHouse DDL, so accept only a
1378
+ * plain function identifier. Besides catching accidental whitespace /
1379
+ * empty definitions, this prevents a model field from injecting another
1380
+ * type or table clause into generated schema SQL.
1381
+ */
1382
+ if (
1383
+ simpleAggregateFunction.trim() !== simpleAggregateFunction ||
1384
+ !SIMPLE_AGGREGATE_FUNCTION_NAME_REGEX.test(simpleAggregateFunction)
1385
+ ) {
1386
+ throw new BadDataException(
1387
+ `Column ${column.key} has invalid simpleAggregateFunction "${simpleAggregateFunction}".`,
1388
+ );
1389
+ }
1390
+ }
1391
+
1364
1392
  if (column.type === TableColumnType.AggregateFunction) {
1365
1393
  const def: string | undefined = column.aggregateFunctionDefinition;
1366
1394
  if (!def) {
@@ -1371,7 +1399,7 @@ export default class StatementGenerator<TBaseModel extends AnalyticsBaseModel> {
1371
1399
  return SQL`AggregateFunction(`.append(def).append(SQL`)`);
1372
1400
  }
1373
1401
 
1374
- const statement: Statement | undefined = {
1402
+ const scalarStatement: Statement | undefined = {
1375
1403
  [TableColumnType.Text]: SQL`String`,
1376
1404
  [TableColumnType.ObjectID]: SQL`String`,
1377
1405
  [TableColumnType.Boolean]: SQL`Bool`,
@@ -1394,13 +1422,21 @@ export default class StatementGenerator<TBaseModel extends AnalyticsBaseModel> {
1394
1422
  [TableColumnType.UInt64]: SQL`UInt64`,
1395
1423
  }[column.type];
1396
1424
 
1397
- if (!statement) {
1425
+ if (!scalarStatement) {
1398
1426
  throw new BadDataException(
1399
1427
  `Unknown column type: ${column.type}. Please add support for this column type.`,
1400
1428
  );
1401
1429
  }
1402
1430
 
1403
- return statement;
1431
+ if (simpleAggregateFunction) {
1432
+ return SQL`SimpleAggregateFunction(`
1433
+ .append(simpleAggregateFunction)
1434
+ .append(SQL`, `)
1435
+ .append(scalarStatement)
1436
+ .append(SQL`)`);
1437
+ }
1438
+
1439
+ return scalarStatement;
1404
1440
  }
1405
1441
 
1406
1442
  /**
@@ -1413,7 +1449,8 @@ export default class StatementGenerator<TBaseModel extends AnalyticsBaseModel> {
1413
1449
  */
1414
1450
  public toFullColumnType(column: AnalyticsTableColumn): Statement {
1415
1451
  const isAggregateFunction: boolean =
1416
- column.type === TableColumnType.AggregateFunction;
1452
+ column.type === TableColumnType.AggregateFunction ||
1453
+ Boolean(column.simpleAggregateFunction);
1417
1454
 
1418
1455
  let typeStatement: Statement = this.toColumnType(column);
1419
1456
 
@@ -103,6 +103,19 @@ class Express {
103
103
 
104
104
  if (!this.httpServer) {
105
105
  this.httpServer = createServer(this.app);
106
+
107
+ /*
108
+ * Keep upstream (nginx) connection reuse safe. When the ingress pools
109
+ * idle upstream connections (proxy keepalive), the backend must NOT be
110
+ * the side that closes an idle socket first — otherwise nginx can pick a
111
+ * socket the backend already closed and 502 a request it cannot safely
112
+ * retry (e.g. a POST on the telemetry ingest path). Node's default
113
+ * keepAliveTimeout (~5s) is well below nginx's upstream keepalive_timeout
114
+ * (60s), so raise it above that window. headersTimeout must stay greater
115
+ * than keepAliveTimeout or Node can cut an idle socket mid-request.
116
+ */
117
+ this.httpServer.keepAliveTimeout = 75 * 1000;
118
+ this.httpServer.headersTimeout = 76 * 1000;
106
119
  }
107
120
 
108
121
  /*