@oneuptime/common 11.3.9 → 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/MutableMetric.ts +333 -0
- 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 +183 -1
- package/Server/Utils/AnalyticsDatabase/ClusterConfig.ts +2 -0
- package/Server/Utils/AnalyticsDatabase/Statement.ts +2 -0
- package/Tests/Server/Services/AnalyticsDatabaseService.test.ts +143 -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/Monitor/MonitorStepMetricMonitor.ts +0 -27
- package/build/dist/Models/AnalyticsModels/MutableMetric.js +275 -0
- package/build/dist/Models/AnalyticsModels/MutableMetric.js.map +1 -0
- 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 +131 -6
- 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/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/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";
|
|
@@ -25,6 +28,7 @@ type TelemetrySource = {
|
|
|
25
28
|
*/
|
|
26
29
|
attributeKeysColumn?: string | undefined;
|
|
27
30
|
timeColumn: string;
|
|
31
|
+
isMutableMetricSource?: boolean | undefined;
|
|
28
32
|
};
|
|
29
33
|
|
|
30
34
|
type TelemetryAttributesCacheEntry = {
|
|
@@ -51,6 +55,7 @@ export class TelemetryAttributeService {
|
|
|
51
55
|
|
|
52
56
|
private getTelemetrySource(
|
|
53
57
|
telemetryType: TelemetryType,
|
|
58
|
+
metricName?: string | undefined,
|
|
54
59
|
): TelemetrySource | null {
|
|
55
60
|
switch (telemetryType) {
|
|
56
61
|
case TelemetryType.Log:
|
|
@@ -62,6 +67,17 @@ export class TelemetryAttributeService {
|
|
|
62
67
|
timeColumn: "time",
|
|
63
68
|
};
|
|
64
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
|
+
|
|
65
81
|
return {
|
|
66
82
|
service: MetricDatabaseService,
|
|
67
83
|
tableName: MetricDatabaseService.model.tableName,
|
|
@@ -98,6 +114,7 @@ export class TelemetryAttributeService {
|
|
|
98
114
|
}): Promise<string[]> {
|
|
99
115
|
const source: TelemetrySource | null = this.getTelemetrySource(
|
|
100
116
|
data.telemetryType,
|
|
117
|
+
data.metricName,
|
|
101
118
|
);
|
|
102
119
|
|
|
103
120
|
if (!source) {
|
|
@@ -108,6 +125,7 @@ export class TelemetryAttributeService {
|
|
|
108
125
|
data.projectId,
|
|
109
126
|
data.telemetryType,
|
|
110
127
|
data.metricName,
|
|
128
|
+
source.tableName,
|
|
111
129
|
);
|
|
112
130
|
|
|
113
131
|
const cachedEntry: TelemetryAttributesCacheEntry | null =
|
|
@@ -149,8 +167,11 @@ export class TelemetryAttributeService {
|
|
|
149
167
|
projectId: ObjectID,
|
|
150
168
|
telemetryType: TelemetryType,
|
|
151
169
|
metricName?: string | undefined,
|
|
170
|
+
sourceTableName?: string | undefined,
|
|
152
171
|
): string {
|
|
153
|
-
const base: string = `${projectId.toString()}:${telemetryType}
|
|
172
|
+
const base: string = `${projectId.toString()}:${telemetryType}:${
|
|
173
|
+
sourceTableName || "default"
|
|
174
|
+
}`;
|
|
154
175
|
if (metricName) {
|
|
155
176
|
return `${base}:${metricName}`;
|
|
156
177
|
}
|
|
@@ -251,10 +272,23 @@ export class TelemetryAttributeService {
|
|
|
251
272
|
attributeKeysColumn?: string | undefined;
|
|
252
273
|
timeColumn: string;
|
|
253
274
|
metricName?: string | undefined;
|
|
275
|
+
isMutableMetricSource?: boolean | undefined;
|
|
254
276
|
}): Statement {
|
|
255
277
|
const lookbackStartDate: Date =
|
|
256
278
|
TelemetryAttributeService.getLookbackStartDate();
|
|
257
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
|
+
|
|
258
292
|
/*
|
|
259
293
|
* Two notable choices here:
|
|
260
294
|
*
|
|
@@ -332,6 +366,7 @@ export class TelemetryAttributeService {
|
|
|
332
366
|
attributeKeysColumn: data.source.attributeKeysColumn,
|
|
333
367
|
timeColumn: data.source.timeColumn,
|
|
334
368
|
metricName: data.metricName,
|
|
369
|
+
isMutableMetricSource: data.source.isMutableMetricSource,
|
|
335
370
|
});
|
|
336
371
|
|
|
337
372
|
const dbResult: Results = await data.source.service.executeQuery(statement);
|
|
@@ -377,6 +412,7 @@ export class TelemetryAttributeService {
|
|
|
377
412
|
}): Promise<string[]> {
|
|
378
413
|
const source: TelemetrySource | null = this.getTelemetrySource(
|
|
379
414
|
data.telemetryType,
|
|
415
|
+
data.metricName,
|
|
380
416
|
);
|
|
381
417
|
|
|
382
418
|
if (!source) {
|
|
@@ -402,6 +438,19 @@ export class TelemetryAttributeService {
|
|
|
402
438
|
const lookbackStartDate: Date =
|
|
403
439
|
TelemetryAttributeService.getLookbackStartDate();
|
|
404
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
|
+
|
|
405
454
|
const statement: Statement = SQL`
|
|
406
455
|
SELECT DISTINCT ${data.source.attributesColumn}[${{
|
|
407
456
|
type: TableColumnType.Text,
|
|
@@ -499,6 +548,139 @@ export class TelemetryAttributeService {
|
|
|
499
548
|
return Boolean(val);
|
|
500
549
|
});
|
|
501
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
|
+
}
|
|
502
684
|
}
|
|
503
685
|
|
|
504
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) {
|