@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
|
@@ -5,6 +5,7 @@ import Model from "../../Models/DatabaseModels/CephCluster";
|
|
|
5
5
|
import Label from "../../Models/DatabaseModels/Label";
|
|
6
6
|
import { OnCreate } from "../Types/Database/Hooks";
|
|
7
7
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
8
|
+
import ResourceHeartbeat from "../Utils/Telemetry/ResourceHeartbeat";
|
|
8
9
|
import ObjectID from "../../Types/ObjectID";
|
|
9
10
|
import QueryHelper from "../Types/Database/QueryHelper";
|
|
10
11
|
import OneUptimeDate from "../../Types/Date";
|
|
@@ -181,7 +182,6 @@ export class Service extends DatabaseService<Model> {
|
|
|
181
182
|
capacityUsedPercent?: number | undefined;
|
|
182
183
|
},
|
|
183
184
|
): Promise<void> {
|
|
184
|
-
const cacheKey: string = clusterId.toString();
|
|
185
185
|
const extrasFingerprint: string = crypto
|
|
186
186
|
.createHash("sha1")
|
|
187
187
|
.update(
|
|
@@ -200,34 +200,6 @@ export class Service extends DatabaseService<Model> {
|
|
|
200
200
|
)
|
|
201
201
|
.digest("hex");
|
|
202
202
|
|
|
203
|
-
let cached: string | null = null;
|
|
204
|
-
try {
|
|
205
|
-
cached = await GlobalCache.getString(LAST_SEEN_CACHE_NAMESPACE, cacheKey);
|
|
206
|
-
} catch {
|
|
207
|
-
/*
|
|
208
|
-
* Cache unavailable — fail open and refresh lastSeenAt anyway. A
|
|
209
|
-
* cache error must never skip the DB write below, otherwise the
|
|
210
|
-
* resource is wrongly marked "disconnected" while telemetry is
|
|
211
|
-
* still flowing. Mirrors shouldRunMaintenance's fail-open stance.
|
|
212
|
-
*/
|
|
213
|
-
cached = null;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
if (cached === extrasFingerprint) {
|
|
217
|
-
return; // same data was written recently
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
try {
|
|
221
|
-
await GlobalCache.setString(
|
|
222
|
-
LAST_SEEN_CACHE_NAMESPACE,
|
|
223
|
-
cacheKey,
|
|
224
|
-
extrasFingerprint,
|
|
225
|
-
{ expiresInSeconds: LAST_SEEN_THROTTLE_SECONDS },
|
|
226
|
-
);
|
|
227
|
-
} catch {
|
|
228
|
-
// Best-effort throttle write; proceed with the DB update regardless.
|
|
229
|
-
}
|
|
230
|
-
|
|
231
203
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
232
204
|
const liveness: any = {
|
|
233
205
|
lastSeenAt: OneUptimeDate.getCurrentDate(),
|
|
@@ -235,91 +207,59 @@ export class Service extends DatabaseService<Model> {
|
|
|
235
207
|
};
|
|
236
208
|
|
|
237
209
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
238
|
-
const
|
|
210
|
+
const metadata: any = {};
|
|
239
211
|
|
|
240
212
|
if (extra?.cephVersion) {
|
|
241
|
-
|
|
213
|
+
metadata.cephVersion = extra.cephVersion;
|
|
242
214
|
}
|
|
243
215
|
if (extra?.fsid) {
|
|
244
|
-
|
|
216
|
+
metadata.fsid = extra.fsid;
|
|
245
217
|
}
|
|
246
218
|
if (extra?.agentVersion) {
|
|
247
|
-
|
|
219
|
+
metadata.agentVersion = extra.agentVersion;
|
|
248
220
|
}
|
|
249
221
|
/*
|
|
250
222
|
* Counts and health: 0 is a legitimate value (healthStatus 0 = OK,
|
|
251
223
|
* osdUpCount 0 = every OSD down) — gate on undefined, not falsiness.
|
|
252
224
|
*/
|
|
253
225
|
if (extra?.monCount !== undefined) {
|
|
254
|
-
|
|
226
|
+
metadata.monCount = extra.monCount;
|
|
255
227
|
}
|
|
256
228
|
if (extra?.osdCount !== undefined) {
|
|
257
|
-
|
|
229
|
+
metadata.osdCount = extra.osdCount;
|
|
258
230
|
}
|
|
259
231
|
if (extra?.osdUpCount !== undefined) {
|
|
260
|
-
|
|
232
|
+
metadata.osdUpCount = extra.osdUpCount;
|
|
261
233
|
}
|
|
262
234
|
if (extra?.osdInCount !== undefined) {
|
|
263
|
-
|
|
235
|
+
metadata.osdInCount = extra.osdInCount;
|
|
264
236
|
}
|
|
265
237
|
if (extra?.poolCount !== undefined) {
|
|
266
|
-
|
|
238
|
+
metadata.poolCount = extra.poolCount;
|
|
267
239
|
}
|
|
268
240
|
if (extra?.healthStatus !== undefined) {
|
|
269
|
-
|
|
241
|
+
metadata.healthStatus = extra.healthStatus;
|
|
270
242
|
}
|
|
271
243
|
if (extra?.capacityUsedPercent !== undefined) {
|
|
272
|
-
|
|
244
|
+
metadata.capacityUsedPercent = extra.capacityUsedPercent;
|
|
273
245
|
}
|
|
274
246
|
|
|
275
247
|
/*
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
*
|
|
279
|
-
*
|
|
280
|
-
* Collector-supplied strings are clamped to their declared column
|
|
281
|
-
* widths inside updateColumnsByIdWithoutHooks — see
|
|
282
|
-
* DatabaseService.truncateStringColumnsToMaxLength.
|
|
248
|
+
* One gated, non-blocking heartbeat write. The gates, the fail-open /
|
|
249
|
+
* fail-closed split and the liveness-only fallback all live in
|
|
250
|
+
* ResourceHeartbeat — see there for why this row's throttle used to
|
|
251
|
+
* provide no throttling at all.
|
|
283
252
|
*/
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
* reject the statement, lastSeenAt silently stops advancing and
|
|
295
|
-
* markDisconnectedClusters flips a perfectly healthy Ceph cluster to
|
|
296
|
-
* "disconnected" 15 minutes later — with telemetry still arriving the
|
|
297
|
-
* whole time. That was issue #3006 on the Host path (a host.ip list
|
|
298
|
-
* longer than the then-varchar(500) column). Retry with liveness only
|
|
299
|
-
* so the enrichment is what gets dropped, never the heartbeat.
|
|
300
|
-
*/
|
|
301
|
-
logger.warn(
|
|
302
|
-
`CephClusterService.updateLastSeen: metadata write failed for Ceph cluster ${clusterId.toString()}, falling back to a liveness-only update: ${
|
|
303
|
-
err instanceof Error ? err.message : String(err)
|
|
304
|
-
}`,
|
|
305
|
-
);
|
|
306
|
-
|
|
307
|
-
/*
|
|
308
|
-
* The throttle fingerprint was already stored, so without this the
|
|
309
|
-
* next batch would short-circuit on a cache hit and skip the retry
|
|
310
|
-
* for the rest of the window.
|
|
311
|
-
*/
|
|
312
|
-
try {
|
|
313
|
-
await GlobalCache.deleteKey(LAST_SEEN_CACHE_NAMESPACE, cacheKey);
|
|
314
|
-
} catch {
|
|
315
|
-
// Best-effort; the fallback write below is what actually matters.
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
await this.updateColumnsByIdWithoutHooks({
|
|
319
|
-
id: clusterId,
|
|
320
|
-
data: liveness,
|
|
321
|
-
});
|
|
322
|
-
}
|
|
253
|
+
await ResourceHeartbeat.write({
|
|
254
|
+
service: this,
|
|
255
|
+
id: clusterId,
|
|
256
|
+
cacheNamespace: LAST_SEEN_CACHE_NAMESPACE,
|
|
257
|
+
throttleInSeconds: LAST_SEEN_THROTTLE_SECONDS,
|
|
258
|
+
liveness: liveness,
|
|
259
|
+
metadata: metadata,
|
|
260
|
+
fingerprint: extrasFingerprint,
|
|
261
|
+
describe: `ceph cluster ${clusterId.toString()}`,
|
|
262
|
+
});
|
|
323
263
|
}
|
|
324
264
|
|
|
325
265
|
/**
|
|
@@ -2,6 +2,7 @@ import DatabaseService from "./DatabaseService";
|
|
|
2
2
|
import Model from "../../Models/DatabaseModels/CloudResource";
|
|
3
3
|
import Label from "../../Models/DatabaseModels/Label";
|
|
4
4
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
5
|
+
import ResourceHeartbeat from "../Utils/Telemetry/ResourceHeartbeat";
|
|
5
6
|
import ObjectID from "../../Types/ObjectID";
|
|
6
7
|
import QueryHelper from "../Types/Database/QueryHelper";
|
|
7
8
|
import OneUptimeDate from "../../Types/Date";
|
|
@@ -154,7 +155,6 @@ export class Service extends DatabaseService<Model> {
|
|
|
154
155
|
runtimeVersion?: string | undefined;
|
|
155
156
|
},
|
|
156
157
|
): Promise<void> {
|
|
157
|
-
const cacheKey: string = cloudResourceId.toString();
|
|
158
158
|
const extrasFingerprint: string = crypto
|
|
159
159
|
.createHash("sha1")
|
|
160
160
|
.update(
|
|
@@ -170,34 +170,6 @@ export class Service extends DatabaseService<Model> {
|
|
|
170
170
|
)
|
|
171
171
|
.digest("hex");
|
|
172
172
|
|
|
173
|
-
let cached: string | null = null;
|
|
174
|
-
try {
|
|
175
|
-
cached = await GlobalCache.getString(LAST_SEEN_CACHE_NAMESPACE, cacheKey);
|
|
176
|
-
} catch {
|
|
177
|
-
/*
|
|
178
|
-
* Cache unavailable — fail open and refresh lastSeenAt anyway. A
|
|
179
|
-
* cache error must never skip the DB write below, otherwise the
|
|
180
|
-
* resource is wrongly marked "disconnected" while telemetry is
|
|
181
|
-
* still flowing. Mirrors shouldRunMaintenance's fail-open stance.
|
|
182
|
-
*/
|
|
183
|
-
cached = null;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
if (cached === extrasFingerprint) {
|
|
187
|
-
return; // same data was written recently
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
try {
|
|
191
|
-
await GlobalCache.setString(
|
|
192
|
-
LAST_SEEN_CACHE_NAMESPACE,
|
|
193
|
-
cacheKey,
|
|
194
|
-
extrasFingerprint,
|
|
195
|
-
{ expiresInSeconds: LAST_SEEN_THROTTLE_SECONDS },
|
|
196
|
-
);
|
|
197
|
-
} catch {
|
|
198
|
-
// Best-effort throttle write; proceed with the DB update regardless.
|
|
199
|
-
}
|
|
200
|
-
|
|
201
173
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
202
174
|
const liveness: any = {
|
|
203
175
|
lastSeenAt: OneUptimeDate.getCurrentDate(),
|
|
@@ -205,78 +177,46 @@ export class Service extends DatabaseService<Model> {
|
|
|
205
177
|
};
|
|
206
178
|
|
|
207
179
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
208
|
-
const
|
|
180
|
+
const metadata: any = {};
|
|
209
181
|
|
|
210
182
|
if (extra?.agentVersion) {
|
|
211
|
-
|
|
183
|
+
metadata.agentVersion = extra.agentVersion;
|
|
212
184
|
}
|
|
213
185
|
if (extra?.cloudPlatform) {
|
|
214
|
-
|
|
186
|
+
metadata.cloudPlatform = extra.cloudPlatform;
|
|
215
187
|
}
|
|
216
188
|
if (extra?.cloudProvider) {
|
|
217
|
-
|
|
189
|
+
metadata.cloudProvider = extra.cloudProvider;
|
|
218
190
|
}
|
|
219
191
|
if (extra?.cloudRegion) {
|
|
220
|
-
|
|
192
|
+
metadata.cloudRegion = extra.cloudRegion;
|
|
221
193
|
}
|
|
222
194
|
if (extra?.cloudAccountId) {
|
|
223
|
-
|
|
195
|
+
metadata.cloudAccountId = extra.cloudAccountId;
|
|
224
196
|
}
|
|
225
197
|
if (extra?.runtimeName) {
|
|
226
|
-
|
|
198
|
+
metadata.runtimeName = extra.runtimeName;
|
|
227
199
|
}
|
|
228
200
|
if (extra?.runtimeVersion) {
|
|
229
|
-
|
|
201
|
+
metadata.runtimeVersion = extra.runtimeVersion;
|
|
230
202
|
}
|
|
231
203
|
|
|
232
204
|
/*
|
|
233
|
-
*
|
|
234
|
-
*
|
|
235
|
-
*
|
|
236
|
-
*
|
|
237
|
-
* Collector-supplied strings are clamped to their declared column
|
|
238
|
-
* widths inside updateColumnsByIdWithoutHooks — see
|
|
239
|
-
* DatabaseService.truncateStringColumnsToMaxLength.
|
|
205
|
+
* One gated, non-blocking heartbeat write. The gates, the fail-open /
|
|
206
|
+
* fail-closed split and the liveness-only fallback all live in
|
|
207
|
+
* ResourceHeartbeat — see there for why this row's throttle used to
|
|
208
|
+
* provide no throttling at all.
|
|
240
209
|
*/
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
* reject the statement, lastSeenAt silently stops advancing and
|
|
252
|
-
* markDisconnectedResources flips a perfectly healthy cloud resource
|
|
253
|
-
* to "disconnected" 15 minutes later — with telemetry still arriving
|
|
254
|
-
* the whole time. That was issue #3006 on the Host path (a host.ip
|
|
255
|
-
* list longer than the then-varchar(500) column). Retry with liveness
|
|
256
|
-
* only so the enrichment is what gets dropped, never the heartbeat.
|
|
257
|
-
*/
|
|
258
|
-
logger.warn(
|
|
259
|
-
`CloudResourceService.updateLastSeen: metadata write failed for cloud resource ${cloudResourceId.toString()}, falling back to a liveness-only update: ${
|
|
260
|
-
err instanceof Error ? err.message : String(err)
|
|
261
|
-
}`,
|
|
262
|
-
);
|
|
263
|
-
|
|
264
|
-
/*
|
|
265
|
-
* The throttle fingerprint was already stored, so without this the
|
|
266
|
-
* next batch would short-circuit on a cache hit and skip the retry
|
|
267
|
-
* for the rest of the window.
|
|
268
|
-
*/
|
|
269
|
-
try {
|
|
270
|
-
await GlobalCache.deleteKey(LAST_SEEN_CACHE_NAMESPACE, cacheKey);
|
|
271
|
-
} catch {
|
|
272
|
-
// Best-effort; the fallback write below is what actually matters.
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
await this.updateColumnsByIdWithoutHooks({
|
|
276
|
-
id: cloudResourceId,
|
|
277
|
-
data: liveness,
|
|
278
|
-
});
|
|
279
|
-
}
|
|
210
|
+
await ResourceHeartbeat.write({
|
|
211
|
+
service: this,
|
|
212
|
+
id: cloudResourceId,
|
|
213
|
+
cacheNamespace: LAST_SEEN_CACHE_NAMESPACE,
|
|
214
|
+
throttleInSeconds: LAST_SEEN_THROTTLE_SECONDS,
|
|
215
|
+
liveness: liveness,
|
|
216
|
+
metadata: metadata,
|
|
217
|
+
fingerprint: extrasFingerprint,
|
|
218
|
+
describe: `cloud resource ${cloudResourceId.toString()}`,
|
|
219
|
+
});
|
|
280
220
|
}
|
|
281
221
|
|
|
282
222
|
@CaptureSpan()
|
|
@@ -2400,6 +2400,117 @@ class DatabaseService<TBaseModel extends BaseModel> extends BaseService {
|
|
|
2400
2400
|
await repository.manager.query(sql, params);
|
|
2401
2401
|
}
|
|
2402
2402
|
|
|
2403
|
+
/*
|
|
2404
|
+
* Same single-statement, hook-free, no-version-bump write as
|
|
2405
|
+
* `updateColumnsByIdWithoutHooks`, except it YIELDS instead of queueing:
|
|
2406
|
+
* if another transaction already holds the row's lock, this write is
|
|
2407
|
+
* abandoned and the method returns false. It never waits.
|
|
2408
|
+
*
|
|
2409
|
+
* Why this exists. A single-statement UPDATE holds its row lock for a very
|
|
2410
|
+
* short time, but "short" does not mean "safe" once the arrival rate on ONE
|
|
2411
|
+
* row exceeds the service rate. Postgres queues lock waiters strictly, so
|
|
2412
|
+
* every waiter pins a backend for the sum of everyone ahead of it — the
|
|
2413
|
+
* classic convoy. In the outage this was written for, ~25K concurrent
|
|
2414
|
+
* ingest jobs across 100 worker pods funnelled onto ~2,300 Service rows and
|
|
2415
|
+
* produced 1,017 active connections with 892 of them parked on row locks;
|
|
2416
|
+
* the tail had been waiting 3.7 hours. Nothing about that is fixable by
|
|
2417
|
+
* making the statement faster, and no Postgres tier changes it: the queue
|
|
2418
|
+
* is the bug.
|
|
2419
|
+
*
|
|
2420
|
+
* `FOR UPDATE SKIP LOCKED` inverts it. A contended writer does zero work
|
|
2421
|
+
* and returns immediately, so the number of backends blocked on any single
|
|
2422
|
+
* row is capped at ONE no matter how many workers arrive together. The
|
|
2423
|
+
* convoy cannot form — and cannot form even if every upstream throttle
|
|
2424
|
+
* fails open at once (Redis down, fence bug, cache flush). That is the
|
|
2425
|
+
* point: this is the structural backstop underneath the throttles, not a
|
|
2426
|
+
* second copy of them.
|
|
2427
|
+
*
|
|
2428
|
+
* Use ONLY where losing the write is harmless because a concurrent writer
|
|
2429
|
+
* is writing the same thing — liveness timestamps and other idempotent
|
|
2430
|
+
* bookkeeping, whose contended value is by construction the value the
|
|
2431
|
+
* winner is already storing. It is the WRONG primitive for a write carrying
|
|
2432
|
+
* information the winner does not have (a changed attribute); for those the
|
|
2433
|
+
* caller must re-attempt, and `false` is the signal to do it. Returning a
|
|
2434
|
+
* boolean rather than swallowing the skip is deliberate: a silent drop here
|
|
2435
|
+
* would be indistinguishable from a successful write at the call site,
|
|
2436
|
+
* which is exactly how a "reliable" heartbeat quietly stops beating.
|
|
2437
|
+
*
|
|
2438
|
+
* Returns true when the row was updated, false when it was locked (or no
|
|
2439
|
+
* longer exists).
|
|
2440
|
+
*/
|
|
2441
|
+
@CaptureSpan()
|
|
2442
|
+
public async updateColumnsByIdIfUnlockedWithoutHooks(input: {
|
|
2443
|
+
id: ObjectID;
|
|
2444
|
+
data: PartialEntity<TBaseModel>;
|
|
2445
|
+
skipUpdateDateColumn?: boolean;
|
|
2446
|
+
}): Promise<boolean> {
|
|
2447
|
+
if (!input.id) {
|
|
2448
|
+
throw new BadDataException("id is required");
|
|
2449
|
+
}
|
|
2450
|
+
|
|
2451
|
+
const repository: Repository<TBaseModel> = this.getRepository();
|
|
2452
|
+
const metadata: EntityMetadata = repository.metadata;
|
|
2453
|
+
const driver: Driver = repository.manager.connection.driver;
|
|
2454
|
+
|
|
2455
|
+
const setClauses: Array<string> = [];
|
|
2456
|
+
const params: Array<unknown> = [];
|
|
2457
|
+
|
|
2458
|
+
// Clamp on a shallow COPY — see updateColumnsByIdWithoutHooks.
|
|
2459
|
+
const data: ObjectLiteral = this.truncateStringColumnsToMaxLength({
|
|
2460
|
+
...(input.data as ObjectLiteral),
|
|
2461
|
+
});
|
|
2462
|
+
|
|
2463
|
+
for (const [propertyName, value] of Object.entries(data)) {
|
|
2464
|
+
const column: ColumnMetadata | undefined =
|
|
2465
|
+
metadata.findColumnWithPropertyName(propertyName);
|
|
2466
|
+
if (!column) {
|
|
2467
|
+
throw new BadDataException(
|
|
2468
|
+
`updateColumnsByIdIfUnlockedWithoutHooks: unknown column "${propertyName}" on "${metadata.tableName}"`,
|
|
2469
|
+
);
|
|
2470
|
+
}
|
|
2471
|
+
if (typeof value === "function") {
|
|
2472
|
+
throw new BadDataException(
|
|
2473
|
+
`updateColumnsByIdIfUnlockedWithoutHooks: SQL-expression values are not supported (column "${propertyName}"); pass a literal value.`,
|
|
2474
|
+
);
|
|
2475
|
+
}
|
|
2476
|
+
params.push(driver.preparePersistentValue(value, column));
|
|
2477
|
+
setClauses.push(`"${column.databaseName}" = $${params.length}`);
|
|
2478
|
+
}
|
|
2479
|
+
|
|
2480
|
+
if (setClauses.length === 0) {
|
|
2481
|
+
return false;
|
|
2482
|
+
}
|
|
2483
|
+
|
|
2484
|
+
if (metadata.updateDateColumn && !input.skipUpdateDateColumn) {
|
|
2485
|
+
setClauses.push(
|
|
2486
|
+
`"${metadata.updateDateColumn.databaseName}" = CURRENT_TIMESTAMP`,
|
|
2487
|
+
);
|
|
2488
|
+
}
|
|
2489
|
+
|
|
2490
|
+
const primaryColumnName: string =
|
|
2491
|
+
metadata.primaryColumns[0]?.databaseName || "_id";
|
|
2492
|
+
params.push(input.id.toString());
|
|
2493
|
+
|
|
2494
|
+
/*
|
|
2495
|
+
* The lock is taken by the sub-SELECT and inherited by the UPDATE within
|
|
2496
|
+
* the same implicit transaction, so the outer write never blocks either.
|
|
2497
|
+
* RETURNING is how the affected-row count comes back: TypeORM's `query()`
|
|
2498
|
+
* surfaces the driver's rows and a bare UPDATE returns none, so without it
|
|
2499
|
+
* a skipped write is indistinguishable from an applied one.
|
|
2500
|
+
*/
|
|
2501
|
+
const sql: string = `UPDATE "${metadata.tableName}" SET ${setClauses.join(
|
|
2502
|
+
", ",
|
|
2503
|
+
)} WHERE "${primaryColumnName}" IN (SELECT "${primaryColumnName}" FROM "${
|
|
2504
|
+
metadata.tableName
|
|
2505
|
+
}" WHERE "${primaryColumnName}" = $${
|
|
2506
|
+
params.length
|
|
2507
|
+
} FOR UPDATE SKIP LOCKED) RETURNING "${primaryColumnName}"`;
|
|
2508
|
+
|
|
2509
|
+
const result: unknown = await repository.manager.query(sql, params);
|
|
2510
|
+
|
|
2511
|
+
return Array.isArray(result) && result.length > 0;
|
|
2512
|
+
}
|
|
2513
|
+
|
|
2403
2514
|
/*
|
|
2404
2515
|
* Atomically add to numeric columns and set literal columns for one row,
|
|
2405
2516
|
* in a SINGLE auto-committed UPDATE, with no hooks and no `version` bump.
|
|
@@ -5,6 +5,7 @@ import Model from "../../Models/DatabaseModels/DockerHost";
|
|
|
5
5
|
import Label from "../../Models/DatabaseModels/Label";
|
|
6
6
|
import { OnCreate } from "../Types/Database/Hooks";
|
|
7
7
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
8
|
+
import ResourceHeartbeat from "../Utils/Telemetry/ResourceHeartbeat";
|
|
8
9
|
import ObjectID from "../../Types/ObjectID";
|
|
9
10
|
import QueryHelper from "../Types/Database/QueryHelper";
|
|
10
11
|
import OneUptimeDate from "../../Types/Date";
|
|
@@ -180,7 +181,6 @@ export class Service extends DatabaseService<Model> {
|
|
|
180
181
|
agentVersion?: string | undefined;
|
|
181
182
|
},
|
|
182
183
|
): Promise<void> {
|
|
183
|
-
const cacheKey: string = hostId.toString();
|
|
184
184
|
const extrasFingerprint: string = crypto
|
|
185
185
|
.createHash("sha1")
|
|
186
186
|
.update(
|
|
@@ -192,34 +192,6 @@ export class Service extends DatabaseService<Model> {
|
|
|
192
192
|
)
|
|
193
193
|
.digest("hex");
|
|
194
194
|
|
|
195
|
-
let cached: string | null = null;
|
|
196
|
-
try {
|
|
197
|
-
cached = await GlobalCache.getString(LAST_SEEN_CACHE_NAMESPACE, cacheKey);
|
|
198
|
-
} catch {
|
|
199
|
-
/*
|
|
200
|
-
* Cache unavailable — fail open and refresh lastSeenAt anyway. A
|
|
201
|
-
* cache error must never skip the DB write below, otherwise the
|
|
202
|
-
* resource is wrongly marked "disconnected" while telemetry is
|
|
203
|
-
* still flowing. Mirrors shouldRunMaintenance's fail-open stance.
|
|
204
|
-
*/
|
|
205
|
-
cached = null;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
if (cached === extrasFingerprint) {
|
|
209
|
-
return; // same data was written recently
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
try {
|
|
213
|
-
await GlobalCache.setString(
|
|
214
|
-
LAST_SEEN_CACHE_NAMESPACE,
|
|
215
|
-
cacheKey,
|
|
216
|
-
extrasFingerprint,
|
|
217
|
-
{ expiresInSeconds: LAST_SEEN_THROTTLE_SECONDS },
|
|
218
|
-
);
|
|
219
|
-
} catch {
|
|
220
|
-
// Best-effort throttle write; proceed with the DB update regardless.
|
|
221
|
-
}
|
|
222
|
-
|
|
223
195
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
224
196
|
const liveness: any = {
|
|
225
197
|
lastSeenAt: OneUptimeDate.getCurrentDate(),
|
|
@@ -227,66 +199,34 @@ export class Service extends DatabaseService<Model> {
|
|
|
227
199
|
};
|
|
228
200
|
|
|
229
201
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
230
|
-
const
|
|
202
|
+
const metadata: any = {};
|
|
231
203
|
|
|
232
204
|
if (extra?.osType) {
|
|
233
|
-
|
|
205
|
+
metadata.osType = extra.osType;
|
|
234
206
|
}
|
|
235
207
|
if (extra?.osVersion) {
|
|
236
|
-
|
|
208
|
+
metadata.osVersion = extra.osVersion;
|
|
237
209
|
}
|
|
238
210
|
if (extra?.agentVersion) {
|
|
239
|
-
|
|
211
|
+
metadata.agentVersion = extra.agentVersion;
|
|
240
212
|
}
|
|
241
213
|
|
|
242
214
|
/*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
*
|
|
247
|
-
* Collector-supplied strings are clamped to their declared column
|
|
248
|
-
* widths inside updateColumnsByIdWithoutHooks — see
|
|
249
|
-
* DatabaseService.truncateStringColumnsToMaxLength.
|
|
215
|
+
* One gated, non-blocking heartbeat write. The gates, the fail-open /
|
|
216
|
+
* fail-closed split and the liveness-only fallback all live in
|
|
217
|
+
* ResourceHeartbeat — see there for why this row's throttle used to
|
|
218
|
+
* provide no throttling at all.
|
|
250
219
|
*/
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
* reject the statement, lastSeenAt silently stops advancing and
|
|
262
|
-
* markDisconnectedHosts flips a perfectly healthy Docker host to
|
|
263
|
-
* "disconnected" 15 minutes later — with telemetry still arriving the
|
|
264
|
-
* whole time. That was issue #3006 on the Host path (a host.ip list
|
|
265
|
-
* longer than the then-varchar(500) column). Retry with liveness only
|
|
266
|
-
* so the enrichment is what gets dropped, never the heartbeat.
|
|
267
|
-
*/
|
|
268
|
-
logger.warn(
|
|
269
|
-
`DockerHostService.updateLastSeen: metadata write failed for Docker host ${hostId.toString()}, falling back to a liveness-only update: ${
|
|
270
|
-
err instanceof Error ? err.message : String(err)
|
|
271
|
-
}`,
|
|
272
|
-
);
|
|
273
|
-
|
|
274
|
-
/*
|
|
275
|
-
* The throttle fingerprint was already stored, so without this the
|
|
276
|
-
* next batch would short-circuit on a cache hit and skip the retry
|
|
277
|
-
* for the rest of the window.
|
|
278
|
-
*/
|
|
279
|
-
try {
|
|
280
|
-
await GlobalCache.deleteKey(LAST_SEEN_CACHE_NAMESPACE, cacheKey);
|
|
281
|
-
} catch {
|
|
282
|
-
// Best-effort; the fallback write below is what actually matters.
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
await this.updateColumnsByIdWithoutHooks({
|
|
286
|
-
id: hostId,
|
|
287
|
-
data: liveness,
|
|
288
|
-
});
|
|
289
|
-
}
|
|
220
|
+
await ResourceHeartbeat.write({
|
|
221
|
+
service: this,
|
|
222
|
+
id: hostId,
|
|
223
|
+
cacheNamespace: LAST_SEEN_CACHE_NAMESPACE,
|
|
224
|
+
throttleInSeconds: LAST_SEEN_THROTTLE_SECONDS,
|
|
225
|
+
liveness: liveness,
|
|
226
|
+
metadata: metadata,
|
|
227
|
+
fingerprint: extrasFingerprint,
|
|
228
|
+
describe: `docker host ${hostId.toString()}`,
|
|
229
|
+
});
|
|
290
230
|
}
|
|
291
231
|
|
|
292
232
|
/**
|