Skip to content

test(longhaul): add kill-operator-pod and kill-primary-pod chaos operations#421

Open
WentingWu666666 wants to merge 6 commits into
documentdb:mainfrom
WentingWu666666:developer/wentingwu/longhaul-chaos-ops
Open

test(longhaul): add kill-operator-pod and kill-primary-pod chaos operations#421
WentingWu666666 wants to merge 6 commits into
documentdb:mainfrom
WentingWu666666:developer/wentingwu/longhaul-chaos-ops

Conversation

@WentingWu666666

@WentingWu666666 WentingWu666666 commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

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.mdOperations) 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:

Operation Fault Asserts
kill-operator-pod Deletes the operator pod The data plane keeps serving throughout (control-plane restart must not drop writes); the operator Deployment returns to Available.
kill-primary-pod Deletes the CNPG primary pod CNPG promotes a standby (automatic failover); writes resume and the cluster returns to full topology. Requires HA (instancesPerNode>=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 OutagePolicy moved from a raw write-failure count to a wall-clock write-outage duration:

  • OutagePolicy.MaxWriteOutage time.Duration replaces AllowedWriteFailures 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 with LONGHAUL_NUM_WRITERS. The rate is computed once at startup from NumWriters and the fixed writer cadence (workload.AggregateWriteRate) and stamped onto each disruption window.
  • Two orthogonal guardrails, both fail independently (ExceededPolicy trips 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.
  • Shared budgets to prevent drift:
    • journal.NoOutagePolicy (~300ms cushion) for operations that keep the write path up throughout: kill-operator-pod and 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 by kill-primary-pod and upgrade-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 by MustRecoverWithin, not the write-outage budget.

Design notes

  • Operator discovery. The operator Deployment name is a chart-pinned literal (documentdb-operator, singleton install), so it is a safe constant; the namespace is install-overridable, exposed as LONGHAUL_OPERATOR_NAMESPACE (default documentdb-operator). There is no back-reference from the DocumentDB CR to the operator, so cluster-derived discovery isn't viable. kill-operator-pod builds its pod selector from the Deployment's own spec.selector.matchLabels rather than a hardcoded label (the operator label is release-name dependent).
  • Primary discovery. kill-primary-pod reads the CNPG Cluster.Status.CurrentPrimary (CNPG cluster name == DocumentDB CR name) to target the primary pod, then waits for steady state.
  • HA guard. kill-primary-pod skips (does not fail) when instancesPerNode<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-based OutagePolicy, WritesPerSecond per window + EstimatedWriteOutage, SetWriteRate, and the shared NoOutagePolicy / PrimaryHandoverPolicy helpers.
  • workload/writer.go: AggregateWriteRate(numWriters).
  • monitor/{health,k8sclient}.go: GetPrimaryInstance / DeletePod on ClusterClient; 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/list on clusters.postgresql.cnpg.io + delete on pods (kill-primary), and get on deployments + get/list/delete on pods in the operator namespace (kill-operator). Documented in test/longhaul/README.md.

Verification

cd test/longhaul
go build ./...   # clean
go vet ./...     # clean
go test ./...    # all packages pass

wentingwu000 and others added 3 commits July 10, 2026 12:39
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>
@documentdb-triage-tool documentdb-triage-tool Bot added dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation enhancement New feature or request test labels Jul 10, 2026
@documentdb-triage-tool

Copy link
Copy Markdown

🤖 Auto-triaged by documentdb-triage-tool.

Applied: test, documentation, dependencies, enhancement
Project fields suggested: Component test · Priority P2 · Effort XL · Status In Progress
Confidence: 0.88 (mixed)

Reasoning

component 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 @patty-chow so the rules can be tuned. The bot will not re-label items that already have component labels.

wentingwu000 and others added 3 commits July 10, 2026 13:29
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>
@WentingWu666666 WentingWu666666 changed the title test: add kill-operator-pod and kill-primary-pod chaos operations to long-haul test(longhaul): add kill-operator-pod and kill-primary-pod chaos operations Jul 10, 2026
@WentingWu666666 WentingWu666666 marked this pull request as ready for review July 10, 2026 17:52
@WentingWu666666 WentingWu666666 requested a review from xgerman as a code owner July 10, 2026 17:52
Copilot AI review requested due to automatic review settings July 10, 2026 17:52

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation enhancement New feature or request test

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants