test(longhaul): add kill-operator-pod and kill-primary-pod chaos operations#421
Open
WentingWu666666 wants to merge 6 commits into
Open
Conversation
Add the first two chaos fault-injection operations to the long-haul operation scheduler: - kill-operator-pod: deletes the operator pod via its Deployment selector and waits for the Deployment to become Available again. Asserts the CNPG data plane is unaffected by an operator restart (small write-failure budget). - kill-primary-pod: deletes the CNPG primary pod to exercise the automatic failover path, guarded by an HA precondition (instancesPerNode>=2). Both plug into the existing Operation interface and are selected by the weighted scheduler (one disruptive op at a time, cooldown + steady-state gate). Recovery is judged by the health monitor and workload verifier. Adds ClusterClient.GetPrimaryInstance (reads CNPG Cluster status.currentPrimary) and DeletePod, a LONGHAUL_OPERATOR_NAMESPACE config knob (default documentdb-operator), unit tests, and README operations + RBAC notes. Controlled failover was intentionally excluded: DocumentDB exposes no single-cluster manual switchover, so it would only test upstream CNPG. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Wenting Wu <wentingwu@microsoft.com>
Introduce journal.NoOutagePolicy (and the NoOutageWriteFailureCushion constant) as the single budget for operations that keep the write path (client -> gateway -> primary) up throughout, so they must not cause write failures. Apply it to: - kill-operator-pod (control-plane fault; was an inline budget of 5), and - scale-up / scale-down, which only add/remove a standby replica — the primary is never touched, so their previous ad-hoc budgets (20 / 50) were too lenient and could mask a regression that disrupted writes. The cushion is a small non-zero value (5) that absorbs unrelated background noise without tolerating a real outage: at the default workload rate (~50 writes/s) it is well under a second of stray errors. Centralizing it lets the value be recalibrated against real long-haul runs in one place. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Wenting Wu <wentingwu@microsoft.com>
Express OutagePolicy as a wall-clock write-outage duration (MaxWriteOutage) instead of a raw write-failure count. The journal converts the observed failure count into an estimated outage using the workload's aggregate write rate, so budgets no longer scale with LONGHAUL_NUM_WRITERS. - policy: OutagePolicy.AllowedWriteFailures -> MaxWriteOutage; DisruptionWindow gains WritesPerSecond + EstimatedWriteOutage(); NoOutageWriteFailureCushion -> NoOutageWriteOutageCushion (300ms). - journal: New() defaults to DefaultWritesPerSecond; SetWriteRate lets main.go supply the real rate; OpenDisruptionWindow stamps the rate. - workload: expose AggregateWriteRate(numWriters). - ops: kill-primary 30s, upgrade 45s (was 50/200 failures). - report: surface Est. Write Outage column. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Wenting Wu <wentingwu@microsoft.com>
|
🤖 Auto-triaged by documentdb-triage-tool. Applied: Reasoningcomponent from path globs (test, docs, dependencies); effort from diff stats (746+78 LOC, 23 files); LLM: Adds new chaos fault-injection operations (kill-operator-pod, kill-primary-pod) to the long-haul test suite, touching multiple files across the test infrastructure including health monitoring, k8s client, config, and operation implementations. If a label is wrong, remove it manually and ping |
Document that MaxWriteOutage (data plane) and MustRecoverWithin (control plane) assert on orthogonal subsystems and fail independently, and that MustRecoverWithin is the only path that fails the run when the cluster never converges (op errors are logged, not scored). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Wenting Wu <wentingwu@microsoft.com>
MustRecoverWithin bounds the managed database cluster's return to full topology (all pods Ready, CR Ready) — not the operator/control plane. Reframe as write-availability vs. full-topology recovery to avoid implying scale/kill-primary recovery involves the operator. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Wenting Wu <wentingwu@microsoft.com>
kill-primary-pod and upgrade-documentdb both interrupt writes for a single primary handover, so they now share journal.PrimaryHandoverPolicy (30s) instead of diverging (was 30s vs 45s). The 45s was inflated by conflating the rolling upgrade's whole-topology restart with its write-outage window; that longer restart is bounded by MustRecoverWithin instead. A graceful switchover (upgrade) is no worse than an ungraceful failover (kill-primary), which also pays a detection delay. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Wenting Wu <wentingwu@microsoft.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Long-haul chaos operations (kill-operator-pod + kill-primary-pod)
Adds the first two Chaos / HA operations from the long-haul design (
docs/designs/long-haul-test-design.md→ Operations) to the driver introduced in #405, and reworks the outage-budget model they depend on. Scope: fault-inject the control plane and the database primary on the canary cluster, and assert — over a multi-day run — that the data plane keeps serving and the cluster reconverges to its declared topology.What this PR adds
Two new operations registered with the scheduler:
kill-operator-podDeploymentreturns toAvailable.kill-primary-podinstancesPerNode>=2).The continuous workload verifier independently catches any data loss caused by a failover.
Outage policy: duration-based, writer-count-independent
The chaos ops needed a principled disruption budget, so
OutagePolicymoved from a raw write-failure count to a wall-clock write-outage duration:OutagePolicy.MaxWriteOutage time.DurationreplacesAllowedWriteFailures int64. The journal converts the observed failure count into an estimated outage using the workload's aggregate write rate (WriteFailures / WritesPerSecond), so budgets no longer scale withLONGHAUL_NUM_WRITERS. The rate is computed once at startup fromNumWritersand the fixed writer cadence (workload.AggregateWriteRate) and stamped onto each disruption window.ExceededPolicytrips if either is exceeded):MaxWriteOutage— client-visible write availability (data plane).MustRecoverWithin— the managed cluster's return to full declared topology (all pods Ready, CR Ready). Because a failed op is only logged (not scored), this is the sole mechanism that fails the run when the cluster never reconverges — e.g. a failover where writes resume fast but a replacement standby never rejoins.journal.NoOutagePolicy(~300ms cushion) for operations that keep the write path up throughout:kill-operator-podand the pre-existing scale ops (which only add/remove a standby). One fully-failed write tick maps to ~100ms of estimated outage regardless of writer count, so the cushion absorbs unrelated noise without tolerating a real outage.journal.PrimaryHandoverPolicy(30s) shared bykill-primary-podandupgrade-documentdb: both interrupt writes for exactly one primary handover (an ungraceful failover vs. a graceful switchover). A rolling upgrade's longer whole-topology restart is bounded byMustRecoverWithin, not the write-outage budget.Design notes
Deploymentname is a chart-pinned literal (documentdb-operator, singleton install), so it is a safe constant; the namespace is install-overridable, exposed asLONGHAUL_OPERATOR_NAMESPACE(defaultdocumentdb-operator). There is no back-reference from the DocumentDB CR to the operator, so cluster-derived discovery isn't viable.kill-operator-podbuilds its pod selector from the Deployment's ownspec.selector.matchLabelsrather than a hardcoded label (the operator label is release-name dependent).kill-primary-podreads the CNPGCluster.Status.CurrentPrimary(CNPG cluster name == DocumentDB CR name) to target the primary pod, then waits for steady state.kill-primary-podskips (does not fail) wheninstancesPerNode<2— killing the sole instance is guaranteed downtime with no failover target. Skips don't consume the scheduler cooldown.Changes
operations/kill_operator.go,operations/kill_primary.go(+ unit tests): the two chaos ops.journal/policy.go,journal/journal.go: duration-basedOutagePolicy,WritesPerSecondper window +EstimatedWriteOutage,SetWriteRate, and the sharedNoOutagePolicy/PrimaryHandoverPolicyhelpers.workload/writer.go:AggregateWriteRate(numWriters).monitor/{health,k8sclient}.go:GetPrimaryInstance/DeletePodonClusterClient; CNPG scheme registration.operations/scale.go,operations/upgrade.go: adopt the shared policies (scale ops were previously over-lenient count budgets).config/config.go:LONGHAUL_OPERATOR_NAMESPACE+ validation.report/report.go: disruption-window table gains an Est. Write Outage column.cmd/longhaul/main.go: supply the write rate to the journal; register both ops.go.mod: promote cloudnative-pg to a direct dependency.Follow-up dependency
The driver ServiceAccount needs, in
deploy/rbac.yaml(added by the CI/CD PR):get/listonclusters.postgresql.cnpg.io+deleteonpods(kill-primary), andgetondeployments+get/list/deleteonpodsin the operator namespace (kill-operator). Documented intest/longhaul/README.md.Verification