@oneuptime/common 11.3.8 → 11.3.10

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 (80) hide show
  1. package/Models/AnalyticsModels/ExceptionInstance.ts +27 -0
  2. package/Models/AnalyticsModels/Log.ts +1 -0
  3. package/Models/AnalyticsModels/Metric.ts +1 -0
  4. package/Models/AnalyticsModels/MutableMetric.ts +333 -0
  5. package/Models/AnalyticsModels/Profile.ts +1 -0
  6. package/Models/AnalyticsModels/Span.ts +1 -0
  7. package/Server/Infrastructure/Queue.ts +19 -2
  8. package/Server/Infrastructure/QueueWorker.ts +67 -1
  9. package/Server/Services/AlertService.ts +167 -206
  10. package/Server/Services/AlertStateTimelineService.ts +121 -1
  11. package/Server/Services/AnalyticsDatabaseService.ts +19 -23
  12. package/Server/Services/IncidentService.ts +179 -252
  13. package/Server/Services/IncidentStateTimelineService.ts +120 -1
  14. package/Server/Services/Index.ts +2 -0
  15. package/Server/Services/MetricAggregationService.ts +73 -3
  16. package/Server/Services/MetricService.ts +228 -0
  17. package/Server/Services/MutableMetricService.ts +275 -0
  18. package/Server/Services/TelemetryAttributeService.ts +186 -5
  19. package/Server/Utils/AnalyticsDatabase/ClusterConfig.ts +2 -0
  20. package/Server/Utils/AnalyticsDatabase/Statement.ts +2 -0
  21. package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +81 -2
  22. package/Tests/Server/Services/AnalyticsDatabaseService.test.ts +143 -0
  23. package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +208 -0
  24. package/Tests/Types/Metrics/MetricPipelineRuleFilterCondition.test.ts +208 -0
  25. package/Tests/Types/Monitor/MonitorStepEntityScope.test.ts +0 -56
  26. package/Types/AnalyticsDatabase/AnalyticsTableEngine.ts +1 -0
  27. package/Types/AnalyticsDatabase/AnalyticsTableName.ts +1 -0
  28. package/Types/AnalyticsDatabase/TableColumn.ts +16 -0
  29. package/Types/Monitor/MonitorStepMetricMonitor.ts +0 -27
  30. package/build/dist/Models/AnalyticsModels/ExceptionInstance.js +24 -0
  31. package/build/dist/Models/AnalyticsModels/ExceptionInstance.js.map +1 -1
  32. package/build/dist/Models/AnalyticsModels/Log.js +1 -0
  33. package/build/dist/Models/AnalyticsModels/Log.js.map +1 -1
  34. package/build/dist/Models/AnalyticsModels/Metric.js +1 -0
  35. package/build/dist/Models/AnalyticsModels/Metric.js.map +1 -1
  36. package/build/dist/Models/AnalyticsModels/MutableMetric.js +275 -0
  37. package/build/dist/Models/AnalyticsModels/MutableMetric.js.map +1 -0
  38. package/build/dist/Models/AnalyticsModels/Profile.js +1 -0
  39. package/build/dist/Models/AnalyticsModels/Profile.js.map +1 -1
  40. package/build/dist/Models/AnalyticsModels/Span.js +1 -0
  41. package/build/dist/Models/AnalyticsModels/Span.js.map +1 -1
  42. package/build/dist/Server/Infrastructure/Queue.js +14 -2
  43. package/build/dist/Server/Infrastructure/Queue.js.map +1 -1
  44. package/build/dist/Server/Infrastructure/QueueWorker.js +45 -1
  45. package/build/dist/Server/Infrastructure/QueueWorker.js.map +1 -1
  46. package/build/dist/Server/Services/AlertService.js +133 -170
  47. package/build/dist/Server/Services/AlertService.js.map +1 -1
  48. package/build/dist/Server/Services/AlertStateTimelineService.js +97 -0
  49. package/build/dist/Server/Services/AlertStateTimelineService.js.map +1 -1
  50. package/build/dist/Server/Services/AnalyticsDatabaseService.js +16 -15
  51. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  52. package/build/dist/Server/Services/IncidentService.js +142 -207
  53. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  54. package/build/dist/Server/Services/IncidentStateTimelineService.js +97 -0
  55. package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
  56. package/build/dist/Server/Services/Index.js +2 -0
  57. package/build/dist/Server/Services/Index.js.map +1 -1
  58. package/build/dist/Server/Services/MetricAggregationService.js +49 -3
  59. package/build/dist/Server/Services/MetricAggregationService.js.map +1 -1
  60. package/build/dist/Server/Services/MetricService.js +155 -0
  61. package/build/dist/Server/Services/MetricService.js.map +1 -1
  62. package/build/dist/Server/Services/MutableMetricService.js +182 -0
  63. package/build/dist/Server/Services/MutableMetricService.js.map +1 -0
  64. package/build/dist/Server/Services/TelemetryAttributeService.js +132 -7
  65. package/build/dist/Server/Services/TelemetryAttributeService.js.map +1 -1
  66. package/build/dist/Server/Utils/AnalyticsDatabase/ClusterConfig.js +2 -0
  67. package/build/dist/Server/Utils/AnalyticsDatabase/ClusterConfig.js.map +1 -1
  68. package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js +2 -0
  69. package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js.map +1 -1
  70. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +66 -2
  71. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
  72. package/build/dist/Types/AnalyticsDatabase/AnalyticsTableEngine.js +1 -0
  73. package/build/dist/Types/AnalyticsDatabase/AnalyticsTableEngine.js.map +1 -1
  74. package/build/dist/Types/AnalyticsDatabase/AnalyticsTableName.js +1 -0
  75. package/build/dist/Types/AnalyticsDatabase/AnalyticsTableName.js.map +1 -1
  76. package/build/dist/Types/AnalyticsDatabase/TableColumn.js +7 -0
  77. package/build/dist/Types/AnalyticsDatabase/TableColumn.js.map +1 -1
  78. package/build/dist/Types/Monitor/MonitorStepMetricMonitor.js +0 -13
  79. package/build/dist/Types/Monitor/MonitorStepMetricMonitor.js.map +1 -1
  80. package/package.json +1 -1
@@ -0,0 +1,275 @@
1
+ import AnalyticsDatabaseService, {
2
+ DbJSONResponse,
3
+ Results,
4
+ } from "./AnalyticsDatabaseService";
5
+ import ClickhouseDatabase from "../Infrastructure/ClickhouseDatabase";
6
+ import { SQL, Statement } from "../Utils/AnalyticsDatabase/Statement";
7
+ import AlertMetricType from "../../Types/Alerts/AlertMetricType";
8
+ import AnalyticsTableName from "../../Types/AnalyticsDatabase/AnalyticsTableName";
9
+ import TableColumnType from "../../Types/AnalyticsDatabase/TableColumnType";
10
+ import IncidentMetricType from "../../Types/Incident/IncidentMetricType";
11
+ import { JSONObject } from "../../Types/JSON";
12
+ import ObjectID from "../../Types/ObjectID";
13
+ import ServiceType from "../../Types/Telemetry/ServiceType";
14
+ import MutableMetric from "../../Models/AnalyticsModels/MutableMetric";
15
+ import { MetricPointType } from "../../Models/AnalyticsModels/Metric";
16
+ import OneUptimeDate from "../../Types/Date";
17
+ import Includes from "../../Types/BaseDatabase/Includes";
18
+
19
+ type MutableMetricPointIdentity = {
20
+ name: string;
21
+ metricPointId: string;
22
+ };
23
+
24
+ export class MutableMetricService extends AnalyticsDatabaseService<MutableMetric> {
25
+ private static readonly mutableMetricNames: Set<string> = new Set<string>([
26
+ ...Object.values(AlertMetricType),
27
+ ...Object.values(IncidentMetricType),
28
+ ]);
29
+
30
+ public constructor(clickhouseDatabase?: ClickhouseDatabase | undefined) {
31
+ super({ modelType: MutableMetric, database: clickhouseDatabase });
32
+ }
33
+
34
+ public static isMutableMetricName(metricName: string | undefined): boolean {
35
+ if (!metricName) {
36
+ return false;
37
+ }
38
+
39
+ return MutableMetricService.mutableMetricNames.has(metricName);
40
+ }
41
+
42
+ public static getMutableMetricNames(): Array<string> {
43
+ return Array.from(MutableMetricService.mutableMetricNames);
44
+ }
45
+
46
+ public async createMutableMetrics(data: {
47
+ metrics: Array<MutableMetric>;
48
+ }): Promise<void> {
49
+ if (data.metrics.length === 0) {
50
+ return;
51
+ }
52
+
53
+ const version: number = this.getNextVersion();
54
+
55
+ for (const metric of data.metrics) {
56
+ metric.version = metric.version || version;
57
+ metric.isDeleted = metric.isDeleted || false;
58
+ }
59
+
60
+ await this.createMany({
61
+ items: data.metrics,
62
+ props: {
63
+ isRoot: true,
64
+ },
65
+ });
66
+ }
67
+
68
+ public async replaceEntityMetrics(data: {
69
+ projectId: ObjectID;
70
+ primaryEntityId: ObjectID;
71
+ primaryEntityType: ServiceType;
72
+ metricNames: Array<string>;
73
+ metrics: Array<MutableMetric>;
74
+ retentionDate: Date;
75
+ }): Promise<void> {
76
+ const version: number = this.getNextVersion();
77
+ const desiredMetricKeys: Set<string> = new Set<string>();
78
+
79
+ for (const metric of data.metrics) {
80
+ const metricName: string | undefined = metric.name;
81
+ const metricPointId: string | undefined = metric.metricPointId;
82
+
83
+ if (!metricName || !metricPointId) {
84
+ continue;
85
+ }
86
+
87
+ desiredMetricKeys.add(
88
+ this.getMetricPointKey({
89
+ name: metricName,
90
+ metricPointId: metricPointId,
91
+ }),
92
+ );
93
+ metric.version = version;
94
+ metric.isDeleted = false;
95
+ }
96
+
97
+ const existingMetricPointIdentities: Array<MutableMetricPointIdentity> =
98
+ await this.getActiveMetricPointIdentities({
99
+ projectId: data.projectId,
100
+ primaryEntityId: data.primaryEntityId,
101
+ primaryEntityType: data.primaryEntityType,
102
+ metricNames: data.metricNames,
103
+ });
104
+
105
+ const tombstoneMetrics: Array<MutableMetric> = [];
106
+
107
+ for (const existingMetricPointIdentity of existingMetricPointIdentities) {
108
+ const metricPointKey: string = this.getMetricPointKey(
109
+ existingMetricPointIdentity,
110
+ );
111
+
112
+ if (desiredMetricKeys.has(metricPointKey)) {
113
+ continue;
114
+ }
115
+
116
+ tombstoneMetrics.push(
117
+ this.createTombstoneMetric({
118
+ projectId: data.projectId,
119
+ primaryEntityId: data.primaryEntityId,
120
+ primaryEntityType: data.primaryEntityType,
121
+ name: existingMetricPointIdentity.name,
122
+ metricPointId: existingMetricPointIdentity.metricPointId,
123
+ version: version,
124
+ retentionDate: data.retentionDate,
125
+ }),
126
+ );
127
+ }
128
+
129
+ await this.createMutableMetrics({
130
+ metrics: [...data.metrics, ...tombstoneMetrics],
131
+ });
132
+ }
133
+
134
+ public async tombstoneEntityMetrics(data: {
135
+ projectId: ObjectID;
136
+ primaryEntityId: ObjectID;
137
+ primaryEntityType: ServiceType;
138
+ metricNames: Array<string>;
139
+ retentionDate: Date;
140
+ }): Promise<void> {
141
+ const existingMetricPointIdentities: Array<MutableMetricPointIdentity> =
142
+ await this.getActiveMetricPointIdentities({
143
+ projectId: data.projectId,
144
+ primaryEntityId: data.primaryEntityId,
145
+ primaryEntityType: data.primaryEntityType,
146
+ metricNames: data.metricNames,
147
+ });
148
+
149
+ const version: number = this.getNextVersion();
150
+ const tombstoneMetrics: Array<MutableMetric> =
151
+ existingMetricPointIdentities.map(
152
+ (
153
+ existingMetricPointIdentity: MutableMetricPointIdentity,
154
+ ): MutableMetric => {
155
+ return this.createTombstoneMetric({
156
+ projectId: data.projectId,
157
+ primaryEntityId: data.primaryEntityId,
158
+ primaryEntityType: data.primaryEntityType,
159
+ name: existingMetricPointIdentity.name,
160
+ metricPointId: existingMetricPointIdentity.metricPointId,
161
+ version: version,
162
+ retentionDate: data.retentionDate,
163
+ });
164
+ },
165
+ );
166
+
167
+ await this.createMutableMetrics({
168
+ metrics: tombstoneMetrics,
169
+ });
170
+ }
171
+
172
+ private async getActiveMetricPointIdentities(data: {
173
+ projectId: ObjectID;
174
+ primaryEntityId: ObjectID;
175
+ primaryEntityType: ServiceType;
176
+ metricNames: Array<string>;
177
+ }): Promise<Array<MutableMetricPointIdentity>> {
178
+ if (data.metricNames.length === 0) {
179
+ return [];
180
+ }
181
+
182
+ if (!this.database) {
183
+ this.useDefaultDatabase();
184
+ }
185
+
186
+ const databaseName: string = this.database.getDatasourceOptions().database!;
187
+ const tableName: string = AnalyticsTableName.MutableMetric;
188
+
189
+ const statement: Statement = SQL`
190
+ SELECT name, metricPointId
191
+ FROM (
192
+ SELECT
193
+ name,
194
+ metricPointId,
195
+ argMax(isDeleted, version) AS isDeleted
196
+ FROM ${databaseName}.${tableName}
197
+ WHERE projectId = ${{
198
+ value: data.projectId,
199
+ type: TableColumnType.ObjectID,
200
+ }}
201
+ AND primaryEntityId = ${{
202
+ value: data.primaryEntityId,
203
+ type: TableColumnType.ObjectID,
204
+ }}
205
+ AND primaryEntityType = ${{
206
+ value: data.primaryEntityType,
207
+ type: TableColumnType.Text,
208
+ }}
209
+ AND name IN ${{
210
+ value: new Includes(data.metricNames),
211
+ type: TableColumnType.Text,
212
+ }}
213
+ GROUP BY name, metricPointId
214
+ )
215
+ WHERE isDeleted = false
216
+ `;
217
+
218
+ const dbResult: Results = await this.executeQuery(statement);
219
+ const response: DbJSONResponse = await dbResult.json<{
220
+ data?: Array<JSONObject>;
221
+ }>();
222
+
223
+ return (response.data || []).map(
224
+ (row: JSONObject): MutableMetricPointIdentity => {
225
+ return {
226
+ name: row["name"] as string,
227
+ metricPointId: row["metricPointId"] as string,
228
+ };
229
+ },
230
+ );
231
+ }
232
+
233
+ private createTombstoneMetric(data: {
234
+ projectId: ObjectID;
235
+ primaryEntityId: ObjectID;
236
+ primaryEntityType: ServiceType;
237
+ name: string;
238
+ metricPointId: string;
239
+ version: number;
240
+ retentionDate: Date;
241
+ }): MutableMetric {
242
+ const now: Date = OneUptimeDate.getCurrentDate();
243
+ const metric: MutableMetric = new MutableMetric();
244
+
245
+ metric.projectId = data.projectId;
246
+ metric.primaryEntityId = data.primaryEntityId;
247
+ metric.primaryEntityType = data.primaryEntityType;
248
+ metric.name = data.name;
249
+ metric.metricPointId = data.metricPointId;
250
+ metric.value = 0;
251
+ metric.time = now;
252
+ metric.timeUnixNano = OneUptimeDate.toUnixNano(now);
253
+ metric.metricPointType = MetricPointType.Sum;
254
+ metric.attributes = {};
255
+ metric.attributeKeys = [];
256
+ metric.version = data.version;
257
+ metric.isDeleted = true;
258
+ metric.retentionDate = data.retentionDate;
259
+
260
+ return metric;
261
+ }
262
+
263
+ private getMetricPointKey(data: {
264
+ name: string;
265
+ metricPointId: string;
266
+ }): string {
267
+ return `${data.name}|${data.metricPointId}`;
268
+ }
269
+
270
+ private getNextVersion(): number {
271
+ return Date.now() * 1000 + Number(process.hrtime.bigint() % BigInt(1000));
272
+ }
273
+ }
274
+
275
+ export default new MutableMetricService();
@@ -4,6 +4,9 @@ import LogDatabaseService from "./LogService";
4
4
  import MetricDatabaseService from "./MetricService";
5
5
  import SpanDatabaseService from "./SpanService";
6
6
  import ExceptionInstanceService from "./ExceptionInstanceService";
7
+ import MutableMetricDatabaseService, {
8
+ MutableMetricService as MutableMetricServiceClass,
9
+ } from "./MutableMetricService";
7
10
  import TableColumnType from "../../Types/AnalyticsDatabase/TableColumnType";
8
11
  import { JSONObject } from "../../Types/JSON";
9
12
  import ObjectID from "../../Types/ObjectID";
@@ -20,12 +23,12 @@ type TelemetrySource = {
20
23
  tableName: string;
21
24
  attributesColumn: string;
22
25
  /*
23
- * Some tables (e.g. ExceptionInstance) don't have a separate
24
- * attributeKeys array column only the attributes map. Leave this
25
- * undefined for those; the SQL falls back to mapKeys(attributes).
26
+ * Older telemetry tables may lack a separate attributeKeys array column.
27
+ * Leave this undefined for those; the SQL falls back to mapKeys(attributes).
26
28
  */
27
29
  attributeKeysColumn?: string | undefined;
28
30
  timeColumn: string;
31
+ isMutableMetricSource?: boolean | undefined;
29
32
  };
30
33
 
31
34
  type TelemetryAttributesCacheEntry = {
@@ -52,6 +55,7 @@ export class TelemetryAttributeService {
52
55
 
53
56
  private getTelemetrySource(
54
57
  telemetryType: TelemetryType,
58
+ metricName?: string | undefined,
55
59
  ): TelemetrySource | null {
56
60
  switch (telemetryType) {
57
61
  case TelemetryType.Log:
@@ -63,6 +67,17 @@ export class TelemetryAttributeService {
63
67
  timeColumn: "time",
64
68
  };
65
69
  case TelemetryType.Metric:
70
+ if (MutableMetricServiceClass.isMutableMetricName(metricName)) {
71
+ return {
72
+ service: MutableMetricDatabaseService,
73
+ tableName: MutableMetricDatabaseService.model.tableName,
74
+ attributesColumn: "attributes",
75
+ attributeKeysColumn: "attributeKeys",
76
+ timeColumn: "time",
77
+ isMutableMetricSource: true,
78
+ };
79
+ }
80
+
66
81
  return {
67
82
  service: MetricDatabaseService,
68
83
  tableName: MetricDatabaseService.model.tableName,
@@ -83,7 +98,7 @@ export class TelemetryAttributeService {
83
98
  service: ExceptionInstanceService,
84
99
  tableName: ExceptionInstanceService.model.tableName,
85
100
  attributesColumn: "attributes",
86
- // ExceptionInstance has no attributeKeys column.
101
+ attributeKeysColumn: "attributeKeys",
87
102
  timeColumn: "time",
88
103
  };
89
104
  default:
@@ -99,6 +114,7 @@ export class TelemetryAttributeService {
99
114
  }): Promise<string[]> {
100
115
  const source: TelemetrySource | null = this.getTelemetrySource(
101
116
  data.telemetryType,
117
+ data.metricName,
102
118
  );
103
119
 
104
120
  if (!source) {
@@ -109,6 +125,7 @@ export class TelemetryAttributeService {
109
125
  data.projectId,
110
126
  data.telemetryType,
111
127
  data.metricName,
128
+ source.tableName,
112
129
  );
113
130
 
114
131
  const cachedEntry: TelemetryAttributesCacheEntry | null =
@@ -150,8 +167,11 @@ export class TelemetryAttributeService {
150
167
  projectId: ObjectID,
151
168
  telemetryType: TelemetryType,
152
169
  metricName?: string | undefined,
170
+ sourceTableName?: string | undefined,
153
171
  ): string {
154
- const base: string = `${projectId.toString()}:${telemetryType}`;
172
+ const base: string = `${projectId.toString()}:${telemetryType}:${
173
+ sourceTableName || "default"
174
+ }`;
155
175
  if (metricName) {
156
176
  return `${base}:${metricName}`;
157
177
  }
@@ -252,10 +272,23 @@ export class TelemetryAttributeService {
252
272
  attributeKeysColumn?: string | undefined;
253
273
  timeColumn: string;
254
274
  metricName?: string | undefined;
275
+ isMutableMetricSource?: boolean | undefined;
255
276
  }): Statement {
256
277
  const lookbackStartDate: Date =
257
278
  TelemetryAttributeService.getLookbackStartDate();
258
279
 
280
+ if (data.isMutableMetricSource) {
281
+ return TelemetryAttributeService.buildMutableMetricAttributesStatement({
282
+ projectId: data.projectId,
283
+ tableName: data.tableName,
284
+ attributesColumn: data.attributesColumn,
285
+ attributeKeysColumn: data.attributeKeysColumn,
286
+ timeColumn: data.timeColumn,
287
+ metricName: data.metricName,
288
+ lookbackStartDate,
289
+ });
290
+ }
291
+
259
292
  /*
260
293
  * Two notable choices here:
261
294
  *
@@ -333,6 +366,7 @@ export class TelemetryAttributeService {
333
366
  attributeKeysColumn: data.source.attributeKeysColumn,
334
367
  timeColumn: data.source.timeColumn,
335
368
  metricName: data.metricName,
369
+ isMutableMetricSource: data.source.isMutableMetricSource,
336
370
  });
337
371
 
338
372
  const dbResult: Results = await data.source.service.executeQuery(statement);
@@ -378,6 +412,7 @@ export class TelemetryAttributeService {
378
412
  }): Promise<string[]> {
379
413
  const source: TelemetrySource | null = this.getTelemetrySource(
380
414
  data.telemetryType,
415
+ data.metricName,
381
416
  );
382
417
 
383
418
  if (!source) {
@@ -403,6 +438,19 @@ export class TelemetryAttributeService {
403
438
  const lookbackStartDate: Date =
404
439
  TelemetryAttributeService.getLookbackStartDate();
405
440
 
441
+ if (data.source.isMutableMetricSource) {
442
+ return TelemetryAttributeService.buildMutableMetricAttributeValuesStatement(
443
+ {
444
+ projectId: data.projectId,
445
+ source: data.source,
446
+ metricName: data.metricName,
447
+ attributeKey: data.attributeKey,
448
+ searchText: data.searchText,
449
+ lookbackStartDate,
450
+ },
451
+ );
452
+ }
453
+
406
454
  const statement: Statement = SQL`
407
455
  SELECT DISTINCT ${data.source.attributesColumn}[${{
408
456
  type: TableColumnType.Text,
@@ -500,6 +548,139 @@ export class TelemetryAttributeService {
500
548
  return Boolean(val);
501
549
  });
502
550
  }
551
+
552
+ private static buildMutableMetricAttributesStatement(data: {
553
+ projectId: ObjectID;
554
+ tableName: string;
555
+ attributesColumn: string;
556
+ attributeKeysColumn?: string | undefined;
557
+ timeColumn: string;
558
+ metricName?: string | undefined;
559
+ lookbackStartDate: Date;
560
+ }): Statement {
561
+ const attributeKeysColumn: string =
562
+ data.attributeKeysColumn || "attributeKeys";
563
+
564
+ const statement: Statement = SQL`
565
+ SELECT arrayDistinct(arrayFlatten(groupUniqArrayArray(${attributeKeysColumn}))) AS keys
566
+ FROM (
567
+ SELECT
568
+ argMax(${data.timeColumn}, version) AS ${data.timeColumn},
569
+ argMax(${data.attributesColumn}, version) AS ${data.attributesColumn},
570
+ argMax(${attributeKeysColumn}, version) AS ${attributeKeysColumn},
571
+ argMax(retentionDate, version) AS retentionDate,
572
+ argMax(isDeleted, version) AS isDeleted
573
+ FROM ${data.tableName}
574
+ WHERE projectId = ${{
575
+ type: TableColumnType.ObjectID,
576
+ value: data.projectId,
577
+ }}`;
578
+
579
+ if (data.metricName) {
580
+ statement.append(
581
+ SQL`
582
+ AND name = ${{
583
+ type: TableColumnType.Text,
584
+ value: data.metricName,
585
+ }}`,
586
+ );
587
+ }
588
+
589
+ statement.append(SQL`
590
+ GROUP BY projectId, name, primaryEntityId, primaryEntityType, metricPointId
591
+ )
592
+ WHERE isDeleted = false
593
+ AND retentionDate >= now()
594
+ AND ${data.timeColumn} >= ${{
595
+ type: TableColumnType.Date,
596
+ value: data.lookbackStartDate,
597
+ }}
598
+ AND NOT empty(${attributeKeysColumn})`);
599
+
600
+ statement.append(
601
+ " SETTINGS max_execution_time = 45, timeout_overflow_mode = 'break'",
602
+ );
603
+
604
+ return statement;
605
+ }
606
+
607
+ private static buildMutableMetricAttributeValuesStatement(data: {
608
+ projectId: ObjectID;
609
+ source: TelemetrySource;
610
+ metricName?: string | undefined;
611
+ attributeKey: string;
612
+ searchText?: string | undefined;
613
+ lookbackStartDate: Date;
614
+ }): Statement {
615
+ const statement: Statement = SQL`
616
+ SELECT DISTINCT ${data.source.attributesColumn}[${{
617
+ type: TableColumnType.Text,
618
+ value: data.attributeKey,
619
+ }}] AS attributeValue
620
+ FROM (
621
+ SELECT
622
+ argMax(${data.source.timeColumn}, version) AS ${data.source.timeColumn},
623
+ argMax(${data.source.attributesColumn}, version) AS ${data.source.attributesColumn},
624
+ argMax(retentionDate, version) AS retentionDate,
625
+ argMax(isDeleted, version) AS isDeleted
626
+ FROM ${data.source.tableName}
627
+ WHERE projectId = ${{
628
+ type: TableColumnType.ObjectID,
629
+ value: data.projectId,
630
+ }}`;
631
+
632
+ if (data.metricName) {
633
+ statement.append(
634
+ SQL`
635
+ AND name = ${{
636
+ type: TableColumnType.Text,
637
+ value: data.metricName,
638
+ }}`,
639
+ );
640
+ }
641
+
642
+ statement.append(SQL`
643
+ GROUP BY projectId, name, primaryEntityId, primaryEntityType, metricPointId
644
+ )
645
+ WHERE isDeleted = false
646
+ AND retentionDate >= now()
647
+ AND ${data.source.timeColumn} >= ${{
648
+ type: TableColumnType.Date,
649
+ value: data.lookbackStartDate,
650
+ }}
651
+ AND mapContains(${data.source.attributesColumn}, ${{
652
+ type: TableColumnType.Text,
653
+ value: data.attributeKey,
654
+ }})`);
655
+
656
+ if (data.searchText && data.searchText.trim().length > 0) {
657
+ statement.append(
658
+ SQL`
659
+ AND ${data.source.attributesColumn}[${{
660
+ type: TableColumnType.Text,
661
+ value: data.attributeKey,
662
+ }}] ILIKE ${{
663
+ type: TableColumnType.Text,
664
+ value: `%${data.searchText.trim()}%`,
665
+ }}`,
666
+ );
667
+ }
668
+
669
+ statement.append(
670
+ SQL`
671
+ ORDER BY attributeValue ASC
672
+ LIMIT ${{
673
+ type: TableColumnType.Number,
674
+ value: TelemetryAttributeService.ATTRIBUTE_VALUES_LIMIT,
675
+ }}`,
676
+ );
677
+
678
+ statement.append(
679
+ " SETTINGS max_execution_time = 45, timeout_overflow_mode = 'break'",
680
+ );
681
+
682
+ return statement;
683
+ }
503
684
  }
504
685
 
505
686
  export default new TelemetryAttributeService();
@@ -82,6 +82,8 @@ export function getStorageEngine(engine: AnalyticsTableEngine): string {
82
82
  switch (engine) {
83
83
  case AnalyticsTableEngine.AggregatingMergeTree:
84
84
  return "ReplicatedAggregatingMergeTree";
85
+ case AnalyticsTableEngine.ReplacingMergeTree:
86
+ return "ReplicatedReplacingMergeTree(version)";
85
87
  case AnalyticsTableEngine.MergeTree:
86
88
  default:
87
89
  return "ReplicatedMergeTree";
@@ -194,6 +194,8 @@ export class Statement implements BaseQueryParams {
194
194
  [TableColumnType.ArrayNumber]: "Array(Int32)",
195
195
  [TableColumnType.ArrayText]: "Array(String)",
196
196
  [TableColumnType.LongNumber]: "Int128",
197
+ [TableColumnType.BigNumber]: "Int64",
198
+ [TableColumnType.UInt64]: "UInt64",
197
199
  };
198
200
 
199
201
  if ((statementParam as StatementParameter).value instanceof Includes) {