diff --git a/docs/backend/telemetry-ingest-shaping.md b/docs/backend/telemetry-ingest-shaping.md index 65b026c0..658f2f48 100644 --- a/docs/backend/telemetry-ingest-shaping.md +++ b/docs/backend/telemetry-ingest-shaping.md @@ -36,7 +36,9 @@ Why two mechanisms: * successful Azure Storage calls (Table/Queue/Blob) in both shapes — the HTTP shape (`Azure table`, target `*.table.core.windows.net`) and the Azure SDK ActivitySource shape (`InProc | Microsoft.Tables`, `InProc | Microsoft.Storage`); * storage calls with an **expected outcome**: `404` (point-read miss — EventTypeIndex, BlockedDevices lookups), `412` (ETag precondition), `409` (idempotent insert conflict). HTTP-shape rows carry the code in `ResultCode`; InProc rows only in `Properties["Error"]` (`Status: 404 (`). Before this filter these were 100 % of the remaining "failed" storage rows (~540 MB/week, two rows per call); -* successful Azure SignalR Service REST calls (`*.service.signalr.net` — group add/remove per connection, ~470 MB/week). +* successful Azure SignalR Service REST calls the worker makes itself (`*.service.signalr.net`). + +The bulk of SignalR REST traffic (group add/remove per connection, ~470 MB/week) is emitted by the **host** process for the SignalR output binding (`SDKVersion rdddsc:*`) and never passes the worker pipeline — live-verified after the 2026-08-23 deploy (worker rows: 0, host rows: ~560/h). Those are removed by the second data flow of the same workspace transformation: `AppDependencies | where not(Success == true and Target endswith '.service.signalr.net')`. Everything else passes: storage 429/5xx/auth/timeouts, failed SignalR calls, all HTTP/Graph/NVD/MSRC dependencies, the worker's own `InProc Invoke` span, and every non-dependency item. diff --git a/docs/log.md b/docs/log.md index 9642c25a..f01d15b5 100644 --- a/docs/log.md +++ b/docs/log.md @@ -2,7 +2,7 @@ ## 2026-08-23 -* **New**: `backend/telemetry-ingest-shaping.md` — `RequestTelemetryMiddleware` sets `SamplingPercentage=100` on its own item (worker request rows were adaptively sampled, ItemCount 2–5 on weekdays, despite host.json `excludedTypes`), the redundant host HTTP request copy is dropped by a workspace transformation DCR (`infra/appinsights-workspace-transforms.bicep`, timer/queue rows kept), and `StorageDependencyFilterProcessor` now also drops expected storage outcomes (404/412/409, both InProc and HTTP shapes) and successful SignalR REST calls. Measured ≈ −1.3 GB/week. +* **New**: `backend/telemetry-ingest-shaping.md` — `RequestTelemetryMiddleware` sets `SamplingPercentage=100` on its own item (worker request rows were adaptively sampled, ItemCount 2–5 on weekdays, despite host.json `excludedTypes`), the redundant host HTTP request copy is dropped by a workspace transformation DCR (`infra/appinsights-workspace-transforms.bicep`, timer/queue rows kept), and `StorageDependencyFilterProcessor` now also drops expected storage outcomes (404/412/409, both InProc and HTTP shapes) and successful SignalR REST calls; the host-emitted SignalR binding calls (bulk of the volume, SDK `rdddsc`) are dropped by a second `AppDependencies` flow in the same DCR. Measured ≈ −1.3 GB/week. * **Update**: `backend/table-schema-sentinel.md` — "Measuring it": `StartupTelemetryService` emits `BackendStartupMs` (process start → ApplicationStarted) and `BackendTableInitMs` as App Insights custom metrics with version/commit/fullPass dimensions; the MCP server writes one `{"type":"boot","readyMs"}` stderr line per start (query in `.claude/commands/mcp-telemetry.md` §A2). * **New**: `backend/table-schema-sentinel.md` — startup table initialization gated by a schema sentinel (SHA-256 over `TableNames.All` in `AdminConfiguration`/`SchemaSentinel`/`tables`): matching sentinel ⇒ zero `CreateTableIfNotExists` calls on a cold start, missing/stale ⇒ one full pass + sentinel rewrite. SessionsIndex startup backfill only after a full pass, `IsSessionIndexEmptyAsync` no longer treats errors as "empty", conditional-insert claim so one scaled-out instance backfills. Daily maintenance runs `IStorageInitializer.EnsureAllAsync` to repair out-of-band table deletions. * **Update**: `backend/verdict-calibration.md` — MCP `get_verdict_calibration` response shaping: per-row trend denominators dropped, explicit nulls, `minSharePct` / `top` with an `omitted` report. diff --git a/infra/appinsights-workspace-transforms.bicep b/infra/appinsights-workspace-transforms.bicep index 2a1e311f..0a95f539 100644 --- a/infra/appinsights-workspace-transforms.bicep +++ b/infra/appinsights-workspace-transforms.bicep @@ -1,6 +1,9 @@ // Autopilot-Monitor — Log Analytics workspace transformation (ingestion-time filter) // -// Drops the redundant HOST-side HTTP request record from AppRequests so that exactly one +// Two ingestion-time filters. (2) Successful Azure SignalR REST dependencies emitted by the +// HOST process for the SignalR output binding (SDKVersion rdddsc:*) — the worker-side +// StorageDependencyFilterProcessor never sees those. Failed SignalR calls are kept. +// (1) Drops the redundant HOST-side HTTP request record from AppRequests so that exactly one // canonical request row remains: the worker-side copy emitted by RequestTelemetryMiddleware // (Properties.Source == 'WorkerMiddleware', carries TenantId/UserId/CorrelationId and is // sampling-exempt per item). Non-HTTP host invocations (timer/queue triggers) have an empty @@ -57,6 +60,14 @@ resource transforms 'Microsoft.Insights/dataCollectionRules@2023-03-11' = { destinations: [ 'ws' ] transformKql: 'source | where tostring(Properties.Source) == \'WorkerMiddleware\' or isempty(Url)' } + { + // Successful Azure SignalR REST calls (group add/remove per connection) are emitted by + // the HOST process for the SignalR output binding (SDKVersion rdddsc:*), so the worker's + // StorageDependencyFilterProcessor never sees them. Failed SignalR calls are kept. + streams: [ 'Microsoft-Table-AppDependencies' ] + destinations: [ 'ws' ] + transformKql: 'source | where not(Success == true and Target endswith \'.service.signalr.net\')' + } ] } }