Skip to content

fix(ha): harden failover runtime and controller - #347

Merged
ajroetker merged 184 commits into
mainfrom
agent/ha-failover-runtime-hardening
Aug 12, 2026
Merged

fix(ha): harden failover runtime and controller#347
ajroetker merged 184 commits into
mainfrom
agent/ha-failover-runtime-hardening

Conversation

@bpopadiuk

@bpopadiuk bpopadiuk commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Codex (GPT-5): Delivers the Antfly runtime and operator foundation for production-ready hot-standby HA, paired with the Colony control-plane work in https://github.com/antflydb/colony/pull/346.

Current Antfly revision: 7bd93ae4b1e1d98db09731c81b19d3f2f971042c.

Production-readiness scope

  • Runtime-owned portable seed capture, publication, materialization, activation, catch-up, cleanup, and durable receipts.
  • Fail-closed startup, public mutation gating, Lease watchdog fencing, promotion, rewind/rejoin, replacement, and stale-primary rejection.
  • Exact topology, PVC incarnation, timeline, epoch, LSN, generation, process, and operation authority throughout the HA lifecycle.
  • Operator orchestration for seed actions, startup dependencies, source-PVC scheduling, persistent extension packages, and HA status/admin surfaces.
  • Backward-compatible one-way migration from legacy Swarm resources to the Standalone runtime without changing StatefulSet, Service, PVC, selector, mount, or storage identity.

Safety properties

  • Lease authority is bound to the exact runtime process boot ID. Fresh self-held Leases without a process annotation remain pending until the runtime observes its own bound proof; mismatched authority fences closed.
  • Acknowledged logical mutations use synchronous RemoteApply durability and a final authority check before returning success. Fence transitions wait for admitted mutations to finish their local commit and HA-tail append, so a rejected acknowledgement cannot strand an unlogged local fork.
  • Watchdog observation, renewal, TLS, timeout, response, and validation failures fence closed.
  • Promotion and rejoin preserve exact timeline and authority identity; stale or isolated former primaries cannot remain acknowledgement authorities.
  • Seed publication and prefix cleanup are serialized by Zig-native object-store CAS authority outside the deleted prefix. Cleanup leaves a durable tombstone, and an old publisher cannot recreate a deleted generation.
  • Restore completion is withheld until replay, checkpoint, coverage, and query availability prove the restored projection usable.

Dependency-free runtime transport

The Lease transport remains dependency-free Zig through Antfly's existing httpx path. It preserves projected Kubernetes CA validation, DNS hostname verification and SNI, service-account bearer authentication, one absolute monotonic request deadline, bounded response handling, and fail-closed watchdog fencing.

The checked-in Zig-native TLS compatibility fix handles an optional TLS 1.3 CertificateRequest by sending an empty client certificate while preserving transcript boundaries. The abandoned OpenSSL Lease transport, system links, package additions, and host-glibc workaround are absent.

Deterministic evidence

  • Operator full module tests passed through make test.
  • HA suite: 373/373 passed, including the commit-versus-fence ordering regression, Lease fencing, startup gates, action dependency scoping, publication/cleanup writer exclusion, durable tombstones, mutation barriers, migration identity, mixed-table whole-instance replication, and TLS optional CertificateRequest.
  • Standalone runtime suite: 54/54 passed.
  • Linux amd64 musl and Linux arm64 musl release builds passed and produced statically linked executables.
  • Operator startup-gate receipt selection now skips unrelated namespace collisions and passed the full controller package.
  • git diff --check passed.

Review and certification boundary

  • Seed snapshot preparation still holds the exclusive mutation barrier while producing the exact backup_lsn-bound source. Safely shortening that outage requires an immutable storage-engine snapshot/checkpoint handle or equivalent two-phase boundary.
  • Current feature-related CI is green: operator, SDK, generated-source checks, and the full zig-base gate passed. The isolated e2e-base rerun reproduced only two unrelated timing failures: managed-embedding retry sequencing and automatic shard-split finalization. Neither exercises HA or the changed paths.
  • No live KinD or full live matrix was run. The authoritative live pass count remains zero, and live exact-pair certification remains explicit manual follow-up.

Colony PR346 pins this exact Antfly gitlink and operator pseudo-version/checksum.

Boris Popadiuk and others added 28 commits July 13, 2026 15:15
…-seed-artifacts

# Conflicts:
#	zig/lib/httpx/src/client/client.zig
…time-hardening

# Conflicts:
#	zig/pkg/antfly/src/common/http/std_http_listener.zig
@bpopadiuk

Copy link
Copy Markdown
Contributor Author

Codex (GPT-5): This response was written and posted by Codex, not Boris.

I re-evaluated the blocking review against final Antfly head 4d3d7cc86ddb5de70765f6475fea0b231e72d793.

The six concrete blockers are addressed:

  1. Seed capture is separated from Lease renewal with single-flight behavior, so slow capture cannot starve the watchdog/renewal path.
  2. Schema mutation acquires the HA barrier once; the nested writer-preferring deadlock has deterministic interleaving coverage.
  3. Portable-artifact receipt decoding uses the complete file/chunk integrity schema emitted by Zig.
  4. Prepared runtime snapshots are removed after canonical capture and covered across multiple generations.
  5. Restore readiness now requires queryable index coverage/generation state; the immediate-query regression and focused three-test restore set pass.
  6. Portable-seed admission requires the complete executable topology and PVC-incarnation tuple.

Authentication is now one explicit fail-closed boundary: the dedicated HA bearer is required and is aligned across the outer listener and inner handler. The real handler path is exercised end to end.

The Lease transport remains Zig-native. The OpenSSL C transport, ssl/crypto links, package additions, and host-glibc workaround were removed. The narrow Zig 0.16 TLS compatibility delta handles an optional initial-handshake CertificateRequest, is pinned by upstream and patch hashes, and has projected-CA, DNS verification, bearer-auth, hostname-mismatch, bounded-response, monotonic-timeout, and watchdog-fencing coverage.

Final deterministic evidence includes operator make test, standalone 54/54, data runtime 75/75, HA 358/358, focused restore 3/3, and tls-compat-check. Final-head CI passes operator, SDK, and zig-base, including the Linux GNU build and relevant TLS/standalone aggregates. Linux amd64/arm64 musl archives, the macOS arm64 archive, and the runtime image were built and inspected; the Antfly runtime has no dynamic ssl/crypto dependency.

I agree with these remaining review concerns and am deferring them explicitly rather than claiming closure:

  • snapshot preparation still holds the exclusive mutation barrier while establishing the exact backup_lsn-bound source; shortening it safely needs an immutable storage-engine snapshot/checkpoint design;
  • production-scale capture throughput, Job resources/deadlines, and artifact-size measurements;
  • broader TLS interoperability/fuzzing beyond the deterministic regression;
  • controller/state-machine decomposition;
  • live exact-pair certification, explicitly excluded from this deterministic task (authoritative live pass count: zero).

The initial final-head e2e-base run failed only two unrelated managed-embedding/autoscaling cases after 148 base and 34 inference tests passed. The related IndexRebuilding restore failure from the earlier review was not dismissed: it was fixed and its focused final-head regressions pass. No unrelated product test or implementation was changed.

The PR is current with main. I have not merged it.

restore: RestoreSource,
options: RestoreOptions,
) !?db_mod.generation_lifecycle.StagedGeneration {
if (try restoreSnapshotAlreadyApplied(alloc, path, group_id, restore, options)) return null;

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.

(not your fault but maybe you could look into) Not sure why this code is in raft/ seems a little bit of a smell? Does it get used for HA?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

nice catch this is a weird + unused merge conflict resolution artifact. i am reverting it to the state of main

const httpx = @import("httpx");
const common = @import("../common/http/http_common.zig");

pub const ZigLeaseExecutor = struct {

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.

Should this be in this package? Should it just be LeaseExecutor?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yeah think you're right about the name - going to change it

//! portable storage artifacts. Keep this module below both layers so decoding
//! a seed never imports the metadata control loop into storage-only binaries.

pub const TableRecord = struct {

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.

Should this be in this package?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

codex seems to like this particular layout/separation of concerns, what do you make of this?

Codex (GPT-5): This placement is intentional. These topology wire records are consumed by both metadata/table_manager.zig and storage-only HA seed materialization. Defining them under metadata would force the storage layer to import the metadata control-plane dependency tree. Keeping the types in common provides a lower-level shared boundary, while table_manager.zig re-exports them for metadata callers. I think the current package is therefore the right dependency direction.

bpopadiuk and others added 10 commits July 17, 2026 16:14
…time-hardening

# Conflicts:
#	scripts/ci/zig-build-e2e-binaries.sh
#	zig/build.zig
#	zig/e2e/antfly/test_standby.py
#	zig/lib/httpx/src/client/client.zig
#	zig/pkg/antfly/src/api/http_server.zig
#	zig/pkg/antfly/src/api/public_table_http.zig
#	zig/pkg/antfly/src/api/table_writes.zig
#	zig/pkg/antfly/src/common/http/std_http_executor.zig
#	zig/pkg/antfly/src/data/runtime.zig
#	zig/pkg/antfly/src/metadata/table_manager.zig
#	zig/pkg/antfly/src/raft/storage/catalog.zig
#	zig/pkg/antfly/src/standalone/runtime.zig
#	zig/pkg/antfly/src/storage/db/db.zig
#	zig/pkg/antfly/src/storage/ha/http_admin.zig
#	zig/pkg/antfly/src/storage/ha/http_internal.zig
#	zig/pkg/antfly/src/storage/ha/standby.zig
@ajroetker
ajroetker merged commit eca7efb into main Aug 12, 2026
10 of 11 checks passed
@ajroetker
ajroetker deleted the agent/ha-failover-runtime-hardening branch August 12, 2026 00:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants