--- name: citus-backport description: >- Backport one or more merged citusdata/citus `main` commits/PRs onto the active release branches. Covers the release model, cherry-picking, remapping SQL schema changes to each branch's default_version, the SQL upgrade/downgrade ladder for Major-Version-Upgrade safety, running regression tests, one clean commit per PR per branch, and triaging release-branch CI (pre-existing baseline reds vs reds the backport introduced). USE WHEN asked to backport / port / cherry-pick a change to release-13.2 / release-14.0 (or the newest two majors), when a backport hits a SQL migration / udf / multi_extension conflict, when a "trivial C-only" backport fails to compile on an older PG the release branch still supports (e.g. PG15 `rteperminfos`), when release-branch CI is red and you must prove which reds are pre-existing, or when you must prove a SQL-schema backport is Major-Version-Upgrade-safe via `ALTER EXTENSION citus UPDATE` across a real multi-node cluster. DO NOT USE for non-Citus repos, new features, or PR review. license: See the repository LICENSE file. --- # Backport a Citus change to the release branches This skill encodes how to backport merged `main` commit(s) to Citus release branches: the release model, the SQL-schema remap recipe, the PG-version compatibility traps, and how to triage the release branches' baseline-red CI. Follow the repository's normal build/test/style conventions (see `CONTRIBUTING.md` and `src/test/regress/README.md`) on top of the guidance here. ## Reference deep-dives (load on demand) All live next to this file under `references/`: - **`references/sql-schema-backport.md`** — the hard case: a commit that changes the SQL schema (new UDF / column / migration). Exact file-by-file remap recipe, the `multi_extension.out` and `upgrade_list_citus_objects.out` edits, the `install-downgrades` trap, and the cross-branch **ladder-propagation** step (copy the introduction paths UP to every higher major for MVU safety). - **`references/ci-triage.md`** — reading backport CI: the PG-version compile traps (main drops old PGs the release branch keeps), the N-1 mixed-version jobs, and the step-by-step proof that a red job is pre-existing baseline noise vs a real regression you introduced. - **`references/manual-upgrade-testing.md`** — the manual MVU proof: stand up a multi-node cluster at an OLD Citus major, then `ALTER EXTENSION citus UPDATE` across the backport branches (13.x tail → cross-major → 14.x tail), capturing `citus_version()` + `pg_extension.extversion` per node per hop. The `make install-all` / single-shared-PG requirement. A C-only backport usually needs only this hub + `ci-triage.md`. A SQL-schema backport needs all four (add `manual-upgrade-testing.md` as the final proof once CI is green). ## Release model (internalize this first) - **ONE branch per MAJOR.** `release-13.2` serves the WHOLE 13.x line; `release-14.0` serves ALL of 14.x; `main` is the next major. Citus no longer cuts a branch per minor (this changed ~2026-05). - **Backport FEATURES, not just bugfixes**, to the **newest two majors** (13 & 14 as of this writing). - **Branch names LIE about the version.** `release-13.2` currently ships 13.4; `release-14.0` ships 14.2. **NEVER infer the target minor from the branch name** — read `default_version` from `src/backend/distributed/citus.control` on each target branch. That value drives every SQL edit. - **N-1 compatibility is required and CI-gated.** A backport is N-1-safe when it is purely ADDITIVE (new UDF / GUC / column) and does not change an existing UDF signature or wire format used cross-version. If it's additive, it's safe. See `references/ci-triage.md` for the jobs. ## Workflow (per PR, per target branch) 1. **Use an isolated build per target major.** A dedicated checkout/worktree + its own PostgreSQL install per release branch keeps builds/tests from clobbering each other and your main checkout. Two worktrees of the same clone SHARE one git object store — all branches are visible/pushable from either, and a `+`-prefixed branch in `git branch` is checked out in a sibling worktree. 2. **Make the SHAs reachable**, then `git checkout -b bp-- origin/` and `git cherry-pick -x ` (the `-x` records `(cherry picked from commit )`). 3. **Resolve:** - **C-only:** usually applies clean or with trivial context conflicts. THEN check PG-version compat (`references/ci-triage.md` §PG-version) — the #1 non-obvious C-backport failure. - **SQL-schema:** migration / udf / `multi_extension.out` conflict → remap by hand per `references/sql-schema-backport.md`. Do NOT just accept `main`'s next-major (e.g. `15.0-1`) files. 4. **Build in the isolated env:** `make -sj"$(nproc 2>/dev/null || sysctl -n hw.logicalcpu)" install`. For any SQL change ALSO install downgrades (see `references/sql-schema-backport.md` — plain `make install` skips them and `multi_extension` will fail with cascading "no update path" errors otherwise; use `make install-all`). 5. **Test only what's relevant** (CI runs the whole suite): the feature's own test + `multi_extension` for SQL changes. From `src/test/regress`: `python citus_tests/run_test.py ` (it manages its own cluster). Regenerate any `expected/*.out` with the runner — **never hand-edit .out** (column widths / row counts are dynamic). 6. **Collapse to ONE commit per PR per branch.** Keep the original message; keep a `DESCRIPTION:` first line iff the change is user-facing (this feeds the changelog). `git checkout -- configure` if a build regenerated it (configure is never part of a backport). **Multiple PRs to the SAME target → ONE branch per target, not per PR** (unsquashed: one commit per PR, in main's chronological merge order; SQL-ladder commits sit right after their PR's cherry-pick). Name it `bp-release-` (e.g. `bp-release-13.2`). Build cleanly from the SQL-heavy PR's branch, then cherry-pick the C-only PRs on top. **Cherry-pick an already-adapted backport commit WITHOUT `-x`** — its message already carries the single `(cherry picked from )` trailer; `-x` would double it. 7. **Push to your fork.** `bp-*` branches don't match CI's push trigger (`main`/`release-*` only), so kick CI with `gh workflow run "Build & Test" --repo /citus --ref `. Open PRs only when the change owner asks. 8. **Wait for CI and triage** per `references/ci-triage.md`. Separate baseline-red noise (N-1 / flaky / coverage) from **genuinely-new failures you introduced**. The two you WILL hit on a release branch: (a) a version-pinned `.out` that prints a shared helper's changed *signature*, and (b) a PG-version planner divergence on the OLDER PG the branch keeps but `main` dropped (PREFER a PG-guarded file like `pg16.sql` over a `_N.out` alt file). A third you must NOT chase green by mangling the test: a new GUC/UDF whose test runs under N-1 shows a NEW red in the (non-blocking) **N-1 mixed-version** jobs — the old lib lacks it and falls back, which IS the N-1 contract. The clean fix is to move that test line into the `multi_1_create_citus_schedule` placeholder section (schedule mechanics + N-1 label rule in the hard-rules below and `ci-triage.md §"NEW N-1 red"`). **Fix-commit rule (all fixes):** pre-review → amend + `git push --force-with-lease`; post-review → add ONE new fix commit, never rewrite reviewed SHAs (replace a bad remote fix commit with `--force-with-lease=:`). Report evidence; don't declare done on red. 9. **Propagate the ladder UP (SQL-schema backports only).** A new SQL object must also be reachable on every HIGHER major's upgrade ladder, or a future 13.x→14.y Major Version Upgrade loses it. As SEPARATE commits (its own PR on main), copy the introduction step/downgrade/udf files byte-for-byte onto release-14.0 (for a 13.x path) and main (for 13.x and 14.x paths, given today's two most recent major versions); main's own top path stays untouched. Full topology + recipe in `references/sql-schema-backport.md` §Step 5. 10. **Add `multi_extension.sql` ladder-test coverage** for every new step. The test only walks the main upgrade path, so a step OFF that path (a 13.x/14.x maintenance-tail detour) is never exercised unless you add a detour block matching the on-walk shape (and the file's existing comment style — no editorial parentheticals): a round-trip no-op + a snapshot at the detour, then retarget the *existing* next round-trip to bounce ` ↔ ` rather than adding a separate "return" block. That shifts exactly one downstream snapshot; everything after stays byte-identical. A branch's own top/default_version is already covered by its snapshot block. Never hand-edit `.out`; prefer append-only on reviewed branches. Full pattern + the `diff -w` header gotcha in `references/sql-schema-backport.md`. 11. **Prove MVU safety on a real cluster (SQL-schema backports, after CI is green).** The ladder propagation only *claims* a future 13→14 Major Version Upgrade keeps the object; prove it. Stand up a multi-node cluster at an OLD major, then `ALTER EXTENSION citus UPDATE` on EVERY node across the backport branches (13.x tail → cross-major → 14.x tail → cross-major → main tail), capturing `citus_version()` and `pg_extension.extversion` per node per hop, plus a functional check that the backported object survived. Build all Citus versions into ONE shared PG with **`make install-all`** (plain `install` omits the downgrade + bridge scripts the cross-major route needs → "no update path"). Full recipe and the single-PG requirement in `references/manual-upgrade-testing.md`. ## Hard rules / traps (full detail in the references) - **Do NOT run `make reindent` in a build environment whose `citus_indent` version differs from the branch's** — a version-skewed formatter reformats 100+ unrelated files and errors out. Trust cherry-picked formatting; CI `check-style` verifies it. If you ran it by accident: `git reset --hard HEAD`. - **`make install` does NOT install downgrade scripts.** For any SQL change run BOTH `make -C src/backend/distributed install-downgrades` and `make -C src/backend/columnar install-downgrades` (columnar is a separate extension). `make install-all` does both. - **A single build environment builds ONE PG only** — it cannot catch a compile break on another PG the release branch supports. Rely on CI's per-PG `Build for PGNN` jobs; scan cherry-picked C for struct fields / APIs newer than the branch's OLDEST PG and add `#if PG_VERSION_NUM >= PG_VERSION_NN`. - **The release branches are BASELINE-RED.** Several N-1 / flaky jobs fail on the pristine base branch with no backport. Subtract the baseline before blaming your change — prove it with the upstream `citusdata/citus` base-branch run. Details + exact commands in `references/ci-triage.md`. - **A new SQL object is defined MULTIPLE times on purpose.** After ladder propagation the same UDF appears in the 13.x, 14.x AND main introduction steps (triple-defined on main). That is intentional MVU/N-1 safety, NOT duplication to collapse. Never "dedupe" it. Details: `references/sql-schema-backport.md` §Step 5. - **Show evidence.** Present the proof (build-job outcomes, `regression.diffs` excerpts, the upstream-baseline comparison), not a bare "CI is green/red". - **A backport onto an older-PG release branch surfaces failures `main` never saw** (release-13.2 still builds PG15; the branch ships version-pinned `expected/*.out`). Two patterns: (1) a shared helper whose SIGNATURE you changed prints in a pinned `.out` (`normalize.sed` masks the line NUMBER, not the signature) — do NOT "fix" it by reverting the helper if the backported TEST needs it (it's required, not over-reach); (2) a PG15-vs-PG16+ EXPLAIN/planner diff → PREFER a PG-guarded file (`pg16.sql` `\q`-skips _0.out` alt. Full recipe: `references/ci-triage.md` §Genuinely-new failures. - **A NEW GUC/UDF whose test runs under N-1 has a CLEAN fix, not "leave it".** Move the test line from its schedule into the `multi_1_create_citus_schedule` placeholder section (N-1 make_targets omit `check-multi-1-create-citus`). N-1 version label = the branch's CURRENT N-1 = the previous minor (13.3-1 on release-13.2, 14.1-1 on release-14.0), read from the live `citus_version:` / `citus_libdir:` pin in build_and_test.yml — not the (possibly-stale) number in the neighbor comment. Details: `references/ci-triage.md` §"NEW N-1 red".