Skip to content

Scheduler: in-repo election, failover, and catch-up test coverage#1875

Merged
kriszyp merged 4 commits into
mainfrom
scheduler-cluster-tests
Jul 21, 2026
Merged

Scheduler: in-repo election, failover, and catch-up test coverage#1875
kriszyp merged 4 commits into
mainfrom
scheduler-cluster-tests

Conversation

@jcohen-hdb

Copy link
Copy Markdown
Member

Closes #1866.

Summary

Adds unit coverage for the scheduler engine's election/heartbeat/failover/catch-up orchestration (electRole, becomeLeader, heartbeat, failoverCheck, runCatchUp, executeJob) — previously backed in this repo only by pure-helper tests, with true-network validation living in the harper-pro cluster harness.

Approach — same-process simulated peer (the deploy-tracking-peer-branch.test.ts pattern): true multi-node replication isn't available in this repo (replication ships as a separate component), but the engine's entire knowledge of other nodes flows through exactly two observables — server.nodes (normally populated by the replication component) and rows in hdb_scheduler_state (normally replicated). The test supplies both, against the real engine module and a real store, no sinon. Staleness is data-driven (a lease heartbeat stamped past the threshold is a dead leader), so nothing waits out production intervals; heartbeat/failover ticks are driven through three new @internal pass-through seams (electionSettledForTests, runHeartbeatForTests, runFailoverCheckForTests — same pattern as setCoolingFunctionForTests).

Scenarios (a single node walked through its lifecycle): sticky-election deferral to a fresh peer lease → no premature promotion → stale-lease promotion with catch-up firing the missed occurrence (catchUp: true asserted) → lease renewal → step-down when a heartbeat finds a fresh competing lease → cold-start election win → roster-driven cold-start election loss (the one outcome that only passes if nodeRoster()/pickNextLeader wiring is correct, not just the pure function) → new-job baseline without immediate fire → registration replacement (upgrading a previously assertion-free test).

Where to focus review

  • The three engine seams are intended as pure pass-throughs with zero production behavior change (one held reference to an already-created election promise, reset in stopSchedulerEngine).
  • The lifecycle tests are deliberately sequential (mocha serial order); each later test states its precondition.
  • What this deliberately does NOT cover: true concurrent-write lease races and network-partition behavior — those remain in the harper-pro cluster harness (2-node kill/failover test), which is planned as a companion contribution there.

A Harper-domain review pass ran pre-PR; its findings (inert roster stub, state-row cleanup, one overclaiming comment) are addressed in the second commit. Note: npm run build on current main reports pre-existing type errors in agent/bestPractices.ts and resources/DatabaseTransaction.ts — unrelated to this diff, which adds no TS errors.

Refs #1828, #951.

Generated by an LLM (Claude Fable 5) pairing with @jcohen-hdb.

🤖 Generated with Claude Code

jcohen-hdb and others added 2 commits July 20, 2026 11:06
Covers the orchestration paths that previously had only pure-helper unit
tests here (electRole, becomeLeader, heartbeat, failoverCheck, runCatchUp,
executeJob) using the same-process simulated-peer pattern: the engine's
entire view of other nodes flows through server.nodes and rows in
hdb_scheduler_state, so the test supplies both against the real engine
module and a real store. Staleness is data-driven, so nothing waits out
production thresholds; heartbeat/failover ticks are driven through small
ForTests seams (same pattern as setCoolingFunctionForTests).

Scenarios: sticky election deferral, no-promotion while the leader beats,
stale-leader promotion with catch-up firing the missed occurrence
(catchUp: true), lease renewal, step-down when a heartbeat finds a fresh
competing lease (racing write), cold-start election, new-job baseline
without immediate fire, and registration replacement (previously an
assertion-free test).

True-network multi-node coverage remains in the harper-pro cluster
harness (replication is not part of this repo).

Refs #1866, #1828, #951.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…, comment accuracy

The new 'loses a cold-start election to an alphabetically-preferred roster
peer' case is the one outcome that only passes if nodeRoster()/pickNextLeader
wiring is correct (review: the roster stub was previously inert with respect
to every assertion).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jcohen-hdb
jcohen-hdb requested a review from kriszyp July 20, 2026 17:16

@gemini-code-assist gemini-code-assist Bot 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.

Code Review

This pull request introduces in-repo test coverage for the scheduler engine's election, heartbeat, and failover orchestration, adding test-only hooks to resources/scheduler/engine.ts and a comprehensive test suite in unitTests/resources/scheduler/engineElection.test.js. The feedback highlights two important improvements: first, addressing a potential race condition where an in-flight election promise could resolve after the engine is stopped, leaking timers; and second, replacing a flaky real-time delay in the heartbeat test with a deterministic backdated timestamp.

Comment thread resources/scheduler/engine.ts
Comment thread unitTests/resources/scheduler/engineElection.test.js
… stop; condition-wait for heartbeat assertion

stopSchedulerEngine now bumps an engine epoch that electRole, becomeLeader,
and failoverCheck capture before their awaits and re-check after — an
in-flight election or promotion can no longer resurrect timers or claim
the lease after a stop (with a regression test). The heartbeat-renewal
assertion uses the shared waitFor condition-wait instead of a fixed 5ms
sleep (coarse-clock flake).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jcohen-hdb
jcohen-hdb marked this pull request as ready for review July 20, 2026 18:30
…oots

harper dev <fixture> runs symlinkHarperModule against the component dir,
planting node_modules/harper inside integrationTests/fixtures/* — untracked
and unignored, it has previously slipped into a commit (#1828 required an
amend). Discovered during runtime verification of this branch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@kriszyp
kriszyp merged commit 1e1edc6 into main Jul 21, 2026
93 of 98 checks passed
@kriszyp
kriszyp deleted the scheduler-cluster-tests branch July 21, 2026 02:16
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.

Scheduler: add multi-node election/failover integration test coverage in-repo

2 participants