Make dotnet-eng-status deployment notifications best-effort (fix intermittent 500)#6643
Make dotnet-eng-status deployment notifications best-effort (fix intermittent 500)#6643missymessa wants to merge 6 commits into
Conversation
The DeploymentController start/end endpoints are best-effort telemetry (Grafana annotation + table upsert). Any failure was retried on every exception and then rethrown, surfacing as a 500 to the calling deploy pipeline. Narrow retries to transient failures and swallow errors. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: b47fce22-0f00-4880-8509-85c2b30187ee
what's the deployment annotation used for? where does it surface?
this seems good
isn't this just masking the failure? if we're just going to do this, then it sounds like the notifications aren't needed. can we just remove them? was this for rollout scorecard or something else? |
|
There was a transient error where one will have to go delete a file in Azure portal for this service to work. I think Haruna and Meghna did that a few times. It would be nice if this could cover that case too |
…zure Table record Per review feedback (garath/chcosta): remove the fragile direct writes to the Grafana annotations API from MarkStart/MarkEnd and rely solely on the Azure Table store, which Grafana reads from directly. This eliminates the Grafana HTTP dependency (and its ApiToken/retry path) that was the source of the intermittent 500s, so the best-effort endpoints simply record start/end to table storage. Also simplifies the failure log messages. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 26b50f05-b732-427b-a818-8e6d58638b47
|
Good questions — and the review consensus (thanks @garath) actually resolves the masking concern by changing the approach: What the annotation is / where it surfaces: the deployment annotation is a Grafana dashboard annotation — a vertical marker on the time-series dashboards showing when each service was deployed (start→end span, tagged On "isn't this just masking the failure?": you're right that swallowing a failing Grafana push would just hide a broken notification. So instead of masking it, this revision removes the fragile direct call to the Grafana annotations API entirely. So we keep the useful signal (the durable table record that Grafana renders), and the endpoint no longer has a flaky external call to mask. The best-effort try/catch now only guards the Azure Table write (managed-identity, SDK-retried), so a genuine storage outage still won't fail the deploy pipeline, but there's no longer a Grafana-push failure being hidden. Pushed in b4f7c83. |
|
Thanks @epananth — that "have to go delete a file in the Azure portal to get the service working again" case is the leftover Root cause there: the So that failure mode is covered by #6580; this PR (#6643) is the separate fix for the intermittent 500s from the deployment-notification endpoint. Let me know if you've seen the app_offline.htm situation recur after #6580 shipped — if so I'll reopen that thread. |
…ntTableOptions Per review feedback (garath): the DeploymentController no longer talks to Grafana, so binding it to GrafanaOptions was inconsistent. Introduce a dedicated DeploymentTableOptions (TableUri/TableName) bound to a new "Deployments" config section (matching the existing AzureTableTokenStoreOptions pattern) and use it in both DeploymentController and AnnotationsController, which read/write the same deployments annotation table. GrafanaOptions is slimmed to just WebhookSecret (still used by AlertHookController); the now-dead BaseUrl/ApiToken fields (only used by the removed Grafana annotations HTTP calls) and their settings.json entries are removed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9df4c7ab-7f6e-4f13-a16c-3b01a0449ca2
garath
left a comment
There was a problem hiding this comment.
Oh, sorry for swapping the review status. I just noticed that removing grafana-api-token in the config also removes its need to be in the secret-manager manifest. That's a manually-managed secret so removing it is a big win for overhead.
These secrets are no longer needed:
|
The only runtime consumer of the grafana-api-token secret (minted by this type) is DotNet.Status.Web, which is being migrated off Grafana in dotnet#6643. Remove the secret type and its Readme entry. Note: the 3 dotneteng-status .vault-config manifests still declare a grafana-api-token secret of this type; those entries are being removed separately. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: f4dd4f39-9896-49e1-a690-c9930af65ebd
The DotNet.Status.Web deployment-notification endpoints no longer call the Grafana annotations HTTP API (they write to the Azure Table that Grafana reads from), so the manually-managed grafana-api-token secret is no longer needed in the dotneteng-status local/prod/staging manifests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 66a3f42d-8d83-467c-bed6-651841b22ee8
|
Done in 189ac85 — removed the
Confirmed it's safe: that secret was only consumed by the DotNet.Status.Web Grafana-annotations HTTP call (now removed). The dashboard-deploy pipeline ( |
Problem
The Helix deploy pipelines (
dotnet-helix-service-deploy,dotnet-helix-machinesdeploy) intermittently fail their "Notify dotnet-eng-status/start" step with an HTTP 500, e.g. build 3024453:The 500 comes from
DeploymentController.MarkStartin the DotNetStatus (dotnet-eng-status) app, not from Helix. Recording a deployment annotation is best-effort telemetry, but the controller had two problems that let any hiccup become a 500 that shows up as a red step in the deploy pipeline:ExponentialRetry.RetryAsync(..., isRetryable: e => true), which retries every exception (including non-transient 4xx fromEnsureSuccessStatusCode()) up to 10 times and then rethrows.Fix
IsTransientFailurehelper: retry only network errors, timeouts, and 5xx responses (HttpRequestExceptionwithStatusCodenull or >= 500, orTaskCanceled/OperationCanceled). Non-transient failures now fail fast instead of being retried 10× and rethrown.MarkStartandMarkEndbodies in try/catch that logs the error and returnsNoContent(). Best-effort deployment notifications never fail the calling pipeline again.MarkEndnow logs and returnsNoContent()instead ofNotFound()when no matching start record exists (a missing/failed start notification should not turn the end notification into a 404).Notes
Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com