@oneuptime/common 11.5.12 → 11.6.0
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/DatabaseModels/AIInsight.ts +30 -0
- package/Models/DatabaseModels/EnterpriseLicense.ts +21 -0
- package/Models/DatabaseModels/EnterpriseLicenseInstance.ts +25 -0
- package/Models/DatabaseModels/GlobalConfig.ts +76 -0
- package/Models/DatabaseModels/Project.ts +30 -0
- package/Models/DatabaseModels/TelemetryException.ts +128 -0
- package/Server/API/AIAgentDataAPI.ts +129 -5
- package/Server/API/AIInvestigationAPI.ts +87 -0
- package/Server/API/EnterpriseLicenseAPI.ts +57 -0
- package/Server/API/GlobalConfigAPI.ts +62 -1
- package/Server/EnvironmentConfig.ts +22 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1784640000000-AddExceptionTriageAndPrivacyColumns.ts +56 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1784659816363-AddInstanceVersionAndLatestReleaseColumns.ts +38 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1784705487674-AddEnterpriseLicenseEvaluationColumns.ts +25 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
- package/Server/Services/AIRunService.ts +8 -0
- package/Server/Services/AnalyticsDatabaseService.ts +48 -45
- package/Server/Services/DatabaseService.ts +66 -1
- package/Server/Services/TelemetryExceptionService.ts +320 -24
- package/Server/Services/WorkflowService.ts +100 -39
- package/Server/Types/Workflow/Components/Schedule.ts +57 -32
- package/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.ts +15 -4
- package/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.ts +15 -4
- package/Server/Utils/AI/SRE/Insights/InsightScanner.ts +222 -24
- package/Server/Utils/AI/SRE/Insights/InsightTriageRunner.ts +225 -9
- package/Server/Utils/AI/SRE/SubjectCodeFixRun.ts +39 -0
- package/Server/Utils/AI/SRE/TelemetryImprovementTaskTrigger.ts +114 -0
- package/Server/Utils/AnalyticsDatabase/InsertDedupContext.ts +59 -0
- package/Server/Utils/Express.ts +13 -0
- package/Server/Utils/Telemetry/ExceptionSanitizer.ts +222 -0
- package/Server/Utils/Telemetry/TelemetryFanInWriter.ts +799 -0
- package/Server/Utils/Telemetry/TelemetryWriterClient.ts +263 -0
- package/Server/Utils/Telemetry/TelemetryWriterServer.ts +240 -0
- package/Server/Utils/Telemetry/TelemetryWriterShedMetrics.ts +93 -0
- package/Tests/Server/Services/DatabaseServiceSanitizeUpdateData.test.ts +232 -0
- package/Tests/Server/Services/TelemetryExceptionCodeFixRun.test.ts +205 -1
- package/Tests/Server/Services/UpdateOneByIdKeepsRowId.test.ts +107 -0
- package/Tests/Server/Services/WorkflowService.test.ts +308 -0
- package/Tests/Server/Utils/AI/Insights/InsightScanner.test.ts +150 -141
- package/Tests/Server/Utils/AI/Insights/InsightTriageRunner.test.ts +277 -1
- package/Tests/Server/Utils/Attribution.test.ts +203 -0
- package/Tests/Server/Utils/ExceptionSanitizer.test.ts +70 -0
- package/Tests/Server/Utils/InsightTriageClassification.test.ts +57 -0
- package/Tests/Server/Utils/Marketing/ConversionUploadProvider.test.ts +183 -0
- package/Tests/Server/Utils/Marketing/GoogleAds.test.ts +340 -0
- package/Tests/Server/Utils/Monitor/Criteria/ExceptionMonitorCriteria.test.ts +87 -0
- package/Tests/Server/Utils/Monitor/Criteria/LogMonitorCriteria.test.ts +165 -0
- package/Tests/Server/Utils/Monitor/Criteria/TraceMonitorCriteria.test.ts +86 -0
- package/Tests/Server/Utils/Telemetry/TelemetryFanInWriter.test.ts +1565 -0
- package/Tests/Server/Utils/Telemetry/TelemetryWriterClient.test.ts +313 -0
- package/Tests/Server/Utils/Telemetry/TelemetryWriterServer.test.ts +270 -0
- package/Tests/Server/Utils/Telemetry/TelemetryWriterShedMetrics.test.ts +127 -0
- package/Tests/Server/Utils/TelemetryImprovementTaskTrigger.test.ts +141 -0
- package/Tests/Types/AI/CodeFixTaskType.test.ts +3 -0
- package/Tests/Types/Monitor/MonitorCriteriaMetricVariables.test.ts +228 -0
- package/Tests/Types/Monitor/MonitorStepDefaultTelemetryConfig.test.ts +169 -0
- package/Tests/Types/Monitor/MonitorStepExceptionMonitor.test.ts +289 -0
- package/Tests/Types/Monitor/MonitorStepLogMonitor.test.ts +231 -0
- package/Tests/Types/Monitor/MonitorStepMetricViewConfigUtil.test.ts +245 -0
- package/Tests/Types/Monitor/MonitorStepNetworkDeviceMonitor.test.ts +155 -0
- package/Tests/Types/Monitor/MonitorStepTelemetrySerialization.test.ts +141 -0
- package/Tests/Types/Monitor/MonitorStepTraceMonitor.test.ts +137 -0
- package/Tests/Types/Workspace/NotificationRules/NotificationRuleCondition.test.ts +311 -0
- package/Tests/UI/Components/ModelTableSelectFromColumns.test.ts +259 -0
- package/Tests/UI/Utils/Breadcrumb/BreadcrumbTrailResolver.test.ts +461 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +2852 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +758 -0
- package/Tests/UI/Utils/NotificationMethodUtil.test.ts +564 -0
- package/Tests/Utils/Array.test.ts +156 -0
- package/Tests/Utils/CronTab.test.ts +199 -0
- package/Tests/Utils/ModelImportExport.test.ts +101 -0
- package/Tests/Utils/Number.test.ts +179 -0
- package/Tests/Utils/VersionUtil.test.ts +348 -0
- package/Tests/jest.setup.ts +1 -0
- package/Types/AI/CodeFixTaskContext.ts +6 -0
- package/Types/AI/CodeFixTaskType.ts +38 -1
- package/Types/AI/ExceptionAIClassification.ts +27 -0
- package/Types/EnterpriseLicense/EnterpriseLicenseInstanceSummary.ts +6 -0
- package/Types/Log/LogScrubPatternType.ts +7 -0
- package/Types/Monitor/MonitorStep.ts +24 -4
- package/Types/Monitor/MonitorStepMetricViewConfigUtil.ts +105 -0
- package/Types/Trace/TraceScrubPatternType.ts +7 -0
- package/UI/Components/EditionLabel/EditionLabel.tsx +445 -12
- package/UI/Components/ModelTable/BaseModelTable.tsx +15 -46
- package/UI/Components/ModelTable/SelectFromColumns.ts +129 -0
- package/UI/Components/Workflow/ArgumentsForm.tsx +68 -30
- package/UI/Components/Workflow/CronScheduleField.tsx +411 -0
- package/UI/Utils/Breadcrumb/BreadcrumbTrailResolver.ts +176 -0
- package/UI/Utils/NotificationMethodUtil.ts +310 -0
- package/Utils/CronTab.ts +823 -0
- package/Utils/ModelImportExport.ts +21 -6
- package/Utils/VersionUtil.ts +260 -0
- package/build/dist/Models/DatabaseModels/AIInsight.js +31 -0
- package/build/dist/Models/DatabaseModels/AIInsight.js.map +1 -1
- package/build/dist/Models/DatabaseModels/EnterpriseLicense.js +22 -0
- package/build/dist/Models/DatabaseModels/EnterpriseLicense.js.map +1 -1
- package/build/dist/Models/DatabaseModels/EnterpriseLicenseInstance.js +26 -0
- package/build/dist/Models/DatabaseModels/EnterpriseLicenseInstance.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalConfig.js +81 -0
- package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +31 -0
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TelemetryException.js +134 -0
- package/build/dist/Models/DatabaseModels/TelemetryException.js.map +1 -1
- package/build/dist/Server/API/AIAgentDataAPI.js +97 -8
- package/build/dist/Server/API/AIAgentDataAPI.js.map +1 -1
- package/build/dist/Server/API/AIInvestigationAPI.js +55 -0
- package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
- package/build/dist/Server/API/EnterpriseLicenseAPI.js +47 -0
- package/build/dist/Server/API/EnterpriseLicenseAPI.js.map +1 -1
- package/build/dist/Server/API/GlobalConfigAPI.js +52 -1
- package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +17 -0
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784640000000-AddExceptionTriageAndPrivacyColumns.js +33 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784640000000-AddExceptionTriageAndPrivacyColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784659816363-AddInstanceVersionAndLatestReleaseColumns.js +18 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784659816363-AddInstanceVersionAndLatestReleaseColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784705487674-AddEnterpriseLicenseEvaluationColumns.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784705487674-AddEnterpriseLicenseEvaluationColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/AIRunService.js +9 -2
- package/build/dist/Server/Services/AIRunService.js.map +1 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js +44 -16
- package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +53 -1
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryExceptionService.js +263 -22
- package/build/dist/Server/Services/TelemetryExceptionService.js.map +1 -1
- package/build/dist/Server/Services/WorkflowService.js +80 -30
- package/build/dist/Server/Services/WorkflowService.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/Schedule.js +44 -19
- package/build/dist/Server/Types/Workflow/Components/Schedule.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js +12 -4
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js +12 -4
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/Insights/InsightScanner.js +193 -22
- package/build/dist/Server/Utils/AI/SRE/Insights/InsightScanner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/Insights/InsightTriageRunner.js +178 -9
- package/build/dist/Server/Utils/AI/SRE/Insights/InsightTriageRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/SubjectCodeFixRun.js +37 -0
- package/build/dist/Server/Utils/AI/SRE/SubjectCodeFixRun.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/TelemetryImprovementTaskTrigger.js +98 -0
- package/build/dist/Server/Utils/AI/SRE/TelemetryImprovementTaskTrigger.js.map +1 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/InsertDedupContext.js +24 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/InsertDedupContext.js.map +1 -0
- package/build/dist/Server/Utils/Express.js +12 -0
- package/build/dist/Server/Utils/Express.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/ExceptionSanitizer.js +149 -0
- package/build/dist/Server/Utils/Telemetry/ExceptionSanitizer.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryFanInWriter.js +496 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryFanInWriter.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryWriterClient.js +165 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryWriterClient.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryWriterServer.js +136 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryWriterServer.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryWriterShedMetrics.js +76 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryWriterShedMetrics.js.map +1 -0
- package/build/dist/Types/AI/CodeFixTaskType.js +38 -1
- package/build/dist/Types/AI/CodeFixTaskType.js.map +1 -1
- package/build/dist/Types/AI/ExceptionAIClassification.js +28 -0
- package/build/dist/Types/AI/ExceptionAIClassification.js.map +1 -0
- package/build/dist/Types/Log/LogScrubPatternType.js +7 -0
- package/build/dist/Types/Log/LogScrubPatternType.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStep.js +20 -4
- package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStepMetricViewConfigUtil.js +73 -0
- package/build/dist/Types/Monitor/MonitorStepMetricViewConfigUtil.js.map +1 -0
- package/build/dist/Types/Trace/TraceScrubPatternType.js +7 -0
- package/build/dist/Types/Trace/TraceScrubPatternType.js.map +1 -1
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js +261 -13
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +12 -37
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/SelectFromColumns.js +82 -0
- package/build/dist/UI/Components/ModelTable/SelectFromColumns.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ArgumentsForm.js +30 -6
- package/build/dist/UI/Components/Workflow/ArgumentsForm.js.map +1 -1
- package/build/dist/UI/Components/Workflow/CronScheduleField.js +209 -0
- package/build/dist/UI/Components/Workflow/CronScheduleField.js.map +1 -0
- package/build/dist/UI/Utils/Breadcrumb/BreadcrumbTrailResolver.js +129 -0
- package/build/dist/UI/Utils/Breadcrumb/BreadcrumbTrailResolver.js.map +1 -0
- package/build/dist/UI/Utils/NotificationMethodUtil.js +189 -0
- package/build/dist/UI/Utils/NotificationMethodUtil.js.map +1 -0
- package/build/dist/Utils/CronTab.js +609 -0
- package/build/dist/Utils/CronTab.js.map +1 -0
- package/build/dist/Utils/ModelImportExport.js +20 -6
- package/build/dist/Utils/ModelImportExport.js.map +1 -1
- package/build/dist/Utils/VersionUtil.js +193 -0
- package/build/dist/Utils/VersionUtil.js.map +1 -0
- package/package.json +1 -1
|
@@ -5,6 +5,7 @@ import { JSONObject } from "../../Types/JSON";
|
|
|
5
5
|
import PartialEntity from "../../Types/Database/PartialEntity";
|
|
6
6
|
import EnterpriseLicenseInstanceSummary from "../../Types/EnterpriseLicense/EnterpriseLicenseInstanceSummary";
|
|
7
7
|
import EnterpriseLicenseUsageUtil from "../../Utils/EnterpriseLicense/EnterpriseLicenseUsage";
|
|
8
|
+
import VersionUtil from "../../Utils/VersionUtil";
|
|
8
9
|
import LIMIT_MAX from "../../Types/Database/LimitMax";
|
|
9
10
|
import ObjectID from "../../Types/ObjectID";
|
|
10
11
|
import PositiveNumber from "../../Types/PositiveNumber";
|
|
@@ -29,6 +30,8 @@ export interface LicenseInstanceUpsert {
|
|
|
29
30
|
licenseId: ObjectID;
|
|
30
31
|
instanceId: string;
|
|
31
32
|
host: string | undefined;
|
|
33
|
+
// OneUptime version the instance is running. Absent on instances too old to report it.
|
|
34
|
+
oneuptimeVersion?: string | null | undefined;
|
|
32
35
|
// Usage fields are only set on report-user-count, not on validate.
|
|
33
36
|
userCount?: number | undefined;
|
|
34
37
|
userEmailHashes?: Array<string> | undefined;
|
|
@@ -84,6 +87,7 @@ export default class EnterpriseLicenseAPI extends BaseAPI<
|
|
|
84
87
|
userLimit: true,
|
|
85
88
|
currentUserCount: true,
|
|
86
89
|
userCountUpdatedAt: true,
|
|
90
|
+
isEvaluationLicense: true,
|
|
87
91
|
},
|
|
88
92
|
props: {
|
|
89
93
|
isRoot: true,
|
|
@@ -117,12 +121,16 @@ export default class EnterpriseLicenseAPI extends BaseAPI<
|
|
|
117
121
|
req.body["instanceId"],
|
|
118
122
|
);
|
|
119
123
|
const instanceHost: string = this.parseShortText(req.body["host"]);
|
|
124
|
+
const instanceVersion: string | null | undefined = this.parseVersion(
|
|
125
|
+
req.body["version"],
|
|
126
|
+
);
|
|
120
127
|
|
|
121
128
|
if (instanceId) {
|
|
122
129
|
await this.upsertLicenseInstance({
|
|
123
130
|
licenseId: license.id!,
|
|
124
131
|
instanceId: instanceId,
|
|
125
132
|
host: instanceHost || undefined,
|
|
133
|
+
oneuptimeVersion: instanceVersion,
|
|
126
134
|
});
|
|
127
135
|
}
|
|
128
136
|
|
|
@@ -154,6 +162,7 @@ export default class EnterpriseLicenseAPI extends BaseAPI<
|
|
|
154
162
|
userCountUpdatedAt: license.userCountUpdatedAt
|
|
155
163
|
? license.userCountUpdatedAt.toISOString()
|
|
156
164
|
: null,
|
|
165
|
+
isEvaluationLicense: Boolean(license.isEvaluationLicense),
|
|
157
166
|
instances: this.getInstanceSummaries(instances),
|
|
158
167
|
token,
|
|
159
168
|
});
|
|
@@ -196,6 +205,7 @@ export default class EnterpriseLicenseAPI extends BaseAPI<
|
|
|
196
205
|
select: {
|
|
197
206
|
_id: true,
|
|
198
207
|
userLimit: true,
|
|
208
|
+
isEvaluationLicense: true,
|
|
199
209
|
},
|
|
200
210
|
props: {
|
|
201
211
|
isRoot: true,
|
|
@@ -212,6 +222,9 @@ export default class EnterpriseLicenseAPI extends BaseAPI<
|
|
|
212
222
|
req.body["instanceId"],
|
|
213
223
|
);
|
|
214
224
|
const instanceHost: string = this.parseShortText(req.body["host"]);
|
|
225
|
+
const instanceVersion: string | null | undefined = this.parseVersion(
|
|
226
|
+
req.body["version"],
|
|
227
|
+
);
|
|
215
228
|
const userEmailHashes: Array<string> =
|
|
216
229
|
EnterpriseLicenseUsageUtil.sanitizeUserEmailHashes(
|
|
217
230
|
req.body["userEmailHashes"],
|
|
@@ -230,6 +243,7 @@ export default class EnterpriseLicenseAPI extends BaseAPI<
|
|
|
230
243
|
licenseId: license.id!,
|
|
231
244
|
instanceId: instanceId,
|
|
232
245
|
host: instanceHost || undefined,
|
|
246
|
+
oneuptimeVersion: instanceVersion,
|
|
233
247
|
userCount: userCount,
|
|
234
248
|
userEmailHashes: userEmailHashes,
|
|
235
249
|
masterAdminEmails: masterAdminEmails,
|
|
@@ -273,6 +287,7 @@ export default class EnterpriseLicenseAPI extends BaseAPI<
|
|
|
273
287
|
userCountUpdatedAt: reportedAt.toISOString(),
|
|
274
288
|
userLimit:
|
|
275
289
|
typeof license.userLimit === "number" ? license.userLimit : null,
|
|
290
|
+
isEvaluationLicense: Boolean(license.isEvaluationLicense),
|
|
276
291
|
instances: this.getInstanceSummaries(instances),
|
|
277
292
|
});
|
|
278
293
|
} catch (err) {
|
|
@@ -290,6 +305,31 @@ export default class EnterpriseLicenseAPI extends BaseAPI<
|
|
|
290
305
|
return value.trim().substring(0, 100);
|
|
291
306
|
}
|
|
292
307
|
|
|
308
|
+
/*
|
|
309
|
+
* report-user-count is unauthenticated, so the reported version is only
|
|
310
|
+
* stored when it is a version we could actually compare against a release —
|
|
311
|
+
* garbage never reaches the admin dashboard or the customer's modal.
|
|
312
|
+
*
|
|
313
|
+
* Three outcomes, and the callers depend on the distinction:
|
|
314
|
+
* undefined — the instance sent no version at all (it predates this
|
|
315
|
+
* field). Leave whatever is stored alone.
|
|
316
|
+
* null — it sent something, but not a version. Clear the stored
|
|
317
|
+
* value, so an instance rolled back to a build with no
|
|
318
|
+
* APP_VERSION stops advertising the version it used to run.
|
|
319
|
+
* string — a valid, canonical version to store.
|
|
320
|
+
*/
|
|
321
|
+
private parseVersion(value: unknown): string | null | undefined {
|
|
322
|
+
if (value === undefined || value === null) {
|
|
323
|
+
return undefined;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/*
|
|
327
|
+
* Canonicalized rather than passed through, so the stored string is never
|
|
328
|
+
* something the UIs would render with a doubled "v".
|
|
329
|
+
*/
|
|
330
|
+
return VersionUtil.canonicalize(value);
|
|
331
|
+
}
|
|
332
|
+
|
|
293
333
|
private async findLicenseInstances(
|
|
294
334
|
licenseId: ObjectID,
|
|
295
335
|
): Promise<Array<EnterpriseLicenseInstance>> {
|
|
@@ -304,6 +344,7 @@ export default class EnterpriseLicenseAPI extends BaseAPI<
|
|
|
304
344
|
userCount: true,
|
|
305
345
|
userEmailHashes: true,
|
|
306
346
|
lastReportedAt: true,
|
|
347
|
+
oneuptimeVersion: true,
|
|
307
348
|
},
|
|
308
349
|
sort: {
|
|
309
350
|
createdAt: SortOrder.Ascending,
|
|
@@ -331,6 +372,7 @@ export default class EnterpriseLicenseAPI extends BaseAPI<
|
|
|
331
372
|
lastReportedAt: instance.lastReportedAt
|
|
332
373
|
? instance.lastReportedAt.toISOString()
|
|
333
374
|
: null,
|
|
375
|
+
version: instance.oneuptimeVersion || null,
|
|
334
376
|
};
|
|
335
377
|
},
|
|
336
378
|
);
|
|
@@ -370,6 +412,11 @@ export default class EnterpriseLicenseAPI extends BaseAPI<
|
|
|
370
412
|
newInstance.host = data.host;
|
|
371
413
|
}
|
|
372
414
|
|
|
415
|
+
// Null means "reported something that is not a version" — nothing to store.
|
|
416
|
+
if (data.oneuptimeVersion) {
|
|
417
|
+
newInstance.oneuptimeVersion = data.oneuptimeVersion;
|
|
418
|
+
}
|
|
419
|
+
|
|
373
420
|
if (data.userCount !== undefined) {
|
|
374
421
|
newInstance.userCount = data.userCount;
|
|
375
422
|
}
|
|
@@ -434,6 +481,16 @@ export default class EnterpriseLicenseAPI extends BaseAPI<
|
|
|
434
481
|
updateData.host = data.host;
|
|
435
482
|
}
|
|
436
483
|
|
|
484
|
+
/*
|
|
485
|
+
* Null clears the column: an instance rebuilt onto a build with no
|
|
486
|
+
* APP_VERSION must stop advertising the version it used to run, because
|
|
487
|
+
* lastReportedAt is refreshed on the same request and would otherwise
|
|
488
|
+
* read as "confirmed running v11.5.13 as of today".
|
|
489
|
+
*/
|
|
490
|
+
if (data.oneuptimeVersion !== undefined) {
|
|
491
|
+
updateData.oneuptimeVersion = data.oneuptimeVersion;
|
|
492
|
+
}
|
|
493
|
+
|
|
437
494
|
if (data.userCount !== undefined) {
|
|
438
495
|
updateData.userCount = data.userCount;
|
|
439
496
|
}
|
|
@@ -17,8 +17,14 @@ import API from "../../Utils/API";
|
|
|
17
17
|
import HTTPErrorResponse from "../../Types/API/HTTPErrorResponse";
|
|
18
18
|
import HTTPResponse from "../../Types/API/HTTPResponse";
|
|
19
19
|
import PartialEntity from "../../Types/Database/PartialEntity";
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
AppVersion,
|
|
22
|
+
DisableUpdateCheck,
|
|
23
|
+
EnterpriseLicenseValidationUrl,
|
|
24
|
+
Host,
|
|
25
|
+
} from "../EnvironmentConfig";
|
|
21
26
|
import EnterpriseLicenseInstanceSummary from "../../Types/EnterpriseLicense/EnterpriseLicenseInstanceSummary";
|
|
27
|
+
import VersionUtil from "../../Utils/VersionUtil";
|
|
22
28
|
import UserMiddleware from "../Middleware/UserAuthorization";
|
|
23
29
|
|
|
24
30
|
export default class GlobalConfigAPI extends BaseAPI<
|
|
@@ -67,11 +73,15 @@ export default class GlobalConfigAPI extends BaseAPI<
|
|
|
67
73
|
enterpriseLicenseExpiresAt: true,
|
|
68
74
|
enterpriseLicenseKey: true,
|
|
69
75
|
enterpriseLicenseToken: true,
|
|
76
|
+
enterpriseLicenseIsEvaluation: true,
|
|
70
77
|
enterpriseLicenseUserLimit: true,
|
|
71
78
|
enterpriseLicenseCurrentUserCount: true,
|
|
72
79
|
enterpriseLicenseUserCountUpdatedAt: true,
|
|
73
80
|
enterpriseLicenseInstances: true,
|
|
74
81
|
instanceId: true,
|
|
82
|
+
latestReleaseVersion: true,
|
|
83
|
+
latestReleasePublishedAt: true,
|
|
84
|
+
latestReleaseCheckedAt: true,
|
|
75
85
|
},
|
|
76
86
|
props: {
|
|
77
87
|
isRoot: true,
|
|
@@ -106,6 +116,12 @@ export default class GlobalConfigAPI extends BaseAPI<
|
|
|
106
116
|
? config?.enterpriseLicenseToken || null
|
|
107
117
|
: null,
|
|
108
118
|
licenseValid: licenseValid,
|
|
119
|
+
/*
|
|
120
|
+
* Whether this is an evaluation/testing license. Benign like the
|
|
121
|
+
* company name and expiry, so it is not gated behind sign-in — the
|
|
122
|
+
* edition modal shows the evaluation notice to anyone who opens it.
|
|
123
|
+
*/
|
|
124
|
+
isEvaluationLicense: Boolean(config?.enterpriseLicenseIsEvaluation),
|
|
109
125
|
userLimit:
|
|
110
126
|
typeof config?.enterpriseLicenseUserLimit === "number"
|
|
111
127
|
? config.enterpriseLicenseUserLimit
|
|
@@ -126,6 +142,38 @@ export default class GlobalConfigAPI extends BaseAPI<
|
|
|
126
142
|
isAuthenticatedUser && config?.instanceId
|
|
127
143
|
? config.instanceId.toString()
|
|
128
144
|
: null,
|
|
145
|
+
/*
|
|
146
|
+
* Which build this installation runs, and whether a newer one has
|
|
147
|
+
* been released, are gated the same way as the instance topology:
|
|
148
|
+
* telling an anonymous visitor on the login page that this server
|
|
149
|
+
* is behind on patches advertises an unpatched target.
|
|
150
|
+
*/
|
|
151
|
+
currentVersion: isAuthenticatedUser ? AppVersion : null,
|
|
152
|
+
latestVersion: isAuthenticatedUser
|
|
153
|
+
? config?.latestReleaseVersion || null
|
|
154
|
+
: null,
|
|
155
|
+
latestVersionPublishedAt:
|
|
156
|
+
isAuthenticatedUser && config?.latestReleasePublishedAt
|
|
157
|
+
? config.latestReleasePublishedAt.toISOString()
|
|
158
|
+
: null,
|
|
159
|
+
latestVersionCheckedAt:
|
|
160
|
+
isAuthenticatedUser && config?.latestReleaseCheckedAt
|
|
161
|
+
? config.latestReleaseCheckedAt.toISOString()
|
|
162
|
+
: null,
|
|
163
|
+
isUpdateAvailable: isAuthenticatedUser
|
|
164
|
+
? VersionUtil.isUpdateAvailable({
|
|
165
|
+
currentVersion: AppVersion,
|
|
166
|
+
latestVersion: config?.latestReleaseVersion,
|
|
167
|
+
})
|
|
168
|
+
: false,
|
|
169
|
+
/*
|
|
170
|
+
* Lets the modal say "update checks are off" instead of "has not
|
|
171
|
+
* checked yet", which would be a promise the installation is never
|
|
172
|
+
* going to keep.
|
|
173
|
+
*/
|
|
174
|
+
isUpdateCheckDisabled: isAuthenticatedUser
|
|
175
|
+
? DisableUpdateCheck
|
|
176
|
+
: false,
|
|
129
177
|
};
|
|
130
178
|
|
|
131
179
|
return Response.sendJsonObjectResponse(req, res, responseBody);
|
|
@@ -179,6 +227,12 @@ export default class GlobalConfigAPI extends BaseAPI<
|
|
|
179
227
|
licenseKey,
|
|
180
228
|
instanceId: instanceId.toString(),
|
|
181
229
|
host: Host,
|
|
230
|
+
/*
|
|
231
|
+
* Sent here as well as from the daily report job so the version
|
|
232
|
+
* lands on the license server the moment the key is validated,
|
|
233
|
+
* rather than up to 24 hours later.
|
|
234
|
+
*/
|
|
235
|
+
version: AppVersion,
|
|
182
236
|
},
|
|
183
237
|
});
|
|
184
238
|
|
|
@@ -239,6 +293,9 @@ export default class GlobalConfigAPI extends BaseAPI<
|
|
|
239
293
|
}
|
|
240
294
|
}
|
|
241
295
|
|
|
296
|
+
const isEvaluationLicense: boolean =
|
|
297
|
+
payload["isEvaluationLicense"] === true;
|
|
298
|
+
|
|
242
299
|
const instances: Array<EnterpriseLicenseInstanceSummary> =
|
|
243
300
|
Array.isArray(payload["instances"])
|
|
244
301
|
? (payload[
|
|
@@ -251,6 +308,7 @@ export default class GlobalConfigAPI extends BaseAPI<
|
|
|
251
308
|
enterpriseLicenseKey: licenseKeyRaw || null,
|
|
252
309
|
enterpriseLicenseExpiresAt: licenseExpiry || null,
|
|
253
310
|
enterpriseLicenseToken: licenseToken || null,
|
|
311
|
+
enterpriseLicenseIsEvaluation: isEvaluationLicense,
|
|
254
312
|
enterpriseLicenseUserLimit: userLimit,
|
|
255
313
|
enterpriseLicenseCurrentUserCount: currentUserCount,
|
|
256
314
|
enterpriseLicenseUserCountUpdatedAt: userCountUpdatedAt,
|
|
@@ -287,6 +345,8 @@ export default class GlobalConfigAPI extends BaseAPI<
|
|
|
287
345
|
newConfig.enterpriseLicenseToken = licenseToken;
|
|
288
346
|
}
|
|
289
347
|
|
|
348
|
+
newConfig.enterpriseLicenseIsEvaluation = isEvaluationLicense;
|
|
349
|
+
|
|
290
350
|
if (licenseExpiry) {
|
|
291
351
|
newConfig.enterpriseLicenseExpiresAt = licenseExpiry;
|
|
292
352
|
}
|
|
@@ -321,6 +381,7 @@ export default class GlobalConfigAPI extends BaseAPI<
|
|
|
321
381
|
expiresAt: licenseExpiry ? licenseExpiry.toISOString() : null,
|
|
322
382
|
licenseKey: licenseKeyRaw || null,
|
|
323
383
|
token: licenseToken || null,
|
|
384
|
+
isEvaluationLicense: isEvaluationLicense,
|
|
324
385
|
userLimit: userLimit,
|
|
325
386
|
currentUserCount: currentUserCount,
|
|
326
387
|
userCountUpdatedAt: userCountUpdatedAt
|
|
@@ -665,6 +665,14 @@ export const IpWhitelist: string = process.env["IP_WHITELIST"] || "";
|
|
|
665
665
|
export const DisableTelemetry: boolean =
|
|
666
666
|
process.env["DISABLE_TELEMETRY"] === "true";
|
|
667
667
|
|
|
668
|
+
/*
|
|
669
|
+
* Opt out of the daily "is a newer OneUptime released?" check against the
|
|
670
|
+
* GitHub API. Deliberately separate from DISABLE_TELEMETRY, which turns off
|
|
671
|
+
* the OpenTelemetry SDK and says nothing about outbound calls.
|
|
672
|
+
*/
|
|
673
|
+
export const DisableUpdateCheck: boolean =
|
|
674
|
+
process.env["DISABLE_UPDATE_CHECK"] === "true";
|
|
675
|
+
|
|
668
676
|
export const EnableProfiling: boolean =
|
|
669
677
|
process.env["ENABLE_PROFILING"] === "true";
|
|
670
678
|
|
|
@@ -782,6 +790,20 @@ export const EnterpriseLicenseUserCountReportUrl: URL = URL.fromString(
|
|
|
782
790
|
"https://oneuptime.com/api/enterprise-license/report-user-count",
|
|
783
791
|
);
|
|
784
792
|
|
|
793
|
+
/*
|
|
794
|
+
* GitHub's "latest release" endpoint. It already excludes drafts and
|
|
795
|
+
* prereleases, so whatever it returns is a version an administrator can
|
|
796
|
+
* safely be told to upgrade to.
|
|
797
|
+
*
|
|
798
|
+
* Overridable so an installation with no route to github.com can point the
|
|
799
|
+
* check at an internal mirror instead of turning it off entirely. The mirror
|
|
800
|
+
* must answer with GitHub's release shape (tag_name, html_url, published_at).
|
|
801
|
+
*/
|
|
802
|
+
export const LatestReleaseCheckUrl: URL = URL.fromString(
|
|
803
|
+
process.env["LATEST_RELEASE_CHECK_URL"] ||
|
|
804
|
+
"https://api.github.com/repos/OneUptime/oneuptime/releases/latest",
|
|
805
|
+
);
|
|
806
|
+
|
|
785
807
|
// Inbound Email Configuration for Incoming Email Monitor
|
|
786
808
|
export enum InboundEmailProviderType {
|
|
787
809
|
SendGrid = "SendGrid",
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* AI exception triage + privacy columns:
|
|
5
|
+
*
|
|
6
|
+
* - TelemetryException.unhandled: sticky rollup of OTel exception.escaped
|
|
7
|
+
* (maintained by the ingest upsert with OR semantics).
|
|
8
|
+
* - TelemetryException.aiClassification: triage verdict (code-fault,
|
|
9
|
+
* user-error, expected-denial, infrastructure, unknown).
|
|
10
|
+
* - TelemetryException.aiFixDeclinedAt: stamped when a human closes an
|
|
11
|
+
* AI fix PR without merging; suppresses further automatic fix attempts.
|
|
12
|
+
* - AIInsight.classification: the same triage verdict on the insight row.
|
|
13
|
+
* - Project.autoArchiveNonActionableExceptions: opt-in auto-archive of
|
|
14
|
+
* expected-denial exception groups.
|
|
15
|
+
*/
|
|
16
|
+
export class AddExceptionTriageAndPrivacyColumns1784640000000
|
|
17
|
+
implements MigrationInterface
|
|
18
|
+
{
|
|
19
|
+
public name: string = "AddExceptionTriageAndPrivacyColumns1784640000000";
|
|
20
|
+
|
|
21
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
22
|
+
await queryRunner.query(
|
|
23
|
+
`ALTER TABLE "TelemetryException" ADD "unhandled" boolean NOT NULL DEFAULT false`,
|
|
24
|
+
);
|
|
25
|
+
await queryRunner.query(
|
|
26
|
+
`ALTER TABLE "TelemetryException" ADD "aiClassification" character varying(100)`,
|
|
27
|
+
);
|
|
28
|
+
await queryRunner.query(
|
|
29
|
+
`ALTER TABLE "TelemetryException" ADD "aiFixDeclinedAt" TIMESTAMP WITH TIME ZONE`,
|
|
30
|
+
);
|
|
31
|
+
await queryRunner.query(
|
|
32
|
+
`ALTER TABLE "AIInsight" ADD "classification" character varying(100)`,
|
|
33
|
+
);
|
|
34
|
+
await queryRunner.query(
|
|
35
|
+
`ALTER TABLE "Project" ADD "autoArchiveNonActionableExceptions" boolean NOT NULL DEFAULT false`,
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
40
|
+
await queryRunner.query(
|
|
41
|
+
`ALTER TABLE "Project" DROP COLUMN "autoArchiveNonActionableExceptions"`,
|
|
42
|
+
);
|
|
43
|
+
await queryRunner.query(
|
|
44
|
+
`ALTER TABLE "AIInsight" DROP COLUMN "classification"`,
|
|
45
|
+
);
|
|
46
|
+
await queryRunner.query(
|
|
47
|
+
`ALTER TABLE "TelemetryException" DROP COLUMN "aiFixDeclinedAt"`,
|
|
48
|
+
);
|
|
49
|
+
await queryRunner.query(
|
|
50
|
+
`ALTER TABLE "TelemetryException" DROP COLUMN "aiClassification"`,
|
|
51
|
+
);
|
|
52
|
+
await queryRunner.query(
|
|
53
|
+
`ALTER TABLE "TelemetryException" DROP COLUMN "unhandled"`,
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
export class AddInstanceVersionAndLatestReleaseColumns1784659816363
|
|
4
|
+
implements MigrationInterface
|
|
5
|
+
{
|
|
6
|
+
public name: string =
|
|
7
|
+
"AddInstanceVersionAndLatestReleaseColumns1784659816363";
|
|
8
|
+
|
|
9
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
10
|
+
await queryRunner.query(
|
|
11
|
+
`ALTER TABLE "EnterpriseLicenseInstance" ADD "oneuptimeVersion" character varying(100)`,
|
|
12
|
+
);
|
|
13
|
+
await queryRunner.query(
|
|
14
|
+
`ALTER TABLE "GlobalConfig" ADD "latestReleaseVersion" character varying(100)`,
|
|
15
|
+
);
|
|
16
|
+
await queryRunner.query(
|
|
17
|
+
`ALTER TABLE "GlobalConfig" ADD "latestReleasePublishedAt" TIMESTAMP WITH TIME ZONE`,
|
|
18
|
+
);
|
|
19
|
+
await queryRunner.query(
|
|
20
|
+
`ALTER TABLE "GlobalConfig" ADD "latestReleaseCheckedAt" TIMESTAMP WITH TIME ZONE`,
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
25
|
+
await queryRunner.query(
|
|
26
|
+
`ALTER TABLE "GlobalConfig" DROP COLUMN "latestReleaseCheckedAt"`,
|
|
27
|
+
);
|
|
28
|
+
await queryRunner.query(
|
|
29
|
+
`ALTER TABLE "GlobalConfig" DROP COLUMN "latestReleasePublishedAt"`,
|
|
30
|
+
);
|
|
31
|
+
await queryRunner.query(
|
|
32
|
+
`ALTER TABLE "GlobalConfig" DROP COLUMN "latestReleaseVersion"`,
|
|
33
|
+
);
|
|
34
|
+
await queryRunner.query(
|
|
35
|
+
`ALTER TABLE "EnterpriseLicenseInstance" DROP COLUMN "oneuptimeVersion"`,
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
export class AddEnterpriseLicenseEvaluationColumns1784705487674
|
|
4
|
+
implements MigrationInterface
|
|
5
|
+
{
|
|
6
|
+
public name: string = "AddEnterpriseLicenseEvaluationColumns1784705487674";
|
|
7
|
+
|
|
8
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
9
|
+
await queryRunner.query(
|
|
10
|
+
`ALTER TABLE "EnterpriseLicense" ADD "isEvaluationLicense" boolean NOT NULL DEFAULT false`,
|
|
11
|
+
);
|
|
12
|
+
await queryRunner.query(
|
|
13
|
+
`ALTER TABLE "GlobalConfig" ADD "enterpriseLicenseIsEvaluation" boolean DEFAULT false`,
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
18
|
+
await queryRunner.query(
|
|
19
|
+
`ALTER TABLE "GlobalConfig" DROP COLUMN "enterpriseLicenseIsEvaluation"`,
|
|
20
|
+
);
|
|
21
|
+
await queryRunner.query(
|
|
22
|
+
`ALTER TABLE "EnterpriseLicense" DROP COLUMN "isEvaluationLicense"`,
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -461,6 +461,9 @@ import { AddNetworkDeviceInventoryAndDiscoverySchedule1784211212164 } from "./17
|
|
|
461
461
|
import { AddEnterpriseLicenseNotificationColumns1784218257664 } from "./1784218257664-AddEnterpriseLicenseNotificationColumns";
|
|
462
462
|
import { AddAttributionColumnsToUserAndProject1784293516000 } from "./1784293516000-AddAttributionColumnsToUserAndProject";
|
|
463
463
|
import { AddMarketingConversionTable1784298000000 } from "./1784298000000-AddMarketingConversionTable";
|
|
464
|
+
import { AddExceptionTriageAndPrivacyColumns1784640000000 } from "./1784640000000-AddExceptionTriageAndPrivacyColumns";
|
|
465
|
+
import { AddInstanceVersionAndLatestReleaseColumns1784659816363 } from "./1784659816363-AddInstanceVersionAndLatestReleaseColumns";
|
|
466
|
+
import { AddEnterpriseLicenseEvaluationColumns1784705487674 } from "./1784705487674-AddEnterpriseLicenseEvaluationColumns";
|
|
464
467
|
|
|
465
468
|
export default [
|
|
466
469
|
InitialMigration,
|
|
@@ -926,4 +929,7 @@ export default [
|
|
|
926
929
|
AddEnterpriseLicenseNotificationColumns1784218257664,
|
|
927
930
|
AddAttributionColumnsToUserAndProject1784293516000,
|
|
928
931
|
AddMarketingConversionTable1784298000000,
|
|
932
|
+
AddExceptionTriageAndPrivacyColumns1784640000000,
|
|
933
|
+
AddInstanceVersionAndLatestReleaseColumns1784659816363,
|
|
934
|
+
AddEnterpriseLicenseEvaluationColumns1784705487674,
|
|
929
935
|
];
|
|
@@ -187,6 +187,14 @@ export class Service extends DatabaseService<Model> {
|
|
|
187
187
|
run.triggeredByIncidentId || run.triggeredByAlertId
|
|
188
188
|
? null
|
|
189
189
|
: "Queued code-fix run has no incident or alert subject.";
|
|
190
|
+
} else if (
|
|
191
|
+
run.codeFixTaskType === CodeFixTaskType.ImproveLogging ||
|
|
192
|
+
run.codeFixTaskType === CodeFixTaskType.ImproveTracing
|
|
193
|
+
) {
|
|
194
|
+
// Service-scoped instrumentation recipes carry the service instead.
|
|
195
|
+
missingContextMessage = run.taskContext?.telemetryServiceId
|
|
196
|
+
? null
|
|
197
|
+
: "Queued code-fix run has no telemetry service in its task context.";
|
|
190
198
|
} else {
|
|
191
199
|
missingContextMessage = run.taskContext?.traceId
|
|
192
200
|
? null
|
|
@@ -47,7 +47,7 @@ import {
|
|
|
47
47
|
ResponseJSON,
|
|
48
48
|
ResultSet,
|
|
49
49
|
} from "@clickhouse/client";
|
|
50
|
-
import {
|
|
50
|
+
import { nextInsertDedupToken } from "../Utils/AnalyticsDatabase/InsertDedupContext";
|
|
51
51
|
import AnalyticsBaseModel from "../../Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel";
|
|
52
52
|
import { WorkflowRoute } from "../../ServiceRoute";
|
|
53
53
|
import Protocol from "../../Types/API/Protocol";
|
|
@@ -140,40 +140,42 @@ export const MigrationExecuteOptions: ClickhouseExecuteOptions = {
|
|
|
140
140
|
},
|
|
141
141
|
};
|
|
142
142
|
|
|
143
|
-
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
* "<tokenBase>:<table>:<chunkIndex>"` plus async_insert_deduplicate=1 /
|
|
149
|
-
* wait_for_async_insert=1, so a stalled-job retry that re-processes the
|
|
150
|
-
* same payload re-issues byte-identical tokens and ClickHouse drops the
|
|
151
|
-
* duplicate blocks (on replicated tables; on plain MergeTree the token is
|
|
152
|
-
* ignored unless non_replicated_deduplication_window is set — no harm
|
|
153
|
-
* either way). The chunk counter is per table because one job inserts
|
|
154
|
-
* into several tables (e.g. Span + ExceptionInstance) in a deterministic
|
|
155
|
-
* order.
|
|
143
|
+
/*
|
|
144
|
+
* The insert-dedup ambient context lives in
|
|
145
|
+
* Utils/AnalyticsDatabase/InsertDedupContext so that both this service and
|
|
146
|
+
* TelemetryFanInWriter can consume deterministic tokens without an import
|
|
147
|
+
* cycle. Re-exported here for existing callers.
|
|
156
148
|
*
|
|
157
|
-
* HTTP-path inserts run outside the context and
|
|
158
|
-
*
|
|
159
|
-
* affordable off the request
|
|
149
|
+
* HTTP-path inserts run outside the context and are always fire-and-forget
|
|
150
|
+
* (wait_for_async_insert=0) — flush waiting, when opted into via
|
|
151
|
+
* TELEMETRY_WAIT_FOR_ASYNC_INSERT, is only affordable off the request
|
|
152
|
+
* thread and therefore only applies to tokened (queue-job) inserts.
|
|
160
153
|
*/
|
|
161
|
-
export
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
154
|
+
export {
|
|
155
|
+
runWithInsertDedup,
|
|
156
|
+
nextInsertDedupToken,
|
|
157
|
+
type InsertDedupContextStore,
|
|
158
|
+
} from "../Utils/AnalyticsDatabase/InsertDedupContext";
|
|
165
159
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
160
|
+
/*
|
|
161
|
+
* Ack mode for telemetry ClickHouse inserts.
|
|
162
|
+
*
|
|
163
|
+
* Default (false): wait_for_async_insert=0 — fire-and-forget. ClickHouse
|
|
164
|
+
* acks as soon as the batch is accepted into its async-insert buffer and
|
|
165
|
+
* owns flushing from there; inserts release their query slot almost
|
|
166
|
+
* immediately. The trade: flush-time errors surface only in server logs
|
|
167
|
+
* (system.asynchronous_inserts / part log), and a ClickHouse crash between
|
|
168
|
+
* buffer-accept and flush loses that buffer even though callers were acked.
|
|
169
|
+
*
|
|
170
|
+
* Set TELEMETRY_WAIT_FOR_ASYNC_INSERT=true to make every tokened insert
|
|
171
|
+
* wait for the durable flush before acking (ack-after-flush end to end
|
|
172
|
+
* through the fan-in writer and writer tier) — at the cost of each waiting
|
|
173
|
+
* insert holding a ClickHouse query slot until its buffer flushes.
|
|
174
|
+
*/
|
|
175
|
+
export function shouldWaitForAsyncInsert(): boolean {
|
|
176
|
+
const raw: string | undefined =
|
|
177
|
+
process.env["TELEMETRY_WAIT_FOR_ASYNC_INSERT"];
|
|
178
|
+
return raw === "true" || raw === "1";
|
|
177
179
|
}
|
|
178
180
|
|
|
179
181
|
export default class AnalyticsDatabaseService<
|
|
@@ -259,30 +261,31 @@ export default class AnalyticsDatabaseService<
|
|
|
259
261
|
let dedupToken: string | undefined = options?.dedupToken;
|
|
260
262
|
|
|
261
263
|
if (!dedupToken) {
|
|
262
|
-
|
|
263
|
-
insertDedupContext.getStore();
|
|
264
|
-
if (dedupStore) {
|
|
265
|
-
const chunkIndex: number =
|
|
266
|
-
dedupStore.chunkIndexByTable.get(tableName) ?? 0;
|
|
267
|
-
dedupStore.chunkIndexByTable.set(tableName, chunkIndex + 1);
|
|
268
|
-
dedupToken = `${dedupStore.tokenBase}:${tableName}:${chunkIndex}`;
|
|
269
|
-
}
|
|
264
|
+
dedupToken = nextInsertDedupToken(tableName);
|
|
270
265
|
}
|
|
271
266
|
|
|
267
|
+
const waitForAsyncInsert: 0 | 1 = shouldWaitForAsyncInsert() ? 1 : 0;
|
|
268
|
+
|
|
272
269
|
let clickhouseSettings: ClickHouseSettings = {
|
|
273
270
|
async_insert: 1,
|
|
274
|
-
wait_for_async_insert:
|
|
271
|
+
wait_for_async_insert: waitForAsyncInsert,
|
|
275
272
|
};
|
|
276
273
|
|
|
277
274
|
if (dedupToken) {
|
|
278
275
|
/*
|
|
279
|
-
*
|
|
280
|
-
*
|
|
281
|
-
*
|
|
276
|
+
* Dedup settings ride along in both ack modes. For async inserts
|
|
277
|
+
* ClickHouse dedups by content hash of the insert body when
|
|
278
|
+
* async_insert_deduplicate=1 (the explicit token is not yet honored
|
|
279
|
+
* for async inserts — ClickHouse #52018), so byte-identical queue
|
|
280
|
+
* retries are still dropped. The ack mode is a separate, deliberate
|
|
281
|
+
* trade (see shouldWaitForAsyncInsert): by default ClickHouse owns
|
|
282
|
+
* flushing and an ack means "accepted into the async-insert buffer";
|
|
283
|
+
* with TELEMETRY_WAIT_FOR_ASYNC_INSERT=true the ack waits for the
|
|
284
|
+
* durable flush instead.
|
|
282
285
|
*/
|
|
283
286
|
clickhouseSettings = {
|
|
284
287
|
async_insert: 1,
|
|
285
|
-
wait_for_async_insert:
|
|
288
|
+
wait_for_async_insert: waitForAsyncInsert,
|
|
286
289
|
async_insert_deduplicate: 1,
|
|
287
290
|
insert_deduplication_token: dedupToken,
|
|
288
291
|
};
|