@oneuptime/common 12.0.5 → 12.0.6
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/AIRun.ts +33 -0
- package/Models/DatabaseModels/DataSource.ts +744 -0
- package/Models/DatabaseModels/Index.ts +3 -0
- package/Models/DatabaseModels/ProjectCallSMSConfig.ts +10 -2
- package/Models/DatabaseModels/User.ts +2 -0
- package/Models/DatabaseModels/UserOnCallLog.ts +2 -0
- package/Models/DatabaseModels/UserOnCallLogTimeline.ts +2 -0
- package/Models/DatabaseModels/UserSession.ts +2 -0
- package/Models/DatabaseModels/UserTotpAuth.ts +6 -4
- package/Models/DatabaseModels/UserWebAuthn.ts +5 -3
- package/Server/API/AIInsightAPI.ts +47 -4
- package/Server/API/AIInvestigationAPI.ts +25 -0
- package/Server/API/DashboardAPI.ts +176 -492
- package/Server/API/DataSourceAPI.ts +268 -0
- package/Server/API/StatusPageAPI.ts +11 -1
- package/Server/API/UserAPI.ts +119 -0
- package/Server/Infrastructure/ClickhouseConfig.ts +59 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786303472848-AddDataSourceTable.ts +39 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786600000000-AddInvestigationAnalysisTldr.ts +24 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786700000000-DropProjectCallSMSConfigCredentialUniques.ts +76 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
- package/Server/Infrastructure/Queue.ts +26 -0
- package/Server/Infrastructure/QueueWorker.ts +17 -0
- package/Server/Middleware/TelemetryIngest.ts +8 -3
- package/Server/Services/AIInsightService.ts +132 -12
- package/Server/Services/AIRunService.ts +34 -0
- package/Server/Services/AIService.ts +17 -2
- package/Server/Services/AlertService.ts +38 -32
- package/Server/Services/AnalyticsDatabaseService.ts +8 -4
- package/Server/Services/DataSourceService.ts +200 -0
- package/Server/Services/HostService.ts +92 -0
- package/Server/Services/IncidentService.ts +45 -0
- package/Server/Services/Index.ts +2 -0
- package/Server/Services/MonitorService.ts +49 -0
- package/Server/Services/MonitorStatusService.ts +166 -0
- package/Server/Services/OpenTelemetryIngestService.ts +255 -184
- package/Server/Services/RumSessionPinService.ts +7 -21
- package/Server/Services/StatusPageSubscriberService.ts +13 -0
- package/Server/Services/UserService.ts +280 -9
- package/Server/Static/Vendor/README.md +62 -0
- package/Server/Static/Vendor/fonts/InterVariable.woff2 +0 -0
- package/Server/Static/Vendor/highlight/highlight.min.js +1244 -0
- package/Server/Static/Vendor/highlight/languages/apache.min.js +15 -0
- package/Server/Static/Vendor/highlight/languages/bash.min.js +21 -0
- package/Server/Static/Vendor/highlight/languages/c.min.js +41 -0
- package/Server/Static/Vendor/highlight/languages/cpp.min.js +47 -0
- package/Server/Static/Vendor/highlight/languages/csharp.min.js +49 -0
- package/Server/Static/Vendor/highlight/languages/css.min.js +31 -0
- package/Server/Static/Vendor/highlight/languages/diff.min.js +9 -0
- package/Server/Static/Vendor/highlight/languages/dns.min.js +11 -0
- package/Server/Static/Vendor/highlight/languages/dockerfile.min.js +8 -0
- package/Server/Static/Vendor/highlight/languages/go.min.js +20 -0
- package/Server/Static/Vendor/highlight/languages/graphql.min.js +12 -0
- package/Server/Static/Vendor/highlight/languages/http.min.js +14 -0
- package/Server/Static/Vendor/highlight/languages/ini.min.js +15 -0
- package/Server/Static/Vendor/highlight/languages/java.min.js +38 -0
- package/Server/Static/Vendor/highlight/languages/javascript.min.js +81 -0
- package/Server/Static/Vendor/highlight/languages/json.min.js +8 -0
- package/Server/Static/Vendor/highlight/languages/kotlin.min.js +46 -0
- package/Server/Static/Vendor/highlight/languages/lua.min.js +15 -0
- package/Server/Static/Vendor/highlight/languages/makefile.min.js +14 -0
- package/Server/Static/Vendor/highlight/languages/nginx.min.js +21 -0
- package/Server/Static/Vendor/highlight/languages/perl.min.js +41 -0
- package/Server/Static/Vendor/highlight/languages/php.min.js +59 -0
- package/Server/Static/Vendor/highlight/languages/powershell.min.js +39 -0
- package/Server/Static/Vendor/highlight/languages/python.min.js +42 -0
- package/Server/Static/Vendor/highlight/languages/r.min.js +26 -0
- package/Server/Static/Vendor/highlight/languages/ruby.min.js +54 -0
- package/Server/Static/Vendor/highlight/languages/rust.min.js +27 -0
- package/Server/Static/Vendor/highlight/languages/scala.min.js +28 -0
- package/Server/Static/Vendor/highlight/languages/sql.min.js +21 -0
- package/Server/Static/Vendor/highlight/languages/swift.min.js +68 -0
- package/Server/Static/Vendor/highlight/languages/typescript.min.js +99 -0
- package/Server/Static/Vendor/highlight/languages/xml.min.js +29 -0
- package/Server/Static/Vendor/highlight/languages/yaml.min.js +28 -0
- package/Server/Static/Vendor/highlight/styles/github-dark.min.css +10 -0
- package/Server/Static/Vendor/highlight/styles/vs2015.min.css +1 -0
- package/Server/Static/Vendor/tailwind/tailwind-3.4.5.js +21028 -0
- package/Server/Types/Database/Permissions/BasePermission.ts +7 -0
- package/Server/Types/Database/Permissions/DeletePermission.ts +2 -0
- package/Server/Types/Database/Permissions/ReadPermission.ts +3 -0
- package/Server/Types/Database/Permissions/TenantPermission.ts +101 -16
- package/Server/Utils/AI/CodeFix/FixRunBudget.ts +2 -2
- package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +90 -11
- package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +1 -0
- package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +1 -0
- package/Server/Utils/AI/SRE/Insights/Detectors/ExceptionIdentity.ts +125 -0
- package/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.ts +25 -15
- package/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.ts +256 -57
- package/Server/Utils/AI/SRE/Insights/InsightStore.ts +76 -18
- package/Server/Utils/AI/SRE/InvestigationTldr.ts +352 -0
- package/Server/Utils/AnalyticsDatabase/InsertDedupContext.ts +7 -2
- package/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.ts +1793 -0
- package/Server/Utils/Dashboard/PublicDashboardViewConfig.ts +65 -0
- package/Server/Utils/DataSource/ConnectorRegistry.ts +53 -0
- package/Server/Utils/DataSource/Connectors/ClickHouseConnector.ts +338 -0
- package/Server/Utils/DataSource/Connectors/ElasticsearchConnector.ts +717 -0
- package/Server/Utils/DataSource/Connectors/LokiConnector.ts +606 -0
- package/Server/Utils/DataSource/Connectors/MySqlConnector.ts +307 -0
- package/Server/Utils/DataSource/Connectors/PostgresConnector.ts +260 -0
- package/Server/Utils/DataSource/Connectors/PrometheusConnector.ts +555 -0
- package/Server/Utils/DataSource/Connectors/RestApiConnector.ts +597 -0
- package/Server/Utils/DataSource/Connectors/SqlServerConnector.ts +318 -0
- package/Server/Utils/DataSource/EgressGuard.ts +446 -0
- package/Server/Utils/DataSource/HttpFetch.ts +221 -0
- package/Server/Utils/DataSource/SqlQueryGuard.ts +383 -0
- package/Server/Utils/DataSource/SqlResultShaper.ts +372 -0
- package/Server/Utils/DataSource/Types.ts +93 -0
- package/Server/Utils/Database/PostgresErrorTranslator.ts +172 -17
- package/Server/Utils/InProcessMemo.ts +141 -0
- package/Server/Utils/InstanceHealth/TelemetryIngestion.ts +628 -0
- package/Server/Utils/Monitor/MonitorMetricUtil.ts +58 -0
- package/Server/Utils/Monitor/MonitorResource.ts +14 -0
- package/Server/Utils/Response.ts +12 -1
- package/Server/Utils/SSRFProtection.ts +237 -0
- package/Server/Utils/StartServer.ts +8 -0
- package/Server/Utils/StatusPageSubscriberWebhook.ts +30 -0
- package/Server/Utils/Telemetry/CaptureSpan.ts +30 -18
- package/Server/Utils/Telemetry/IngestionTimestamp.ts +75 -0
- package/Server/Utils/Telemetry/ResourceHeartbeat.ts +99 -7
- package/Server/Utils/Telemetry/Telemetry.ts +112 -1
- package/Server/Utils/Telemetry.ts +104 -3
- package/Server/Utils/VendorAssets.ts +165 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +28 -5
- package/Server/Utils/Workspace/Slack/Slack.ts +5 -0
- package/Server/Views/Partials/Head.ejs +21 -7
- package/Tests/App/AdminDashboard/AdminProjectsIdFilter.test.tsx +207 -0
- package/Tests/App/AdminDashboard/AdminUsersIdFilter.test.tsx +216 -0
- package/Tests/App/Dashboard/AIInsightViewActions.test.tsx +422 -0
- package/Tests/App/Dashboard/AddWidgetModal.test.tsx +10 -3
- package/Tests/App/Dashboard/AlertsSideMenu.test.tsx +387 -0
- package/Tests/App/Dashboard/ChatActivityFeed.test.tsx +199 -0
- package/Tests/App/Dashboard/DataSourceChartSyntheticConfig.test.tsx +736 -0
- package/Tests/App/Dashboard/DataSourceQueryEditor.test.tsx +868 -0
- package/Tests/App/Dashboard/DataSourceWidgetCatalog.test.ts +440 -0
- package/Tests/App/Dashboard/DataSourceWidgetFetching.test.tsx +2109 -0
- package/Tests/App/Dashboard/DataSourceWidgetViews.test.tsx +726 -0
- package/Tests/App/Dashboard/IncidentsSideMenu.test.tsx +447 -0
- package/Tests/App/Dashboard/InvestigationPanel.test.tsx +514 -3
- package/Tests/App/Dashboard/ProductMenuBreadcrumbs.test.tsx +391 -0
- package/Tests/App/Dashboard/ScheduledMaintenanceSideMenu.test.tsx +326 -0
- package/Tests/App/Dashboard/SideMenuHarness.tsx +187 -0
- package/Tests/App/Dashboard/WidgetCatalog.test.ts +6 -1
- package/Tests/App/StatusPage/StatusPageResourceExplorer.test.tsx +1858 -0
- package/Tests/Models/DatabaseModels/DataSourceModelSecrets.test.ts +70 -0
- package/Tests/Server/API/AIInvestigationAPI.test.ts +187 -0
- package/Tests/Server/API/DashboardPublicDataSourceStrip.test.ts +686 -0
- package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +585 -823
- package/Tests/Server/API/DataSourceAPIRoutes.test.ts +486 -0
- package/Tests/Server/API/StatusPageSubscriberOwnership.test.ts +291 -0
- package/Tests/Server/API/UserAuthenticationAPI.test.ts +641 -0
- package/Tests/Server/Infrastructure/ClickhouseKeepAliveOptions.test.ts +441 -0
- package/Tests/Server/Middleware/TelemetryIngestTokenLog.test.ts +140 -0
- package/Tests/Server/Services/AIInsightService.test.ts +266 -11
- package/Tests/Server/Services/AIRunAnalysisTldrPersistence.test.ts +160 -0
- package/Tests/Server/Services/AIServiceDailyBudget.test.ts +4 -0
- package/Tests/Server/Services/AlertMetricResourceAttributes.test.ts +352 -0
- package/Tests/Server/Services/DataSourceServiceSettings.test.ts +244 -0
- package/Tests/Server/Services/HostServiceFindOrCreateMemo.test.ts +397 -0
- package/Tests/Server/Services/HostServiceUpdateLastSeen.test.ts +41 -2
- package/Tests/Server/Services/IncidentMetricResourceAttributes.test.ts +408 -0
- package/Tests/Server/Services/MonitorStatusServiceDeleteReferenceCleanup.test.ts +351 -0
- package/Tests/Server/Services/MonitorStatusServiceValidation.test.ts +171 -0
- package/Tests/Server/Services/OpenTelemetryResourceRetentionMemo.test.ts +398 -0
- package/Tests/Server/Services/OpenTelemetryServiceResolutionCache.test.ts +19 -0
- package/Tests/Server/Services/ProjectCallSMSConfigCredentialUniqueness.test.ts +305 -0
- package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +36 -1
- package/Tests/Server/Services/RumSessionPinUniqueViolationRecovery.test.ts +163 -0
- package/Tests/Server/Services/UserAuthenticationService.test.ts +973 -0
- package/Tests/Server/Types/Database/ObjectIdColumnQuery.test.ts +262 -0
- package/Tests/Server/Types/Database/Permissions/TenantPermission.test.ts +358 -0
- package/Tests/Server/Utils/AI/FixRunBudget.test.ts +2 -2
- package/Tests/Server/Utils/AI/Insights/ExceptionSpikeDetector.test.ts +6 -1
- package/Tests/Server/Utils/AI/Insights/InsightStore.test.ts +121 -4
- package/Tests/Server/Utils/AI/Insights/NewExceptionDetector.test.ts +361 -10
- package/Tests/Server/Utils/AI/InvestigationSettlement.test.ts +292 -0
- package/Tests/Server/Utils/AI/InvestigationTldr.test.ts +623 -0
- package/Tests/Server/Utils/AI/InvestigationTldrWiring.test.ts +98 -0
- package/Tests/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.test.ts +857 -0
- package/Tests/Server/Utils/Dashboard/PublicDashboardViewConfig.test.ts +480 -0
- package/Tests/Server/Utils/DataSource/ClickHouseConnector.test.ts +759 -0
- package/Tests/Server/Utils/DataSource/ConnectorRegistry.test.ts +37 -0
- package/Tests/Server/Utils/DataSource/EgressGuard.test.ts +645 -0
- package/Tests/Server/Utils/DataSource/ElasticsearchConnector.test.ts +1206 -0
- package/Tests/Server/Utils/DataSource/HttpFetch.test.ts +616 -0
- package/Tests/Server/Utils/DataSource/LokiConnector.test.ts +970 -0
- package/Tests/Server/Utils/DataSource/MySqlConnector.test.ts +935 -0
- package/Tests/Server/Utils/DataSource/PostgresConnector.test.ts +854 -0
- package/Tests/Server/Utils/DataSource/PrometheusConnector.test.ts +881 -0
- package/Tests/Server/Utils/DataSource/RestApiConnector.test.ts +1020 -0
- package/Tests/Server/Utils/DataSource/SqlQueryGuard.test.ts +425 -0
- package/Tests/Server/Utils/DataSource/SqlResultShaper.test.ts +555 -0
- package/Tests/Server/Utils/DataSource/SqlServerConnector.test.ts +934 -0
- package/Tests/Server/Utils/Database/PostgresErrorTranslator.test.ts +228 -7
- package/Tests/Server/Utils/InProcessMemo.test.ts +292 -0
- package/Tests/Server/Utils/InstanceHealth/TelemetryIngestion.test.ts +960 -0
- package/Tests/Server/Utils/MicrosoftTeamsWebhookUrlValidation.test.ts +103 -0
- package/Tests/Server/Utils/Monitor/MonitorMetricResourceAttributes.test.ts +336 -0
- package/Tests/Server/Utils/OfflineAssetHygiene.test.ts +521 -0
- package/Tests/Server/Utils/ResponseRenderAnalytics.test.ts +143 -0
- package/Tests/Server/Utils/SSRFProtection.test.ts +193 -0
- package/Tests/Server/Utils/StatusPageSubscriberWebhook.test.ts +101 -0
- package/Tests/Server/Utils/TailwindBuildCopy.test.ts +225 -0
- package/Tests/Server/Utils/Telemetry/ResourceHeartbeatL1Memo.test.ts +355 -0
- package/Tests/Server/Utils/Telemetry/TelemetryFanInWriter.test.ts +128 -0
- package/Tests/Server/Utils/Telemetry/TelemetryUtilAttributeKeys.test.ts +381 -0
- package/Tests/Server/Utils/Telemetry.test.ts +87 -0
- package/Tests/Server/Utils/VendorAssetIntegrity.test.ts +343 -0
- package/Tests/Server/Utils/VendorAssets.test.ts +671 -0
- package/Tests/Server/Views/OfflineRender.test.ts +297 -0
- package/Tests/Types/Dashboard/DataSourceComponentTypes.test.ts +618 -0
- package/Tests/Types/DateClickhouseFormat.test.ts +192 -0
- package/Tests/Types/Password.test.ts +363 -0
- package/Tests/UI/Components/ConfirmModal.test.tsx +7 -0
- package/Tests/UI/Components/ErrorBoundary.test.tsx +53 -0
- package/Tests/UI/Components/Modal.test.tsx +281 -1
- package/Tests/UI/Components/ObjectIdFilterPipeline.test.tsx +396 -0
- package/Tests/UI/Components/StatusPage/ResourceGroupNavigator.test.tsx +1147 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +116 -1
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +2 -0
- package/Tests/Utils/Analytics.test.ts +23 -0
- package/Tests/Utils/Dashboard/Components/DashboardDataSourceComponents.test.ts +640 -0
- package/Tests/Utils/DataSource/DataSourceQueryText.test.ts +138 -0
- package/Tests/Utils/DataSource/DataSourceValueReducer.test.ts +526 -0
- package/Tests/Utils/Metrics/MetricResourceAttributeUtil.test.ts +691 -0
- package/Tests/Utils/StatusPage/GroupHierarchyView.test.ts +1452 -0
- package/Tests/Utils/StatusPage/ResourceExplorer.test.ts +931 -0
- package/Tests/Utils/Telemetry/CaptureSpanExportGating.test.ts +288 -0
- package/Tests/Utils/Telemetry/IngestionTimestamp.test.ts +170 -0
- package/Types/AI/AIInsightEvidence.ts +6 -0
- package/Types/Analytics/RevenueEvent.ts +31 -0
- package/Types/Dashboard/DashboardComponentType.ts +31 -0
- package/Types/Dashboard/DashboardComponents/DashboardDataSourceChartComponent.ts +27 -0
- package/Types/Dashboard/DashboardComponents/DashboardDataSourceGaugeComponent.ts +32 -0
- package/Types/Dashboard/DashboardComponents/DashboardDataSourceTableComponent.ts +30 -0
- package/Types/Dashboard/DashboardComponents/DashboardDataSourceValueComponent.ts +55 -0
- package/Types/DataSource/DataSourceLimits.ts +62 -0
- package/Types/DataSource/DataSourceQueryConfig.ts +41 -0
- package/Types/DataSource/DataSourceTableResult.ts +38 -0
- package/Types/DataSource/DataSourceType.ts +213 -0
- package/Types/Date.ts +43 -3
- package/Types/Password.ts +64 -0
- package/Types/Permission.ts +46 -0
- package/Types/UserAuthenticationStatus.ts +34 -0
- package/UI/Components/Button/Button.tsx +1 -1
- package/UI/Components/ErrorBoundary.tsx +459 -108
- package/UI/Components/Icon/Icon.tsx +10 -9
- package/UI/Components/Modal/ConfirmModal.tsx +6 -1
- package/UI/Components/Modal/Modal.tsx +267 -35
- package/UI/Components/Modal/ModalFooter.tsx +6 -1
- package/UI/Components/MoreMenu/MoreMenu.tsx +19 -4
- package/UI/Components/MoreMenu/MoreMenuItem.tsx +6 -1
- package/UI/Components/StatusPage/ResourceGrid.tsx +314 -0
- package/UI/Components/StatusPage/ResourceGroupNavigator.tsx +808 -0
- package/UI/Components/StatusPage/ResourceList.tsx +570 -0
- package/UI/Styles/Theme.css +18 -0
- package/UI/Utils/API/RequestOptions.ts +7 -0
- package/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.ts +1 -0
- package/UI/Utils/ModelAPI/ModelAPI.ts +1 -0
- package/UI/Utils/TestDataSource.ts +61 -0
- package/UI/esbuild-config.js +45 -0
- package/Utils/Analytics.ts +15 -0
- package/Utils/Dashboard/Components/DashboardDataSourceChartComponent.ts +88 -0
- package/Utils/Dashboard/Components/DashboardDataSourceGaugeComponent.ts +123 -0
- package/Utils/Dashboard/Components/DashboardDataSourceShared.ts +16 -0
- package/Utils/Dashboard/Components/DashboardDataSourceTableComponent.ts +72 -0
- package/Utils/Dashboard/Components/DashboardDataSourceValueComponent.ts +126 -0
- package/Utils/Dashboard/Components/Index.ts +28 -0
- package/Utils/DataSource/DataSourceQueryText.ts +94 -0
- package/Utils/DataSource/DataSourceValueReducer.ts +156 -0
- package/Utils/Metrics/MetricResourceAttributeUtil.ts +380 -0
- package/Utils/NetworkDiscovery/ScanTargetUtil.ts +8 -6
- package/Utils/StatusPage/GroupHierarchyView.ts +931 -0
- package/Utils/StatusPage/ResourceExplorer.ts +853 -0
- package/build/dist/Models/DatabaseModels/AIRun.js +34 -0
- package/build/dist/Models/DatabaseModels/AIRun.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DataSource.js +774 -0
- package/build/dist/Models/DatabaseModels/DataSource.js.map +1 -0
- package/build/dist/Models/DatabaseModels/Index.js +2 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProjectCallSMSConfig.js +14 -4
- package/build/dist/Models/DatabaseModels/ProjectCallSMSConfig.js.map +1 -1
- package/build/dist/Models/DatabaseModels/User.js +2 -0
- package/build/dist/Models/DatabaseModels/User.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserOnCallLog.js +2 -0
- package/build/dist/Models/DatabaseModels/UserOnCallLog.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js +2 -0
- package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserSession.js +2 -0
- package/build/dist/Models/DatabaseModels/UserSession.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserTotpAuth.js +6 -4
- package/build/dist/Models/DatabaseModels/UserTotpAuth.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserWebAuthn.js +5 -3
- package/build/dist/Models/DatabaseModels/UserWebAuthn.js.map +1 -1
- package/build/dist/Server/API/AIInsightAPI.js +34 -4
- package/build/dist/Server/API/AIInsightAPI.js.map +1 -1
- package/build/dist/Server/API/AIInvestigationAPI.js +23 -0
- package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
- package/build/dist/Server/API/DashboardAPI.js +119 -433
- package/build/dist/Server/API/DashboardAPI.js.map +1 -1
- package/build/dist/Server/API/DataSourceAPI.js +173 -0
- package/build/dist/Server/API/DataSourceAPI.js.map +1 -0
- package/build/dist/Server/API/StatusPageAPI.js +11 -1
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/API/UserAPI.js +94 -2
- package/build/dist/Server/API/UserAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/ClickhouseConfig.js +49 -0
- package/build/dist/Server/Infrastructure/ClickhouseConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786303472848-AddDataSourceTable.js +20 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786303472848-AddDataSourceTable.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786600000000-AddInvestigationAnalysisTldr.js +19 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786600000000-AddInvestigationAnalysisTldr.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786700000000-DropProjectCallSMSConfigCredentialUniques.js +64 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786700000000-DropProjectCallSMSConfigCredentialUniques.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/Infrastructure/Queue.js +27 -0
- package/build/dist/Server/Infrastructure/Queue.js.map +1 -1
- package/build/dist/Server/Infrastructure/QueueWorker.js +13 -0
- package/build/dist/Server/Infrastructure/QueueWorker.js.map +1 -1
- package/build/dist/Server/Middleware/TelemetryIngest.js +8 -3
- package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
- package/build/dist/Server/Services/AIInsightService.js +110 -10
- package/build/dist/Server/Services/AIInsightService.js.map +1 -1
- package/build/dist/Server/Services/AIRunService.js +34 -0
- package/build/dist/Server/Services/AIRunService.js.map +1 -1
- package/build/dist/Server/Services/AIService.js +16 -2
- package/build/dist/Server/Services/AIService.js.map +1 -1
- package/build/dist/Server/Services/AlertService.js +28 -35
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js +8 -4
- package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
- package/build/dist/Server/Services/DataSourceService.js +159 -0
- package/build/dist/Server/Services/DataSourceService.js.map +1 -0
- package/build/dist/Server/Services/HostService.js +74 -0
- package/build/dist/Server/Services/HostService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +35 -24
- package/build/dist/Server/Services/IncidentService.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/MonitorService.js +45 -0
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/MonitorStatusService.js +129 -0
- package/build/dist/Server/Services/MonitorStatusService.js.map +1 -1
- package/build/dist/Server/Services/OpenTelemetryIngestService.js +222 -159
- package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
- package/build/dist/Server/Services/RumSessionPinService.js +7 -15
- package/build/dist/Server/Services/RumSessionPinService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberService.js +10 -0
- package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
- package/build/dist/Server/Services/UserService.js +241 -10
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/BasePermission.js +6 -0
- package/build/dist/Server/Types/Database/Permissions/BasePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/DeletePermission.js +1 -0
- package/build/dist/Server/Types/Database/Permissions/DeletePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/ReadPermission.js +2 -0
- package/build/dist/Server/Types/Database/Permissions/ReadPermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/TenantPermission.js +68 -12
- package/build/dist/Server/Types/Database/Permissions/TenantPermission.js.map +1 -1
- package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js +2 -2
- package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +68 -8
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +1 -0
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +1 -0
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionIdentity.js +97 -0
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionIdentity.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js +17 -15
- 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 +178 -54
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/Insights/InsightStore.js +65 -16
- package/build/dist/Server/Utils/AI/SRE/Insights/InsightStore.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/InvestigationTldr.js +303 -0
- package/build/dist/Server/Utils/AI/SRE/InvestigationTldr.js.map +1 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/InsertDedupContext.js.map +1 -1
- package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js +1261 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js.map +1 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardViewConfig.js +47 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardViewConfig.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/ConnectorRegistry.js +42 -0
- package/build/dist/Server/Utils/DataSource/ConnectorRegistry.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/ClickHouseConnector.js +187 -0
- package/build/dist/Server/Utils/DataSource/Connectors/ClickHouseConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/ElasticsearchConnector.js +493 -0
- package/build/dist/Server/Utils/DataSource/Connectors/ElasticsearchConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/LokiConnector.js +399 -0
- package/build/dist/Server/Utils/DataSource/Connectors/LokiConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/MySqlConnector.js +195 -0
- package/build/dist/Server/Utils/DataSource/Connectors/MySqlConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/PostgresConnector.js +149 -0
- package/build/dist/Server/Utils/DataSource/Connectors/PostgresConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/PrometheusConnector.js +362 -0
- package/build/dist/Server/Utils/DataSource/Connectors/PrometheusConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/RestApiConnector.js +342 -0
- package/build/dist/Server/Utils/DataSource/Connectors/RestApiConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/SqlServerConnector.js +186 -0
- package/build/dist/Server/Utils/DataSource/Connectors/SqlServerConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/EgressGuard.js +305 -0
- package/build/dist/Server/Utils/DataSource/EgressGuard.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/HttpFetch.js +134 -0
- package/build/dist/Server/Utils/DataSource/HttpFetch.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/SqlQueryGuard.js +331 -0
- package/build/dist/Server/Utils/DataSource/SqlQueryGuard.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/SqlResultShaper.js +280 -0
- package/build/dist/Server/Utils/DataSource/SqlResultShaper.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Types.js +2 -0
- package/build/dist/Server/Utils/DataSource/Types.js.map +1 -0
- package/build/dist/Server/Utils/Database/PostgresErrorTranslator.js +103 -9
- package/build/dist/Server/Utils/Database/PostgresErrorTranslator.js.map +1 -1
- package/build/dist/Server/Utils/InProcessMemo.js +113 -0
- package/build/dist/Server/Utils/InProcessMemo.js.map +1 -0
- package/build/dist/Server/Utils/InstanceHealth/TelemetryIngestion.js +406 -0
- package/build/dist/Server/Utils/InstanceHealth/TelemetryIngestion.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +34 -0
- package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +14 -0
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/Response.js +9 -1
- package/build/dist/Server/Utils/Response.js.map +1 -1
- package/build/dist/Server/Utils/SSRFProtection.js +192 -0
- package/build/dist/Server/Utils/SSRFProtection.js.map +1 -0
- package/build/dist/Server/Utils/StartServer.js +7 -0
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/StatusPageSubscriberWebhook.js +24 -0
- package/build/dist/Server/Utils/StatusPageSubscriberWebhook.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/CaptureSpan.js +25 -11
- package/build/dist/Server/Utils/Telemetry/CaptureSpan.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/IngestionTimestamp.js +32 -0
- package/build/dist/Server/Utils/Telemetry/IngestionTimestamp.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js +92 -5
- package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/Telemetry.js +92 -6
- package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry.js +93 -6
- package/build/dist/Server/Utils/Telemetry.js.map +1 -1
- package/build/dist/Server/Utils/VendorAssets.js +121 -0
- package/build/dist/Server/Utils/VendorAssets.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +25 -4
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js +5 -0
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
- package/build/dist/Types/Analytics/RevenueEvent.js +26 -0
- package/build/dist/Types/Analytics/RevenueEvent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponentType.js +27 -0
- package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceChartComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceChartComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceGaugeComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceGaugeComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceTableComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceTableComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceValueComponent.js +16 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceValueComponent.js.map +1 -0
- package/build/dist/Types/DataSource/DataSourceLimits.js +47 -0
- package/build/dist/Types/DataSource/DataSourceLimits.js.map +1 -0
- package/build/dist/Types/DataSource/DataSourceQueryConfig.js +10 -0
- package/build/dist/Types/DataSource/DataSourceQueryConfig.js.map +1 -0
- package/build/dist/Types/DataSource/DataSourceTableResult.js +14 -0
- package/build/dist/Types/DataSource/DataSourceTableResult.js.map +1 -0
- package/build/dist/Types/DataSource/DataSourceType.js +183 -0
- package/build/dist/Types/DataSource/DataSourceType.js.map +1 -0
- package/build/dist/Types/Date.js +38 -3
- package/build/dist/Types/Date.js.map +1 -1
- package/build/dist/Types/Password.js +56 -0
- package/build/dist/Types/Password.js.map +1 -0
- package/build/dist/Types/Permission.js +42 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/UserAuthenticationStatus.js +2 -0
- package/build/dist/Types/UserAuthenticationStatus.js.map +1 -0
- package/build/dist/UI/Components/ErrorBoundary.js +388 -72
- package/build/dist/UI/Components/ErrorBoundary.js.map +1 -1
- package/build/dist/UI/Components/Icon/Icon.js +10 -4
- package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
- package/build/dist/UI/Components/Modal/ConfirmModal.js +1 -1
- package/build/dist/UI/Components/Modal/ConfirmModal.js.map +1 -1
- package/build/dist/UI/Components/Modal/Modal.js +178 -19
- package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
- package/build/dist/UI/Components/Modal/ModalFooter.js +3 -1
- package/build/dist/UI/Components/Modal/ModalFooter.js.map +1 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenu.js +9 -6
- package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js +7 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js.map +1 -1
- package/build/dist/UI/Components/StatusPage/ResourceGrid.js +119 -0
- package/build/dist/UI/Components/StatusPage/ResourceGrid.js.map +1 -0
- package/build/dist/UI/Components/StatusPage/ResourceGroupNavigator.js +354 -0
- package/build/dist/UI/Components/StatusPage/ResourceGroupNavigator.js.map +1 -0
- package/build/dist/UI/Components/StatusPage/ResourceList.js +203 -0
- package/build/dist/UI/Components/StatusPage/ResourceList.js.map +1 -0
- package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js +1 -6
- package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js.map +1 -1
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +3 -8
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
- package/build/dist/UI/Utils/TestDataSource.js +39 -0
- package/build/dist/UI/Utils/TestDataSource.js.map +1 -0
- package/build/dist/Utils/Analytics.js +4 -0
- package/build/dist/Utils/Analytics.js.map +1 -1
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceChartComponent.js +79 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceChartComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceGaugeComponent.js +111 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceGaugeComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceShared.js +15 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceShared.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceTableComponent.js +62 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceTableComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceValueComponent.js +112 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceValueComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/Index.js +16 -0
- package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
- package/build/dist/Utils/DataSource/DataSourceQueryText.js +64 -0
- package/build/dist/Utils/DataSource/DataSourceQueryText.js.map +1 -0
- package/build/dist/Utils/DataSource/DataSourceValueReducer.js +124 -0
- package/build/dist/Utils/DataSource/DataSourceValueReducer.js.map +1 -0
- package/build/dist/Utils/Metrics/MetricResourceAttributeUtil.js +281 -0
- package/build/dist/Utils/Metrics/MetricResourceAttributeUtil.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js +8 -6
- package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js.map +1 -1
- package/build/dist/Utils/StatusPage/GroupHierarchyView.js +560 -0
- package/build/dist/Utils/StatusPage/GroupHierarchyView.js.map +1 -0
- package/build/dist/Utils/StatusPage/ResourceExplorer.js +519 -0
- package/build/dist/Utils/StatusPage/ResourceExplorer.js.map +1 -0
- package/jest.config.json +1 -0
- package/package.json +5 -1
- package/tsconfig.json +12 -10
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
import GlobalCache from "../../../../Server/Infrastructure/GlobalCache";
|
|
2
|
+
import ResourceHeartbeat from "../../../../Server/Utils/Telemetry/ResourceHeartbeat";
|
|
3
|
+
import SingleFlight from "../../../../Server/Utils/SingleFlight";
|
|
4
|
+
import ObjectID from "../../../../Types/ObjectID";
|
|
5
|
+
import { afterEach, beforeEach, describe, expect, test } from "@jest/globals";
|
|
6
|
+
|
|
7
|
+
/*
|
|
8
|
+
* The time-based L1 in front of ResourceHeartbeat's Redis gate cascade.
|
|
9
|
+
*
|
|
10
|
+
* SingleFlight already collapses CONCURRENT duplicate heartbeats, but every
|
|
11
|
+
* SEQUENTIAL batch — the next job for the same host a second later — still
|
|
12
|
+
* paid 1-3 serialized Redis round trips (liveness SET NX, fingerprint
|
|
13
|
+
* compare-and-claim, rate-limit SET NX) to learn "nothing to do". The memo
|
|
14
|
+
* remembers a SETTLED heartbeat for (resource, fingerprint) for
|
|
15
|
+
* min(60s, liveness window) so those batches cost zero network round trips.
|
|
16
|
+
*
|
|
17
|
+
* What is pinned here, in order of importance:
|
|
18
|
+
*
|
|
19
|
+
* 1. A repeat identical heartbeat inside the window touches NEITHER Redis
|
|
20
|
+
* nor Postgres — the whole point of the change.
|
|
21
|
+
* 2. The memo expires with the window: after it, Redis is consulted again.
|
|
22
|
+
* 3. A CHANGED fingerprint bypasses the memo, so the change-promptness
|
|
23
|
+
* contract of the compare-and-claim gate is untouched.
|
|
24
|
+
* 4. Outcomes that must retry are never memoized: a failed metadata write,
|
|
25
|
+
* a lock-skipped enrichment write, and any outcome where a gate probe
|
|
26
|
+
* threw (a Redis outage must keep failing open per batch — memoizing
|
|
27
|
+
* an outage outcome would suppress liveness for a healthy resource).
|
|
28
|
+
*
|
|
29
|
+
* The gates are modelled faithfully (SET NX only succeeds on an absent key,
|
|
30
|
+
* compare-and-claim only on a differing value) — same discipline as the
|
|
31
|
+
* neighbouring heartbeat suites, and for the same reason: a stubbed-constant
|
|
32
|
+
* gate would let this suite agree with a memo that breaks the throttle.
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
const RESOURCE_ID: ObjectID = ObjectID.generate();
|
|
36
|
+
const NAMESPACE: string = "heartbeat-memo-test-last-seen";
|
|
37
|
+
const WINDOW_SECONDS: number = 60;
|
|
38
|
+
|
|
39
|
+
type WriteCall = { id: ObjectID; data: Record<string, unknown> };
|
|
40
|
+
|
|
41
|
+
let cache: Map<string, string>;
|
|
42
|
+
let writes: Array<WriteCall>;
|
|
43
|
+
let writeLands: boolean;
|
|
44
|
+
let writeThrows: boolean;
|
|
45
|
+
let nowMs: number;
|
|
46
|
+
|
|
47
|
+
/** The service seam ResourceHeartbeat writes through, recording instead. */
|
|
48
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
49
|
+
const fakeService: any = {
|
|
50
|
+
updateColumnsByIdIfUnlockedWithoutHooks: async (input: {
|
|
51
|
+
id: ObjectID;
|
|
52
|
+
data: unknown;
|
|
53
|
+
}): Promise<boolean> => {
|
|
54
|
+
writes.push({
|
|
55
|
+
id: input.id,
|
|
56
|
+
data: { ...(input.data as Record<string, unknown>) },
|
|
57
|
+
});
|
|
58
|
+
if (
|
|
59
|
+
writeThrows &&
|
|
60
|
+
Object.keys(writes[writes.length - 1]!.data).length > 1
|
|
61
|
+
) {
|
|
62
|
+
throw new Error("value too long for type character varying(100)");
|
|
63
|
+
}
|
|
64
|
+
return writeLands;
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
/* eslint-enable @typescript-eslint/no-explicit-any */
|
|
68
|
+
|
|
69
|
+
function gateCallCount(): number {
|
|
70
|
+
return (
|
|
71
|
+
(GlobalCache.setStringIfNotExists as jest.Mock).mock.calls.length +
|
|
72
|
+
(GlobalCache.setStringIfChanged as jest.Mock).mock.calls.length
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function heartbeat(input?: {
|
|
77
|
+
fingerprint?: string;
|
|
78
|
+
metadata?: Record<string, unknown>;
|
|
79
|
+
throttleInSeconds?: number;
|
|
80
|
+
}): Promise<void> {
|
|
81
|
+
await ResourceHeartbeat.write({
|
|
82
|
+
service: fakeService,
|
|
83
|
+
id: RESOURCE_ID,
|
|
84
|
+
cacheNamespace: NAMESPACE,
|
|
85
|
+
throttleInSeconds: input?.throttleInSeconds ?? WINDOW_SECONDS,
|
|
86
|
+
liveness: { lastSeenAt: "now" } as never,
|
|
87
|
+
metadata: (input?.metadata ?? undefined) as never,
|
|
88
|
+
fingerprint: input?.fingerprint ?? "fp-default",
|
|
89
|
+
describe: `test resource ${RESOURCE_ID.toString()}`,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
describe("ResourceHeartbeat recent-heartbeat L1 memo", () => {
|
|
94
|
+
beforeEach(() => {
|
|
95
|
+
cache = new Map<string, string>();
|
|
96
|
+
writes = [];
|
|
97
|
+
writeLands = true;
|
|
98
|
+
writeThrows = false;
|
|
99
|
+
nowMs = 1_700_000_000_000;
|
|
100
|
+
|
|
101
|
+
SingleFlight.clear();
|
|
102
|
+
ResourceHeartbeat.clearRecentHeartbeatMemo();
|
|
103
|
+
|
|
104
|
+
jest.spyOn(Date, "now").mockImplementation(() => {
|
|
105
|
+
return nowMs;
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
jest
|
|
109
|
+
.spyOn(GlobalCache, "setStringIfNotExists")
|
|
110
|
+
.mockImplementation(
|
|
111
|
+
async (namespace: string, key: string, value: string) => {
|
|
112
|
+
const full: string = `${namespace}:${key}`;
|
|
113
|
+
if (cache.has(full)) {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
cache.set(full, value);
|
|
117
|
+
return true;
|
|
118
|
+
},
|
|
119
|
+
);
|
|
120
|
+
jest
|
|
121
|
+
.spyOn(GlobalCache, "setStringIfChanged")
|
|
122
|
+
.mockImplementation(
|
|
123
|
+
async (namespace: string, key: string, value: string) => {
|
|
124
|
+
const full: string = `${namespace}:${key}`;
|
|
125
|
+
if (cache.get(full) === value) {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
cache.set(full, value);
|
|
129
|
+
return true;
|
|
130
|
+
},
|
|
131
|
+
);
|
|
132
|
+
jest
|
|
133
|
+
.spyOn(GlobalCache, "deleteKey")
|
|
134
|
+
.mockImplementation(async (namespace: string, key: string) => {
|
|
135
|
+
cache.delete(`${namespace}:${key}`);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
afterEach(() => {
|
|
140
|
+
jest.restoreAllMocks();
|
|
141
|
+
SingleFlight.clear();
|
|
142
|
+
ResourceHeartbeat.clearRecentHeartbeatMemo();
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
describe("the settled-heartbeat skip", () => {
|
|
146
|
+
test("a repeat identical heartbeat touches neither Redis nor Postgres", async () => {
|
|
147
|
+
await heartbeat({ metadata: { osType: "linux" }, fingerprint: "fp-a" });
|
|
148
|
+
|
|
149
|
+
const gatesAfterFirst: number = gateCallCount();
|
|
150
|
+
const writesAfterFirst: number = writes.length;
|
|
151
|
+
expect(writesAfterFirst).toBe(1);
|
|
152
|
+
|
|
153
|
+
await heartbeat({ metadata: { osType: "linux" }, fingerprint: "fp-a" });
|
|
154
|
+
await heartbeat({ metadata: { osType: "linux" }, fingerprint: "fp-a" });
|
|
155
|
+
|
|
156
|
+
expect(gateCallCount()).toBe(gatesAfterFirst);
|
|
157
|
+
expect(writes).toHaveLength(writesAfterFirst);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
test("a liveness-only heartbeat (no metadata) is memoized too", async () => {
|
|
161
|
+
await heartbeat();
|
|
162
|
+
const gatesAfterFirst: number = gateCallCount();
|
|
163
|
+
|
|
164
|
+
await heartbeat();
|
|
165
|
+
|
|
166
|
+
expect(gateCallCount()).toBe(gatesAfterFirst);
|
|
167
|
+
expect(writes).toHaveLength(1);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test("a refused window is memoized after ONE probe — the steady state costs one round trip per window", async () => {
|
|
171
|
+
/*
|
|
172
|
+
* Another pod already holds the liveness gate and the fingerprint is
|
|
173
|
+
* already stored: this pod's first batch probes and is refused, and
|
|
174
|
+
* every later batch in the window must not re-ask.
|
|
175
|
+
*/
|
|
176
|
+
cache.set(`${NAMESPACE}:${RESOURCE_ID.toString()}`, "1");
|
|
177
|
+
cache.set(`${NAMESPACE}-fingerprint:${RESOURCE_ID.toString()}`, "fp-a");
|
|
178
|
+
|
|
179
|
+
await heartbeat({ metadata: { osType: "linux" }, fingerprint: "fp-a" });
|
|
180
|
+
const gatesAfterFirst: number = gateCallCount();
|
|
181
|
+
expect(writes).toHaveLength(0);
|
|
182
|
+
|
|
183
|
+
await heartbeat({ metadata: { osType: "linux" }, fingerprint: "fp-a" });
|
|
184
|
+
|
|
185
|
+
expect(gateCallCount()).toBe(gatesAfterFirst);
|
|
186
|
+
expect(writes).toHaveLength(0);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test("different resources do not share an entry", async () => {
|
|
190
|
+
await heartbeat();
|
|
191
|
+
|
|
192
|
+
const otherId: ObjectID = ObjectID.generate();
|
|
193
|
+
await ResourceHeartbeat.write({
|
|
194
|
+
service: fakeService,
|
|
195
|
+
id: otherId,
|
|
196
|
+
cacheNamespace: NAMESPACE,
|
|
197
|
+
throttleInSeconds: WINDOW_SECONDS,
|
|
198
|
+
liveness: { lastSeenAt: "now" } as never,
|
|
199
|
+
metadata: undefined as never,
|
|
200
|
+
fingerprint: "fp-default",
|
|
201
|
+
describe: `test resource ${otherId.toString()}`,
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
// Both wrote — the second resource was not suppressed by the first's memo.
|
|
205
|
+
expect(writes).toHaveLength(2);
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
describe("memo lifetime", () => {
|
|
210
|
+
test("expires with the window: Redis is consulted again afterwards", async () => {
|
|
211
|
+
await heartbeat();
|
|
212
|
+
const gatesAfterFirst: number = gateCallCount();
|
|
213
|
+
|
|
214
|
+
nowMs += WINDOW_SECONDS * 1000 + 1;
|
|
215
|
+
await heartbeat();
|
|
216
|
+
|
|
217
|
+
expect(gateCallCount()).toBeGreaterThan(gatesAfterFirst);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
test("is capped at the caller's own window when that is shorter than 60s", async () => {
|
|
221
|
+
await heartbeat({ throttleInSeconds: 10 });
|
|
222
|
+
const gatesAfterFirst: number = gateCallCount();
|
|
223
|
+
|
|
224
|
+
// 11s later the 10s window may have reopened; the memo must not outlive it.
|
|
225
|
+
nowMs += 11 * 1000;
|
|
226
|
+
await heartbeat({ throttleInSeconds: 10 });
|
|
227
|
+
|
|
228
|
+
expect(gateCallCount()).toBeGreaterThan(gatesAfterFirst);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
test("never outlives the pre-jitter Redis window it fronts", async () => {
|
|
232
|
+
/*
|
|
233
|
+
* Inside the pre-jitter window the Redis liveness key cannot have
|
|
234
|
+
* expired (its jittered TTL >= the window), so a skipped probe is
|
|
235
|
+
* outcome-identical to a refused probe. Just before the boundary the
|
|
236
|
+
* memo may still answer; the write count must therefore stay at one
|
|
237
|
+
* either way.
|
|
238
|
+
*/
|
|
239
|
+
await heartbeat();
|
|
240
|
+
nowMs += (WINDOW_SECONDS - 1) * 1000;
|
|
241
|
+
await heartbeat();
|
|
242
|
+
|
|
243
|
+
expect(writes).toHaveLength(1);
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
describe("change promptness", () => {
|
|
248
|
+
test("a changed fingerprint bypasses the memo and reaches Redis immediately", async () => {
|
|
249
|
+
await heartbeat({ metadata: { osType: "linux" }, fingerprint: "fp-a" });
|
|
250
|
+
const gatesAfterFirst: number = gateCallCount();
|
|
251
|
+
|
|
252
|
+
await heartbeat({ metadata: { osType: "windows" }, fingerprint: "fp-b" });
|
|
253
|
+
|
|
254
|
+
expect(gateCallCount()).toBeGreaterThan(gatesAfterFirst);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
test("the changed payload still lands in the next window, exactly as before the memo", async () => {
|
|
258
|
+
await heartbeat({ metadata: { osType: "linux" }, fingerprint: "fp-a" });
|
|
259
|
+
|
|
260
|
+
// Both windows reopen (as their Redis TTLs would).
|
|
261
|
+
cache.delete(`${NAMESPACE}:${RESOURCE_ID.toString()}`);
|
|
262
|
+
cache.delete(`${NAMESPACE}-write-window:${RESOURCE_ID.toString()}`);
|
|
263
|
+
|
|
264
|
+
await heartbeat({ metadata: { osType: "windows" }, fingerprint: "fp-b" });
|
|
265
|
+
|
|
266
|
+
expect(writes).toHaveLength(2);
|
|
267
|
+
expect(writes[1]!.data["osType"]).toBe("windows");
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
describe("outcomes that must retry are not memoized", () => {
|
|
272
|
+
test("a failed metadata write retries on the very next batch", async () => {
|
|
273
|
+
writeThrows = true;
|
|
274
|
+
|
|
275
|
+
await heartbeat({ metadata: { osType: "linux" }, fingerprint: "fp-a" });
|
|
276
|
+
// Enriched attempt + liveness-only fallback.
|
|
277
|
+
expect(writes).toHaveLength(2);
|
|
278
|
+
|
|
279
|
+
await heartbeat({ metadata: { osType: "linux" }, fingerprint: "fp-a" });
|
|
280
|
+
|
|
281
|
+
// Retried — not silently swallowed by the memo.
|
|
282
|
+
expect(writes).toHaveLength(4);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
test("a lock-skipped enrichment write retries on the very next batch", async () => {
|
|
286
|
+
writeLands = false;
|
|
287
|
+
|
|
288
|
+
await heartbeat({ metadata: { osType: "linux" }, fingerprint: "fp-a" });
|
|
289
|
+
expect(writes).toHaveLength(1);
|
|
290
|
+
|
|
291
|
+
writeLands = true;
|
|
292
|
+
await heartbeat({ metadata: { osType: "linux" }, fingerprint: "fp-a" });
|
|
293
|
+
|
|
294
|
+
expect(writes).toHaveLength(2);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
test("a liveness-gate outage keeps failing open on every batch", async () => {
|
|
298
|
+
/*
|
|
299
|
+
* If an outage outcome were memoized, one failed probe would suppress
|
|
300
|
+
* heartbeats for a healthy resource for a full memo TTL — the exact
|
|
301
|
+
* "shown disconnected while telemetry keeps arriving" failure mode
|
|
302
|
+
* the fail-open policy exists to prevent.
|
|
303
|
+
*/
|
|
304
|
+
jest
|
|
305
|
+
.spyOn(GlobalCache, "setStringIfNotExists")
|
|
306
|
+
.mockRejectedValue(new Error("redis down"));
|
|
307
|
+
|
|
308
|
+
await heartbeat();
|
|
309
|
+
await heartbeat();
|
|
310
|
+
await heartbeat();
|
|
311
|
+
|
|
312
|
+
expect(writes).toHaveLength(3);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
test("an enrichment-gate outage is not memoized — enrichment recovers with Redis", async () => {
|
|
316
|
+
jest
|
|
317
|
+
.spyOn(GlobalCache, "setStringIfChanged")
|
|
318
|
+
.mockRejectedValue(new Error("redis down"));
|
|
319
|
+
|
|
320
|
+
await heartbeat({ metadata: { osType: "linux" }, fingerprint: "fp-a" });
|
|
321
|
+
// Liveness-only write; enrichment failed closed.
|
|
322
|
+
expect(writes).toHaveLength(1);
|
|
323
|
+
expect(writes[0]!.data["osType"]).toBeUndefined();
|
|
324
|
+
|
|
325
|
+
// Redis comes back; the same batch content must now enrich promptly.
|
|
326
|
+
(GlobalCache.setStringIfChanged as jest.Mock).mockImplementation(
|
|
327
|
+
async (namespace: string, key: string, value: string) => {
|
|
328
|
+
const full: string = `${namespace}:${key}`;
|
|
329
|
+
if (cache.get(full) === value) {
|
|
330
|
+
return false;
|
|
331
|
+
}
|
|
332
|
+
cache.set(full, value);
|
|
333
|
+
return true;
|
|
334
|
+
},
|
|
335
|
+
);
|
|
336
|
+
|
|
337
|
+
await heartbeat({ metadata: { osType: "linux" }, fingerprint: "fp-a" });
|
|
338
|
+
|
|
339
|
+
expect(writes).toHaveLength(2);
|
|
340
|
+
expect(writes[1]!.data["osType"]).toBe("linux");
|
|
341
|
+
});
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
describe("clearRecentHeartbeatMemo", () => {
|
|
345
|
+
test("drops the memo so the next call probes again — the test-only escape hatch works", async () => {
|
|
346
|
+
await heartbeat();
|
|
347
|
+
const gatesAfterFirst: number = gateCallCount();
|
|
348
|
+
|
|
349
|
+
ResourceHeartbeat.clearRecentHeartbeatMemo();
|
|
350
|
+
await heartbeat();
|
|
351
|
+
|
|
352
|
+
expect(gateCallCount()).toBeGreaterThan(gatesAfterFirst);
|
|
353
|
+
});
|
|
354
|
+
});
|
|
355
|
+
});
|
|
@@ -828,6 +828,134 @@ describe("TelemetryFanInWriter", () => {
|
|
|
828
828
|
expect(target.insertJsonRows).toHaveBeenCalledTimes(1);
|
|
829
829
|
expect(callOptions(target, 0).clickhouseSettings).toBe(settingsFirst);
|
|
830
830
|
});
|
|
831
|
+
|
|
832
|
+
/*
|
|
833
|
+
* The production shape after the batched-inserts change: one table can
|
|
834
|
+
* receive tokened submissions (low-volume signals like syslog, still
|
|
835
|
+
* inside runWithInsertDedup) and untokened ones (traces/logs/metrics)
|
|
836
|
+
* in the same flush window. Tokened submissions must keep per-job
|
|
837
|
+
* statement granularity; the untokened remainder must collapse into
|
|
838
|
+
* one minted-token statement.
|
|
839
|
+
*/
|
|
840
|
+
test("mixed tokened/untokened batch: tokened submissions insert individually under their tokens, the untokened remainder merges under one minted token", async () => {
|
|
841
|
+
const target: TestTarget = makeTarget();
|
|
842
|
+
const writer: TelemetryFanInWriter = new TelemetryFanInWriter(
|
|
843
|
+
makeOptions({ maxBatchRows: 8, maxWaitMs: 60_000 }),
|
|
844
|
+
);
|
|
845
|
+
|
|
846
|
+
const tokenedFirst: FanInSubmitResult = await runWithInsertDedup(
|
|
847
|
+
"job-a",
|
|
848
|
+
() => {
|
|
849
|
+
return writer.submit(target, makeRows(2));
|
|
850
|
+
},
|
|
851
|
+
);
|
|
852
|
+
/*
|
|
853
|
+
* TWO untokened submissions, not one: merging a single-element group is
|
|
854
|
+
* indistinguishable from inserting it alone, so one would make the
|
|
855
|
+
* "merges" half of this assertion vacuous — a regression to
|
|
856
|
+
* one-statement-per-submission would still produce the same call count.
|
|
857
|
+
*/
|
|
858
|
+
const untokenedFirst: FanInSubmitResult = await writer.submit(
|
|
859
|
+
target,
|
|
860
|
+
makeRows(2, 2),
|
|
861
|
+
);
|
|
862
|
+
const untokenedSecond: FanInSubmitResult = await writer.submit(
|
|
863
|
+
target,
|
|
864
|
+
makeRows(2, 4),
|
|
865
|
+
);
|
|
866
|
+
// Reaching maxBatchRows (8) cuts and dispatches the batch.
|
|
867
|
+
const tokenedSecond: FanInSubmitResult = await runWithInsertDedup(
|
|
868
|
+
"job-b",
|
|
869
|
+
() => {
|
|
870
|
+
return writer.submit(target, makeRows(2, 6));
|
|
871
|
+
},
|
|
872
|
+
);
|
|
873
|
+
|
|
874
|
+
await Promise.all([
|
|
875
|
+
tokenedFirst.flushed,
|
|
876
|
+
untokenedFirst.flushed,
|
|
877
|
+
untokenedSecond.flushed,
|
|
878
|
+
tokenedSecond.flushed,
|
|
879
|
+
]);
|
|
880
|
+
|
|
881
|
+
/*
|
|
882
|
+
* Exactly three statements for four submissions: each tokened
|
|
883
|
+
* submission alone under its deterministic per-job token (in batch
|
|
884
|
+
* order), then BOTH untokened submissions collapsed into one statement
|
|
885
|
+
* under a single minted fanin: token.
|
|
886
|
+
*/
|
|
887
|
+
expect(target.insertJsonRows).toHaveBeenCalledTimes(3);
|
|
888
|
+
|
|
889
|
+
expect(callOptions(target, 0).dedupToken).toBe("job-a:TestTable:0");
|
|
890
|
+
expect(rowSeqs(callRows(target, 0))).toEqual([0, 1]);
|
|
891
|
+
|
|
892
|
+
expect(callOptions(target, 1).dedupToken).toBe("job-b:TestTable:0");
|
|
893
|
+
expect(rowSeqs(callRows(target, 1))).toEqual([6, 7]);
|
|
894
|
+
|
|
895
|
+
expect(callOptions(target, 2).dedupToken).toMatch(/^fanin:TestTable:/);
|
|
896
|
+
expect(rowSeqs(callRows(target, 2))).toEqual([2, 3, 4, 5]);
|
|
897
|
+
});
|
|
898
|
+
|
|
899
|
+
test("mixed batch failure isolation: the merged untokened statement failing rejects ALL its submissions with one shared error; tokened ones in the same batch still succeed", async () => {
|
|
900
|
+
const target: TestTarget = makeTarget({
|
|
901
|
+
impl: async (
|
|
902
|
+
_rows: Array<JSONObject>,
|
|
903
|
+
options?: InsertRowsOptions,
|
|
904
|
+
): Promise<void> => {
|
|
905
|
+
if (options?.dedupToken?.startsWith("fanin:")) {
|
|
906
|
+
// Non-retryable: plain error with no recognized code/message.
|
|
907
|
+
throw new Error("merged statement rejected");
|
|
908
|
+
}
|
|
909
|
+
},
|
|
910
|
+
});
|
|
911
|
+
const writer: TelemetryFanInWriter = new TelemetryFanInWriter(
|
|
912
|
+
makeOptions({ maxBatchRows: 6, maxWaitMs: 60_000 }),
|
|
913
|
+
);
|
|
914
|
+
|
|
915
|
+
const tokened: FanInSubmitResult = await runWithInsertDedup(
|
|
916
|
+
"job-a",
|
|
917
|
+
() => {
|
|
918
|
+
return writer.submit(target, makeRows(2));
|
|
919
|
+
},
|
|
920
|
+
);
|
|
921
|
+
const untokenedFirst: FanInSubmitResult = await writer.submit(
|
|
922
|
+
target,
|
|
923
|
+
makeRows(2, 2),
|
|
924
|
+
);
|
|
925
|
+
const untokenedSecond: FanInSubmitResult = await writer.submit(
|
|
926
|
+
target,
|
|
927
|
+
makeRows(2, 4),
|
|
928
|
+
);
|
|
929
|
+
|
|
930
|
+
// The tokened submission's ack resolves even though its batch-mate failed.
|
|
931
|
+
await tokened.flushed;
|
|
932
|
+
|
|
933
|
+
const errFirst: Error = await captureRejection(untokenedFirst.flushed);
|
|
934
|
+
const errSecond: Error = await captureRejection(untokenedSecond.flushed);
|
|
935
|
+
|
|
936
|
+
expect(errFirst).toBeInstanceOf(FanInInsertError);
|
|
937
|
+
expect(errFirst.message).toContain("merged statement rejected");
|
|
938
|
+
expect(errFirst.message).toContain("after 1 attempt(s)");
|
|
939
|
+
/*
|
|
940
|
+
* The SAME error instance reaches both untokened acks — that identity
|
|
941
|
+
* is what proves they shared one merged statement rather than each
|
|
942
|
+
* failing its own.
|
|
943
|
+
*/
|
|
944
|
+
expect(errSecond).toBe(errFirst);
|
|
945
|
+
|
|
946
|
+
// Two statements: the tokened one (ok) and the merged untokened one (failed).
|
|
947
|
+
expect(target.insertJsonRows).toHaveBeenCalledTimes(2);
|
|
948
|
+
expect(callOptions(target, 0).dedupToken).toBe("job-a:TestTable:0");
|
|
949
|
+
expect(callOptions(target, 1).dedupToken).toMatch(/^fanin:TestTable:/);
|
|
950
|
+
expect(rowSeqs(callRows(target, 1))).toEqual([2, 3, 4, 5]);
|
|
951
|
+
|
|
952
|
+
// Failure still drains the writer's accounting.
|
|
953
|
+
expect(writer.getStats()).toEqual({
|
|
954
|
+
bufferedRows: 0,
|
|
955
|
+
pendingRows: 0,
|
|
956
|
+
activeInserts: 0,
|
|
957
|
+
});
|
|
958
|
+
});
|
|
831
959
|
});
|
|
832
960
|
});
|
|
833
961
|
|