Files
james.maesandClaude Opus 5.5 00bc20b899 feat: qbit-cluster v1 - node registry, heartbeats, leases, drain, lost-node alerts
- cluster_node, cluster_lease, cluster_setting as standard QQQ tables
  (cluster.read / cluster.write), node status possible values, help for
  every table, field, process, widget and value source
- Liquibase changelogs in the jar: schema (idempotent, no FKs, no
  superuser) and insert-only seeded settings
- runtime: register + heartbeat on the database clock, drain requests
  flowing to QNodeLifecycle.startDraining, DRAINING/STOPPED tracking,
  lost-node sweep on the cluster-sweeper lease with the cluster.node.lost
  alert, idempotent purge of old rows; ClusterRuntimeService
- ClusterLeases: one atomic compare-and-set UPDATE per change
  (ConditionalUpdateAction), tryAcquire / renew / release / isLeader
- record actions Drain node and Cancel drain (standard ETL Review /
  Result, cluster.drain), Cluster app with the Nodes widget by role,
  cluster.node.heartbeat health check
- CI (CircleCI qqq-orb), release-rc workflow, docs, 35 tests (memory
  backend, plus Liquibase and the RDBMS module on H2)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 11:13:14 -05:00

3.5 KiB

AGENTS.md

This file provides guidance to Codex and other agents when working with this repository.

Project overview

QBit Cluster runs a QQQ application as one unit on N nodes: node registry and heartbeats (cluster_node), lost-node detection with the cluster.node.lost alert, compare-and-set leases for singletons (cluster_lease, ClusterLeases), seeded settings (cluster_setting), Drain node / Cancel drain record actions, the cluster.node.heartbeat health check and the Cluster app with the Nodes widget. Read docs/DESIGN.md and README.md first.

Build commands

Java 21; always use the wrapper (offline in this workspace):

./mvnw -o verify                       # checkstyle, tests, JaCoCo (>=70% instructions, >=90% classes)
./mvnw -o test -Dtest=ClusterLeasesTest

Tests use JUnit 5, AssertJ, MOCK auth and the memory backend; ClusterChangelogTest runs the shipped changelogs through real Liquibase on H2 and the runtime through the RDBMS module. No Docker. If core cluster classes are missing, the shared ~/.m2 4.1.0-SNAPSHOT was overwritten: reinstall QQQ from the feature/cluster-ha-p1 worktree.

Architecture by package

  • com.kingsrook.qbits.cluster: ClusterQBitProducer (adds everything directly), ClusterQBitConfig, ClusterLeases.
  • model: ClusterTables (table metadata, field and table names), NodeStatus, ClusterSettings (defaults + load), ClusterSettingValidator.
  • runtime: ClusterRuntime (thread, tick, stop), ClusterRuntimeService, ClusterNodeRegistry (own row), ClusterNodeInfo (env, roles, JVM), ClusterSweeper, ClusterAlerts, ClusterSystem (system-session scope).
  • processes: ClusterNodeActions (metadata), ClusterNodeActionSteps (Review / Result).
  • widgets: ClusterNodesWidgetRenderer. health: ClusterHeartbeatHealthCheck.

Invariants

  • Every write that two nodes could race on is a ConditionalUpdateAction compare-and-set; times come from the backend clock (databaseNow), never the pod clock.
  • The qbit never runs DDL. Schema and defaults ship as Liquibase SQL in the jar; defaults are insert-only with empty rollbacks. Column names = snake_case of field names; keep the changelog, ClusterTables and ClusterSettings.DEFAULTS in step (tests enforce it).
  • Processes are record actions only (never in navigation), standard streamed ETL, plain labels.
  • Renderers read in the viewer's session; only the runtime's own bookkeeping runs as system.
  • Every table, field, process, widget and possible-value source has help in help/qbit-cluster.yaml (ClusterQBitProducerTest.helpIsComplete). Summaries are plain text, at most 120 characters, no underscores.

Naming and code style

Tables cluster_*, permissions cluster.read, cluster.write, cluster.drain, processes clusterDrainNode / clusterCancelDrain, widget clusterNodes, lease cluster-sweeper. Kingsrook style: three-space indent, braces on new lines, return (x);, flower-box Javadoc on every class and method, the Apache license header. Do not weaken checkstyle or coverage gates.

Dependencies

Parent com.kingsrook:qbit-build-parent:2.0.0; the default qqq-snapshot profile imports qqq-bom-pom ${qqq.snapshot.version} (4.1.0-SNAPSHOT until a QQQ RC ships the cluster APIs).

Release flow

CircleCI (kingsrook/qqq-orb): feature branches test; develop publishes SNAPSHOTs; release/* publishes RCs (cut by .github/workflows/release-rc.yml); vX.Y.Z tags publish releases. Publishing requires the owner's authorization. Update CHANGELOG before a release.