cockroachdb/cockroach
Cluster version and upgrade
pkg/clusterversion/ defines the monotonically-increasing integer that gates every backwards-incompatible behavior in CockroachDB. pkg/upgrade/ runs the migrations that ratchet that integer forward. Together they implement the rolling-upgrade contract: a cluster can be upgraded one node at a time without downtime, and old nodes never observe behaviors only newer nodes understand.
The gating contract
A version
Vis "active" once every node has confirmed it can run code that depends onV. Code may only execute behavior gated onVafterVis active.
CockroachDB enforces this with two pieces of state:
- Binary version — the largest cluster version a node's binary knows about. Determined at compile time.
- Active cluster version — what the cluster has agreed to use. Stored persistently in
system.settings(keyversion) and on every store (pkg/storage/min_version.go).
A new release ships with a higher binary version. Once every node in the cluster runs a binary that supports V, the operator (or an auto-upgrade job) calls SET CLUSTER SETTING version = V, and the migration framework ratchets the active version forward in steps.
Files
pkg/clusterversion/
├── cockroach_versions.go // ordered list of all versions, with bool aliases
├── setting.go // *clusterversion.Handle and active-version watch
├── keyed_versions.go // version metadata
├── clusterversion.proto // wire format
└── …
pkg/upgrade/
├── upgrade.go // Upgrade interface
├── upgrademanager/ // run migrations in cluster-version order
├── upgrades/ // one Go file per migration
└── …Adding a new gated behavior
- Append a constant to
pkg/clusterversion/cockroach_versions.go(preserve order; the integer must increase). - Reference it from the gated code:
if cv.IsActive(ctx, clusterversion.V25_4FooNew) { ... }. - If the upgrade requires running code (creating system tables, rewriting data, …), add a migration in
pkg/upgrade/upgrades/<name>.goand register it in the package'sinit().
The clusterversion.Handle is wired into pkg/server/server.go and pkg/server/server_sql.go and is reachable from anywhere via settings.Values.
Migration framework
pkg/upgrade/upgrademanager/ runs migrations strictly in order:
- Set the active version to the next gated version.
- If a migration is registered, run it as a job and wait for completion. The migration may read and write any KV/SQL state; the framework provides a
*kv.DB, an internal SQL executor, and (for system-tenant migrations) per-store hooks. - After the migration completes, persist the new version everywhere and ack.
Migrations are idempotent and resumable. The pkg/upgrade/migrationstable/ schema records progress in system.migrations so a restarted manager picks up where the previous one left off.
Per-store version
Every store carries a STORAGE_MIN_VERSION file (pkg/storage/min_version.go). When the active version advances, each replica's Engine.SetMinVersion is called. This:
- Writes the new version to a known store-local key.
- Writes a small file outside the engine so the next process start can read the version without opening the store.
- Optionally ratchets Pebble's "format major version" to enable backwards-incompatible storage features.
Mixed-version safety in practice
When you write code, ask three questions:
- Is this behavior already in some version? If so, gate on
IsActive(version). - Will the on-the-wire change break old nodes? If yes, you need a version + a migration that drains old behavior first.
- Can a node downgrade? Once a version is active, downgrade is forbidden. The release process runs nightly mixed-version logictest and roachtest configurations to catch regressions.
The CRDB CLI also exposes:
SHOW CLUSTER SETTING version; -- active version
SHOW CLUSTER SETTING cluster_settings; -- includes "version"
SET CLUSTER SETTING version = '25.4'; -- finalize an upgradeRelated pages
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.