@oneuptime/common 14.0.10 → 14.0.11
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/AIConversation.ts +127 -1
- package/Models/DatabaseModels/CephCluster.ts +12 -0
- package/Models/DatabaseModels/CloudResource.ts +12 -0
- package/Models/DatabaseModels/DatabaseServer.ts +12 -0
- package/Models/DatabaseModels/DockerHost.ts +12 -0
- package/Models/DatabaseModels/DockerSwarmCluster.ts +12 -0
- package/Models/DatabaseModels/GlobalOidc.ts +0 -4
- package/Models/DatabaseModels/GlobalOidcProject.ts +0 -4
- package/Models/DatabaseModels/GlobalSso.ts +0 -4
- package/Models/DatabaseModels/GlobalSsoProject.ts +0 -4
- package/Models/DatabaseModels/Host.ts +12 -0
- package/Models/DatabaseModels/IncidentForm.ts +594 -0
- package/Models/DatabaseModels/IncidentFormSubmission.ts +283 -0
- package/Models/DatabaseModels/IncidentTemplate.ts +59 -0
- package/Models/DatabaseModels/Index.ts +14 -0
- package/Models/DatabaseModels/IoTFleet.ts +12 -0
- package/Models/DatabaseModels/KubernetesCluster.ts +12 -0
- package/Models/DatabaseModels/MessageQueue.ts +1102 -0
- package/Models/DatabaseModels/MessageQueueLabelRule.ts +553 -0
- package/Models/DatabaseModels/MessageQueueOwnerRule.ts +633 -0
- package/Models/DatabaseModels/MessageQueueOwnerTeam.ts +491 -0
- package/Models/DatabaseModels/MessageQueueOwnerUser.ts +490 -0
- package/Models/DatabaseModels/PodmanHost.ts +12 -0
- package/Models/DatabaseModels/Project.ts +59 -0
- package/Models/DatabaseModels/ProjectOidc.ts +0 -4
- package/Models/DatabaseModels/ProjectSso.ts +0 -4
- package/Models/DatabaseModels/ProxmoxCluster.ts +12 -0
- package/Models/DatabaseModels/RumApplication.ts +12 -0
- package/Models/DatabaseModels/ServerlessFunction.ts +12 -0
- package/Models/DatabaseModels/Service.ts +12 -0
- package/Models/DatabaseModels/StatusPageOidc.ts +0 -4
- package/Models/DatabaseModels/StatusPageSso.ts +0 -4
- package/Models/DatabaseModels/TeamComplianceSetting.ts +47 -2
- package/Models/DatabaseModels/VMwareVCenter.ts +12 -0
- package/Server/API/AIChatAPI.ts +13 -114
- package/Server/API/AIInvestigationAPI.ts +3 -3
- package/Server/API/AIInvestigationConversationAPI.ts +266 -0
- package/Server/API/IncidentFormAPI.ts +376 -0
- package/Server/API/ProjectOIDC.ts +16 -25
- package/Server/API/ProjectSSO.ts +16 -24
- package/Server/API/StatusPageAPI.ts +42 -77
- package/Server/Enterprise/EnterpriseEdition.ts +93 -28
- package/Server/Enterprise/EnterpriseFeature.ts +25 -3
- package/Server/Enterprise/EnterpriseLicenseSnapshot.ts +1 -1
- package/Server/Enterprise/EnterpriseServerModule.ts +4 -1
- package/Server/EnvironmentConfig.ts +1 -1
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796400000000-AddIncidentForms.ts +129 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796500000000-AddTeamComplianceRuleNotificationChannels.ts +52 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796600000000-AddAIInvestigationConversationAndTimeLimits.ts +75 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796700000000-AddMessageQueueTables.ts +477 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
- package/Server/Middleware/IncidentFormRateLimit.ts +847 -0
- package/Server/Middleware/UserAuthorization.ts +16 -38
- package/Server/Services/AIService.ts +10 -13
- package/Server/Services/IncidentCustomFieldService.ts +112 -2
- package/Server/Services/IncidentEpisodeMemberService.ts +24 -4
- package/Server/Services/IncidentEpisodeService.ts +10 -1
- package/Server/Services/IncidentFormService.ts +1566 -0
- package/Server/Services/IncidentFormSubmissionService.ts +97 -0
- package/Server/Services/IncidentGroupingEngineService.ts +45 -8
- package/Server/Services/IncidentService.ts +123 -5
- package/Server/Services/IncidentTemplateService.ts +22 -0
- package/Server/Services/Index.ts +14 -0
- package/Server/Services/MessageQueueLabelRuleEngineService.ts +321 -0
- package/Server/Services/MessageQueueLabelRuleService.ts +14 -0
- package/Server/Services/MessageQueueOwnerRuleEngineService.ts +412 -0
- package/Server/Services/MessageQueueOwnerRuleService.ts +14 -0
- package/Server/Services/MessageQueueOwnerTeamService.ts +62 -0
- package/Server/Services/MessageQueueOwnerUserService.ts +62 -0
- package/Server/Services/MessageQueueService.ts +1463 -0
- package/Server/Services/OpenTelemetryIngestService.ts +37 -4
- package/Server/Services/ProjectService.ts +0 -59
- package/Server/Services/StatusPagePrivateUserService.ts +1 -4
- package/Server/Services/StatusPageService.ts +1 -51
- package/Server/Services/TeamComplianceSettingService.ts +163 -42
- package/Server/Services/UserNotificationRuleService.ts +8 -1
- package/Server/Services/WorkspaceNotificationSummaryService.ts +12 -3
- package/Server/Types/Database/Permissions/CreatePermission.ts +17 -3
- package/Server/Types/Database/Permissions/EditionPermission.ts +140 -38
- package/Server/Types/Database/Permissions/OwnerTableRegistry.ts +16 -0
- package/Server/Types/Database/Permissions/TablePermission.ts +3 -3
- package/Server/Types/Database/Permissions/UpdatePermission.ts +20 -6
- package/Server/Utils/AI/Chat/AgentContextCompactor.ts +200 -0
- package/Server/Utils/AI/Chat/ChatAgentRunner.ts +307 -48
- package/Server/Utils/AI/Chat/ChatRunCancellation.ts +156 -0
- package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +78 -18
- package/Server/Utils/AI/Chat/ObservabilityChatPrompt.ts +1 -1
- package/Server/Utils/AI/Chat/ToolOutputPager.ts +322 -0
- package/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.ts +88 -30
- package/Server/Utils/AI/ClusterAccess/KubectlJobRunner.ts +13 -1
- package/Server/Utils/AI/CodeFix/CodeFixReadiness.ts +0 -32
- package/Server/Utils/AI/CodeFix/FixRunBudget.ts +27 -25
- package/Server/Utils/AI/Remediation/RemediationCommandTools.ts +7 -1
- package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +34 -14
- package/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.ts +69 -24
- package/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.ts +9 -0
- package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +53 -10
- package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +27 -10
- package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +27 -10
- package/Server/Utils/AI/SRE/InvestigationQueue.ts +16 -19
- package/Server/Utils/AI/SRE/InvestigationThread.ts +646 -0
- package/Server/Utils/AI/SRE/InvestigationThreadService.ts +715 -0
- package/Server/Utils/AI/Toolbox/Serializer.ts +10 -3
- package/Server/Utils/EditionEnforcement.ts +19 -202
- package/Server/Utils/LLM/LLMService.ts +259 -63
- package/Server/Utils/RelaxedScimTeamLocksReport.ts +245 -0
- package/Server/Utils/Rules/RuleRun/RuleRunRegistry.ts +25 -0
- package/Server/Utils/SameOriginRequest.ts +203 -0
- package/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.ts +2 -0
- package/Server/Utils/Telemetry/EntityRegistry.ts +4 -1
- package/Server/Utils/Telemetry/MessageQueueDiscovery.ts +1402 -0
- package/Server/Utils/Telemetry/ResourceEntityFilter.ts +4 -1
- package/Server/Utils/VendorAssets.ts +3 -3
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.ts +3 -1
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.ts +3 -1
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +10 -1
- package/Tests/App/Accounts/IncidentFormPage.test.tsx +2036 -0
- package/Tests/App/Accounts/IncidentFormServerMessages.test.ts +119 -0
- package/Tests/App/Accounts/IncidentFormUtils.test.ts +1157 -0
- package/Tests/App/Accounts/LoginPasskey.test.tsx +6 -4
- package/Tests/App/Accounts/LoginSso.test.tsx +724 -0
- package/Tests/App/AdminDashboard/AdminAuthenticationRequireSso.test.tsx +338 -0
- package/Tests/App/AdminDashboard/AdminAuthenticationSsoCardLocales.test.tsx +331 -0
- package/Tests/App/AdminDashboard/AdminEnterprisePluginPage.test.tsx +47 -33
- package/Tests/App/AdminDashboard/GlobalSsoOidcPages.test.tsx +1021 -0
- package/Tests/App/Dashboard/AffectedResourcesDisplay.test.tsx +876 -78
- package/Tests/App/Dashboard/AffectedResourcesSlo.test.tsx +20 -3
- package/Tests/App/Dashboard/CephProxmoxVMwareDocumentationCards.test.tsx +359 -0
- package/Tests/App/Dashboard/CephSetupGuide.test.ts +961 -0
- package/Tests/App/Dashboard/CloudDocumentationCard.test.tsx +381 -95
- package/Tests/App/Dashboard/CloudSetupGuide.test.ts +1463 -0
- package/Tests/App/Dashboard/ClusterAccessNotice.test.tsx +174 -12
- package/Tests/App/Dashboard/ContainerSetupGuideCards.test.tsx +345 -0
- package/Tests/App/Dashboard/CoreListEmptyStates.test.tsx +501 -0
- package/Tests/App/Dashboard/DashboardLazyPages.test.tsx +0 -1
- package/Tests/App/Dashboard/DatabaseDocumentationCard.test.tsx +400 -0
- package/Tests/App/Dashboard/DatabaseDocumentationMarkdown.test.ts +5 -0
- package/Tests/App/Dashboard/DatabaseSetupGuide.test.ts +1869 -0
- package/Tests/App/Dashboard/DatabaseTelemetryScope.test.ts +44 -0
- package/Tests/App/Dashboard/DatabaseTelemetryTabs.test.tsx +50 -9
- package/Tests/App/Dashboard/DockerSetupGuide.test.ts +899 -0
- package/Tests/App/Dashboard/DockerSwarmSetupGuide.test.ts +964 -0
- package/Tests/App/Dashboard/EnterpriseFeatureUpgrade.test.tsx +141 -7
- package/Tests/App/Dashboard/EnterprisePluginPage.test.tsx +52 -39
- package/Tests/App/Dashboard/EventOverviewPages.test.tsx +7 -1
- package/Tests/App/Dashboard/GettingStartedTaskRoutes.test.tsx +331 -0
- package/Tests/App/Dashboard/HomeOverviewStatsFirstRun.test.tsx +925 -0
- package/Tests/App/Dashboard/HostDocumentationCard.test.tsx +225 -0
- package/Tests/App/Dashboard/HostSetupGuide.test.ts +1183 -0
- package/Tests/App/Dashboard/IdentityEnterpriseShells.test.tsx +69 -45
- package/Tests/App/Dashboard/IncidentCreateTemplateCustomFieldSettings.test.tsx +745 -0
- package/Tests/App/Dashboard/IncidentCreateTemplateOwners.test.tsx +414 -0
- package/Tests/App/Dashboard/IncidentCustomFieldCreateSettingsForm.test.ts +1042 -0
- package/Tests/App/Dashboard/IncidentCustomFieldSettingsCard.test.tsx +1816 -0
- package/Tests/App/Dashboard/IncidentFormPlan.test.ts +89 -0
- package/Tests/App/Dashboard/IncidentFormShareLinkCard.test.tsx +846 -0
- package/Tests/App/Dashboard/IncidentFormsPages.test.tsx +1324 -0
- package/Tests/App/Dashboard/IncidentTemplateCustomFieldSettings.test.tsx +630 -0
- package/Tests/App/Dashboard/IncidentsSideMenu.test.tsx +13 -0
- package/Tests/App/Dashboard/IncomingEmailMonitorLinkVerifyHint.test.tsx +104 -0
- package/Tests/App/Dashboard/InvestigationConversation.test.tsx +1107 -0
- package/Tests/App/Dashboard/InvestigationConversationData.test.ts +546 -0
- package/Tests/App/Dashboard/InvestigationEvidenceList.test.tsx +170 -5
- package/Tests/App/Dashboard/InvestigationNotStarted.test.tsx +207 -2
- package/Tests/App/Dashboard/InvestigationPanel.test.tsx +729 -34
- package/Tests/App/Dashboard/InvestigationPanelStatus.test.tsx +9 -21
- package/Tests/App/Dashboard/InvestigationReportView.test.tsx +274 -37
- package/Tests/App/Dashboard/InvestigationStatusBadge.test.tsx +130 -0
- package/Tests/App/Dashboard/IoTDocumentationCard.test.tsx +218 -0
- package/Tests/App/Dashboard/IoTSetupGuide.test.ts +1124 -0
- package/Tests/App/Dashboard/KubernetesClusterAiAccessSettings.test.ts +20 -6
- package/Tests/App/Dashboard/KubernetesClusterAiDocumentationMarkdown.test.ts +107 -83
- package/Tests/App/Dashboard/KubernetesSetupGuide.test.ts +787 -0
- package/Tests/App/Dashboard/MessageQueueBreadcrumbs.test.tsx +228 -0
- package/Tests/App/Dashboard/MessageQueueBrokerHealth.test.tsx +1538 -0
- package/Tests/App/Dashboard/MessageQueueBrokerHealthMetricChart.test.tsx +969 -0
- package/Tests/App/Dashboard/MessageQueueBrokerHealthReads.test.ts +889 -0
- package/Tests/App/Dashboard/MessageQueueBrokerMetricsReach.test.ts +236 -0
- package/Tests/App/Dashboard/MessageQueueDocumentationCard.test.tsx +479 -0
- package/Tests/App/Dashboard/MessageQueueListPages.test.tsx +1232 -0
- package/Tests/App/Dashboard/MessageQueueMetricDescriptions.test.ts +244 -0
- package/Tests/App/Dashboard/MessageQueueMonitorLink.test.ts +952 -0
- package/Tests/App/Dashboard/MessageQueueMonitorLinkMonitorCreate.test.tsx +859 -0
- package/Tests/App/Dashboard/MessageQueueOverview.test.tsx +1773 -0
- package/Tests/App/Dashboard/MessageQueueOverviewPresentation.test.ts +196 -0
- package/Tests/App/Dashboard/MessageQueuePresentation.test.ts +799 -0
- package/Tests/App/Dashboard/MessageQueueProducersConsumers.test.tsx +680 -0
- package/Tests/App/Dashboard/MessageQueueRoutes.test.tsx +383 -0
- package/Tests/App/Dashboard/MessageQueueRulePages.test.tsx +278 -0
- package/Tests/App/Dashboard/MessageQueueSetupGuide.test.ts +2778 -0
- package/Tests/App/Dashboard/MessageQueueSideMenus.test.tsx +273 -0
- package/Tests/App/Dashboard/MessageQueueSpanPopulations.test.ts +930 -0
- package/Tests/App/Dashboard/MessageQueueSpanStore.ts +351 -0
- package/Tests/App/Dashboard/MessageQueueTelemetryScope.test.ts +375 -0
- package/Tests/App/Dashboard/MessageQueueTelemetryScopeTabs.test.tsx +668 -0
- package/Tests/App/Dashboard/MessageQueueViewPages.test.tsx +685 -0
- package/Tests/App/Dashboard/MonitorAndHomeNotOperationalMenus.test.tsx +342 -0
- package/Tests/App/Dashboard/MonitorCreateFirstRunDefaults.test.tsx +623 -0
- package/Tests/App/Dashboard/MonitorCreateFromMonitorBackedDevice.test.tsx +442 -0
- package/Tests/App/Dashboard/MonitorLogsPageViewSummary.test.tsx +814 -0
- package/Tests/App/Dashboard/MonitorSetupCardBodies.test.tsx +246 -0
- package/Tests/App/Dashboard/NavigationSearchKeywords.test.tsx +8 -0
- package/Tests/App/Dashboard/OnboardingSsoEmptyState.test.tsx +27 -12
- package/Tests/App/Dashboard/PlanGatedPage.test.tsx +292 -0
- package/Tests/App/Dashboard/PodmanSetupGuide.test.ts +948 -0
- package/Tests/App/Dashboard/ProductOrientation.test.tsx +5 -4
- package/Tests/App/Dashboard/ProductSettingsSectionCollapsed.test.tsx +266 -0
- package/Tests/App/Dashboard/ProxmoxSetupGuide.test.ts +1318 -0
- package/Tests/App/Dashboard/ResourceAiAgentAttentionServerParity.test.ts +555 -0
- package/Tests/App/Dashboard/ResourceAiAgentPage.test.tsx +298 -9
- package/Tests/App/Dashboard/ResourceAiAgentStatus.test.ts +453 -0
- package/Tests/App/Dashboard/ResourceArchiveSettings.test.tsx +16 -0
- package/Tests/App/Dashboard/ResourceConnectionGuideCard.test.tsx +444 -0
- package/Tests/App/Dashboard/ResourceConnectionGuideOverviewsInfra.test.tsx +798 -0
- package/Tests/App/Dashboard/ResourceConnectionGuideOverviewsPlatforms.test.tsx +717 -0
- package/Tests/App/Dashboard/ResourceConnectionGuides.test.ts +981 -0
- package/Tests/App/Dashboard/ResourceConnectionState.test.ts +132 -0
- package/Tests/App/Dashboard/RumDocumentationCard.test.tsx +293 -0
- package/Tests/App/Dashboard/RumSessionReplayRetentionSettings.test.tsx +467 -0
- package/Tests/App/Dashboard/RumSetupGuide.test.ts +758 -0
- package/Tests/App/Dashboard/ScheduledMaintenanceOverviewPage.test.tsx +25 -0
- package/Tests/App/Dashboard/ServerlessDocumentationCard.test.tsx +254 -0
- package/Tests/App/Dashboard/ServerlessSetupGuide.test.ts +1074 -0
- package/Tests/App/Dashboard/SetupGuide.test.ts +246 -0
- package/Tests/App/Dashboard/SetupGuideCard.test.tsx +756 -0
- package/Tests/App/Dashboard/SloListSideMenu.test.tsx +120 -0
- package/Tests/App/Dashboard/SsoPages.test.tsx +833 -0
- package/Tests/App/Dashboard/TelemetryDocumentation.test.tsx +253 -0
- package/Tests/App/Dashboard/TelemetryRetentionShells.test.tsx +730 -0
- package/Tests/App/Dashboard/VMwareSetupGuide.test.ts +1033 -0
- package/Tests/App/StatusPage/StatusPageSsoEmptyState.test.tsx +87 -7
- package/Tests/Models/DatabaseModels/DatabaseServerModels.test.ts +28 -9
- package/Tests/Models/DatabaseModels/IncidentFormModel.test.ts +1250 -0
- package/Tests/Models/DatabaseModels/MessageQueueModels.test.ts +1983 -0
- package/Tests/Models/DatabaseModels/StaticRuleCriteriaInheritance.test.ts +4 -2
- package/Tests/Models/DatabaseModels/TeamComplianceSettingModel.test.ts +190 -14
- package/Tests/Models/IncidentTemplateCustomFieldSettingsColumn.test.ts +291 -0
- package/Tests/Server/API/AIInvestigationConversationAPI.test.ts +412 -0
- package/Tests/Server/API/GlobalConfigLicense.test.ts +38 -9
- package/Tests/Server/API/IncidentFormAPI.test.ts +942 -0
- package/Tests/Server/API/IncidentFormPublicRoutes.test.ts +1653 -0
- package/Tests/Server/API/KubernetesClusterAiAccessAPI.test.ts +2 -1
- package/Tests/Server/API/{SsoProviderListingsEdition.test.ts → SsoProviderListings.test.ts} +164 -110
- package/Tests/Server/Enterprise/EnterpriseEdition.test.ts +76 -54
- package/Tests/Server/Enterprise/EnterpriseFeatureActive.test.ts +125 -42
- package/Tests/Server/Enterprise/EnterpriseLicenseSnapshot.test.ts +36 -10
- package/Tests/Server/Enterprise/EnterpriseModelFeatureMap.test.ts +51 -9
- package/Tests/Server/Enterprise/FakeEnterpriseModule.ts +13 -8
- package/Tests/Server/Enterprise/SsoInCommunityEdition.test.ts +540 -0
- package/Tests/Server/Infrastructure/Postgres/AddConfigurableRuleCriteriaMigration.test.ts +3 -0
- package/Tests/Server/Infrastructure/Postgres/AddIncidentFormsMigration.test.ts +438 -0
- package/Tests/Server/Infrastructure/Postgres/AddTeamComplianceRuleNotificationChannelsMigration.test.ts +255 -0
- package/Tests/Server/Infrastructure/Postgres/MessageQueueTablesMigration.test.ts +637 -0
- package/Tests/Server/Middleware/IncidentFormRateLimit.test.ts +1630 -0
- package/Tests/Server/Middleware/UserAuthorization.test.ts +16 -17
- package/Tests/Server/Middleware/UserAuthorizationSsoEnforcement.test.ts +544 -0
- package/Tests/Server/Services/AIServiceDailyBudget.test.ts +37 -8
- package/Tests/Server/Services/IncidentCreatedFeedTitle.test.ts +212 -0
- package/Tests/Server/Services/IncidentCustomFieldDeleteFormQuestions.test.ts +297 -0
- package/Tests/Server/Services/IncidentFormOwnersCreatedNotification.test.ts +497 -0
- package/Tests/Server/Services/IncidentFormPostgres.test.ts +958 -0
- package/Tests/Server/Services/IncidentFormPublicForm.test.ts +1260 -0
- package/Tests/Server/Services/IncidentFormPublicSubmission.test.ts +2520 -0
- package/Tests/Server/Services/IncidentFormRegistration.test.ts +159 -0
- package/Tests/Server/Services/IncidentFormService.test.ts +663 -0
- package/Tests/Server/Services/IncidentFormSubmissionService.test.ts +430 -0
- package/Tests/Server/Services/IncidentFormSubmissionTemplateBranch.test.ts +473 -0
- package/Tests/Server/Services/IncidentGroupingTemplateSubstitution.test.ts +81 -0
- package/Tests/Server/Services/IncidentTemplateCustomFieldSettings.test.ts +262 -0
- package/Tests/Server/Services/IncidentTemplateOwnersOnDeclare.test.ts +566 -0
- package/Tests/Server/Services/IncidentTitleMarkdownSinks.test.ts +640 -0
- package/Tests/Server/Services/LlmLogServiceTokenAggregateParams.test.ts +3 -3
- package/Tests/Server/Services/MessageQueueOwnerServices.test.ts +440 -0
- package/Tests/Server/Services/MessageQueueRegistries.test.ts +577 -0
- package/Tests/Server/Services/MessageQueueRuleEngines.test.ts +900 -0
- package/Tests/Server/Services/MessageQueueService.test.ts +2210 -0
- package/Tests/Server/Services/MessageQueueSqlPostgres.test.ts +1271 -0
- package/Tests/Server/Services/OpenTelemetryResourceRetentionMemo.test.ts +28 -1
- package/Tests/Server/Services/OpenTelemetryRetentionOverrideEdition.test.ts +474 -0
- package/Tests/Server/Services/OpenTelemetryServiceResolutionCache.test.ts +21 -0
- package/Tests/Server/Services/OwnerRowUniqueness.test.ts +3 -0
- package/Tests/Server/Services/ProjectServiceAuditLogLicense.test.ts +0 -1
- package/Tests/Server/Services/RuleRun/LabelRuleEnginesGroupBRun.test.ts +56 -6
- package/Tests/Server/Services/RuleRun/OwnerRuleEnginesGroupBRun.test.ts +30 -1
- package/Tests/Server/Services/SsoRequirementReads.test.ts +454 -0
- package/Tests/Server/Services/SsoRequirementWrites.test.ts +529 -0
- package/Tests/Server/Services/StaticRuleCriteriaRuntimeCoverage.test.ts +3 -3
- package/Tests/Server/Services/StatusPagePrivateUserInvitationPolicy.test.ts +107 -89
- package/Tests/Server/Services/TeamComplianceSettingPostgres.test.ts +1068 -176
- package/Tests/Server/Services/TeamComplianceSettingService.test.ts +1962 -200
- package/Tests/Server/Types/Database/Permissions/EditionPermission.test.ts +654 -297
- package/Tests/Server/Types/Database/Permissions/RetentionOverrideColumns.test.ts +784 -0
- package/Tests/Server/Types/Database/Permissions/SsoScalePlanGate.test.ts +418 -0
- package/Tests/Server/Utils/AI/AIAlertGating.test.ts +0 -1
- package/Tests/Server/Utils/AI/AIInvestigationEngineRunLimits.test.ts +136 -0
- package/Tests/Server/Utils/AI/AIInvestigationQueue.test.ts +15 -14
- package/Tests/Server/Utils/AI/AgentContextCompactor.test.ts +351 -0
- package/Tests/Server/Utils/AI/ChatAgentRunnerSharedThread.test.ts +842 -0
- package/Tests/Server/Utils/AI/ChatRunCancellation.test.ts +195 -0
- package/Tests/Server/Utils/AI/CodeFixReadiness.test.ts +10 -57
- package/Tests/Server/Utils/AI/FixRunBudget.test.ts +20 -24
- package/Tests/Server/Utils/AI/Insights/InvestigationQueueInsightSubject.test.ts +7 -30
- package/Tests/Server/Utils/AI/InvestigationClusterAccessWiring.test.ts +225 -29
- package/Tests/Server/Utils/AI/InvestigationInfrastructureAccessWiring.test.ts +266 -25
- package/Tests/Server/Utils/AI/InvestigationThread.test.ts +580 -0
- package/Tests/Server/Utils/AI/InvestigationThreadService.test.ts +801 -0
- package/Tests/Server/Utils/AI/KubectlInvestigationToolkit.test.ts +525 -43
- package/Tests/Server/Utils/AI/KubectlJobRunner.test.ts +4 -2
- package/Tests/Server/Utils/AI/LLMServiceUnreachableProvider.test.ts +435 -0
- package/Tests/Server/Utils/AI/ObservabilityAssistantLimits.test.ts +462 -0
- package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +14 -25
- package/Tests/Server/Utils/AI/RemediationExecutionRunnerResourceMode.test.ts +2 -0
- package/Tests/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.test.ts +297 -15
- package/Tests/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.test.ts +10 -6
- package/Tests/Server/Utils/AI/ToolOutputPager.test.ts +382 -0
- package/Tests/Server/Utils/AI/ToolResultSerializer.test.ts +23 -4
- package/Tests/Server/Utils/AI/Toolbox/LogTools.test.ts +25 -6
- package/Tests/Server/Utils/AI/Toolbox/Serializer.test.ts +14 -3
- package/Tests/Server/Utils/EditionEnforcement.test.ts +107 -147
- package/Tests/Server/Utils/{CommunityEditionSsoReport.test.ts → RelaxedScimTeamLocksReport.test.ts} +212 -208
- package/Tests/Server/Utils/SameOriginRequest.test.ts +372 -0
- package/Tests/Server/Utils/Telemetry/EntityRegistry.test.ts +20 -0
- package/Tests/Server/Utils/Telemetry/MessageQueueDiscovery.test.ts +2590 -0
- package/Tests/Server/Utils/Telemetry/MessageQueueSpanQueryTwin.ts +246 -0
- package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +23 -0
- package/Tests/Types/AI/AIAgentRunLimits.test.ts +255 -0
- package/Tests/Types/Code/JsonSyntax.test.ts +1300 -0
- package/Tests/Types/CustomField/CustomFieldCreateSettings.test.ts +1113 -0
- package/Tests/Types/CustomField/CustomFieldMappingCatalog.test.ts +91 -0
- package/Tests/Types/CustomField/CustomFieldValueValidator.test.ts +30 -0
- package/Tests/Types/Incident/IncidentFormIpAllowlist.test.ts +227 -0
- package/Tests/Types/Incident/IncidentFormPublic.test.ts +1916 -0
- package/Tests/Types/MessageQueue/MessageQueueEntityKey.test.ts +916 -0
- package/Tests/Types/MessageQueue/MessageQueueIdentity.test.ts +567 -0
- package/Tests/Types/MessageQueue/MessageQueueManualIdentity.test.ts +686 -0
- package/Tests/Types/MessageQueue/MessageQueueMetricCatalog.test.ts +1726 -0
- package/Tests/Types/MessageQueue/MessageQueueMetricResolution.test.ts +1758 -0
- package/Tests/Types/MessageQueue/MessagingInstrumentationFixtures.test.ts +4686 -0
- package/Tests/Types/MessageQueue/MessagingSystem.test.ts +840 -0
- package/Tests/Types/MessageQueue/MessagingTelemetryFixtures.ts +3287 -0
- package/Tests/Types/MessageQueue/MessagingTelemetryResolver.test.ts +2966 -0
- package/Tests/Types/Monitor/MessageQueueAlertTemplates.test.ts +5401 -0
- package/Tests/Types/Monitor/MetricEngineFold.ts +154 -0
- package/Tests/Types/ResourceAiAgent/ResourceAiAccess.test.ts +5 -3
- package/Tests/Types/Team/ComplianceRule.test.ts +588 -37
- package/Tests/Types/Team/TeamComplianceStatus.test.ts +64 -11
- package/Tests/Types/XML.test.ts +51 -0
- package/Tests/UI/Components/CodeEditor/CodeEditor.test.tsx +2362 -0
- package/Tests/UI/Components/CodeEditor/CodeEditorCommands.test.ts +810 -0
- package/Tests/UI/Components/CodeEditor/CodeEditorHighlight.test.ts +338 -0
- package/Tests/UI/Components/CodeEditor/CodeEditorLanguages.test.ts +222 -0
- package/Tests/UI/Components/CodeEditor/CodeEditorStyles.test.ts +807 -0
- package/Tests/UI/Components/CodeEditor/NoMonaco.test.ts +245 -0
- package/Tests/UI/Components/CodeEditor/YamlEditor.test.tsx +729 -0
- package/Tests/UI/Components/CopyTextButton.test.tsx +76 -0
- package/Tests/UI/Components/EditionLabelLapseCopy.ts +51 -14
- package/Tests/UI/Components/EditionLabelLicenseStatus.test.tsx +131 -23
- package/Tests/UI/Components/ErrorMessageAction.test.tsx +142 -0
- package/Tests/UI/Components/FacetSidebarInScope.test.tsx +459 -0
- package/Tests/UI/Components/FacetValuesInScope.test.ts +270 -0
- package/Tests/UI/Components/Forms/FormFieldCodeEditors.test.tsx +294 -0
- package/Tests/UI/Components/Forms/FormFieldMarkdown.test.tsx +126 -0
- package/Tests/UI/Components/Forms/FormFieldYaml.test.tsx +22 -75
- package/Tests/UI/Components/Forms/ModelFormOnBeforeCreateMiscData.test.tsx +254 -0
- package/Tests/UI/Components/Icon.test.tsx +32 -0
- package/Tests/UI/Components/Input.test.tsx +31 -0
- package/Tests/UI/Components/LockedFilterChipScope.test.tsx +501 -0
- package/Tests/UI/Components/MarkdownConverters.test.ts +253 -1
- package/Tests/UI/Components/MarkdownEditor.test.tsx +2785 -2
- package/Tests/UI/Components/MarkdownEditorHistory.test.ts +243 -0
- package/Tests/UI/Components/MarkdownListEditing.test.ts +1118 -0
- package/Tests/UI/Components/MarkdownPaste.test.ts +1180 -0
- package/Tests/UI/Components/MarkdownViewerCopy.test.tsx +466 -0
- package/Tests/UI/Components/MarkdownVisualEditing.test.ts +1089 -0
- package/Tests/UI/Components/ModelTable/BaseModelTableEmptyStateAction.test.tsx +623 -0
- package/Tests/UI/Components/ResetObjectID.test.tsx +282 -0
- package/Tests/UI/Components/RuleCriteria/RuleCriteriaModelTable.test.tsx +3 -3
- package/Tests/UI/Components/Table/EmptyTableMessage.test.ts +439 -0
- package/Tests/UI/Components/Table/TableEmptyState.test.tsx +352 -0
- package/Tests/UI/Components/TableRowMobileTitles.test.tsx +78 -0
- package/Tests/UI/Components/TextArea.test.tsx +19 -0
- package/Tests/UI/Components/Workflow/ModelColumnEditor.render.test.tsx +12 -29
- package/Tests/UI/Components/fixtures/MarkdownPasteFixtures.ts +460 -0
- package/Tests/UI/EsbuildConfig.test.ts +115 -39
- package/Tests/UI/EsbuildEnterpriseAlias.test.ts +4 -4
- package/Tests/UI/Monitor/MonitorLogSummaryRender.test.tsx +377 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +81 -1
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +17 -0
- package/Tests/Utils/LabelRuleImportExport.test.ts +5 -0
- package/Tests/Utils/Markdown/DashboardMarkdownRenderer.ts +81 -0
- package/Tests/Utils/Markdown/UntrustedMarkdown.test.ts +1339 -0
- package/Tests/Utils/Monitor/MonitorLogSummaryUtil.test.ts +862 -0
- package/Types/AI/AIAgentRunLimits.ts +146 -0
- package/Types/Code/CodeType.ts +2 -0
- package/Types/Code/JsonSyntax.ts +923 -0
- package/Types/CustomField/CustomFieldCreateSettings.ts +549 -0
- package/Types/CustomField/CustomFieldMappingCatalog.ts +77 -0
- package/Types/CustomField/CustomFieldValueValidator.ts +10 -7
- package/Types/EnterpriseLicense/EnterpriseLicensePeriods.ts +1 -1
- package/Types/Icon/IconProp.ts +2 -0
- package/Types/Incident/IncidentFormIpAllowlist.ts +206 -0
- package/Types/Incident/IncidentFormPublic.ts +1145 -0
- package/Types/Kubernetes/KubernetesClusterAiAccess.ts +10 -3
- package/Types/MessageQueue/MessageQueueIdentity.ts +256 -0
- package/Types/MessageQueue/MessageQueueManualIdentity.ts +325 -0
- package/Types/MessageQueue/MessageQueueMetricCatalog.ts +1357 -0
- package/Types/MessageQueue/MessagingSystem.ts +518 -0
- package/Types/MessageQueue/MessagingTelemetryResolver.ts +2311 -0
- package/Types/Monitor/MessageQueueAlertTemplates.ts +2128 -0
- package/Types/Permission.ts +310 -0
- package/Types/ResourceAiAgent/ResourceAiAccess.ts +10 -4
- package/Types/Rules/RuleCriteriaFieldRegistry.ts +12 -0
- package/Types/Rules/RuleRun.ts +4 -0
- package/Types/Team/ComplianceNotificationChannel.ts +4 -3
- package/Types/Team/ComplianceRule.ts +71 -18
- package/Types/Team/ComplianceRuleType.ts +2 -2
- package/Types/Team/TeamComplianceStatus.ts +6 -2
- package/Types/Telemetry/EntityType.ts +16 -0
- package/Types/Telemetry/LockedFilterDetail.ts +22 -0
- package/Types/XML.ts +26 -0
- package/UI/Components/CodeEditor/CodeEditor.css +379 -0
- package/UI/Components/CodeEditor/CodeEditor.tsx +1128 -294
- package/UI/Components/CodeEditor/CodeEditorCommands.ts +774 -0
- package/UI/Components/CodeEditor/CodeEditorHighlight.ts +106 -0
- package/UI/Components/CodeEditor/CodeEditorLanguages.ts +164 -0
- package/UI/Components/CodeEditor/YamlEditor.tsx +28 -326
- package/UI/Components/CopyTextButton/CopyTextButton.tsx +5 -1
- package/UI/Components/CustomFields/CustomFieldModelFormFields.tsx +6 -25
- package/UI/Components/EditionLabel/EditionLabel.tsx +14 -13
- package/UI/Components/ErrorMessage/ErrorMessage.tsx +11 -1
- package/UI/Components/Forms/Fields/FormField.tsx +8 -0
- package/UI/Components/Forms/Types/Field.ts +8 -0
- package/UI/Components/Icon/Icon.tsx +18 -0
- package/UI/Components/Input/Input.tsx +11 -4
- package/UI/Components/LogsViewer/LogsViewer.tsx +4 -0
- package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +21 -3
- package/UI/Components/Markdown.tsx/MarkdownConverters.ts +0 -0
- package/UI/Components/Markdown.tsx/MarkdownEditor.tsx +877 -79
- package/UI/Components/Markdown.tsx/MarkdownEditorHistory.ts +284 -0
- package/UI/Components/Markdown.tsx/MarkdownListEditing.ts +1362 -0
- package/UI/Components/Markdown.tsx/MarkdownPaste.ts +1326 -0
- package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +106 -10
- package/UI/Components/Markdown.tsx/MarkdownVisualEditing.ts +1032 -0
- package/UI/Components/ModelTable/BaseModelTable.tsx +85 -7
- package/UI/Components/ResetObjectID/ResetObjectID.tsx +35 -9
- package/UI/Components/Table/EmptyTableMessage.ts +81 -0
- package/UI/Components/Table/Table.tsx +50 -21
- package/UI/Components/Table/TableRow.tsx +9 -1
- package/UI/Components/TelemetryViewer/FacetVisibility.ts +97 -2
- package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +13 -0
- package/UI/Components/TelemetryViewer/components/LockedFilterChip.tsx +214 -7
- package/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.tsx +21 -3
- package/UI/Components/TextArea/TextArea.tsx +9 -2
- package/UI/Components/Workflow/ModelFieldPicker.tsx +10 -12
- package/UI/Styles/Theme.css +8 -0
- package/UI/esbuild-config.js +2 -32
- package/Utils/LabelRuleImportExport.ts +2 -0
- package/Utils/Markdown/UntrustedMarkdown.ts +1513 -0
- package/Utils/Monitor/IncomingEmailMonitorRequestUtil.ts +41 -0
- package/Utils/Monitor/MonitorLogSummaryUtil.ts +212 -0
- package/Utils/Telemetry/EntityKey.ts +48 -0
- package/build/dist/Models/DatabaseModels/AIConversation.js +125 -1
- package/build/dist/Models/DatabaseModels/AIConversation.js.map +1 -1
- package/build/dist/Models/DatabaseModels/CephCluster.js +12 -0
- package/build/dist/Models/DatabaseModels/CephCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/CloudResource.js +12 -0
- package/build/dist/Models/DatabaseModels/CloudResource.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DatabaseServer.js +12 -0
- package/build/dist/Models/DatabaseModels/DatabaseServer.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DockerHost.js +12 -0
- package/build/dist/Models/DatabaseModels/DockerHost.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DockerSwarmCluster.js +12 -0
- package/build/dist/Models/DatabaseModels/DockerSwarmCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalOidc.js +0 -4
- package/build/dist/Models/DatabaseModels/GlobalOidc.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalOidcProject.js +0 -4
- package/build/dist/Models/DatabaseModels/GlobalOidcProject.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalSso.js +0 -4
- package/build/dist/Models/DatabaseModels/GlobalSso.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalSsoProject.js +0 -4
- package/build/dist/Models/DatabaseModels/GlobalSsoProject.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Host.js +12 -0
- package/build/dist/Models/DatabaseModels/Host.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentForm.js +605 -0
- package/build/dist/Models/DatabaseModels/IncidentForm.js.map +1 -0
- package/build/dist/Models/DatabaseModels/IncidentFormSubmission.js +299 -0
- package/build/dist/Models/DatabaseModels/IncidentFormSubmission.js.map +1 -0
- package/build/dist/Models/DatabaseModels/IncidentTemplate.js +60 -0
- package/build/dist/Models/DatabaseModels/IncidentTemplate.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +14 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IoTFleet.js +12 -0
- package/build/dist/Models/DatabaseModels/IoTFleet.js.map +1 -1
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js +12 -0
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/MessageQueue.js +1106 -0
- package/build/dist/Models/DatabaseModels/MessageQueue.js.map +1 -0
- package/build/dist/Models/DatabaseModels/MessageQueueLabelRule.js +562 -0
- package/build/dist/Models/DatabaseModels/MessageQueueLabelRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/MessageQueueOwnerRule.js +643 -0
- package/build/dist/Models/DatabaseModels/MessageQueueOwnerRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/MessageQueueOwnerTeam.js +506 -0
- package/build/dist/Models/DatabaseModels/MessageQueueOwnerTeam.js.map +1 -0
- package/build/dist/Models/DatabaseModels/MessageQueueOwnerUser.js +505 -0
- package/build/dist/Models/DatabaseModels/MessageQueueOwnerUser.js.map +1 -0
- package/build/dist/Models/DatabaseModels/PodmanHost.js +12 -0
- package/build/dist/Models/DatabaseModels/PodmanHost.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +61 -0
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProjectOidc.js +0 -4
- package/build/dist/Models/DatabaseModels/ProjectOidc.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProjectSso.js +0 -4
- package/build/dist/Models/DatabaseModels/ProjectSso.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProxmoxCluster.js +12 -0
- package/build/dist/Models/DatabaseModels/ProxmoxCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RumApplication.js +12 -0
- package/build/dist/Models/DatabaseModels/RumApplication.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ServerlessFunction.js +12 -0
- package/build/dist/Models/DatabaseModels/ServerlessFunction.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Service.js +12 -0
- package/build/dist/Models/DatabaseModels/Service.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageOidc.js +0 -4
- package/build/dist/Models/DatabaseModels/StatusPageOidc.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageSso.js +0 -4
- package/build/dist/Models/DatabaseModels/StatusPageSso.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js +49 -3
- package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js.map +1 -1
- package/build/dist/Models/DatabaseModels/VMwareVCenter.js +12 -0
- package/build/dist/Models/DatabaseModels/VMwareVCenter.js.map +1 -1
- package/build/dist/Server/API/AIChatAPI.js +9 -103
- package/build/dist/Server/API/AIChatAPI.js.map +1 -1
- package/build/dist/Server/API/AIInvestigationAPI.js +3 -3
- package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
- package/build/dist/Server/API/AIInvestigationConversationAPI.js +162 -0
- package/build/dist/Server/API/AIInvestigationConversationAPI.js.map +1 -0
- package/build/dist/Server/API/IncidentFormAPI.js +245 -0
- package/build/dist/Server/API/IncidentFormAPI.js.map +1 -0
- package/build/dist/Server/API/ProjectOIDC.js +16 -24
- package/build/dist/Server/API/ProjectOIDC.js.map +1 -1
- package/build/dist/Server/API/ProjectSSO.js +16 -24
- package/build/dist/Server/API/ProjectSSO.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +42 -75
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/Enterprise/EnterpriseEdition.js +79 -25
- package/build/dist/Server/Enterprise/EnterpriseEdition.js.map +1 -1
- package/build/dist/Server/Enterprise/EnterpriseFeature.js +24 -3
- package/build/dist/Server/Enterprise/EnterpriseFeature.js.map +1 -1
- package/build/dist/Server/Enterprise/EnterpriseLicenseSnapshot.js +1 -1
- package/build/dist/Server/Enterprise/EnterpriseServerModule.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796400000000-AddIncidentForms.js +63 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796400000000-AddIncidentForms.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796500000000-AddTeamComplianceRuleNotificationChannels.js +41 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796500000000-AddTeamComplianceRuleNotificationChannels.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796600000000-AddAIInvestigationConversationAndTimeLimits.js +40 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796600000000-AddAIInvestigationConversationAndTimeLimits.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796700000000-AddMessageQueueTables.js +183 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796700000000-AddMessageQueueTables.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/IncidentFormRateLimit.js +559 -0
- package/build/dist/Server/Middleware/IncidentFormRateLimit.js.map +1 -0
- package/build/dist/Server/Middleware/UserAuthorization.js +11 -30
- package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
- package/build/dist/Server/Services/AIService.js +10 -13
- package/build/dist/Server/Services/AIService.js.map +1 -1
- package/build/dist/Server/Services/IncidentCustomFieldService.js +83 -0
- package/build/dist/Server/Services/IncidentCustomFieldService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeMemberService.js +8 -4
- package/build/dist/Server/Services/IncidentEpisodeMemberService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeService.js +10 -1
- package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
- package/build/dist/Server/Services/IncidentFormService.js +1202 -0
- package/build/dist/Server/Services/IncidentFormService.js.map +1 -0
- package/build/dist/Server/Services/IncidentFormSubmissionService.js +91 -0
- package/build/dist/Server/Services/IncidentFormSubmissionService.js.map +1 -0
- package/build/dist/Server/Services/IncidentGroupingEngineService.js +20 -8
- package/build/dist/Server/Services/IncidentGroupingEngineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +106 -4
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/IncidentTemplateService.js +18 -0
- package/build/dist/Server/Services/IncidentTemplateService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +14 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/MessageQueueLabelRuleEngineService.js +262 -0
- package/build/dist/Server/Services/MessageQueueLabelRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/MessageQueueLabelRuleService.js +13 -0
- package/build/dist/Server/Services/MessageQueueLabelRuleService.js.map +1 -0
- package/build/dist/Server/Services/MessageQueueOwnerRuleEngineService.js +334 -0
- package/build/dist/Server/Services/MessageQueueOwnerRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/MessageQueueOwnerRuleService.js +13 -0
- package/build/dist/Server/Services/MessageQueueOwnerRuleService.js.map +1 -0
- package/build/dist/Server/Services/MessageQueueOwnerTeamService.js +62 -0
- package/build/dist/Server/Services/MessageQueueOwnerTeamService.js.map +1 -0
- package/build/dist/Server/Services/MessageQueueOwnerUserService.js +62 -0
- package/build/dist/Server/Services/MessageQueueOwnerUserService.js.map +1 -0
- package/build/dist/Server/Services/MessageQueueService.js +1140 -0
- package/build/dist/Server/Services/MessageQueueService.js.map +1 -0
- package/build/dist/Server/Services/OpenTelemetryIngestService.js +25 -4
- package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +0 -54
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/StatusPagePrivateUserService.js +1 -4
- package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +0 -46
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Services/TeamComplianceSettingService.js +120 -31
- package/build/dist/Server/Services/TeamComplianceSettingService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationRuleService.js +8 -1
- package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceNotificationSummaryService.js +12 -3
- package/build/dist/Server/Services/WorkspaceNotificationSummaryService.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/CreatePermission.js +6 -3
- package/build/dist/Server/Types/Database/Permissions/CreatePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/EditionPermission.js +109 -37
- package/build/dist/Server/Types/Database/Permissions/EditionPermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/OwnerTableRegistry.js +16 -0
- package/build/dist/Server/Types/Database/Permissions/OwnerTableRegistry.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/TablePermission.js +3 -3
- package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js +9 -6
- package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/AgentContextCompactor.js +128 -0
- package/build/dist/Server/Utils/AI/Chat/AgentContextCompactor.js.map +1 -0
- package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js +192 -50
- package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/ChatRunCancellation.js +133 -0
- package/build/dist/Server/Utils/AI/Chat/ChatRunCancellation.js.map +1 -0
- package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +53 -13
- package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js +1 -1
- package/build/dist/Server/Utils/AI/Chat/ToolOutputPager.js +206 -0
- package/build/dist/Server/Utils/AI/Chat/ToolOutputPager.js.map +1 -0
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js +53 -26
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js.map +1 -1
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js +4 -2
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/CodeFix/CodeFixReadiness.js +1 -29
- package/build/dist/Server/Utils/AI/CodeFix/CodeFixReadiness.js.map +1 -1
- package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js +24 -24
- package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js +7 -2
- package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +29 -14
- package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.js +38 -14
- package/build/dist/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.js.map +1 -1
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.js +2 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +40 -10
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +19 -7
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +19 -7
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/InvestigationQueue.js +15 -18
- package/build/dist/Server/Utils/AI/SRE/InvestigationQueue.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/InvestigationThread.js +412 -0
- package/build/dist/Server/Utils/AI/SRE/InvestigationThread.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/InvestigationThreadService.js +495 -0
- package/build/dist/Server/Utils/AI/SRE/InvestigationThreadService.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/Serializer.js +10 -3
- package/build/dist/Server/Utils/AI/Toolbox/Serializer.js.map +1 -1
- package/build/dist/Server/Utils/EditionEnforcement.js +20 -158
- package/build/dist/Server/Utils/EditionEnforcement.js.map +1 -1
- package/build/dist/Server/Utils/LLM/LLMService.js +193 -54
- package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
- package/build/dist/Server/Utils/RelaxedScimTeamLocksReport.js +160 -0
- package/build/dist/Server/Utils/RelaxedScimTeamLocksReport.js.map +1 -0
- package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js +25 -0
- package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js.map +1 -1
- package/build/dist/Server/Utils/SameOriginRequest.js +151 -0
- package/build/dist/Server/Utils/SameOriginRequest.js.map +1 -0
- package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js +2 -0
- package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +4 -1
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/MessageQueueDiscovery.js +1015 -0
- package/build/dist/Server/Utils/Telemetry/MessageQueueDiscovery.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js +4 -1
- package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js.map +1 -1
- package/build/dist/Server/Utils/VendorAssets.js +3 -3
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js +3 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js +3 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +10 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/AI/AIAgentRunLimits.js +91 -0
- package/build/dist/Types/AI/AIAgentRunLimits.js.map +1 -0
- package/build/dist/Types/Code/CodeType.js +2 -0
- package/build/dist/Types/Code/CodeType.js.map +1 -1
- package/build/dist/Types/Code/JsonSyntax.js +604 -0
- package/build/dist/Types/Code/JsonSyntax.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldCreateSettings.js +310 -0
- package/build/dist/Types/CustomField/CustomFieldCreateSettings.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldMappingCatalog.js +38 -0
- package/build/dist/Types/CustomField/CustomFieldMappingCatalog.js.map +1 -1
- package/build/dist/Types/CustomField/CustomFieldValueValidator.js +10 -7
- package/build/dist/Types/CustomField/CustomFieldValueValidator.js.map +1 -1
- package/build/dist/Types/EnterpriseLicense/EnterpriseLicensePeriods.js +1 -1
- package/build/dist/Types/Icon/IconProp.js +2 -0
- package/build/dist/Types/Icon/IconProp.js.map +1 -1
- package/build/dist/Types/Incident/IncidentFormIpAllowlist.js +119 -0
- package/build/dist/Types/Incident/IncidentFormIpAllowlist.js.map +1 -0
- package/build/dist/Types/Incident/IncidentFormPublic.js +678 -0
- package/build/dist/Types/Incident/IncidentFormPublic.js.map +1 -0
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js +10 -3
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js.map +1 -1
- package/build/dist/Types/MessageQueue/MessageQueueIdentity.js +184 -0
- package/build/dist/Types/MessageQueue/MessageQueueIdentity.js.map +1 -0
- package/build/dist/Types/MessageQueue/MessageQueueManualIdentity.js +190 -0
- package/build/dist/Types/MessageQueue/MessageQueueManualIdentity.js.map +1 -0
- package/build/dist/Types/MessageQueue/MessageQueueMetricCatalog.js +1057 -0
- package/build/dist/Types/MessageQueue/MessageQueueMetricCatalog.js.map +1 -0
- package/build/dist/Types/MessageQueue/MessagingSystem.js +394 -0
- package/build/dist/Types/MessageQueue/MessagingSystem.js.map +1 -0
- package/build/dist/Types/MessageQueue/MessagingTelemetryResolver.js +1768 -0
- package/build/dist/Types/MessageQueue/MessagingTelemetryResolver.js.map +1 -0
- package/build/dist/Types/Monitor/MessageQueueAlertTemplates.js +1207 -0
- package/build/dist/Types/Monitor/MessageQueueAlertTemplates.js.map +1 -0
- package/build/dist/Types/Permission.js +274 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/ResourceAiAgent/ResourceAiAccess.js +10 -4
- package/build/dist/Types/ResourceAiAgent/ResourceAiAccess.js.map +1 -1
- package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js +12 -0
- package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js.map +1 -1
- package/build/dist/Types/Rules/RuleRun.js +4 -0
- package/build/dist/Types/Rules/RuleRun.js.map +1 -1
- package/build/dist/Types/Team/ComplianceNotificationChannel.js +4 -3
- package/build/dist/Types/Team/ComplianceNotificationChannel.js.map +1 -1
- package/build/dist/Types/Team/ComplianceRule.js +48 -14
- package/build/dist/Types/Team/ComplianceRule.js.map +1 -1
- package/build/dist/Types/Team/ComplianceRuleType.js +2 -2
- package/build/dist/Types/Telemetry/EntityType.js +16 -0
- package/build/dist/Types/Telemetry/EntityType.js.map +1 -1
- package/build/dist/Types/XML.js +23 -0
- package/build/dist/Types/XML.js.map +1 -1
- package/build/dist/UI/Components/CodeEditor/CodeEditor.js +641 -266
- package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
- package/build/dist/UI/Components/CodeEditor/CodeEditorCommands.js +464 -0
- package/build/dist/UI/Components/CodeEditor/CodeEditorCommands.js.map +1 -0
- package/build/dist/UI/Components/CodeEditor/CodeEditorHighlight.js +69 -0
- package/build/dist/UI/Components/CodeEditor/CodeEditorHighlight.js.map +1 -0
- package/build/dist/UI/Components/CodeEditor/CodeEditorLanguages.js +108 -0
- package/build/dist/UI/Components/CodeEditor/CodeEditorLanguages.js.map +1 -0
- package/build/dist/UI/Components/CodeEditor/YamlEditor.js +14 -186
- package/build/dist/UI/Components/CodeEditor/YamlEditor.js.map +1 -1
- package/build/dist/UI/Components/CopyTextButton/CopyTextButton.js +5 -1
- package/build/dist/UI/Components/CopyTextButton/CopyTextButton.js.map +1 -1
- package/build/dist/UI/Components/CustomFields/CustomFieldModelFormFields.js +5 -14
- package/build/dist/UI/Components/CustomFields/CustomFieldModelFormFields.js.map +1 -1
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js +14 -13
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
- package/build/dist/UI/Components/ErrorMessage/ErrorMessage.js +1 -1
- package/build/dist/UI/Components/ErrorMessage/ErrorMessage.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js +8 -3
- package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
- package/build/dist/UI/Components/Icon/Icon.js +8 -0
- package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
- package/build/dist/UI/Components/Input/Input.js +11 -3
- package/build/dist/UI/Components/Input/Input.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +2 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +14 -4
- package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js +0 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js +710 -65
- package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownEditorHistory.js +238 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownEditorHistory.js.map +1 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownListEditing.js +1028 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownListEditing.js.map +1 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownPaste.js +1088 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownPaste.js.map +1 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +70 -10
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownVisualEditing.js +887 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownVisualEditing.js.map +1 -0
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +57 -6
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/ResetObjectID/ResetObjectID.js +7 -5
- package/build/dist/UI/Components/ResetObjectID/ResetObjectID.js.map +1 -1
- package/build/dist/UI/Components/Table/EmptyTableMessage.js +51 -0
- package/build/dist/UI/Components/Table/EmptyTableMessage.js.map +1 -0
- package/build/dist/UI/Components/Table/Table.js +30 -11
- package/build/dist/UI/Components/Table/Table.js.map +1 -1
- package/build/dist/UI/Components/Table/TableRow.js +9 -2
- package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/FacetVisibility.js +63 -1
- package/build/dist/UI/Components/TelemetryViewer/FacetVisibility.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +4 -2
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterChip.js +91 -10
- package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterChip.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js +14 -4
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js.map +1 -1
- package/build/dist/UI/Components/TextArea/TextArea.js +9 -3
- package/build/dist/UI/Components/TextArea/TextArea.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ModelFieldPicker.js +1 -2
- package/build/dist/UI/Components/Workflow/ModelFieldPicker.js.map +1 -1
- package/build/dist/Utils/LabelRuleImportExport.js +2 -0
- package/build/dist/Utils/LabelRuleImportExport.js.map +1 -1
- package/build/dist/Utils/Markdown/UntrustedMarkdown.js +988 -0
- package/build/dist/Utils/Markdown/UntrustedMarkdown.js.map +1 -0
- package/build/dist/Utils/Monitor/IncomingEmailMonitorRequestUtil.js +34 -0
- package/build/dist/Utils/Monitor/IncomingEmailMonitorRequestUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitorLogSummaryUtil.js +148 -0
- package/build/dist/Utils/Monitor/MonitorLogSummaryUtil.js.map +1 -0
- package/build/dist/Utils/Telemetry/EntityKey.js +36 -0
- package/build/dist/Utils/Telemetry/EntityKey.js.map +1 -1
- package/package.json +1 -3
- package/Server/Utils/CommunityEditionSsoReport.ts +0 -361
- package/Tests/App/Accounts/LoginSsoEdition.test.tsx +0 -616
- package/Tests/App/AdminDashboard/AdminAuthenticationSsoEdition.test.tsx +0 -217
- package/Tests/App/AdminDashboard/GlobalSSOEnterpriseShells.test.tsx +0 -271
- package/Tests/App/Dashboard/CloudDocumentationMarkdown.test.ts +0 -215
- package/Tests/App/Dashboard/TelemetryResourceRetentionSettings.test.tsx +0 -784
- package/Tests/Server/Middleware/UserAuthorizationEditionEnforcement.test.ts +0 -518
- package/Tests/Server/Services/SsoRequirementReadMasking.test.ts +0 -419
- package/Tests/Server/Services/SsoRequirementWriteGuard.test.ts +0 -769
- package/Tests/UI/Components/CodeEditor.test.tsx +0 -638
- package/Tests/UI/Components/MonacoLoader.test.ts +0 -67
- package/Tests/UI/Components/MonacoRuntime.test.ts +0 -121
- package/Tests/UI/Components/Telemetry/TelemetryRetentionConfigForm.test.tsx +0 -329
- package/Tests/UI/Components/YamlEditor.test.tsx +0 -847
- package/UI/Components/CodeEditor/MonacoLoader.ts +0 -36
- package/UI/Components/Telemetry/TelemetryRetentionConfigForm.tsx +0 -319
- package/UI/Components/Telemetry/TelemetryRetentionConfigSummary.tsx +0 -223
- package/build/dist/Server/Utils/CommunityEditionSsoReport.js +0 -223
- package/build/dist/Server/Utils/CommunityEditionSsoReport.js.map +0 -1
- package/build/dist/UI/Components/CodeEditor/MonacoLoader.js +0 -34
- package/build/dist/UI/Components/CodeEditor/MonacoLoader.js.map +0 -1
- package/build/dist/UI/Components/Telemetry/TelemetryRetentionConfigForm.js +0 -171
- package/build/dist/UI/Components/Telemetry/TelemetryRetentionConfigForm.js.map +0 -1
- package/build/dist/UI/Components/Telemetry/TelemetryRetentionConfigSummary.js +0 -119
- package/build/dist/UI/Components/Telemetry/TelemetryRetentionConfigSummary.js.map +0 -1
|
@@ -0,0 +1,1402 @@
|
|
|
1
|
+
import { SpanKind, SpanStatus } from "../../../Models/AnalyticsModels/Span";
|
|
2
|
+
import AnalyticsTableName from "../../../Types/AnalyticsDatabase/AnalyticsTableName";
|
|
3
|
+
import {
|
|
4
|
+
MessageQueueIdentity,
|
|
5
|
+
buildMessageQueueIdentifier,
|
|
6
|
+
toMessageQueueIdentity,
|
|
7
|
+
} from "../../../Types/MessageQueue/MessageQueueIdentity";
|
|
8
|
+
import {
|
|
9
|
+
MESSAGE_QUEUE_BROKER_METRIC_NAMES,
|
|
10
|
+
MESSAGE_QUEUE_METRICS,
|
|
11
|
+
MESSAGING_CLIENT_METRIC_NAMES,
|
|
12
|
+
MessageQueueMetricDescriptor,
|
|
13
|
+
} from "../../../Types/MessageQueue/MessageQueueMetricCatalog";
|
|
14
|
+
import {
|
|
15
|
+
MessagingSystemDescriptor,
|
|
16
|
+
getMessagingBrokerMetricsSource,
|
|
17
|
+
getMessagingSystemDescriptor,
|
|
18
|
+
getMoreSpecificMessagingSystem,
|
|
19
|
+
} from "../../../Types/MessageQueue/MessagingSystem";
|
|
20
|
+
import {
|
|
21
|
+
AZURE_MESSAGING_PROVIDER_NAMESPACES,
|
|
22
|
+
AZURE_RESOURCE_PROVIDER_ATTRIBUTES,
|
|
23
|
+
AttributeGetter,
|
|
24
|
+
MESSAGING_DESTINATION_ATTRIBUTES,
|
|
25
|
+
MESSAGING_METRIC_RESOLVER_INPUT_ATTRIBUTES,
|
|
26
|
+
MESSAGING_RESOLVER_INPUT_ATTRIBUTES,
|
|
27
|
+
MESSAGING_SYSTEM_ATTRIBUTE,
|
|
28
|
+
MESSAGING_TEMPORARY_FLAG_ATTRIBUTES,
|
|
29
|
+
MESSAGING_TRIGGER_ATTRIBUTES,
|
|
30
|
+
MessagingDirection,
|
|
31
|
+
RABBITMQ_ROUTING_KEY_ATTRIBUTES,
|
|
32
|
+
ResolvedMessagingDestination,
|
|
33
|
+
resolveMessagingMetricDatapoint,
|
|
34
|
+
resolveMessagingSpan,
|
|
35
|
+
} from "../../../Types/MessageQueue/MessagingTelemetryResolver";
|
|
36
|
+
import { QUERY_SETTINGS, escapeSql } from "./ServiceDependencyDiscovery";
|
|
37
|
+
|
|
38
|
+
/*
|
|
39
|
+
* Message queue discovery — the pure half of the queue step of
|
|
40
|
+
* "TelemetryEntity:ComputeServiceDependencies".
|
|
41
|
+
*
|
|
42
|
+
* Ingest already stamps every messaging span and every messaging datapoint
|
|
43
|
+
* with the entity key of the queue it names (MessagingEntityKeyResolver, per
|
|
44
|
+
* row, through the core's resolveMessagingSpan /
|
|
45
|
+
* resolveMessagingMetricDatapoint). This step turns the same evidence into
|
|
46
|
+
* MessageQueue rows: one grouped query over the window's spans and one over
|
|
47
|
+
* its metric datapoints, then the SAME resolvers ingest ran, fed each row's
|
|
48
|
+
* grouped attributes — so the identity a row is found or created under is
|
|
49
|
+
* byte-for-byte the identity whose key the telemetry carries.
|
|
50
|
+
*
|
|
51
|
+
* What makes that hold is what the queries select and group by: exactly the
|
|
52
|
+
* attribute keys the resolvers read (MESSAGING_RESOLVER_INPUT_ATTRIBUTES for
|
|
53
|
+
* spans, MESSAGING_METRIC_RESOLVER_INPUT_ATTRIBUTES for datapoints — the
|
|
54
|
+
* core's own lists, never a copy), plus the span kind (it decides the
|
|
55
|
+
* direction, and with it how a RabbitMQ consumer's joined name splits) or the
|
|
56
|
+
* metric name (it decides which catalog entry reads the datapoint). Every
|
|
57
|
+
* span of one group therefore resolves exactly as each of them did at
|
|
58
|
+
* ingest. None of those keys is per message or per instance by design (the
|
|
59
|
+
* core keeps offsets, partitions, message, client and consumer ids out of
|
|
60
|
+
* its lists), so a topic a thousand pods consume is a handful of rows, not
|
|
61
|
+
* a thousand. A few of them can still HOLD a value per message — a
|
|
62
|
+
* JetStream acknowledgement subject, a destination name beside its
|
|
63
|
+
* template, a routing key carrying an id — which the span query folds
|
|
64
|
+
* before grouping, without changing what the resolver answers (see
|
|
65
|
+
* "Values that vary per message" below).
|
|
66
|
+
*
|
|
67
|
+
* Each query returns at most its row cap, and the rows it keeps are chosen
|
|
68
|
+
* so that no queue is left out run after run: every broker (span query) or
|
|
69
|
+
* metric (metric query) gets its share, and half of each share goes to its
|
|
70
|
+
* busiest groups, the other half to a sample that rotates every run (see
|
|
71
|
+
* "Which rows the cap keeps" below). And each query ends on the server
|
|
72
|
+
* before the cron's client gives up on it: a window too big to read in time
|
|
73
|
+
* fails with ClickHouse's own timeout error, not the client's (see "The
|
|
74
|
+
* queue queries' settings" below).
|
|
75
|
+
*
|
|
76
|
+
* Everything here is synchronous and side-effect free apart from reading one
|
|
77
|
+
* environment variable, so it can be tested without ClickHouse or Postgres.
|
|
78
|
+
*/
|
|
79
|
+
|
|
80
|
+
// Unique marker comments, so the queries can be told apart in logs and tests.
|
|
81
|
+
export const MESSAGE_QUEUE_SPAN_SQL_MARKER: string =
|
|
82
|
+
"message-queue-span-discovery";
|
|
83
|
+
export const MESSAGE_QUEUE_METRIC_SQL_MARKER: string =
|
|
84
|
+
"message-queue-metric-discovery";
|
|
85
|
+
export const MESSAGE_QUEUE_METRIC_PROJECTS_SQL_MARKER: string =
|
|
86
|
+
"message-queue-metric-projects";
|
|
87
|
+
|
|
88
|
+
export const MESSAGE_QUEUE_MIN_SPANS_ENV: string = "MESSAGE_QUEUE_MIN_SPANS";
|
|
89
|
+
export const DEFAULT_MESSAGE_QUEUE_MIN_SPANS: number = 3;
|
|
90
|
+
|
|
91
|
+
/*
|
|
92
|
+
* How much further back than the span window the metric query reads
|
|
93
|
+
* cloud-monitoring datapoints. Cloud monitoring APIs publish their numbers
|
|
94
|
+
* minutes after the fact, and a datapoint is stored under the time it
|
|
95
|
+
* measured, not the time it arrived: the aws_cloudwatch receiver's newest
|
|
96
|
+
* point is typically 11 to 25 minutes old when it is ingested (its `delay`
|
|
97
|
+
* defaults to 10 minutes, on top of a 5-minute period), googlecloudmonitoring's
|
|
98
|
+
* up to 9. Read with the span window alone, most CloudWatch datapoints would
|
|
99
|
+
* already lie behind the window of the first run after they arrived, and an
|
|
100
|
+
* SQS queue its broker reports would never be sighted. 45 more minutes (an
|
|
101
|
+
* hour in all for the 15-minute window) covers datapoints up to 50 minutes
|
|
102
|
+
* late. Only those metrics read further back: a Kafka, RabbitMQ, Pulsar or
|
|
103
|
+
* RocketMQ scrape, a JMX Scraper push and a messaging client metric are
|
|
104
|
+
* stored as they happen.
|
|
105
|
+
*/
|
|
106
|
+
export const MESSAGE_QUEUE_LATE_METRIC_MINUTES: number = 45;
|
|
107
|
+
|
|
108
|
+
const SPAN_TABLE: string = `oneuptime.${AnalyticsTableName.Span}`;
|
|
109
|
+
const METRIC_TABLE: string = `oneuptime.${AnalyticsTableName.Metric}`;
|
|
110
|
+
|
|
111
|
+
/*
|
|
112
|
+
* The one stored span kind the span query leaves out. A messaging operation
|
|
113
|
+
* is never SERVED, so ingest keys every messaging span EXCEPT one stored as
|
|
114
|
+
* SERVER (MessagingEntityKeyResolver.isMessagingSpan turns away
|
|
115
|
+
* `kind === SpanKind.Server` and nothing else) — and the stored kind is not
|
|
116
|
+
* always one of the five the ingest service maps OTLP's to: a trace
|
|
117
|
+
* pipeline's Span Kind Remapper runs before the stamper and stores whatever
|
|
118
|
+
* its mapping says (SPAN_KIND_UNSPECIFIED, which the dashboard offers; any
|
|
119
|
+
* string the API accepts; nothing at all). An allow-list of kinds would
|
|
120
|
+
* leave such a queue's spans unread though they carry its key: never
|
|
121
|
+
* created from them, never sighted, archived while they keep arriving.
|
|
122
|
+
* So the query leaves out this kind alone, and
|
|
123
|
+
* resolveMessagingSpanDiscoveryRows runs the core's resolver again, which
|
|
124
|
+
* refuses any other spelling of SERVER and reads a kind it does not know
|
|
125
|
+
* as no kind — exactly as ingest did. That includes OTLP's number: a
|
|
126
|
+
* mapping saved through the API may write the NUMBER 2, which the stamper
|
|
127
|
+
* sees and the kind column keeps as the text "2", and the core reads both
|
|
128
|
+
* as SERVER.
|
|
129
|
+
*/
|
|
130
|
+
export const MESSAGE_QUEUE_DISCOVERY_EXCLUDED_SPAN_KIND: SpanKind =
|
|
131
|
+
SpanKind.Server;
|
|
132
|
+
|
|
133
|
+
function uniqueSorted(values: Iterable<string>): Array<string> {
|
|
134
|
+
return Array.from(new Set<string>(values)).sort();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/*
|
|
138
|
+
* The metric names the metric query reads: every curated broker metric and
|
|
139
|
+
* every messaging client metric, in the lowercase form ingest stores names
|
|
140
|
+
* in — an IN list over the second sort-key column, so the query touches only
|
|
141
|
+
* those names' ranges of the table. Two kinds of messaging datapoint are
|
|
142
|
+
* deliberately not read, so they can attach to a queue at ingest (its key is
|
|
143
|
+
* stamped on them) but never create or sight one:
|
|
144
|
+
*
|
|
145
|
+
* - an application's own gauge that carries `messaging.system` (BullMQ's
|
|
146
|
+
* `queue.size`, OneUptime's own included): the docs promise it attaches
|
|
147
|
+
* to a queue, never that it creates one, and its name says nothing;
|
|
148
|
+
* - a curated name a RenameMetric pipeline rule stored in mixed case
|
|
149
|
+
* (`Kafka.Consumer_Group.Lag_Sum`): ingest lowercases a name on arrival,
|
|
150
|
+
* but a rule's rename lands in the table verbatim. Ingest still stamps
|
|
151
|
+
* the queue's key on it (it compares names trimmed and lowercased), but
|
|
152
|
+
* matching every stored spelling here would take a lower() over the name
|
|
153
|
+
* column of every row, which no sort-key range can serve.
|
|
154
|
+
*/
|
|
155
|
+
export const MESSAGE_QUEUE_DISCOVERY_METRIC_NAMES: ReadonlyArray<string> =
|
|
156
|
+
uniqueSorted([
|
|
157
|
+
...MESSAGE_QUEUE_BROKER_METRIC_NAMES,
|
|
158
|
+
...MESSAGING_CLIENT_METRIC_NAMES,
|
|
159
|
+
]);
|
|
160
|
+
|
|
161
|
+
/*
|
|
162
|
+
* The curated metrics a cloud provider's monitoring API reports (Azure
|
|
163
|
+
* Monitor, CloudWatch, Cloud Monitoring): the ones read
|
|
164
|
+
* MESSAGE_QUEUE_LATE_METRIC_MINUTES further back.
|
|
165
|
+
*/
|
|
166
|
+
export const MESSAGE_QUEUE_LATE_METRIC_NAMES: ReadonlyArray<string> =
|
|
167
|
+
uniqueSorted(
|
|
168
|
+
MESSAGE_QUEUE_METRICS.filter(
|
|
169
|
+
(descriptor: MessageQueueMetricDescriptor): boolean => {
|
|
170
|
+
return (
|
|
171
|
+
getMessagingBrokerMetricsSource(descriptor.system).kind ===
|
|
172
|
+
"cloud-monitoring"
|
|
173
|
+
);
|
|
174
|
+
},
|
|
175
|
+
).map((descriptor: MessageQueueMetricDescriptor): string => {
|
|
176
|
+
return descriptor.metricName;
|
|
177
|
+
}),
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
// The trigger keys whose mere presence (non-blank) admits a span.
|
|
181
|
+
const PRESENCE_TRIGGER_ATTRIBUTES: ReadonlyArray<string> =
|
|
182
|
+
MESSAGING_TRIGGER_ATTRIBUTES.filter((key: string): boolean => {
|
|
183
|
+
return !AZURE_RESOURCE_PROVIDER_ATTRIBUTES.includes(key);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// The column a query selects the attribute at `index` of its key list as.
|
|
187
|
+
export function getMessagingDiscoveryColumn(index: number): string {
|
|
188
|
+
return `a${index}`;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function columnsByKey(
|
|
192
|
+
keys: ReadonlyArray<string>,
|
|
193
|
+
): ReadonlyMap<string, string> {
|
|
194
|
+
const columns: Map<string, string> = new Map<string, string>();
|
|
195
|
+
keys.forEach((key: string, index: number): void => {
|
|
196
|
+
columns.set(key, getMessagingDiscoveryColumn(index));
|
|
197
|
+
});
|
|
198
|
+
return columns;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const SPAN_ATTRIBUTE_COLUMNS: ReadonlyMap<string, string> = columnsByKey(
|
|
202
|
+
MESSAGING_RESOLVER_INPUT_ATTRIBUTES,
|
|
203
|
+
);
|
|
204
|
+
const METRIC_ATTRIBUTE_COLUMNS: ReadonlyMap<string, string> = columnsByKey(
|
|
205
|
+
MESSAGING_METRIC_RESOLVER_INPUT_ATTRIBUTES,
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
function sqlString(value: string): string {
|
|
209
|
+
return `'${escapeSql(value)}'`;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function sqlArray(values: ReadonlyArray<string>): string {
|
|
213
|
+
return `[${values.map(sqlString).join(", ")}]`;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function sqlTuple(values: ReadonlyArray<string>): string {
|
|
217
|
+
return `(${values.map(sqlString).join(", ")})`;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/*
|
|
221
|
+
* Every span but a stored SERVER one (MESSAGE_QUEUE_DISCOVERY_EXCLUDED_SPAN_KIND).
|
|
222
|
+
* `kind` is Nullable, and a span a pipeline left without a kind is stored
|
|
223
|
+
* NULL: a bare `kind != …` is NULL for it, which would drop a span ingest
|
|
224
|
+
* keyed. ifNull() reads it as ''. Being a non-Nullable expression, it is
|
|
225
|
+
* also the form the kind column's set skip index (idx_kind) can skip blocks
|
|
226
|
+
* of SERVER spans with: on ClickHouse 26.7 no comparison of the bare
|
|
227
|
+
* Nullable column (=, IN, !=) skipped a single granule.
|
|
228
|
+
*/
|
|
229
|
+
function spanKindSql(): string {
|
|
230
|
+
return `ifNull(kind, '') != ${sqlString(MESSAGE_QUEUE_DISCOVERY_EXCLUDED_SPAN_KIND)}`;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// `attributes['k'] AS a<i>` for every key, in list order.
|
|
234
|
+
function attributeColumnsSql(keys: ReadonlyArray<string>): string {
|
|
235
|
+
return keys
|
|
236
|
+
.map((key: string, index: number): string => {
|
|
237
|
+
return `attributes[${sqlString(key)}] AS ${getMessagingDiscoveryColumn(index)}`;
|
|
238
|
+
})
|
|
239
|
+
.join(",\n ");
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function attributeColumnNames(keys: ReadonlyArray<string>): string {
|
|
243
|
+
return keys
|
|
244
|
+
.map((_key: string, index: number): string => {
|
|
245
|
+
return getMessagingDiscoveryColumn(index);
|
|
246
|
+
})
|
|
247
|
+
.join(", ");
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// A usable LIMIT: at least 1, whole, whatever the caller passed.
|
|
251
|
+
function rowCap(maxRows: number): number {
|
|
252
|
+
return Number.isFinite(maxRows) && maxRows >= 1 ? Math.floor(maxRows) : 1;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/*
|
|
256
|
+
* The span-side twin of hasMessagingTrigger, applied after the bloom-indexed
|
|
257
|
+
* presence check on every trigger key: the presence of any other trigger
|
|
258
|
+
* key, or an Azure provider key that names a messaging provider. The Azure
|
|
259
|
+
* SDKs stamp those keys on EVERY client span (Storage, Key Vault and Cosmos
|
|
260
|
+
* DB as much as Service Bus), so without the value condition every Azure SDK
|
|
261
|
+
* span of the window would compete for the row cap. The namespaces are the
|
|
262
|
+
* core's own list (AZURE_MESSAGING_PROVIDER_NAMESPACES, the values its
|
|
263
|
+
* trigger check admits), never a copy. A SUPERSET of the core's check, so no
|
|
264
|
+
* span ingest keys a queue on is ever left out: ClickHouse's trimBoth()
|
|
265
|
+
* trims spaces only, where the core trims every whitespace character before
|
|
266
|
+
* comparing, so the value is searched for the namespace case-insensitively
|
|
267
|
+
* instead of compared whole. Anything extra it lets through (a value merely
|
|
268
|
+
* containing a namespace) resolves to no queue in
|
|
269
|
+
* resolveMessagingSpanDiscoveryRows, which runs the core's check again.
|
|
270
|
+
*/
|
|
271
|
+
function spanTriggerSql(): string {
|
|
272
|
+
const providerChecks: Array<string> = AZURE_RESOURCE_PROVIDER_ATTRIBUTES.map(
|
|
273
|
+
(key: string): string => {
|
|
274
|
+
return `multiSearchAnyCaseInsensitive(attributes[${sqlString(key)}], ${sqlArray(
|
|
275
|
+
AZURE_MESSAGING_PROVIDER_NAMESPACES,
|
|
276
|
+
)})`;
|
|
277
|
+
},
|
|
278
|
+
);
|
|
279
|
+
return [
|
|
280
|
+
`hasAny(attributeKeys, ${sqlArray(PRESENCE_TRIGGER_ATTRIBUTES)})`,
|
|
281
|
+
...providerChecks,
|
|
282
|
+
].join(" OR ");
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/*
|
|
286
|
+
* ---- Values that vary per message --------------------------------------------
|
|
287
|
+
*
|
|
288
|
+
* The span query groups on the VALUES of the resolver's keys, and a few of
|
|
289
|
+
* those keys can hold a new value on every message although the resolver
|
|
290
|
+
* gives all of them one answer, or none:
|
|
291
|
+
*
|
|
292
|
+
* - the Java agent names a JetStream acknowledgement by its whole subject
|
|
293
|
+
* (`$JS.ACK.<stream>.<consumer>.<delivered>.<stream seq>.<consumer
|
|
294
|
+
* seq>.<timestamp>.<pending>`; the template `$JS.ACK` goes beside it
|
|
295
|
+
* only when boundJetStreamAckDestination is on), and a reply inbox
|
|
296
|
+
* (`_INBOX.<id>`) whole in its stable-semconv mode — the resolver names
|
|
297
|
+
* no queue for either;
|
|
298
|
+
* - a destination name beside a template (the spec asks for a template
|
|
299
|
+
* exactly when the name is not low-cardinality): the resolver reads the
|
|
300
|
+
* template, and the name only for whether it is blank;
|
|
301
|
+
* - a RabbitMQ routing key carrying an id (`order.4711.created`, the
|
|
302
|
+
* usual topic-exchange key) beside the exchange it was published to:
|
|
303
|
+
* the resolver names the exchange.
|
|
304
|
+
*
|
|
305
|
+
* Grouped as stored, each such value is a group of its own: ten million
|
|
306
|
+
* acknowledgements in a window took the query past its memory limit on
|
|
307
|
+
* every run (ClickHouse Code 241). The failure is logged and costs the
|
|
308
|
+
* project its span evidence, so span-only queues were never sighted, and
|
|
309
|
+
* were archived a week later. So the query hands the resolver the same
|
|
310
|
+
* answer in far fewer groups:
|
|
311
|
+
*
|
|
312
|
+
* 1. it leaves out the spans the resolver certainly names no queue for —
|
|
313
|
+
* a temporary or anonymous flag that is "true", and a NATS span whose
|
|
314
|
+
* destination is an inbox or acknowledgement subject;
|
|
315
|
+
* 2. a destination key after one that certainly holds text is read only
|
|
316
|
+
* for whether it is blank (a trigger, the Azure legacy evidence), so
|
|
317
|
+
* its text becomes one placeholder;
|
|
318
|
+
* 3. a routing key is blanked beside destinations the resolver reads as
|
|
319
|
+
* one name — none holds a ':' or ',' or spells the default exchange —
|
|
320
|
+
* where nothing reads the key at all.
|
|
321
|
+
*
|
|
322
|
+
* Each rule errs toward keeping a value as stored: "certainly holds text"
|
|
323
|
+
* means a printable, non-space ASCII character, which no trim removes, and
|
|
324
|
+
* every comparison is one ClickHouse makes exactly as the resolver does, or
|
|
325
|
+
* more strictly. The tests hold a TypeScript twin of these rules to the
|
|
326
|
+
* resolver over the core's fixture corpus and generated variants, and the
|
|
327
|
+
* real-ClickHouse suite holds the query to the twin. With them, ten million
|
|
328
|
+
* acknowledgements beside eight hundred thousand Kafka spans are three
|
|
329
|
+
* hundred groups: 4.2 s and 0.8 GiB at 32 threads, nearly all of it the
|
|
330
|
+
* scan's (see "The queue queries' settings").
|
|
331
|
+
*
|
|
332
|
+
* A shape no rule folds still makes a group per message — a joined RabbitMQ
|
|
333
|
+
* name per routing key, a topic named per request that the resolver
|
|
334
|
+
* templates into one. Such groups spill to disk past QUERY_SETTINGS'
|
|
335
|
+
* max_bytes_before_external_group_by, but the more threads the scan runs,
|
|
336
|
+
* the fewer of them merge back from disk within the memory limit: at 32
|
|
337
|
+
* threads a million in a window finished (1.0 GiB) and two million failed
|
|
338
|
+
* (Code 241, which costs the run the project's span evidence), where four
|
|
339
|
+
* threads finished eight million.
|
|
340
|
+
*/
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* What a destination key the resolver reads only for blankness holds in the
|
|
344
|
+
* span query's rows (rule 2): non-blank, like the text it stands for, and a
|
|
345
|
+
* value the resolver reads as scrubbed — so even read as the destination it
|
|
346
|
+
* could never name a queue.
|
|
347
|
+
*/
|
|
348
|
+
export const MESSAGE_QUEUE_DISCOVERY_SHADOWED_VALUE: string = "*";
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* The NATS subjects the resolver names no queue for, lowercased as it
|
|
352
|
+
* compares them: reply inboxes and JetStream acknowledgements, each on its
|
|
353
|
+
* own or followed by "." and anything (rule 1).
|
|
354
|
+
*/
|
|
355
|
+
export const MESSAGE_QUEUE_NATS_GENERATED_SUBJECTS: ReadonlyArray<string> = [
|
|
356
|
+
"_inbox",
|
|
357
|
+
"$js.ack",
|
|
358
|
+
];
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* The non-empty spellings of RabbitMQ's default exchange, lowercased, whose
|
|
362
|
+
* routing key the resolver reads as the queue (rule 3). The empty spelling
|
|
363
|
+
* cannot be a destination the resolver reads, and a joined name that leads
|
|
364
|
+
* with it holds a ':' or ',' anyway.
|
|
365
|
+
*/
|
|
366
|
+
export const MESSAGE_QUEUE_RABBITMQ_DEFAULT_EXCHANGES: ReadonlyArray<string> = [
|
|
367
|
+
"amq.default",
|
|
368
|
+
"<default>",
|
|
369
|
+
];
|
|
370
|
+
|
|
371
|
+
// A system's spellings the catalog maps to it, lowercased (NATS: "jetstream").
|
|
372
|
+
function systemSpellings(system: string): ReadonlyArray<string> {
|
|
373
|
+
const descriptor: MessagingSystemDescriptor | null =
|
|
374
|
+
getMessagingSystemDescriptor(system);
|
|
375
|
+
return uniqueSorted(
|
|
376
|
+
(descriptor ? [descriptor.system, ...descriptor.aliases] : [system]).map(
|
|
377
|
+
(spelling: string): string => {
|
|
378
|
+
return spelling.toLowerCase();
|
|
379
|
+
},
|
|
380
|
+
),
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const NATS_SPELLINGS: ReadonlyArray<string> = systemSpellings("nats");
|
|
385
|
+
|
|
386
|
+
// A span attribute as stored.
|
|
387
|
+
function storedAttributeSql(key: string): string {
|
|
388
|
+
return `attributes[${sqlString(key)}]`;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/*
|
|
392
|
+
* The span query names the destination keys' shared tests once (a WITH
|
|
393
|
+
* clause, which ClickHouse expands where they are used):
|
|
394
|
+
*
|
|
395
|
+
* - destinationText<i>: destination key i (MESSAGING_DESTINATION_ATTRIBUTES
|
|
396
|
+
* order) certainly holds text the resolver reads as non-blank — a
|
|
397
|
+
* printable, non-space ASCII character, which no trim removes. Saying
|
|
398
|
+
* no proves nothing (a name in another script holds text too), so the
|
|
399
|
+
* rules then leave a value as stored;
|
|
400
|
+
* - storedDestinations: every destination key as stored, joined, for the
|
|
401
|
+
* searches that must find nothing in any of them;
|
|
402
|
+
* - firstStoredDestination: the first non-empty destination key as
|
|
403
|
+
* stored, lowercased as ClickHouse lowercases (ASCII only).
|
|
404
|
+
*/
|
|
405
|
+
function destinationTextAlias(position: number): string {
|
|
406
|
+
return `destinationText${position}`;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
const STORED_DESTINATIONS_ALIAS: string = "storedDestinations";
|
|
410
|
+
const FIRST_STORED_DESTINATION_ALIAS: string = "firstStoredDestination";
|
|
411
|
+
|
|
412
|
+
function spanAliasesSql(): string {
|
|
413
|
+
return [
|
|
414
|
+
...MESSAGING_DESTINATION_ATTRIBUTES.map(
|
|
415
|
+
(key: string, position: number): string => {
|
|
416
|
+
return `match(${storedAttributeSql(key)}, '[!-~]') AS ${destinationTextAlias(position)}`;
|
|
417
|
+
},
|
|
418
|
+
),
|
|
419
|
+
`concat(${MESSAGING_DESTINATION_ATTRIBUTES.map(storedAttributeSql).join(
|
|
420
|
+
", ",
|
|
421
|
+
)}) AS ${STORED_DESTINATIONS_ALIAS}`,
|
|
422
|
+
`lower(ifNull(coalesce(${MESSAGING_DESTINATION_ATTRIBUTES.map(
|
|
423
|
+
(key: string): string => {
|
|
424
|
+
return `nullIf(${storedAttributeSql(key)}, '')`;
|
|
425
|
+
},
|
|
426
|
+
).join(", ")}), '')) AS ${FIRST_STORED_DESTINATION_ALIAS}`,
|
|
427
|
+
].join(",\n ");
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Whether any of the first `count` destination keys certainly holds text.
|
|
431
|
+
function anyDestinationTextSql(count: number): string {
|
|
432
|
+
return `(${MESSAGING_DESTINATION_ATTRIBUTES.slice(0, count)
|
|
433
|
+
.map((_key: string, position: number): string => {
|
|
434
|
+
return destinationTextAlias(position);
|
|
435
|
+
})
|
|
436
|
+
.join(" OR ")})`;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/*
|
|
440
|
+
* The spans the resolver certainly names no queue for (rule 1):
|
|
441
|
+
*
|
|
442
|
+
* - a temporary or anonymous flag it reads as true — the stored text
|
|
443
|
+
* "true" in any case (the resolver trims and lowercases it); it checks
|
|
444
|
+
* the flags before it looks for a destination at all;
|
|
445
|
+
* - a NATS span (its `messaging.system` exactly a spelling the catalog
|
|
446
|
+
* maps to NATS) whose first non-empty destination key holds an inbox or
|
|
447
|
+
* acknowledgement subject: every key before it is empty and it starts
|
|
448
|
+
* with a character no trim removes, so it is the destination the
|
|
449
|
+
* resolver reads, and the resolver finds the generated subject in it.
|
|
450
|
+
*/
|
|
451
|
+
function certainlyNoQueueSql(): string {
|
|
452
|
+
const flags: Array<string> = MESSAGING_TEMPORARY_FLAG_ATTRIBUTES.map(
|
|
453
|
+
(key: string): string => {
|
|
454
|
+
return `lower(${storedAttributeSql(key)}) = 'true'`;
|
|
455
|
+
},
|
|
456
|
+
);
|
|
457
|
+
const generated: Array<string> = [
|
|
458
|
+
`${FIRST_STORED_DESTINATION_ALIAS} IN ${sqlTuple(
|
|
459
|
+
MESSAGE_QUEUE_NATS_GENERATED_SUBJECTS,
|
|
460
|
+
)}`,
|
|
461
|
+
...MESSAGE_QUEUE_NATS_GENERATED_SUBJECTS.map((subject: string): string => {
|
|
462
|
+
return `startsWith(${FIRST_STORED_DESTINATION_ALIAS}, ${sqlString(`${subject}.`)})`;
|
|
463
|
+
}),
|
|
464
|
+
];
|
|
465
|
+
return [
|
|
466
|
+
...flags,
|
|
467
|
+
`(lower(${storedAttributeSql(MESSAGING_SYSTEM_ATTRIBUTE)}) IN ${sqlTuple(
|
|
468
|
+
NATS_SPELLINGS,
|
|
469
|
+
)} AND (${generated.join(" OR ")}))`,
|
|
470
|
+
].join(" OR ");
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/*
|
|
474
|
+
* A destination key's column (rule 2): as stored, unless an earlier
|
|
475
|
+
* destination key certainly holds text — the resolver then takes the
|
|
476
|
+
* destination from that one, and reads this one only for whether it is
|
|
477
|
+
* blank — and this one certainly holds text too: then the placeholder,
|
|
478
|
+
* non-blank like it.
|
|
479
|
+
*/
|
|
480
|
+
function destinationColumnSql(key: string): string {
|
|
481
|
+
const position: number = MESSAGING_DESTINATION_ATTRIBUTES.indexOf(key);
|
|
482
|
+
const stored: string = storedAttributeSql(key);
|
|
483
|
+
if (position <= 0) {
|
|
484
|
+
return stored;
|
|
485
|
+
}
|
|
486
|
+
return `if(${anyDestinationTextSql(position)} AND ${destinationTextAlias(
|
|
487
|
+
position,
|
|
488
|
+
)}, ${sqlString(MESSAGE_QUEUE_DISCOVERY_SHADOWED_VALUE)}, ${stored})`;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/*
|
|
492
|
+
* A routing key's column (rule 3): blank where the resolver never reads it —
|
|
493
|
+
* some destination key certainly holds text (so the key is not the
|
|
494
|
+
* destination), none holds a ':' or ',' (so no joined or aio-pika name is
|
|
495
|
+
* split and compared against it) and none spells the default exchange
|
|
496
|
+
* (whose routing key IS the queue) — else as stored. Beside such a
|
|
497
|
+
* destination the resolver keeps the name as it is, whatever the key.
|
|
498
|
+
*/
|
|
499
|
+
function routingKeyColumnSql(key: string): string {
|
|
500
|
+
return `if(${anyDestinationTextSql(
|
|
501
|
+
MESSAGING_DESTINATION_ATTRIBUTES.length,
|
|
502
|
+
)} AND NOT multiSearchAny(${STORED_DESTINATIONS_ALIAS}, ${sqlArray([
|
|
503
|
+
":",
|
|
504
|
+
",",
|
|
505
|
+
])}) AND NOT multiSearchAnyCaseInsensitive(${STORED_DESTINATIONS_ALIAS}, ${sqlArray(
|
|
506
|
+
MESSAGE_QUEUE_RABBITMQ_DEFAULT_EXCHANGES,
|
|
507
|
+
)}), '', ${storedAttributeSql(key)})`;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// One span query column: folded by rule 2 or 3, or as stored.
|
|
511
|
+
function spanAttributeColumnSql(key: string): string {
|
|
512
|
+
if (MESSAGING_DESTINATION_ATTRIBUTES.includes(key)) {
|
|
513
|
+
return destinationColumnSql(key);
|
|
514
|
+
}
|
|
515
|
+
if (RABBITMQ_ROUTING_KEY_ATTRIBUTES.includes(key)) {
|
|
516
|
+
return routingKeyColumnSql(key);
|
|
517
|
+
}
|
|
518
|
+
return storedAttributeSql(key);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// `<column expression> AS a<i>` for every key the span resolver reads.
|
|
522
|
+
function spanAttributeColumnsSql(): string {
|
|
523
|
+
return MESSAGING_RESOLVER_INPUT_ATTRIBUTES.map(
|
|
524
|
+
(key: string, index: number): string => {
|
|
525
|
+
return `${spanAttributeColumnSql(key)} AS ${getMessagingDiscoveryColumn(index)}`;
|
|
526
|
+
},
|
|
527
|
+
).join(",\n ");
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/*
|
|
531
|
+
* ---- Which rows the cap keeps -------------------------------------------------
|
|
532
|
+
*
|
|
533
|
+
* A queue is several rows — one per span kind, consumer group, semconv
|
|
534
|
+
* generation or broker address, one per metric name — and a busy project
|
|
535
|
+
* has more rows than either cap. Cut busiest-first alone, the cap failed
|
|
536
|
+
* two ways: a busy source took every slot (a Kafka scrape's datapoints
|
|
537
|
+
* outnumber a CloudWatch queue's by a hundred to one, so a few hundred
|
|
538
|
+
* topics left no SQS queue a row), and rows that tie (every CloudWatch
|
|
539
|
+
* queue reports as often as the next) were cut in ClickHouse's hash order:
|
|
540
|
+
* the same queues, run after run. A queue left out is never created or
|
|
541
|
+
* sighted, and is archived a week later while its broker keeps reporting
|
|
542
|
+
* it.
|
|
543
|
+
*
|
|
544
|
+
* So the rows are ranked within their share — a metric name; for spans a
|
|
545
|
+
* `messaging.system` value — twice: busiest first, and in a rotating order
|
|
546
|
+
* (the row's grouped values hashed with the window's start, so it changes
|
|
547
|
+
* every run), which also breaks the busiest ranking's ties. They are then
|
|
548
|
+
* taken alternately from the two rankings, and from every share in turn:
|
|
549
|
+
* each share's busiest row, a rotating one, its next busiest, and so on. A
|
|
550
|
+
* project under the cap loses nothing; over it, every share gets its part
|
|
551
|
+
* (a small share all of its rows), its busiest groups are read every run,
|
|
552
|
+
* and the others by turns, a different sample every run (within the bound
|
|
553
|
+
* MESSAGE_QUEUE_RANKED_ROWS_PER_CAPPED_ROW sets).
|
|
554
|
+
*/
|
|
555
|
+
|
|
556
|
+
/*
|
|
557
|
+
* A row's place in this run's rotating order: its grouped values (`values`,
|
|
558
|
+
* SQL) hashed with the window's start.
|
|
559
|
+
*/
|
|
560
|
+
function rotationSql(startSql: string, values: string): string {
|
|
561
|
+
return `cityHash64(toUnixTimestamp64Milli(${startSql}), ${values}) AS rotation`;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* How many grouped rows, per row the cap keeps, are ranked for it: the
|
|
566
|
+
* busiest this many times the cap (ties in the rotating order). Ranking
|
|
567
|
+
* holds and sorts the rows it ranks in full, which is cheap for the rows of
|
|
568
|
+
* any real project but not for millions of per-message groups no rule folds:
|
|
569
|
+
* ranked in full, a million reply topics named per request took 1.49 GiB at
|
|
570
|
+
* 32 threads, and four million took the query past its memory limit at
|
|
571
|
+
* four. So the grouped rows reach the ranking through a top-N heap of this
|
|
572
|
+
* size (ORDER BY … LIMIT), with which those finished at 1.0 GiB and
|
|
573
|
+
* 1.25 GiB. That heap cuts by count alone, so a share is crowded out only
|
|
574
|
+
* where other shares hold more than this many times the cap of busier
|
|
575
|
+
* groups (100,000 for the cron's caps); its own memory is small beside the
|
|
576
|
+
* grouping's (one a fifth its size saved 10 MiB of the million groups'
|
|
577
|
+
* 1.0 GiB).
|
|
578
|
+
*/
|
|
579
|
+
export const MESSAGE_QUEUE_RANKED_ROWS_PER_CAPPED_ROW: number = 50;
|
|
580
|
+
|
|
581
|
+
function rotatingShareOrderSql(data: {
|
|
582
|
+
// The grouped rows, each with its `rotation` (rotationSql).
|
|
583
|
+
groupedSql: string;
|
|
584
|
+
// The columns the query returns, in order.
|
|
585
|
+
columns: string;
|
|
586
|
+
// What a share is, as SQL over the grouped columns.
|
|
587
|
+
shareSql: string;
|
|
588
|
+
// The grouped count the busiest ranking reads.
|
|
589
|
+
countColumn: string;
|
|
590
|
+
maxRows: number;
|
|
591
|
+
}): string {
|
|
592
|
+
const cap: number = rowCap(data.maxRows);
|
|
593
|
+
return `SELECT ${data.columns}
|
|
594
|
+
FROM (
|
|
595
|
+
SELECT
|
|
596
|
+
*,
|
|
597
|
+
row_number() OVER (PARTITION BY ${data.shareSql} ORDER BY ${data.countColumn} DESC, rotation) AS busiestRank,
|
|
598
|
+
row_number() OVER (PARTITION BY ${data.shareSql} ORDER BY rotation) AS rotationRank
|
|
599
|
+
FROM (${data.groupedSql}
|
|
600
|
+
ORDER BY ${data.countColumn} DESC, rotation
|
|
601
|
+
LIMIT ${cap * MESSAGE_QUEUE_RANKED_ROWS_PER_CAPPED_ROW}
|
|
602
|
+
)
|
|
603
|
+
)
|
|
604
|
+
ORDER BY least(2 * busiestRank - 1, 2 * rotationRank), ${data.countColumn} DESC, rotation
|
|
605
|
+
LIMIT ${cap}`;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// The span query's share: the stored `messaging.system` column.
|
|
609
|
+
const SPAN_SHARE_COLUMN: string = getMessagingDiscoveryColumn(
|
|
610
|
+
MESSAGING_RESOLVER_INPUT_ATTRIBUTES.indexOf(MESSAGING_SYSTEM_ATTRIBUTE),
|
|
611
|
+
);
|
|
612
|
+
|
|
613
|
+
/*
|
|
614
|
+
* ---- The queue queries' settings ---------------------------------------------
|
|
615
|
+
*
|
|
616
|
+
* The cron reads each queue query through the App's ClickHouse client, which
|
|
617
|
+
* gives up on a request that has sent it nothing for its request_timeout
|
|
618
|
+
* (ClickhouseConfig: 58 s, an idle-socket timer), and a grouped query in
|
|
619
|
+
* FORMAT JSON sends nothing until it is done. QUERY_SETTINGS' own limit is
|
|
620
|
+
* 60 s, so a query still reading at 58 s was cut off by the client
|
|
621
|
+
* ("Timeout error.") while the server read on. The queue queries therefore
|
|
622
|
+
* end on the server first, at MESSAGE_QUEUE_DISCOVERY_MAX_EXECUTION_SECONDS:
|
|
623
|
+
* a window too big to read by then fails with ClickHouse's own
|
|
624
|
+
* TIMEOUT_EXCEEDED, which the cron logs, and costs that run the project's
|
|
625
|
+
* span (or metric) evidence. On ClickHouse 26.7 the error followed the
|
|
626
|
+
* limit within 50 ms, mid-merge of a grouping spilled to disk too, well
|
|
627
|
+
* inside the client's wait.
|
|
628
|
+
*
|
|
629
|
+
* They 'throw' rather than 'break': a break returns no rows at all. On
|
|
630
|
+
* ClickHouse 26.7 a query broken off at its time limit answers with an
|
|
631
|
+
* empty body — not the groups of the part of the window it read, whatever
|
|
632
|
+
* the query's shape — which the client then fails to parse ("Unexpected end
|
|
633
|
+
* of JSON input"). The real-ClickHouse suite holds both behaviours.
|
|
634
|
+
*
|
|
635
|
+
* Nothing pins the scan's threads: it runs as wide as the server lets it
|
|
636
|
+
* (max_threads, the server's cores by default). Measured on ClickHouse 26.7
|
|
637
|
+
* with 32 cores, four threads — the Logs aggregations' pin — took 14.8 s
|
|
638
|
+
* over 10.8 million spans where 32 took 4.2 s, and ran out of time over
|
|
639
|
+
* 40.8 million, which 32 read in 24 s. The price is memory, which grows
|
|
640
|
+
* with the threads: about 25 MiB each over spans carrying about 35
|
|
641
|
+
* attributes (0.8 GiB at 32 threads, 1.55 GiB at 64, of QUERY_SETTINGS'
|
|
642
|
+
* 1.86 GiB limit), and with it the fewer groups that no rule folds can
|
|
643
|
+
* spill to disk and merge back within the limit (see "Values that vary per
|
|
644
|
+
* message").
|
|
645
|
+
*/
|
|
646
|
+
export const MESSAGE_QUEUE_DISCOVERY_MAX_EXECUTION_SECONDS: number = 45;
|
|
647
|
+
|
|
648
|
+
/*
|
|
649
|
+
* A SETTINGS clause (`SETTINGS a = 1, b = 'x'`) with `overrides` in place of
|
|
650
|
+
* the settings of the same name, or after them where it has none: each
|
|
651
|
+
* setting named once.
|
|
652
|
+
*/
|
|
653
|
+
function withSettings(
|
|
654
|
+
clause: string,
|
|
655
|
+
overrides: ReadonlyArray<[string, string]>,
|
|
656
|
+
): string {
|
|
657
|
+
const settings: Map<string, string> = new Map<string, string>();
|
|
658
|
+
for (const setting of clause
|
|
659
|
+
.trim()
|
|
660
|
+
.replace(/^SETTINGS\s+/, "")
|
|
661
|
+
.split(",")) {
|
|
662
|
+
const equals: number = setting.indexOf("=");
|
|
663
|
+
settings.set(
|
|
664
|
+
setting.slice(0, equals).trim(),
|
|
665
|
+
setting.slice(equals + 1).trim(),
|
|
666
|
+
);
|
|
667
|
+
}
|
|
668
|
+
for (const [name, value] of overrides) {
|
|
669
|
+
settings.set(name, value);
|
|
670
|
+
}
|
|
671
|
+
return `SETTINGS ${Array.from(settings.entries())
|
|
672
|
+
.map((setting: [string, string]): string => {
|
|
673
|
+
return `${setting[0]} = ${setting[1]}`;
|
|
674
|
+
})
|
|
675
|
+
.join(", ")}`;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
/**
|
|
679
|
+
* The SETTINGS clause every queue query ends with: QUERY_SETTINGS' memory
|
|
680
|
+
* limit and spills to disk, and a time limit of its own,
|
|
681
|
+
* MESSAGE_QUEUE_DISCOVERY_MAX_EXECUTION_SECONDS, at which it fails ('throw').
|
|
682
|
+
*/
|
|
683
|
+
export const MESSAGE_QUEUE_DISCOVERY_QUERY_SETTINGS: string = withSettings(
|
|
684
|
+
QUERY_SETTINGS,
|
|
685
|
+
[
|
|
686
|
+
[
|
|
687
|
+
"max_execution_time",
|
|
688
|
+
String(MESSAGE_QUEUE_DISCOVERY_MAX_EXECUTION_SECONDS),
|
|
689
|
+
],
|
|
690
|
+
["timeout_overflow_mode", "'throw'"],
|
|
691
|
+
],
|
|
692
|
+
);
|
|
693
|
+
|
|
694
|
+
export interface MessageQueueDiscoveryWindow {
|
|
695
|
+
projectId: string;
|
|
696
|
+
// ClickHouse DateTime64 expressions, e.g. toDateTime64('...', 9).
|
|
697
|
+
startSql: string;
|
|
698
|
+
endSql: string;
|
|
699
|
+
// Cap on the grouped rows returned per run.
|
|
700
|
+
maxRows: number;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
/**
|
|
704
|
+
* One row per (span kind, value of every attribute resolveMessagingSpan
|
|
705
|
+
* reads — folded where the resolver reads no more than a placeholder, see
|
|
706
|
+
* "Values that vary per message") over the window's messaging spans, with
|
|
707
|
+
* how many spans, how many of them failed and when the newest started;
|
|
708
|
+
* under the row cap, each broker's busiest and a rotating share of the rest
|
|
709
|
+
* (rotatingShareOrderSql). Spans are dropped on their kind (a stored SERVER
|
|
710
|
+
* span never names a queue; every other kind, a remapped or missing one
|
|
711
|
+
* included, may) and on `attributeKeys` (bloom-indexed, far smaller than
|
|
712
|
+
* the attribute map) before the map is read: a span carries a messaging
|
|
713
|
+
* trigger key, and one whose only trigger is an Azure provider key names
|
|
714
|
+
* Service Bus or Event Hubs there. Then the spans the resolver certainly
|
|
715
|
+
* names no queue for are dropped too (rule 1).
|
|
716
|
+
*/
|
|
717
|
+
export function buildMessagingSpanDiscoverySql(
|
|
718
|
+
window: MessageQueueDiscoveryWindow,
|
|
719
|
+
): string {
|
|
720
|
+
const columns: string = attributeColumnNames(
|
|
721
|
+
MESSAGING_RESOLVER_INPUT_ATTRIBUTES,
|
|
722
|
+
);
|
|
723
|
+
return `
|
|
724
|
+
/* ${MESSAGE_QUEUE_SPAN_SQL_MARKER} */
|
|
725
|
+
${rotatingShareOrderSql({
|
|
726
|
+
groupedSql: `
|
|
727
|
+
WITH
|
|
728
|
+
${spanAliasesSql()}
|
|
729
|
+
SELECT
|
|
730
|
+
kind,
|
|
731
|
+
${spanAttributeColumnsSql()},
|
|
732
|
+
count() AS spanCount,
|
|
733
|
+
countIf(statusCode = ${SpanStatus.Error}) AS errorCount,
|
|
734
|
+
toUnixTimestamp64Milli(max(startTime)) AS lastSeenUnixMs,
|
|
735
|
+
${rotationSql(window.startSql, `ifNull(kind, ''), ${columns}`)}
|
|
736
|
+
FROM ${SPAN_TABLE}
|
|
737
|
+
WHERE projectId = '${escapeSql(window.projectId)}'
|
|
738
|
+
AND startTime >= ${window.startSql}
|
|
739
|
+
AND startTime < ${window.endSql}
|
|
740
|
+
AND ${spanKindSql()}
|
|
741
|
+
AND hasAny(attributeKeys, ${sqlArray(MESSAGING_TRIGGER_ATTRIBUTES)})
|
|
742
|
+
AND (${spanTriggerSql()})
|
|
743
|
+
AND NOT (${certainlyNoQueueSql()})
|
|
744
|
+
GROUP BY kind, ${columns}`,
|
|
745
|
+
columns: `kind, ${columns}, spanCount, errorCount, lastSeenUnixMs`,
|
|
746
|
+
shareSql: SPAN_SHARE_COLUMN,
|
|
747
|
+
countColumn: "spanCount",
|
|
748
|
+
maxRows: window.maxRows,
|
|
749
|
+
})}
|
|
750
|
+
${MESSAGE_QUEUE_DISCOVERY_QUERY_SETTINGS}
|
|
751
|
+
`;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
/*
|
|
755
|
+
* The metric query's time predicate: the window, reaching
|
|
756
|
+
* MESSAGE_QUEUE_LATE_METRIC_MINUTES further back for the cloud-monitoring
|
|
757
|
+
* metrics alone. The widest bound is its own conjunct, so the partition key
|
|
758
|
+
* (toYYYYMMDD(time)) and the primary index can prune on it.
|
|
759
|
+
*/
|
|
760
|
+
function metricWindowSql(window: { startSql: string; endSql: string }): string {
|
|
761
|
+
return `time >= ${window.startSql} - INTERVAL ${MESSAGE_QUEUE_LATE_METRIC_MINUTES} MINUTE
|
|
762
|
+
AND time < ${window.endSql}
|
|
763
|
+
AND (time >= ${window.startSql} OR name IN ${sqlTuple(
|
|
764
|
+
MESSAGE_QUEUE_LATE_METRIC_NAMES,
|
|
765
|
+
)})`;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
/**
|
|
769
|
+
* One row per (metric name, value of every attribute
|
|
770
|
+
* resolveMessagingMetricDatapoint reads) over the window's curated broker
|
|
771
|
+
* and messaging client datapoints (MESSAGE_QUEUE_DISCOVERY_METRIC_NAMES),
|
|
772
|
+
* with how many datapoints and when the newest was taken; under the row
|
|
773
|
+
* cap, each metric's busiest and a rotating share of the rest
|
|
774
|
+
* (rotatingShareOrderSql), so a busy scrape never crowds out a cloud
|
|
775
|
+
* provider's metrics. The keys that only mark a curated metric's
|
|
776
|
+
* per-consumer repeat (MESSAGE_QUEUE_METRIC_EXCLUDED_SERIES_ATTRIBUTES:
|
|
777
|
+
* Pulsar's consumer name and id) are neither selected nor filtered on: they
|
|
778
|
+
* are per instance, so grouping on them would split a topic's rows per
|
|
779
|
+
* consumer, and a repeat folds into its subscription's own series of the
|
|
780
|
+
* same topic, which names the same queue.
|
|
781
|
+
*/
|
|
782
|
+
export function buildMessagingMetricDiscoverySql(
|
|
783
|
+
window: MessageQueueDiscoveryWindow,
|
|
784
|
+
): string {
|
|
785
|
+
const columns: string = attributeColumnNames(
|
|
786
|
+
MESSAGING_METRIC_RESOLVER_INPUT_ATTRIBUTES,
|
|
787
|
+
);
|
|
788
|
+
return `
|
|
789
|
+
/* ${MESSAGE_QUEUE_METRIC_SQL_MARKER} */
|
|
790
|
+
${rotatingShareOrderSql({
|
|
791
|
+
groupedSql: `
|
|
792
|
+
SELECT
|
|
793
|
+
name,
|
|
794
|
+
${attributeColumnsSql(MESSAGING_METRIC_RESOLVER_INPUT_ATTRIBUTES)},
|
|
795
|
+
count() AS pointCount,
|
|
796
|
+
toUnixTimestamp64Milli(max(time)) AS lastSeenUnixMs,
|
|
797
|
+
${rotationSql(window.startSql, `name, ${columns}`)}
|
|
798
|
+
FROM ${METRIC_TABLE}
|
|
799
|
+
WHERE projectId = '${escapeSql(window.projectId)}'
|
|
800
|
+
AND name IN ${sqlTuple(MESSAGE_QUEUE_DISCOVERY_METRIC_NAMES)}
|
|
801
|
+
AND ${metricWindowSql(window)}
|
|
802
|
+
GROUP BY name, ${columns}`,
|
|
803
|
+
columns: `name, ${columns}, pointCount, lastSeenUnixMs`,
|
|
804
|
+
shareSql: "name",
|
|
805
|
+
countColumn: "pointCount",
|
|
806
|
+
maxRows: window.maxRows,
|
|
807
|
+
})}
|
|
808
|
+
${MESSAGE_QUEUE_DISCOVERY_QUERY_SETTINGS}
|
|
809
|
+
`;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
/**
|
|
813
|
+
* The projects that sent a datapoint the metric query reads, in the window
|
|
814
|
+
* it reads them in. The cron's project list otherwise comes from spans and
|
|
815
|
+
* service graph metrics only, and a project whose only messaging telemetry
|
|
816
|
+
* is its brokers' metrics (a collector watching Kafka or SQS, no
|
|
817
|
+
* instrumented application) would never have its queues discovered.
|
|
818
|
+
*/
|
|
819
|
+
export function buildMessagingMetricProjectsSql(data: {
|
|
820
|
+
startSql: string;
|
|
821
|
+
endSql: string;
|
|
822
|
+
maxProjects: number;
|
|
823
|
+
}): string {
|
|
824
|
+
return `
|
|
825
|
+
/* ${MESSAGE_QUEUE_METRIC_PROJECTS_SQL_MARKER} */
|
|
826
|
+
SELECT DISTINCT projectId
|
|
827
|
+
FROM ${METRIC_TABLE}
|
|
828
|
+
WHERE name IN ${sqlTuple(MESSAGE_QUEUE_DISCOVERY_METRIC_NAMES)}
|
|
829
|
+
AND ${metricWindowSql(data)}
|
|
830
|
+
LIMIT ${rowCap(data.maxProjects)}
|
|
831
|
+
${MESSAGE_QUEUE_DISCOVERY_QUERY_SETTINGS}
|
|
832
|
+
`;
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
/*
|
|
836
|
+
* A span query row. ClickHouse serializes UInt64 / Int64 aggregates as JSON
|
|
837
|
+
* strings or numbers depending on its settings, so the counts and the time
|
|
838
|
+
* are Number()-coerced. The attribute columns (`a0` …, see
|
|
839
|
+
* getMessagingDiscoveryColumn) hold MESSAGING_RESOLVER_INPUT_ATTRIBUTES'
|
|
840
|
+
* values in list order, '' for a key the spans lack.
|
|
841
|
+
*/
|
|
842
|
+
export interface MessagingSpanDiscoveryRow {
|
|
843
|
+
[column: string]: unknown;
|
|
844
|
+
kind?: string | null | undefined;
|
|
845
|
+
spanCount?: string | number | undefined;
|
|
846
|
+
errorCount?: string | number | undefined;
|
|
847
|
+
lastSeenUnixMs?: string | number | undefined;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
/*
|
|
851
|
+
* A metric query row: the columns hold
|
|
852
|
+
* MESSAGING_METRIC_RESOLVER_INPUT_ATTRIBUTES' values in list order.
|
|
853
|
+
*/
|
|
854
|
+
export interface MessagingMetricDiscoveryRow {
|
|
855
|
+
[column: string]: unknown;
|
|
856
|
+
name?: string | null | undefined;
|
|
857
|
+
pointCount?: string | number | undefined;
|
|
858
|
+
lastSeenUnixMs?: string | number | undefined;
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
export type MessageQueueDirectionCounts = Record<MessagingDirection, number>;
|
|
862
|
+
|
|
863
|
+
/*
|
|
864
|
+
* What one kind of telemetry told discovery about a queue in the window.
|
|
865
|
+
*/
|
|
866
|
+
export interface MessageQueueEvidence {
|
|
867
|
+
// Spans or datapoints (0 when ClickHouse sent a count it could not read).
|
|
868
|
+
count: number;
|
|
869
|
+
// Of the spans, how many failed (statusCode Error); 0 for datapoints.
|
|
870
|
+
errorCount: number;
|
|
871
|
+
// The spans or datapoints per direction they were resolved to.
|
|
872
|
+
directions: MessageQueueDirectionCounts;
|
|
873
|
+
// The broker address the most of them named — display only — or null.
|
|
874
|
+
brokerAddress: string | null;
|
|
875
|
+
// When the newest of them was taken, or null when unknown.
|
|
876
|
+
lastSeenAt: Date | null;
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
export interface DiscoveredMessageQueue {
|
|
880
|
+
// Canonical and family-keyed (toMessageQueueIdentity).
|
|
881
|
+
identity: MessageQueueIdentity;
|
|
882
|
+
// buildMessageQueueIdentifier(identity): what the row is unique on.
|
|
883
|
+
identifier: string;
|
|
884
|
+
/*
|
|
885
|
+
* The most specific system the evidence named, within the identity's
|
|
886
|
+
* family (getMoreSpecificMessagingSystem): "activemq" when the JMX
|
|
887
|
+
* Scraper's metrics joined a JMS application's "jms" spans.
|
|
888
|
+
*/
|
|
889
|
+
system: string;
|
|
890
|
+
// The destination as the most evidence spelled it (original casing).
|
|
891
|
+
destination: string;
|
|
892
|
+
// Application spans: the creation minimum counts these.
|
|
893
|
+
spans: MessageQueueEvidence | null;
|
|
894
|
+
// Curated broker metrics: a single datapoint may create the queue.
|
|
895
|
+
brokerMetrics: MessageQueueEvidence | null;
|
|
896
|
+
/*
|
|
897
|
+
* Messaging client metrics: they sight a queue that exists, like the
|
|
898
|
+
* application's spans, but never create one.
|
|
899
|
+
*/
|
|
900
|
+
clientMetrics: MessageQueueEvidence | null;
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
type EvidenceKind = "spans" | "brokerMetrics" | "clientMetrics";
|
|
904
|
+
|
|
905
|
+
const EVIDENCE_KINDS: ReadonlyArray<EvidenceKind> = [
|
|
906
|
+
"spans",
|
|
907
|
+
"brokerMetrics",
|
|
908
|
+
"clientMetrics",
|
|
909
|
+
];
|
|
910
|
+
|
|
911
|
+
const DIRECTIONS: ReadonlyArray<MessagingDirection> = [
|
|
912
|
+
"publish",
|
|
913
|
+
"consume",
|
|
914
|
+
"settle",
|
|
915
|
+
"unknown",
|
|
916
|
+
];
|
|
917
|
+
|
|
918
|
+
interface EvidenceAccumulator {
|
|
919
|
+
count: number;
|
|
920
|
+
errorCount: number;
|
|
921
|
+
directions: MessageQueueDirectionCounts;
|
|
922
|
+
// broker address → how much evidence named it
|
|
923
|
+
addresses: Map<string, number>;
|
|
924
|
+
// Epoch milliseconds; 0 while unknown.
|
|
925
|
+
lastSeenMs: number;
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
interface QueueAccumulator {
|
|
929
|
+
identity: MessageQueueIdentity;
|
|
930
|
+
identifier: string;
|
|
931
|
+
// specific system → how much evidence named it
|
|
932
|
+
systems: Map<string, number>;
|
|
933
|
+
// display destination → how much evidence spelled it so
|
|
934
|
+
destinations: Map<string, number>;
|
|
935
|
+
evidence: Map<EvidenceKind, EvidenceAccumulator>;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
function toCount(value: unknown): number {
|
|
939
|
+
const parsed: number = Math.round(Number(value));
|
|
940
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
// Epoch milliseconds of a row's newest time; 0 when absent or unreadable.
|
|
944
|
+
function toEpochMs(value: unknown): number {
|
|
945
|
+
if (value === null || value === undefined || value === "") {
|
|
946
|
+
return 0;
|
|
947
|
+
}
|
|
948
|
+
const parsed: number = Number(value);
|
|
949
|
+
return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : 0;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
function newDirectionCounts(): MessageQueueDirectionCounts {
|
|
953
|
+
return { publish: 0, consume: 0, settle: 0, unknown: 0 };
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
function addWeight(
|
|
957
|
+
weights: Map<string, number>,
|
|
958
|
+
key: string,
|
|
959
|
+
weight: number,
|
|
960
|
+
): void {
|
|
961
|
+
weights.set(key, (weights.get(key) || 0) + weight);
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
// Heaviest first, then by value, so every choice below is order-independent.
|
|
965
|
+
function compareByWeightThenKey(
|
|
966
|
+
a: [string, number],
|
|
967
|
+
b: [string, number],
|
|
968
|
+
): number {
|
|
969
|
+
if (a[1] !== b[1]) {
|
|
970
|
+
return b[1] - a[1];
|
|
971
|
+
}
|
|
972
|
+
if (a[0] < b[0]) {
|
|
973
|
+
return -1;
|
|
974
|
+
}
|
|
975
|
+
return a[0] > b[0] ? 1 : 0;
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
function heaviest(weights: Map<string, number>): string | null {
|
|
979
|
+
const sorted: Array<[string, number]> = Array.from(weights.entries()).sort(
|
|
980
|
+
compareByWeightThenKey,
|
|
981
|
+
);
|
|
982
|
+
return sorted.length > 0 ? sorted[0]![0] : null;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
function queueFor(
|
|
986
|
+
queues: Map<string, QueueAccumulator>,
|
|
987
|
+
identity: MessageQueueIdentity,
|
|
988
|
+
identifier: string,
|
|
989
|
+
): QueueAccumulator {
|
|
990
|
+
let queue: QueueAccumulator | undefined = queues.get(identifier);
|
|
991
|
+
if (!queue) {
|
|
992
|
+
queue = {
|
|
993
|
+
identity: identity,
|
|
994
|
+
identifier: identifier,
|
|
995
|
+
systems: new Map<string, number>(),
|
|
996
|
+
destinations: new Map<string, number>(),
|
|
997
|
+
evidence: new Map<EvidenceKind, EvidenceAccumulator>(),
|
|
998
|
+
};
|
|
999
|
+
queues.set(identifier, queue);
|
|
1000
|
+
}
|
|
1001
|
+
return queue;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
function evidenceFor(
|
|
1005
|
+
queue: QueueAccumulator,
|
|
1006
|
+
kind: EvidenceKind,
|
|
1007
|
+
): EvidenceAccumulator {
|
|
1008
|
+
let evidence: EvidenceAccumulator | undefined = queue.evidence.get(kind);
|
|
1009
|
+
if (!evidence) {
|
|
1010
|
+
evidence = {
|
|
1011
|
+
count: 0,
|
|
1012
|
+
errorCount: 0,
|
|
1013
|
+
directions: newDirectionCounts(),
|
|
1014
|
+
addresses: new Map<string, number>(),
|
|
1015
|
+
lastSeenMs: 0,
|
|
1016
|
+
};
|
|
1017
|
+
queue.evidence.set(kind, evidence);
|
|
1018
|
+
}
|
|
1019
|
+
return evidence;
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
/*
|
|
1023
|
+
* One resolved row into its queue — keyed exactly as ingest keys the row
|
|
1024
|
+
* (MessagingEntityKeyResolver): the canonical identity of the resolution,
|
|
1025
|
+
* and only when that identity builds an identifier. Ingest stamps no key on
|
|
1026
|
+
* a row whose identifier cannot be built (lowercasing can push a
|
|
1027
|
+
* 255-character destination past the identifier's length), so no row may
|
|
1028
|
+
* be created for one either.
|
|
1029
|
+
*/
|
|
1030
|
+
function addResolved(
|
|
1031
|
+
queues: Map<string, QueueAccumulator>,
|
|
1032
|
+
kind: EvidenceKind,
|
|
1033
|
+
resolved: ResolvedMessagingDestination,
|
|
1034
|
+
observed: { count: number; errorCount: number; lastSeenMs: number },
|
|
1035
|
+
): void {
|
|
1036
|
+
const identity: MessageQueueIdentity | null = toMessageQueueIdentity({
|
|
1037
|
+
system: resolved.system,
|
|
1038
|
+
brokerScope: resolved.brokerScope,
|
|
1039
|
+
destination: resolved.destination,
|
|
1040
|
+
});
|
|
1041
|
+
const identifier: string | null = identity
|
|
1042
|
+
? buildMessageQueueIdentifier(identity)
|
|
1043
|
+
: null;
|
|
1044
|
+
if (!identity || !identifier) {
|
|
1045
|
+
return;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
const queue: QueueAccumulator = queueFor(queues, identity, identifier);
|
|
1049
|
+
addWeight(queue.systems, resolved.system, observed.count);
|
|
1050
|
+
addWeight(queue.destinations, resolved.destination, observed.count);
|
|
1051
|
+
|
|
1052
|
+
const evidence: EvidenceAccumulator = evidenceFor(queue, kind);
|
|
1053
|
+
evidence.count += observed.count;
|
|
1054
|
+
evidence.errorCount += observed.errorCount;
|
|
1055
|
+
if (DIRECTIONS.includes(resolved.direction)) {
|
|
1056
|
+
evidence.directions[resolved.direction] += observed.count;
|
|
1057
|
+
}
|
|
1058
|
+
if (resolved.brokerAddress) {
|
|
1059
|
+
addWeight(evidence.addresses, resolved.brokerAddress, observed.count);
|
|
1060
|
+
}
|
|
1061
|
+
evidence.lastSeenMs = Math.max(evidence.lastSeenMs, observed.lastSeenMs);
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
/*
|
|
1065
|
+
* The system a queue row should record: the evidence's systems folded with
|
|
1066
|
+
* getMoreSpecificMessagingSystem — the rule a row's own system is refined
|
|
1067
|
+
* by — busiest first, so a family member ("activemq") wins over the family
|
|
1068
|
+
* ("jms") whichever was busier, and the answer never depends on row order.
|
|
1069
|
+
*/
|
|
1070
|
+
function mostSpecificSystem(queue: QueueAccumulator): string {
|
|
1071
|
+
let system: string = "";
|
|
1072
|
+
for (const [candidate] of Array.from(queue.systems.entries()).sort(
|
|
1073
|
+
compareByWeightThenKey,
|
|
1074
|
+
)) {
|
|
1075
|
+
system = getMoreSpecificMessagingSystem(system, candidate) || system;
|
|
1076
|
+
}
|
|
1077
|
+
return system || queue.identity.system;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
function toEvidence(
|
|
1081
|
+
accumulator: EvidenceAccumulator | undefined,
|
|
1082
|
+
): MessageQueueEvidence | null {
|
|
1083
|
+
if (!accumulator) {
|
|
1084
|
+
return null;
|
|
1085
|
+
}
|
|
1086
|
+
return {
|
|
1087
|
+
count: accumulator.count,
|
|
1088
|
+
errorCount: accumulator.errorCount,
|
|
1089
|
+
directions: { ...accumulator.directions },
|
|
1090
|
+
brokerAddress: heaviest(accumulator.addresses),
|
|
1091
|
+
lastSeenAt:
|
|
1092
|
+
accumulator.lastSeenMs > 0 ? new Date(accumulator.lastSeenMs) : null,
|
|
1093
|
+
};
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
/**
|
|
1097
|
+
* All evidence behind a discovered queue: its spans and datapoints of every
|
|
1098
|
+
* kind, the order discovery works through queues in.
|
|
1099
|
+
*/
|
|
1100
|
+
export function getDiscoveredMessageQueueEvidenceCount(
|
|
1101
|
+
discovered: DiscoveredMessageQueue,
|
|
1102
|
+
): number {
|
|
1103
|
+
let count: number = 0;
|
|
1104
|
+
for (const kind of EVIDENCE_KINDS) {
|
|
1105
|
+
count += discovered[kind]?.count || 0;
|
|
1106
|
+
}
|
|
1107
|
+
return count;
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
// Busiest first, then by identifier, so the order is stable.
|
|
1111
|
+
function compareDiscovered(
|
|
1112
|
+
a: DiscoveredMessageQueue,
|
|
1113
|
+
b: DiscoveredMessageQueue,
|
|
1114
|
+
): number {
|
|
1115
|
+
const difference: number =
|
|
1116
|
+
getDiscoveredMessageQueueEvidenceCount(b) -
|
|
1117
|
+
getDiscoveredMessageQueueEvidenceCount(a);
|
|
1118
|
+
if (difference !== 0) {
|
|
1119
|
+
return difference;
|
|
1120
|
+
}
|
|
1121
|
+
if (a.identifier < b.identifier) {
|
|
1122
|
+
return -1;
|
|
1123
|
+
}
|
|
1124
|
+
return a.identifier > b.identifier ? 1 : 0;
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
function finish(
|
|
1128
|
+
queues: Map<string, QueueAccumulator>,
|
|
1129
|
+
): Array<DiscoveredMessageQueue> {
|
|
1130
|
+
const discovered: Array<DiscoveredMessageQueue> = [];
|
|
1131
|
+
for (const queue of queues.values()) {
|
|
1132
|
+
discovered.push({
|
|
1133
|
+
identity: queue.identity,
|
|
1134
|
+
identifier: queue.identifier,
|
|
1135
|
+
system: mostSpecificSystem(queue),
|
|
1136
|
+
destination: heaviest(queue.destinations) || queue.identity.destination,
|
|
1137
|
+
spans: toEvidence(queue.evidence.get("spans")),
|
|
1138
|
+
brokerMetrics: toEvidence(queue.evidence.get("brokerMetrics")),
|
|
1139
|
+
clientMetrics: toEvidence(queue.evidence.get("clientMetrics")),
|
|
1140
|
+
});
|
|
1141
|
+
}
|
|
1142
|
+
return discovered.sort(compareDiscovered);
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
// A getter over a query row's attribute columns: what the resolver reads.
|
|
1146
|
+
function rowAttributeGetter(
|
|
1147
|
+
row: Record<string, unknown>,
|
|
1148
|
+
columns: ReadonlyMap<string, string>,
|
|
1149
|
+
): AttributeGetter {
|
|
1150
|
+
return (key: string): unknown => {
|
|
1151
|
+
const column: string | undefined = columns.get(key);
|
|
1152
|
+
return column === undefined ? undefined : row[column];
|
|
1153
|
+
};
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
/**
|
|
1157
|
+
* Span query rows → the queues they name. Each row goes back through
|
|
1158
|
+
* resolveMessagingSpan — the resolver ingest ran on each of its spans — with
|
|
1159
|
+
* its kind and grouped attributes, so a SERVER span, a span with no
|
|
1160
|
+
* messaging trigger (or an Azure SDK span of another service) and a
|
|
1161
|
+
* temporary, generated, placeholder, scrubbed or overlong destination
|
|
1162
|
+
* resolve to nothing, exactly as they got no key at ingest. Rows that land
|
|
1163
|
+
* on one identity (the same queue named by producers and consumers, by
|
|
1164
|
+
* several semconv generations, by several Pulsar partitions or with several
|
|
1165
|
+
* UUIDs templated away) are merged: spans, failures and directions summed.
|
|
1166
|
+
* Busiest first.
|
|
1167
|
+
*/
|
|
1168
|
+
export function resolveMessagingSpanDiscoveryRows(
|
|
1169
|
+
rows: ReadonlyArray<MessagingSpanDiscoveryRow>,
|
|
1170
|
+
): Array<DiscoveredMessageQueue> {
|
|
1171
|
+
const queues: Map<string, QueueAccumulator> = new Map<
|
|
1172
|
+
string,
|
|
1173
|
+
QueueAccumulator
|
|
1174
|
+
>();
|
|
1175
|
+
|
|
1176
|
+
for (const row of Array.isArray(rows) ? rows : []) {
|
|
1177
|
+
if (!row || typeof row !== "object") {
|
|
1178
|
+
continue;
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
const resolved: ResolvedMessagingDestination | null = resolveMessagingSpan({
|
|
1182
|
+
getAttribute: rowAttributeGetter(row, SPAN_ATTRIBUTE_COLUMNS),
|
|
1183
|
+
kind: typeof row.kind === "string" ? row.kind : null,
|
|
1184
|
+
});
|
|
1185
|
+
|
|
1186
|
+
if (!resolved) {
|
|
1187
|
+
continue;
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
addResolved(queues, "spans", resolved, {
|
|
1191
|
+
count: toCount(row.spanCount),
|
|
1192
|
+
errorCount: toCount(row.errorCount),
|
|
1193
|
+
lastSeenMs: toEpochMs(row.lastSeenUnixMs),
|
|
1194
|
+
});
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
return finish(queues);
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
/**
|
|
1201
|
+
* Metric query rows → the queues they name. Each row goes back through
|
|
1202
|
+
* resolveMessagingMetricDatapoint — the resolver ingest ran on each of its
|
|
1203
|
+
* datapoints — with its metric name and grouped attributes. A curated
|
|
1204
|
+
* broker metric's datapoints are broker evidence (they may create a queue);
|
|
1205
|
+
* anything else the query reads, a messaging client metric, is client
|
|
1206
|
+
* evidence (it sights a queue that exists, never creates one). Rows that
|
|
1207
|
+
* land on one identity are merged, datapoints summed. Busiest first.
|
|
1208
|
+
*/
|
|
1209
|
+
export function resolveMessagingMetricDiscoveryRows(
|
|
1210
|
+
rows: ReadonlyArray<MessagingMetricDiscoveryRow>,
|
|
1211
|
+
): Array<DiscoveredMessageQueue> {
|
|
1212
|
+
const queues: Map<string, QueueAccumulator> = new Map<
|
|
1213
|
+
string,
|
|
1214
|
+
QueueAccumulator
|
|
1215
|
+
>();
|
|
1216
|
+
|
|
1217
|
+
for (const row of Array.isArray(rows) ? rows : []) {
|
|
1218
|
+
if (!row || typeof row !== "object") {
|
|
1219
|
+
continue;
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
const metricName: string = typeof row.name === "string" ? row.name : "";
|
|
1223
|
+
|
|
1224
|
+
const resolved: ResolvedMessagingDestination | null =
|
|
1225
|
+
resolveMessagingMetricDatapoint({
|
|
1226
|
+
metricName: metricName,
|
|
1227
|
+
getAttribute: rowAttributeGetter(row, METRIC_ATTRIBUTE_COLUMNS),
|
|
1228
|
+
});
|
|
1229
|
+
|
|
1230
|
+
if (!resolved) {
|
|
1231
|
+
continue;
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
addResolved(
|
|
1235
|
+
queues,
|
|
1236
|
+
MESSAGE_QUEUE_BROKER_METRIC_NAMES.has(metricName.trim().toLowerCase())
|
|
1237
|
+
? "brokerMetrics"
|
|
1238
|
+
: "clientMetrics",
|
|
1239
|
+
resolved,
|
|
1240
|
+
{
|
|
1241
|
+
count: toCount(row.pointCount),
|
|
1242
|
+
errorCount: 0,
|
|
1243
|
+
lastSeenMs: toEpochMs(row.lastSeenUnixMs),
|
|
1244
|
+
},
|
|
1245
|
+
);
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
return finish(queues);
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
/**
|
|
1252
|
+
* Discovered queues from several sources (the span rows', the metric rows')
|
|
1253
|
+
* merged into one entry per identity: every kind of evidence summed, the
|
|
1254
|
+
* newest time kept, the system folded to the most specific the sources
|
|
1255
|
+
* named, and the destination spelling and broker address the most evidence
|
|
1256
|
+
* carried. Busiest first.
|
|
1257
|
+
*/
|
|
1258
|
+
export function mergeDiscoveredMessageQueues(
|
|
1259
|
+
...lists: Array<ReadonlyArray<DiscoveredMessageQueue>>
|
|
1260
|
+
): Array<DiscoveredMessageQueue> {
|
|
1261
|
+
const queues: Map<string, QueueAccumulator> = new Map<
|
|
1262
|
+
string,
|
|
1263
|
+
QueueAccumulator
|
|
1264
|
+
>();
|
|
1265
|
+
|
|
1266
|
+
for (const list of lists) {
|
|
1267
|
+
for (const discovered of Array.isArray(list) ? list : []) {
|
|
1268
|
+
if (!discovered || !discovered.identifier || !discovered.identity) {
|
|
1269
|
+
continue;
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
const queue: QueueAccumulator = queueFor(
|
|
1273
|
+
queues,
|
|
1274
|
+
discovered.identity,
|
|
1275
|
+
discovered.identifier,
|
|
1276
|
+
);
|
|
1277
|
+
const weight: number = getDiscoveredMessageQueueEvidenceCount(discovered);
|
|
1278
|
+
addWeight(queue.systems, discovered.system, weight);
|
|
1279
|
+
addWeight(queue.destinations, discovered.destination, weight);
|
|
1280
|
+
|
|
1281
|
+
for (const kind of EVIDENCE_KINDS) {
|
|
1282
|
+
const source: MessageQueueEvidence | null = discovered[kind];
|
|
1283
|
+
if (!source) {
|
|
1284
|
+
continue;
|
|
1285
|
+
}
|
|
1286
|
+
const target: EvidenceAccumulator = evidenceFor(queue, kind);
|
|
1287
|
+
target.count += source.count;
|
|
1288
|
+
target.errorCount += source.errorCount;
|
|
1289
|
+
for (const direction of DIRECTIONS) {
|
|
1290
|
+
target.directions[direction] += source.directions[direction] || 0;
|
|
1291
|
+
}
|
|
1292
|
+
if (source.brokerAddress) {
|
|
1293
|
+
addWeight(target.addresses, source.brokerAddress, source.count);
|
|
1294
|
+
}
|
|
1295
|
+
target.lastSeenMs = Math.max(
|
|
1296
|
+
target.lastSeenMs,
|
|
1297
|
+
source.lastSeenAt ? source.lastSeenAt.getTime() : 0,
|
|
1298
|
+
);
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
return finish(queues);
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
/*
|
|
1307
|
+
* Spans a destination needs inside one window before traces may create a
|
|
1308
|
+
* queue for it on their own (env MESSAGE_QUEUE_MIN_SPANS, default 3, at
|
|
1309
|
+
* least 1). One stray span of a one-off script is not a queue worth
|
|
1310
|
+
* listing; a queue that exists is matched and sighted whatever the count.
|
|
1311
|
+
*/
|
|
1312
|
+
export function getMessageQueueMinSpans(): number {
|
|
1313
|
+
const raw: string | undefined = process.env[MESSAGE_QUEUE_MIN_SPANS_ENV];
|
|
1314
|
+
|
|
1315
|
+
if (raw === undefined || raw.trim() === "") {
|
|
1316
|
+
return DEFAULT_MESSAGE_QUEUE_MIN_SPANS;
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
const parsed: number = Number(raw.trim());
|
|
1320
|
+
|
|
1321
|
+
if (!Number.isFinite(parsed) || !Number.isInteger(parsed)) {
|
|
1322
|
+
return DEFAULT_MESSAGE_QUEUE_MIN_SPANS;
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
return Math.max(parsed, 1);
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
// Whether a queue's spans alone meet the minimum (never below 1 span).
|
|
1329
|
+
function spansMeetMinimum(
|
|
1330
|
+
discovered: DiscoveredMessageQueue,
|
|
1331
|
+
minSpans: number,
|
|
1332
|
+
): boolean {
|
|
1333
|
+
const minimum: number = Number.isFinite(minSpans)
|
|
1334
|
+
? Math.max(minSpans, 1)
|
|
1335
|
+
: DEFAULT_MESSAGE_QUEUE_MIN_SPANS;
|
|
1336
|
+
|
|
1337
|
+
return discovered.spans !== null && discovered.spans.count >= minimum;
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
/**
|
|
1341
|
+
* The create policy for a discovered queue that has no row yet, minus the
|
|
1342
|
+
* project budget (MessageQueueService applies it): at least `minSpans`
|
|
1343
|
+
* spans in the window, or any curated broker metric datapoint — the broker
|
|
1344
|
+
* reporting a queue is proof enough that it exists. Messaging client
|
|
1345
|
+
* metrics alone never create one: like an application's own gauge, they
|
|
1346
|
+
* attach to a queue, and sight one that exists.
|
|
1347
|
+
*/
|
|
1348
|
+
export function isMessageQueueAutoCreateCandidate(data: {
|
|
1349
|
+
discovered: DiscoveredMessageQueue;
|
|
1350
|
+
minSpans: number;
|
|
1351
|
+
}): boolean {
|
|
1352
|
+
return (
|
|
1353
|
+
data.discovered.brokerMetrics !== null ||
|
|
1354
|
+
spansMeetMinimum(data.discovered, data.minSpans)
|
|
1355
|
+
);
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
/**
|
|
1359
|
+
* What a row created for this queue records as its discovery source: the
|
|
1360
|
+
* application's traces when its spans meet the minimum, else the broker's
|
|
1361
|
+
* metrics when they reported it ("traces" for anything else, which the
|
|
1362
|
+
* create policy refuses anyway).
|
|
1363
|
+
*/
|
|
1364
|
+
export function getMessageQueueCreationSource(data: {
|
|
1365
|
+
discovered: DiscoveredMessageQueue;
|
|
1366
|
+
minSpans: number;
|
|
1367
|
+
}): "traces" | "broker-metrics" {
|
|
1368
|
+
if (
|
|
1369
|
+
!spansMeetMinimum(data.discovered, data.minSpans) &&
|
|
1370
|
+
data.discovered.brokerMetrics !== null
|
|
1371
|
+
) {
|
|
1372
|
+
return "broker-metrics";
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
return "traces";
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
/**
|
|
1379
|
+
* The broker address a queue's row shows (display only): the one the
|
|
1380
|
+
* application's own evidence named — its spans', else its messaging client
|
|
1381
|
+
* metrics' (both read `server.address` / `server.port`: the broker the
|
|
1382
|
+
* client connected to) — else the broker metrics' (the target a Prometheus
|
|
1383
|
+
* scrape stamps on the resource: the broker's metrics port, or for
|
|
1384
|
+
* scraped exporters the exporter itself, never what applications connect
|
|
1385
|
+
* to), else null.
|
|
1386
|
+
*
|
|
1387
|
+
* A row has ONE address column, and each sighting that carries an address
|
|
1388
|
+
* overwrites it: discovery gives a new row this address and passes it to
|
|
1389
|
+
* EVERY sighting of the run, never a sighting's own source's. Otherwise the
|
|
1390
|
+
* sighting that runs last — the broker's — would leave its scrape target on
|
|
1391
|
+
* the row on every run (its write gate reopens long before the next run).
|
|
1392
|
+
*/
|
|
1393
|
+
export function getDiscoveredMessageQueueBrokerAddress(
|
|
1394
|
+
discovered: DiscoveredMessageQueue,
|
|
1395
|
+
): string | null {
|
|
1396
|
+
return (
|
|
1397
|
+
discovered.spans?.brokerAddress ||
|
|
1398
|
+
discovered.clientMetrics?.brokerAddress ||
|
|
1399
|
+
discovered.brokerMetrics?.brokerAddress ||
|
|
1400
|
+
null
|
|
1401
|
+
);
|
|
1402
|
+
}
|