Repository navigation
Commit 323b654
Report audit instance environment, storage footprint and ingestion health in the usage report (#5965)
* Report audit instance environment data in the usage report
The audit instance serves host and storage facts on a new anonymous GET /api/environment endpoint, gathered from audit-local environment data providers. The primary polls every remote alongside the daily audit verification pass through the new IConfigurationApi.GetRemoteEnvironments, stores the per-instance dictionaries as AuditEnvironmentMetadata in the licensing stores, and the usage report emits them under the Audit.* prefix aggregated across instances: agreement passes through, processor count and memory take the maximum, and disagreement reports Mixed. Remotes that predate the endpoint are omitted rather than guessed.
* Detect whether audit instances share the primary's machine, database or server
Each instance hashes what identifies the machine it runs on and the storage it writes to: the OS machine id on Linux, the machine name elsewhere, NotApplicable in containers, and per engine the RavenDB server URL and database, SQL Server's ServerName and database, or PostgreSQL's cluster system identifier and database, plus the schema. The audit instance serves the hashes on the environment endpoint, the primary compares them with its own during collection, and only the classification reaches the report: Audit.SameMachine as True, False, Mixed, NotApplicable or Unknown, and Audit.DatabaseSharing as SameSchema, SameDatabase, SameServer, SeparateServer, NotApplicable or Unknown, aggregated to the most shared class across instances. A side that cannot be identified reports Unknown rather than guessing from configuration the two sides may spell differently.
* Report storage footprint, edition and service tier for the primary and audit instances
Storage.SizeGB, Storage.MessageCount and Storage.UnresolvedFailedMessages come from catalog statistics scoped to the configured schema on SQL Server and PostgreSQL and from database and collection statistics on RavenDB, never from counting rows. Storage.ServerEdition carries SQL Server's own edition designation and Storage.ServiceObjective the Azure SQL service objective, both NotApplicable elsewhere. The audit instance serves the same keys over the environment endpoint, and the report sums sizes and counts across audit instances with instances sharing a database counted once, keyed by the identity hashes from the sharing detection. A catalog that has no answer, such as a row estimate on a never analysed PostgreSQL table, reports Unknown rather than a guess.
* Collect hourly ingestion rates and busy time for the error and audit sides
Both ingestion pipelines keep running totals in memory: messages stored per completed batch, time spent processing batches, storage write time on the error side, and how far behind the endpoint's own timestamp each message arrived, bucketed at one, ten and sixty minutes. The audit instance serves its counters on the environment endpoint. A new hourly collector in the licensing component polls the primary's counters in process and every audit instance's over the API, folds the deltas into daily records in the licensing store with the audit side summed across instances, and keeps 400 days. A restarted process contributes its whole counters as the delta, a counter set seen for the first time only establishes a baseline, and a collector restart builds on what today's record already holds. The report emits per side the average daily messages, the busiest hour's messages and its busy share, plus storage milliseconds per message for the error side, from the days inside the report window.
Known gap, accepted in the plan: --error-ingestion-only workers keep their own counters and nothing polls them.
* Report health signals for the error and audit sides
Health.Error.FailedImports and Health.Error.RetentionBehindHours come from the primary's persister: failed import rows counted from the store, and how many hours the oldest failed message sits past the retention window on the EF persisters, with RavenDB omitting the retention key because expiry is engine driven. The audit instance serves its failed audit import count on the environment endpoint, summed across instances with shared databases counted once. Restarts are the process start time changes the hourly collector observes between polls, and the lag buckets it accumulates become Health.*.LagOver1MinPercent, LagOver10MinPercent and LagOver60MinPercent over the report window, emitted only when any message carried a usable timestamp.
* Replace hourly ingestion history collector with live counters served on the environment endpoint
Instead of polling ingestion counters hourly, accumulating deltas into daily records in the licensing store, and reading those records back into the report, each instance now computes ingestion and health summaries directly from its running totals at report time.
Both the audit and error sides expose their counters through IEnvironmentDataProvider implementations that call IngestionSummary.Describe, which yields keys like Ingestion.AvgDailyMessages, Ingestion.BusyPercent, Health.UptimeHours, and Health.LagOver*Percent relative to the process uptime. The audit instance serves these on the environment endpoint alongside its existing data; the primary aggregates them under the Audit.* prefix when building the report, summing daily message rates across instances and taking the worst saturation and lag values.
This removes IngestionHistoryCollectorHostedService, IngestionHistory and IngestionDay contracts, IErrorIngestionSnapshotProvider, GetRemoteIngestionCounters from IConfigurationApi, and GetIngestionHistory/SaveIngestionHistory from ILicensingDataStore and both persistence backends.
* Report database hosting, footprint and identity for EF Core audit persisters
SQL Server and PostgreSQL audit persistence now probe how their database is hosted, how much it stores, and what identifies it, the same way the primary instance's EF persisters already did, so the report gets real Storage.* data for audit instances on these backends instead of nothing. SQL Server's edition and service objective are normalised to a small fixed set of categories instead of raw server strings, and both EF persisters resolve the configured schema the same way when none is set.
AuditEnvironmentDataAggregator now passes through only an explicit whitelist of known keys, so a key served by an audit instance of another version can never reach the report unreviewed. ThroughputCollector copies broker metadata into a new dictionary before it reaches the report, so the stored metadata is never mutated. The environment endpoint's machine hash field is renamed from machine_id_hash to machine_name_hash to match what it actually hashes, and RavenDB's storage identity now treats a loopback address as this machine rather than a shared one.
* Fix flaky full-text index size assertion in SQL Server audit footprint test
WaitForFullTextIndex is now exposed as a public method with a default cancellation token so the environment data test can reuse the same wait the search tests rely on, instead of racing the index population. The test now waits for the full-text index to catch up before probing storage footprint, and asserts the footprint is only greater than the tables size rather than an exact sum, since the full-text index size can still drift slightly after the wait. The duplicated inline SQL for computing table and full-text sizes is consolidated into a single SizeGB helper.
* Retry SQL Server storage footprint probe on deadlock or lost scan position
Catalog reads under READ COMMITTED can deadlock with concurrent DDL, so the probe now runs under READ UNCOMMITTED instead. That isolation level can let a scan lose its place when pages move, so both error codes are now retried up to three times with a short backoff before the probe gives up and returns null. Applied to both the primary and audit EF Core SQL Server persisters, and the audit environment data test's inline SQL is updated to match the new isolation level.
* Add tests for mixed and summed database values in throughput report
* Add logging to ConfigurationApi for environment data retrieval errors
---------
Co-authored-by: Warwick Schroeder <warwick.schroeder@particular.net>1 parent ab53db9 commit 323b654
95 files changed
Lines changed: 3972 additions & 32 deletions
File tree
- src
- Particular.LicensingComponent.Contracts
- Particular.LicensingComponent.Persistence.InMemory
- Particular.LicensingComponent.Persistence
- Particular.LicensingComponent.UnitTests
- Infrastructure
- ThroughputCollector
- Particular.LicensingComponent
- AuditThroughput
- ServiceControl.AcceptanceTests/Licensing
- ServiceControl.Api
- Contracts
- ServiceControl.Audit.AcceptanceTests
- ServiceControl.Audit.Persistence.EFCore.PostgreSql
- ServiceControl.Audit.Persistence.EFCore.SqlServer
- ServiceControl.Audit.Persistence.EFCore
- Abstractions
- Implementation
- Infrastructure
- ServiceControl.Audit.Persistence.RavenDB
- ServiceControl.Audit.Persistence.Tests.PostgreSql
- ServiceControl.Audit.Persistence.Tests.RavenDB
- ServiceControl.Audit.Persistence.Tests.SqlServer
- ServiceControl.Audit.Persistence.Tests/EFCore
- ServiceControl.Audit.Persistence/EnvironmentData
- ServiceControl.Audit.UnitTests
- ApprovalFiles
- Auditing/Metrics
- Infrastructure
- ServiceControl.Audit
- Auditing/Metrics
- Infrastructure/WebApi
- ServiceControl.Infrastructure
- Ingestion
- Metrics
- ServiceControl.Persistence.EFCore.PostgreSql
- ServiceControl.Persistence.EFCore.SqlServer
- ServiceControl.Persistence.EFCore
- Implementation
- Infrastructure
- ServiceControl.Persistence.RavenDB
- Throughput
- ServiceControl.Persistence.Tests.PostgreSql
- ServiceControl.Persistence.Tests.RavenDB
- ServiceControl.Persistence.Tests.SqlServer
- ServiceControl.Persistence.Tests/EFCore
- ServiceControl.Persistence
- ServiceControl.UnitTests
- API
- Infrastructure
- Licensing
- Operations/Metrics
- ServiceControl
- Infrastructure/Api
- Operations/Metrics
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
| 17 | + | |
17 | 18 | | |
18 | 19 | | |
19 | 20 | | |
| |||
168 | 169 | | |
169 | 170 | | |
170 | 171 | | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
171 | 180 | | |
172 | 181 | | |
173 | 182 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
36 | 36 | | |
37 | 37 | | |
38 | 38 | | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
39 | 43 | | |
40 | 44 | | |
41 | 45 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
228 | 228 | | |
229 | 229 | | |
230 | 230 | | |
| 231 | + | |
| 232 | + | |
231 | 233 | | |
232 | 234 | | |
233 | 235 | | |
| |||
237 | 239 | | |
238 | 240 | | |
239 | 241 | | |
| 242 | + | |
| 243 | + | |
240 | 244 | | |
241 | 245 | | |
242 | 246 | | |
| |||
257 | 261 | | |
258 | 262 | | |
259 | 263 | | |
| 264 | + | |
| 265 | + | |
260 | 266 | | |
261 | 267 | | |
262 | 268 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
272 | 272 | | |
273 | 273 | | |
274 | 274 | | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
275 | 302 | | |
276 | 303 | | |
277 | 304 | | |
278 | 305 | | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
279 | 309 | | |
280 | 310 | | |
281 | 311 | | |
| |||
309 | 339 | | |
310 | 340 | | |
311 | 341 | | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
312 | 345 | | |
313 | 346 | | |
314 | 347 | | |
| |||
341 | 374 | | |
342 | 375 | | |
343 | 376 | | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
344 | 380 | | |
345 | 381 | | |
346 | 382 | | |
| |||
420 | 456 | | |
421 | 457 | | |
422 | 458 | | |
| 459 | + | |
| 460 | + | |
| 461 | + | |
| 462 | + | |
423 | 463 | | |
424 | 464 | | |
425 | 465 | | |
| |||
456 | 496 | | |
457 | 497 | | |
458 | 498 | | |
| 499 | + | |
| 500 | + | |
| 501 | + | |
| 502 | + | |
| 503 | + | |
| 504 | + | |
459 | 505 | | |
460 | 506 | | |
461 | 507 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
| 13 | + | |
13 | 14 | | |
14 | 15 | | |
15 | 16 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
252 | 252 | | |
253 | 253 | | |
254 | 254 | | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
255 | 261 | | |
256 | 262 | | |
257 | 263 | | |
| |||
0 commit comments