@oneuptime/common 12.0.2 → 12.0.3
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/DatabaseBaseModel/DatabaseBaseModel.ts +14 -0
- package/Models/DatabaseModels/Index.ts +2 -0
- package/Models/DatabaseModels/LogDropFilter.ts +9 -20
- package/Models/DatabaseModels/StatusPageMonitorRule.ts +911 -0
- package/Models/DatabaseModels/StatusPageResource.ts +73 -0
- package/Models/DatabaseModels/TraceDropFilter.ts +9 -20
- package/Server/API/BaseAPI.ts +13 -2
- package/Server/API/DashboardAPI.ts +39 -0
- package/Server/EnvironmentConfig.ts +31 -1
- package/Server/Infrastructure/GlobalCache.ts +115 -0
- package/Server/Infrastructure/Postgres/DataSourceOptions.ts +16 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.ts +123 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
- package/Server/Services/AlertStateTimelineService.ts +20 -0
- package/Server/Services/CephClusterService.ts +26 -86
- package/Server/Services/CloudResourceService.ts +23 -83
- package/Server/Services/DatabaseService.ts +111 -0
- package/Server/Services/DockerHostService.ts +19 -79
- package/Server/Services/DockerSwarmClusterService.ts +27 -88
- package/Server/Services/HostService.ts +46 -99
- package/Server/Services/IncidentStateTimelineService.ts +20 -0
- package/Server/Services/Index.ts +2 -0
- package/Server/Services/IoTFleetService.ts +19 -79
- package/Server/Services/KubernetesClusterService.ts +17 -78
- package/Server/Services/LabelService.ts +57 -10
- package/Server/Services/MetricTypeService.ts +84 -0
- package/Server/Services/MonitorService.ts +89 -14
- package/Server/Services/MonitorStatusTimelineService.ts +29 -0
- package/Server/Services/MonitorTemplateService.ts +65 -0
- package/Server/Services/PodmanHostService.ts +19 -79
- package/Server/Services/ProxmoxClusterService.ts +23 -83
- package/Server/Services/RumApplicationService.ts +19 -80
- package/Server/Services/ScheduledMaintenanceStateTimelineService.ts +21 -0
- package/Server/Services/ServerlessFunctionService.ts +24 -85
- package/Server/Services/ServiceService.ts +299 -73
- package/Server/Services/StatusPageMonitorRuleEngineService.ts +752 -0
- package/Server/Services/StatusPageMonitorRuleService.ts +471 -0
- package/Server/Services/TelemetryEntityRelationshipService.ts +1 -0
- package/Server/Services/TelemetryEntityService.ts +1 -0
- package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +170 -39
- package/Server/Utils/Monitor/MonitorResource.ts +63 -36
- package/Server/Utils/Monitor/MonitorStatusTimeline.ts +36 -0
- package/Server/Utils/Monitor/MonitorStepsProjectValidator.ts +148 -127
- package/Server/Utils/Monitor/MonitorStepsReferenceExtractor.ts +434 -0
- package/Server/Utils/SingleFlight.ts +83 -0
- package/Server/Utils/StatusPage/MonitorRulePatternValidator.ts +46 -0
- package/Server/Utils/Telemetry/EntityRegistry.ts +107 -26
- package/Server/Utils/Telemetry/ResourceHeartbeat.ts +314 -0
- package/Server/Utils/Telemetry/Telemetry.ts +351 -122
- package/Tests/App/Dashboard/WidgetCatalog.test.ts +2 -0
- package/Tests/Models/DatabaseModels/ColumnTransformerInvariants.test.ts +306 -0
- package/Tests/Models/DatabaseModels/NumberColumnDeserialization.test.ts +333 -0
- package/Tests/Models/DatabaseModels/StatusPageMonitorRuleModel.test.ts +190 -0
- package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +26 -0
- package/Tests/Server/Infrastructure/GlobalCache.test.ts +226 -0
- package/Tests/Server/Infrastructure/PostgresLockTimeoutOptions.test.ts +181 -0
- package/Tests/Server/Services/DatabaseServiceUpdateColumnsIfUnlocked.test.ts +342 -0
- package/Tests/Server/Services/DropFilterSaveValidation.test.ts +245 -0
- package/Tests/Server/Services/HostServiceUpdateLastSeen.test.ts +163 -15
- package/Tests/Server/Services/MetricTypeAttachServices.test.ts +222 -0
- package/Tests/Server/Services/MissingReferenceWriteGuard.test.ts +614 -0
- package/Tests/Server/Services/MonitorStatusTimelineFastPath.test.ts +23 -0
- package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +305 -56
- package/Tests/Server/Services/ServiceUpdateLastSeenSplitGates.test.ts +705 -0
- package/Tests/Server/Services/StatusPageMonitorRuleEngineService.test.ts +1719 -0
- package/Tests/Server/Services/StatusPageMonitorRuleHooks.test.ts +1212 -0
- package/Tests/Server/Services/StatusPageMonitorRuleMonitorCreateHook.test.ts +228 -0
- package/Tests/Server/Utils/APIKey/AccessPermission.test.ts +267 -0
- package/Tests/Server/Utils/Database/ProjectScopedReferenceValidator.test.ts +260 -13
- package/Tests/Server/Utils/EntityRegistryRowFence.test.ts +302 -0
- package/Tests/Server/Utils/MetricTypeIndexConvergence.test.ts +598 -0
- package/Tests/Server/Utils/Monitor/MonitorStatusTimelineUnusableStatus.test.ts +154 -0
- package/Tests/Server/Utils/Monitor/MonitorStepsProjectValidator.test.ts +638 -92
- package/Tests/Server/Utils/Monitor/MonitorStepsReferenceExtractor.test.ts +724 -0
- package/Tests/Server/Utils/SingleFlight.test.ts +221 -0
- package/Tests/Types/Dashboard/NetworkDashboardTemplate.test.ts +793 -0
- package/Tests/Types/Database/BigIntColumnTransformer.test.ts +147 -0
- package/Tests/Types/Database/DatabasePropertyTransformer.test.ts +140 -0
- package/Tests/Types/Database/NumericColumnValue.test.ts +127 -0
- package/Tests/Types/Decimal.test.ts +37 -0
- package/Tests/Types/Monitor/SnmpVersionParsing.test.ts +152 -0
- package/Tests/Types/Port.test.ts +38 -0
- package/Tests/UI/Components/LogsHistogram.test.tsx +276 -0
- package/Tests/UI/Components/UseLiveLogsRefresh.test.tsx +489 -0
- package/Tests/UI/Components/UseLogsHistogram.test.tsx +500 -0
- package/Tests/Utils/Dashboard/Components/DashboardComponentsUtil.test.ts +120 -0
- package/Tests/Utils/Dashboard/DashboardNetworkMapComponent.test.ts +327 -0
- package/Types/Dashboard/DashboardComponentType.ts +1 -0
- package/Types/Dashboard/DashboardComponents/ComponentArgument.ts +1 -0
- package/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.ts +40 -0
- package/Types/Dashboard/DashboardTemplates.ts +397 -0
- package/Types/Database/BigIntColumnTransformer.ts +74 -0
- package/Types/Database/DatabaseProperty.ts +26 -0
- package/Types/Database/NumericColumnValue.ts +128 -0
- package/Types/Decimal.ts +16 -4
- package/Types/Permission.ts +48 -0
- package/Types/Port.ts +16 -4
- package/UI/Components/LogsViewer/useLiveLogsRefresh.ts +86 -0
- package/UI/Components/LogsViewer/useLogsHistogram.ts +113 -0
- package/Utils/Dashboard/Components/DashboardNetworkMapComponent.ts +136 -0
- package/Utils/Dashboard/Components/Index.ts +7 -0
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js +12 -0
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +2 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/LogDropFilter.js +9 -20
- package/build/dist/Models/DatabaseModels/LogDropFilter.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageMonitorRule.js +925 -0
- package/build/dist/Models/DatabaseModels/StatusPageMonitorRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/StatusPageResource.js +72 -0
- package/build/dist/Models/DatabaseModels/StatusPageResource.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TraceDropFilter.js +9 -20
- package/build/dist/Models/DatabaseModels/TraceDropFilter.js.map +1 -1
- package/build/dist/Server/API/BaseAPI.js +11 -1
- package/build/dist/Server/API/BaseAPI.js.map +1 -1
- package/build/dist/Server/API/DashboardAPI.js +39 -0
- package/build/dist/Server/API/DashboardAPI.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +27 -1
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/GlobalCache.js +91 -0
- package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js +5 -3
- package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.js +48 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/AlertStateTimelineService.js +19 -0
- package/build/dist/Server/Services/AlertStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/CephClusterService.js +26 -76
- package/build/dist/Server/Services/CephClusterService.js.map +1 -1
- package/build/dist/Server/Services/CloudResourceService.js +23 -73
- package/build/dist/Server/Services/CloudResourceService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +86 -0
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/DockerHostService.js +19 -69
- package/build/dist/Server/Services/DockerHostService.js.map +1 -1
- package/build/dist/Server/Services/DockerSwarmClusterService.js +27 -78
- package/build/dist/Server/Services/DockerSwarmClusterService.js.map +1 -1
- package/build/dist/Server/Services/HostService.js +58 -102
- package/build/dist/Server/Services/HostService.js.map +1 -1
- package/build/dist/Server/Services/IncidentStateTimelineService.js +19 -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/IoTFleetService.js +19 -69
- package/build/dist/Server/Services/IoTFleetService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesClusterService.js +17 -68
- package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
- package/build/dist/Server/Services/LabelService.js +42 -7
- package/build/dist/Server/Services/LabelService.js.map +1 -1
- package/build/dist/Server/Services/MetricTypeService.js +74 -0
- package/build/dist/Server/Services/MetricTypeService.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +81 -9
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/MonitorStatusTimelineService.js +28 -0
- package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
- package/build/dist/Server/Services/MonitorTemplateService.js +60 -0
- package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
- package/build/dist/Server/Services/PodmanHostService.js +19 -69
- package/build/dist/Server/Services/PodmanHostService.js.map +1 -1
- package/build/dist/Server/Services/ProxmoxClusterService.js +23 -73
- package/build/dist/Server/Services/ProxmoxClusterService.js.map +1 -1
- package/build/dist/Server/Services/RumApplicationService.js +19 -70
- package/build/dist/Server/Services/RumApplicationService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js +20 -0
- package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/ServerlessFunctionService.js +24 -75
- package/build/dist/Server/Services/ServerlessFunctionService.js.map +1 -1
- package/build/dist/Server/Services/ServiceService.js +236 -61
- package/build/dist/Server/Services/ServiceService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js +603 -0
- package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/StatusPageMonitorRuleService.js +401 -0
- package/build/dist/Server/Services/StatusPageMonitorRuleService.js.map +1 -0
- package/build/dist/Server/Services/TelemetryEntityRelationshipService.js +1 -0
- package/build/dist/Server/Services/TelemetryEntityRelationshipService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryEntityService.js +1 -0
- package/build/dist/Server/Services/TelemetryEntityService.js.map +1 -1
- package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +105 -29
- package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +56 -32
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js +30 -3
- package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js +110 -98
- package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorStepsReferenceExtractor.js +312 -0
- package/build/dist/Server/Utils/Monitor/MonitorStepsReferenceExtractor.js.map +1 -0
- package/build/dist/Server/Utils/SingleFlight.js +76 -0
- package/build/dist/Server/Utils/SingleFlight.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/MonitorRulePatternValidator.js +29 -0
- package/build/dist/Server/Utils/StatusPage/MonitorRulePatternValidator.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +78 -19
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js +160 -0
- package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/Telemetry.js +279 -104
- package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponentType.js +1 -0
- package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardTemplates.js +368 -0
- package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
- package/build/dist/Types/Database/BigIntColumnTransformer.js +66 -0
- package/build/dist/Types/Database/BigIntColumnTransformer.js.map +1 -0
- package/build/dist/Types/Database/DatabaseProperty.js +25 -0
- package/build/dist/Types/Database/DatabaseProperty.js.map +1 -1
- package/build/dist/Types/Database/NumericColumnValue.js +97 -0
- package/build/dist/Types/Database/NumericColumnValue.js.map +1 -0
- package/build/dist/Types/Decimal.js +14 -3
- package/build/dist/Types/Decimal.js.map +1 -1
- package/build/dist/Types/Permission.js +42 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Port.js +15 -5
- package/build/dist/Types/Port.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/useLiveLogsRefresh.js +44 -0
- package/build/dist/UI/Components/LogsViewer/useLiveLogsRefresh.js.map +1 -0
- package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js +75 -0
- package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardNetworkMapComponent.js +114 -0
- package/build/dist/Utils/Dashboard/Components/DashboardNetworkMapComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/Index.js +4 -0
- package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SingleFlight.js","sourceRoot":"","sources":["../../../../Server/Utils/SingleFlight.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAqB,YAAY;IAM/B;;;;;;;OAOG;IACI,MAAM,CAAC,KAAK,CAAC,GAAG,CAAI,GAAW,EAAE,IAAsB;QAC5D,MAAM,QAAQ,GAAiC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAEtE,IAAI,QAAQ,EAAE,CAAC;YACb,OAAO,CAAC,MAAM,QAAQ,CAAM,CAAC;QAC/B,CAAC;QAED;;;;WAIG;QACH,MAAM,OAAO,GAAe,CAAC,KAAK,IAAI,EAAE;YACtC,OAAO,MAAM,IAAI,EAAE,CAAC;QACtB,CAAC,CAAC,EAAE,CAAC;QAEL,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAEhC,IAAI,CAAC;YACH,OAAO,MAAM,OAAO,CAAC;QACvB,CAAC;gBAAS,CAAC;YACT;;;;;eAKG;YACH,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,OAAO,EAAE,CAAC;gBACvC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC5B,CAAC;QACH,CAAC;IACH,CAAC;IAED,2EAA2E;IACpE,MAAM,CAAC,aAAa;QACzB,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;IAC5B,CAAC;IAED,4CAA4C;IACrC,MAAM,CAAC,KAAK;QACjB,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;;AAtDc,qBAAQ,GAAkC,IAAI,GAAG,EAG7D,CAAC;eAJe,YAAY"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import BadDataException from "../../../Types/Exception/BadDataException";
|
|
2
|
+
import RulePatternMatchUtil from "../../../Utils/Rules/RulePatternMatchUtil";
|
|
3
|
+
/*
|
|
4
|
+
* Write-time validation for the monitor name / description patterns on status
|
|
5
|
+
* page monitor rules.
|
|
6
|
+
*
|
|
7
|
+
* The rule engine accepts a case-insensitive regex or a '*' wildcard glob, the
|
|
8
|
+
* same two syntaxes the network device rules take. Anything else - `api-(01`,
|
|
9
|
+
* `[unclosed` - compiles to nothing and matches nothing, and the engine can
|
|
10
|
+
* only log about it long after the user left the form. Reject it at the write
|
|
11
|
+
* instead, exactly as NetworkDeviceRulePatternValidator does
|
|
12
|
+
* (OneUptime/oneuptime#2940).
|
|
13
|
+
*/
|
|
14
|
+
export default class StatusPageMonitorRulePatternValidator {
|
|
15
|
+
static validate(data) {
|
|
16
|
+
StatusPageMonitorRulePatternValidator.validateOne("Monitor Name Pattern", data.namePattern);
|
|
17
|
+
StatusPageMonitorRulePatternValidator.validateOne("Monitor Description Pattern", data.descriptionPattern);
|
|
18
|
+
}
|
|
19
|
+
static validateOne(title, pattern) {
|
|
20
|
+
if (!pattern) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (RulePatternMatchUtil.isSupportedPattern(pattern)) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
throw new BadDataException(`${title} "${pattern}" is not a valid regular expression, and contains no '*' wildcard to fall back on, so it would never match a monitor. Use a regex such as api-.* or a wildcard such as *api*.`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=MonitorRulePatternValidator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MonitorRulePatternValidator.js","sourceRoot":"","sources":["../../../../../Server/Utils/StatusPage/MonitorRulePatternValidator.ts"],"names":[],"mappings":"AAAA,OAAO,gBAAgB,MAAM,2CAA2C,CAAC;AACzE,OAAO,oBAAoB,MAAM,2CAA2C,CAAC;AAE7E;;;;;;;;;;GAUG;AACH,MAAM,CAAC,OAAO,OAAO,qCAAqC;IACjD,MAAM,CAAC,QAAQ,CAAC,IAGtB;QACC,qCAAqC,CAAC,WAAW,CAC/C,sBAAsB,EACtB,IAAI,CAAC,WAAW,CACjB,CAAC;QACF,qCAAqC,CAAC,WAAW,CAC/C,6BAA6B,EAC7B,IAAI,CAAC,kBAAkB,CACxB,CAAC;IACJ,CAAC;IAEO,MAAM,CAAC,WAAW,CACxB,KAAa,EACb,OAAkC;QAElC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO;QACT,CAAC;QAED,IAAI,oBAAoB,CAAC,kBAAkB,CAAC,OAAO,CAAC,EAAE,CAAC;YACrD,OAAO;QACT,CAAC;QAED,MAAM,IAAI,gBAAgB,CACxB,GAAG,KAAK,KAAK,OAAO,+KAA+K,CACpM,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -4,6 +4,7 @@ import logger from "../Logger";
|
|
|
4
4
|
import TelemetryEntityService from "../../Services/TelemetryEntityService";
|
|
5
5
|
import TelemetryEntityRelationshipService from "../../Services/TelemetryEntityRelationshipService";
|
|
6
6
|
import { deriveRelationships, } from "../../../Utils/Telemetry/EntityRelationship";
|
|
7
|
+
import crypto from "crypto";
|
|
7
8
|
/*
|
|
8
9
|
* Shared entity-registry reconciliation machinery (phases 2 + 5 of the
|
|
9
10
|
* entity model — see Internal/Docs/OpenTelemetryEntities.md). Lives in
|
|
@@ -65,24 +66,66 @@ export const REGISTRY_PROMOTED_TYPES = new Set(Object.values(EntityType).filter(
|
|
|
65
66
|
*/
|
|
66
67
|
const FENCE_NAMESPACE = "otel-maintenance-fence";
|
|
67
68
|
const FENCE_SCOPE = "entity-reconcile";
|
|
69
|
+
const ROW_FENCE_SCOPE = "entity-reconcile-row";
|
|
68
70
|
const FENCE_TTL_SECONDS = 5 * 60; // 5 minutes
|
|
71
|
+
/*
|
|
72
|
+
* Atomic claim. A read-then-write here admits every worker that reads the
|
|
73
|
+
* absent key in the same instant, which at ingest concurrency is all of them —
|
|
74
|
+
* the defect behind the row-lock convoy documented in
|
|
75
|
+
* DatabaseService.updateColumnsByIdIfUnlockedWithoutHooks. TTLs are jittered
|
|
76
|
+
* so a fleet-wide restart cannot re-synchronise the windows and rebuild the
|
|
77
|
+
* herd on a fixed period.
|
|
78
|
+
*/
|
|
69
79
|
async function shouldReconcile(fenceId) {
|
|
70
80
|
try {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
await GlobalCache.setString(FENCE_NAMESPACE, key, "1", {
|
|
77
|
-
expiresInSeconds: FENCE_TTL_SECONDS,
|
|
78
|
-
});
|
|
81
|
+
return await GlobalCache.setStringIfNotExists(FENCE_NAMESPACE, `${FENCE_SCOPE}:${hashFenceId(fenceId)}`, "1", { expiresInSeconds: GlobalCache.withJitter(FENCE_TTL_SECONDS) });
|
|
82
|
+
}
|
|
83
|
+
catch (_a) {
|
|
84
|
+
// If the cache is down, default to running the reconcile.
|
|
79
85
|
return true;
|
|
80
86
|
}
|
|
87
|
+
}
|
|
88
|
+
/*
|
|
89
|
+
* The set-level fence id concatenates every promoted entity key, so it is
|
|
90
|
+
* unbounded in length — a pod with many entities would otherwise produce a
|
|
91
|
+
* multi-kilobyte Redis key. Hashing keeps it fixed-width without changing which
|
|
92
|
+
* sets collide.
|
|
93
|
+
*/
|
|
94
|
+
function hashFenceId(fenceId) {
|
|
95
|
+
return crypto.createHash("sha1").update(fenceId).digest("hex");
|
|
96
|
+
}
|
|
97
|
+
/*
|
|
98
|
+
* Per-ROW fence, sitting inside the set-level one.
|
|
99
|
+
*
|
|
100
|
+
* The set-level fence keys on (project + the whole promoted entity set), which
|
|
101
|
+
* in practice is unique per POD — a pod's own key is in the set. But the writes
|
|
102
|
+
* it gates are per ROW: the single TelemetryEntity row for a Kubernetes cluster
|
|
103
|
+
* takes one UPDATE per pod in that cluster per window, the namespace row one
|
|
104
|
+
* per pod in the namespace, and so on. Throttle granularity finer than write
|
|
105
|
+
* granularity means the throttle does not bound the writes at all, and the
|
|
106
|
+
* mismatch factor is the pod count — the same shape of defect as the Service
|
|
107
|
+
* heartbeat, one layer down.
|
|
108
|
+
*
|
|
109
|
+
* Claiming per row before the lookup also removes the redundant SELECT, not
|
|
110
|
+
* just the redundant UPDATE.
|
|
111
|
+
*/
|
|
112
|
+
async function shouldReconcileRow(rowFenceId) {
|
|
113
|
+
try {
|
|
114
|
+
return await GlobalCache.setStringIfNotExists(FENCE_NAMESPACE, `${ROW_FENCE_SCOPE}:${hashFenceId(rowFenceId)}`, "1", { expiresInSeconds: GlobalCache.withJitter(FENCE_TTL_SECONDS) });
|
|
115
|
+
}
|
|
81
116
|
catch (_a) {
|
|
82
117
|
// If the cache is down, default to running the reconcile.
|
|
83
118
|
return true;
|
|
84
119
|
}
|
|
85
120
|
}
|
|
121
|
+
async function releaseRowFence(rowFenceId) {
|
|
122
|
+
try {
|
|
123
|
+
await GlobalCache.deleteKey(FENCE_NAMESPACE, `${ROW_FENCE_SCOPE}:${hashFenceId(rowFenceId)}`);
|
|
124
|
+
}
|
|
125
|
+
catch (_a) {
|
|
126
|
+
// Best effort — losing the release costs one window of staleness.
|
|
127
|
+
}
|
|
128
|
+
}
|
|
86
129
|
/*
|
|
87
130
|
* Over-budget skips happen per row, but the warn log is fenced to once per
|
|
88
131
|
* (project, entityType) per fence window — an over-budget project would
|
|
@@ -90,15 +133,7 @@ async function shouldReconcile(fenceId) {
|
|
|
90
133
|
*/
|
|
91
134
|
export async function shouldWarnEntityBudgetOnce(data) {
|
|
92
135
|
try {
|
|
93
|
-
|
|
94
|
-
const seen = await GlobalCache.getString(FENCE_NAMESPACE, key);
|
|
95
|
-
if (seen) {
|
|
96
|
-
return false;
|
|
97
|
-
}
|
|
98
|
-
await GlobalCache.setString(FENCE_NAMESPACE, key, "1", {
|
|
99
|
-
expiresInSeconds: FENCE_TTL_SECONDS,
|
|
100
|
-
});
|
|
101
|
-
return true;
|
|
136
|
+
return await GlobalCache.setStringIfNotExists(FENCE_NAMESPACE, `entity-budget-warn:${data.projectId.toString()}:${data.entityType}`, "1", { expiresInSeconds: GlobalCache.withJitter(FENCE_TTL_SECONDS) });
|
|
102
137
|
}
|
|
103
138
|
catch (_a) {
|
|
104
139
|
// If the cache is down, default to warning (visibility over silence).
|
|
@@ -181,6 +216,9 @@ export async function reconcileByNaturalKey(data) {
|
|
|
181
216
|
// Unresolved generic mapped type — TS cannot prove overlap directly.
|
|
182
217
|
return Object.assign({ lastSeenAt: data.lastSeenAt }, (data.buildUpdate ? data.buildUpdate(existing) : {}));
|
|
183
218
|
};
|
|
219
|
+
if (!(await shouldReconcileRow(data.rowFenceId))) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
184
222
|
const existing = await data.service.findOneBy({
|
|
185
223
|
query: data.query,
|
|
186
224
|
select,
|
|
@@ -194,14 +232,29 @@ export async function reconcileByNaturalKey(data) {
|
|
|
194
232
|
* audit). buildBump returns only plain values — lastSeenAt plus, at most,
|
|
195
233
|
* the descriptiveAttributes / labels JSON columns — which the primitive
|
|
196
234
|
* persists via the driver transformer path. See ServiceService.updateLastSeen.
|
|
235
|
+
*
|
|
236
|
+
* SKIP LOCKED: a contended bump yields rather than queueing behind the
|
|
237
|
+
* writer that already holds the row. Registry rows for shared entities (a
|
|
238
|
+
* cluster, a namespace) are written by every pod that reports them, so
|
|
239
|
+
* this is exactly the hot-row shape that convoyed on Service.
|
|
197
240
|
*/
|
|
198
|
-
await data.service.
|
|
241
|
+
const wrote = await data.service.updateColumnsByIdIfUnlockedWithoutHooks({
|
|
199
242
|
id: existing.id,
|
|
200
243
|
data: buildBump(existing),
|
|
201
244
|
});
|
|
245
|
+
if (!wrote) {
|
|
246
|
+
// Skipped under contention — re-open the window so the next batch retries.
|
|
247
|
+
await releaseRowFence(data.rowFenceId);
|
|
248
|
+
}
|
|
202
249
|
return;
|
|
203
250
|
}
|
|
204
251
|
if (data.beforeCreate && !(await data.beforeCreate())) {
|
|
252
|
+
/*
|
|
253
|
+
* Over budget: release the row fence so the gate is re-evaluated next
|
|
254
|
+
* batch rather than being suppressed for a full window by a decision that
|
|
255
|
+
* wrote nothing.
|
|
256
|
+
*/
|
|
257
|
+
await releaseRowFence(data.rowFenceId);
|
|
205
258
|
return;
|
|
206
259
|
}
|
|
207
260
|
try {
|
|
@@ -224,12 +277,18 @@ export async function reconcileByNaturalKey(data) {
|
|
|
224
277
|
});
|
|
225
278
|
if (winner) {
|
|
226
279
|
logger.debug(`EntityRegistry: create raced for ${data.describe} (concurrent insert); bumping lastSeenAt on the winning row.`);
|
|
227
|
-
await data.service.
|
|
280
|
+
await data.service.updateColumnsByIdIfUnlockedWithoutHooks({
|
|
228
281
|
id: winner.id,
|
|
229
282
|
data: buildBump(winner),
|
|
230
283
|
});
|
|
231
284
|
return;
|
|
232
285
|
}
|
|
286
|
+
/*
|
|
287
|
+
* The insert itself was invalid. Re-open the row window so the next batch
|
|
288
|
+
* retries rather than being silently suppressed for the TTL — a genuinely
|
|
289
|
+
* broken row should keep surfacing this warning, not go quiet.
|
|
290
|
+
*/
|
|
291
|
+
await releaseRowFence(data.rowFenceId);
|
|
233
292
|
logger.warn(`EntityRegistry: create failed for ${data.describe}:`);
|
|
234
293
|
logger.warn(err);
|
|
235
294
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EntityRegistry.js","sourceRoot":"","sources":["../../../../../Server/Utils/Telemetry/EntityRegistry.ts"],"names":[],"mappings":"AAMA,OAAO,UAAU,MAAM,qCAAqC,CAAC;AAC7D,OAAO,WAAW,MAAM,kCAAkC,CAAC;AAC3D,OAAO,MAAM,MAAM,WAAW,CAAC;AAE/B,OAAO,sBAAsB,MAAM,uCAAuC,CAAC;AAC3E,OAAO,kCAAkC,MAAM,mDAAmD,CAAC;AACnG,OAAO,EACL,mBAAmB,GAEpB,MAAM,6CAA6C,CAAC;
|
|
1
|
+
{"version":3,"file":"EntityRegistry.js","sourceRoot":"","sources":["../../../../../Server/Utils/Telemetry/EntityRegistry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import GlobalCache from "../../Infrastructure/GlobalCache";
|
|
2
|
+
import SingleFlight from "../SingleFlight";
|
|
3
|
+
import logger from "../Logger";
|
|
4
|
+
/** Derived from the model's liveness namespace — see cacheNamespace. */
|
|
5
|
+
function fingerprintNamespace(cacheNamespace) {
|
|
6
|
+
return `${cacheNamespace}-fingerprint`;
|
|
7
|
+
}
|
|
8
|
+
function writeWindowNamespace(cacheNamespace) {
|
|
9
|
+
return `${cacheNamespace}-write-window`;
|
|
10
|
+
}
|
|
11
|
+
export default class ResourceHeartbeat {
|
|
12
|
+
static async write(input) {
|
|
13
|
+
const cacheKey = input.id.toString();
|
|
14
|
+
/*
|
|
15
|
+
* Collapse duplicate concurrent callers inside THIS process first. One
|
|
16
|
+
* OTLP batch resolves the same resource once per contained resource — the
|
|
17
|
+
* host-metrics collector config OneUptime ships emits hundreds per batch —
|
|
18
|
+
* and without this every one of them independently asks Redis the same
|
|
19
|
+
* question and throws the answer away.
|
|
20
|
+
*/
|
|
21
|
+
await SingleFlight.run(`${input.cacheNamespace}:${cacheKey}:${input.fingerprint}`, async () => {
|
|
22
|
+
await this.writeOnce(input, cacheKey);
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
static async writeOnce(input, cacheKey) {
|
|
26
|
+
var _a, _b;
|
|
27
|
+
/*
|
|
28
|
+
* Liveness gate. Presence-only and jittered: a fleet-wide restart would
|
|
29
|
+
* otherwise re-synchronise every resource's window and rebuild the herd on
|
|
30
|
+
* a fixed period.
|
|
31
|
+
*/
|
|
32
|
+
let writeLiveness = true;
|
|
33
|
+
try {
|
|
34
|
+
writeLiveness = await GlobalCache.setStringIfNotExists(input.cacheNamespace, cacheKey, "1", {
|
|
35
|
+
expiresInSeconds: GlobalCache.withJitter(input.throttleInSeconds),
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
catch (_c) {
|
|
39
|
+
/*
|
|
40
|
+
* Cache unavailable — fail OPEN and refresh liveness anyway. Suppressing
|
|
41
|
+
* the heartbeat during a Redis outage is how a healthy resource is shown
|
|
42
|
+
* as disconnected while its data keeps arriving. Affordable only because
|
|
43
|
+
* the write below cannot queue.
|
|
44
|
+
*/
|
|
45
|
+
writeLiveness = true;
|
|
46
|
+
}
|
|
47
|
+
const hasMetadata = Boolean(input.metadata) && Object.keys(input.metadata).length > 0;
|
|
48
|
+
/*
|
|
49
|
+
* Enrichment gate 1: has the payload actually changed? Compare-and-claim,
|
|
50
|
+
* so a genuinely new value is written promptly rather than waiting out a
|
|
51
|
+
* window.
|
|
52
|
+
*/
|
|
53
|
+
let metadataChanged = false;
|
|
54
|
+
if (hasMetadata) {
|
|
55
|
+
try {
|
|
56
|
+
metadataChanged = await GlobalCache.setStringIfChanged(fingerprintNamespace(input.cacheNamespace), cacheKey, input.fingerprint, {
|
|
57
|
+
expiresInSeconds: GlobalCache.withJitter((_a = input.enrichmentThrottleInSeconds) !== null && _a !== void 0 ? _a : input.throttleInSeconds),
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
catch (_d) {
|
|
61
|
+
/*
|
|
62
|
+
* Fail CLOSED for enrichment, unlike liveness. These columns are
|
|
63
|
+
* descriptive; nothing is mis-reported by writing them a little later,
|
|
64
|
+
* and letting a Redis outage open this gate restores the per-batch
|
|
65
|
+
* write storm this exists to prevent.
|
|
66
|
+
*/
|
|
67
|
+
metadataChanged = false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/*
|
|
71
|
+
* Enrichment gate 2: the rate limiter. Change detection cannot tell a
|
|
72
|
+
* meaningful update apart from a gauge that moves every scrape — both
|
|
73
|
+
* present as "it changed". Since the churning case is unbounded, this
|
|
74
|
+
* bounds both.
|
|
75
|
+
*/
|
|
76
|
+
let writeMetadata = false;
|
|
77
|
+
if (metadataChanged) {
|
|
78
|
+
try {
|
|
79
|
+
writeMetadata = await GlobalCache.setStringIfNotExists(writeWindowNamespace(input.cacheNamespace), cacheKey, "1", {
|
|
80
|
+
expiresInSeconds: GlobalCache.withJitter((_b = input.enrichmentThrottleInSeconds) !== null && _b !== void 0 ? _b : input.throttleInSeconds),
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
catch (_e) {
|
|
84
|
+
writeMetadata = false;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (!writeLiveness && !writeMetadata) {
|
|
88
|
+
// Nothing to say. Issue no statement at all.
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
/*
|
|
92
|
+
* Liveness rides along even on an enrichment-only write: refreshing it
|
|
93
|
+
* early is harmless (it is a floor, not a sample), and splitting the two
|
|
94
|
+
* into separate statements would double the writes we just halved.
|
|
95
|
+
*/
|
|
96
|
+
const data = Object.assign(Object.assign({}, input.liveness), (writeMetadata
|
|
97
|
+
? (input.metadata || {})
|
|
98
|
+
: {}));
|
|
99
|
+
let wrote = false;
|
|
100
|
+
try {
|
|
101
|
+
wrote = await input.service.updateColumnsByIdIfUnlockedWithoutHooks({
|
|
102
|
+
id: input.id,
|
|
103
|
+
data: data,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
catch (err) {
|
|
107
|
+
/*
|
|
108
|
+
* Liveness must survive bad metadata. Every enrichment value is a
|
|
109
|
+
* collector-supplied attribute, so if one of them makes Postgres reject
|
|
110
|
+
* the statement it takes `lastSeenAt` down with it and the resource
|
|
111
|
+
* silently stops looking alive — the disconnection sweep then flips a
|
|
112
|
+
* perfectly healthy resource 15 minutes later with telemetry arriving
|
|
113
|
+
* the whole time. That was issue #3006. Retry with liveness only, so
|
|
114
|
+
* enrichment is what gets dropped, never the heartbeat.
|
|
115
|
+
*/
|
|
116
|
+
logger.warn(`ResourceHeartbeat: metadata write failed for ${input.describe}, falling back to a liveness-only update: ${err instanceof Error ? err.message : String(err)}`);
|
|
117
|
+
await this.releaseGates(input.cacheNamespace, cacheKey, writeMetadata);
|
|
118
|
+
await input.service.updateColumnsByIdIfUnlockedWithoutHooks({
|
|
119
|
+
id: input.id,
|
|
120
|
+
data: input.liveness,
|
|
121
|
+
});
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (!wrote && writeMetadata) {
|
|
125
|
+
/*
|
|
126
|
+
* The row was locked, so this write was SKIPPED rather than applied. For
|
|
127
|
+
* liveness that is a non-event — whoever holds the lock is storing the
|
|
128
|
+
* same timestamp. Enrichment is different: the holder may be writing an
|
|
129
|
+
* older payload, and the gates just claimed would suppress ours for the
|
|
130
|
+
* rest of the window. Release them so the next batch re-attempts.
|
|
131
|
+
*
|
|
132
|
+
* This cannot become a retry storm: each attempt is a non-blocking
|
|
133
|
+
* statement that returns immediately when contended.
|
|
134
|
+
*/
|
|
135
|
+
await this.releaseGates(input.cacheNamespace, cacheKey, true);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Re-open the gates so the next batch retries. When enrichment was at stake
|
|
140
|
+
* those are the gates that must not stay claimed after a write that never
|
|
141
|
+
* happened; otherwise it is the liveness gate.
|
|
142
|
+
*/
|
|
143
|
+
static async releaseGates(cacheNamespace, cacheKey, hadMetadata) {
|
|
144
|
+
const namespaces = hadMetadata
|
|
145
|
+
? [
|
|
146
|
+
fingerprintNamespace(cacheNamespace),
|
|
147
|
+
writeWindowNamespace(cacheNamespace),
|
|
148
|
+
]
|
|
149
|
+
: [cacheNamespace];
|
|
150
|
+
for (const namespace of namespaces) {
|
|
151
|
+
try {
|
|
152
|
+
await GlobalCache.deleteKey(namespace, cacheKey);
|
|
153
|
+
}
|
|
154
|
+
catch (_a) {
|
|
155
|
+
// Best effort — the retry is what actually matters.
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=ResourceHeartbeat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ResourceHeartbeat.js","sourceRoot":"","sources":["../../../../../Server/Utils/Telemetry/ResourceHeartbeat.ts"],"names":[],"mappings":"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"}
|