@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.
- package/Models/AnalyticsModels/ExceptionInstance.ts +27 -0
- package/Models/AnalyticsModels/Log.ts +1 -0
- package/Models/AnalyticsModels/Metric.ts +1 -0
- package/Models/AnalyticsModels/MutableMetric.ts +333 -0
- package/Models/AnalyticsModels/Profile.ts +1 -0
- package/Models/AnalyticsModels/Span.ts +1 -0
- package/Server/Infrastructure/Queue.ts +19 -2
- package/Server/Infrastructure/QueueWorker.ts +67 -1
- package/Server/Services/AlertService.ts +167 -206
- package/Server/Services/AlertStateTimelineService.ts +121 -1
- package/Server/Services/AnalyticsDatabaseService.ts +19 -23
- package/Server/Services/IncidentService.ts +179 -252
- package/Server/Services/IncidentStateTimelineService.ts +120 -1
- package/Server/Services/Index.ts +2 -0
- package/Server/Services/MetricAggregationService.ts +73 -3
- package/Server/Services/MetricService.ts +228 -0
- package/Server/Services/MutableMetricService.ts +275 -0
- package/Server/Services/TelemetryAttributeService.ts +186 -5
- package/Server/Utils/AnalyticsDatabase/ClusterConfig.ts +2 -0
- package/Server/Utils/AnalyticsDatabase/Statement.ts +2 -0
- package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +81 -2
- package/Tests/Server/Services/AnalyticsDatabaseService.test.ts +143 -0
- package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +208 -0
- package/Tests/Types/Metrics/MetricPipelineRuleFilterCondition.test.ts +208 -0
- package/Tests/Types/Monitor/MonitorStepEntityScope.test.ts +0 -56
- package/Types/AnalyticsDatabase/AnalyticsTableEngine.ts +1 -0
- package/Types/AnalyticsDatabase/AnalyticsTableName.ts +1 -0
- package/Types/AnalyticsDatabase/TableColumn.ts +16 -0
- package/Types/Monitor/MonitorStepMetricMonitor.ts +0 -27
- package/build/dist/Models/AnalyticsModels/ExceptionInstance.js +24 -0
- package/build/dist/Models/AnalyticsModels/ExceptionInstance.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/Log.js +1 -0
- package/build/dist/Models/AnalyticsModels/Log.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/Metric.js +1 -0
- package/build/dist/Models/AnalyticsModels/Metric.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/MutableMetric.js +275 -0
- package/build/dist/Models/AnalyticsModels/MutableMetric.js.map +1 -0
- package/build/dist/Models/AnalyticsModels/Profile.js +1 -0
- package/build/dist/Models/AnalyticsModels/Profile.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/Span.js +1 -0
- package/build/dist/Models/AnalyticsModels/Span.js.map +1 -1
- package/build/dist/Server/Infrastructure/Queue.js +14 -2
- package/build/dist/Server/Infrastructure/Queue.js.map +1 -1
- package/build/dist/Server/Infrastructure/QueueWorker.js +45 -1
- package/build/dist/Server/Infrastructure/QueueWorker.js.map +1 -1
- package/build/dist/Server/Services/AlertService.js +133 -170
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/AlertStateTimelineService.js +97 -0
- package/build/dist/Server/Services/AlertStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js +16 -15
- package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +142 -207
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/IncidentStateTimelineService.js +97 -0
- package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +2 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/MetricAggregationService.js +49 -3
- package/build/dist/Server/Services/MetricAggregationService.js.map +1 -1
- package/build/dist/Server/Services/MetricService.js +155 -0
- package/build/dist/Server/Services/MetricService.js.map +1 -1
- package/build/dist/Server/Services/MutableMetricService.js +182 -0
- package/build/dist/Server/Services/MutableMetricService.js.map +1 -0
- package/build/dist/Server/Services/TelemetryAttributeService.js +132 -7
- package/build/dist/Server/Services/TelemetryAttributeService.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/ClusterConfig.js +2 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/ClusterConfig.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js +2 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +66 -2
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
- package/build/dist/Types/AnalyticsDatabase/AnalyticsTableEngine.js +1 -0
- package/build/dist/Types/AnalyticsDatabase/AnalyticsTableEngine.js.map +1 -1
- package/build/dist/Types/AnalyticsDatabase/AnalyticsTableName.js +1 -0
- package/build/dist/Types/AnalyticsDatabase/AnalyticsTableName.js.map +1 -1
- package/build/dist/Types/AnalyticsDatabase/TableColumn.js +7 -0
- package/build/dist/Types/AnalyticsDatabase/TableColumn.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStepMetricMonitor.js +0 -13
- package/build/dist/Types/Monitor/MonitorStepMetricMonitor.js.map +1 -1
- 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
|
-
*
|
|
24
|
-
*
|
|
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
|
-
|
|
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) {
|