Skip to content

RFC ts-ct-migration: partition_mode substrate [PR 0]#30977

Draft
sjust-redpanda wants to merge 5 commits into
redpanda-data:devfrom
sjust-redpanda:sjust/ts-ct-migration-pr0
Draft

RFC ts-ct-migration: partition_mode substrate [PR 0]#30977
sjust-redpanda wants to merge 5 commits into
redpanda-data:devfrom
sjust-redpanda:sjust/ts-ct-migration-pr0

Conversation

@sjust-redpanda

@sjust-redpanda sjust-redpanda commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

🚧 NOT YET READY FOR REVIEW

Summary

Foundational base branch for the tiered-storage→cloud-topics migration.
Introduces partition_mode: a partition's own durable storage mode, recorded
in partition_properties and decoupled from the topic config, so the migration
can be modelled as partition_mode lagging the topic-config-derived topic_mode
until cutover.

Commits:

  • cluster: carry partition_mode in partition_properties_stm — persist a
    per-partition model::redpanda_storage_mode alongside writes_disabled (serde
    bumped to v2, compat_version unchanged so older snapshots decode unset);
    set_partition_mode / partition_mode() / sync_partition_mode API; and a
    change callback fired on the partition_mode apply branch and on raft-snapshot
    restore.
  • features: add the tiered_to_cloud_migration feature — the single
    explicit_only flag that gates the whole migration capability across the
    stack; the partition_mode write is guarded on it, and the later PRs reuse it.
  • storage: split ntp_config storage mode into topic_mode + partition_mode
    topic_mode() is the topic-config-requested mode; partition_mode() is the
    partition's actual mode (falls back to topic_mode() when unset). Serving
    predicates key on partition_mode(); the legacy shadow_indexing fallback is
    preserved. No-op (the two are always equal here).
  • cluster/partition: feed partition_mode into ntp_config — push the
    STM's partition_mode into the log's ntp_config at start and on the STM
    change callback (storage::log gains set_partition_mode, mirroring
    set_overrides).
  • cluster/partition: bootstrap partition_mode on leadership — on becoming
    leader with the feature active, record the partition's current mode (keying on
    the archival manifest, via the new archival_metadata_stm::holds_archived_data(),
    when the partition is really tiered mid-migration). One-time, idempotent.

Testing

  • partition_properties_stm_test: partition_mode default-unset; set / idempotent
    / change with cross-node consistency; independence from writes_disabled on the
    shared snapshot; raft-snapshot round-trip at the right offsets; recovery (local
    kvstore snapshot + replay, and raft-snapshot recovery after dropping a node's
    data); and the change callback fires on a real change but not an idempotent
    no-op.
  • Full end-to-end coverage rides with the later migration PRs (this PR is a no-op).

🤖 Generated with Claude Code

Design Doc

PR 0 · PR 1 · PR 2 · PR 3 · PR 4 · PR 5 · PR 6 · PR 7 · PR 8

@sjust-redpanda

Copy link
Copy Markdown
Contributor Author

Working on updating downstream PRs to use this mechanism rather than clumsily looking at the archival_metadata_stm state.

sjust-redpanda and others added 5 commits July 2, 2026 10:54
Persist a partition-local storage mode (model::redpanda_storage_mode) in the
partition properties STM, alongside the existing writes_disabled property. This
is the partition's own durable record of its storage mode, decoupled from the
topic config; a later change exposes it on ntp_config as partition_mode (vs the
topic-config-derived topic_mode) so the TS->CT migration can be modelled as
reconciling partition_mode toward topic_mode.

Adds update_partition_mode_cmd / operation_type::update_partition_mode, carries
partition_mode on state_snapshot and raft_snapshot (serde version bump to v2,
compat_version unchanged so older snapshots decode with partition_mode = unset),
and the set_partition_mode / partition_mode / sync_partition_mode API. apply is
refactored to dispatch on operation_type and copy-modify the current snapshot so
each property update preserves the others. partition_mode updates are idempotent
by value and ordered by log offset (no per-property revision).

Also adds a change callback (set_partition_mode_change_callback), fired when
partition_mode changes on apply or on raft-snapshot restore, so an owner can
re-read partition_mode() and propagate it (the STM had no change notification;
writes_disabled is polled).

Tests cover: default unset, set/idempotent/change with cross-node consistency;
partition_mode and writes_disabled are independent on the shared snapshot; raft
snapshot carries partition_mode at the right offsets; recovery (local kvstore
snapshot + replay, and raft-snapshot recovery after dropping a node's data); and
the change callback fires on a real change but not on an idempotent no-op.

Inert: no callers yet (the command is wired up and gated behind a feature in
follow-up commits).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Declare feature::tiered_to_cloud_migration (bit 18, explicit_only, v26_2_1). It
gates all behaviors of the TS->CT migration capability across the PR stack: the
partition_mode write introduced in this base branch, and the migration
trigger/cutover in the later PRs. explicit_only means it does not auto-activate
and carries no mixed-version compatibility commitment until an operator opts in.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…mode

Introduce two storage-mode accessors on ntp_config:

  - topic_mode()      the mode the topic config requests (the existing
                      _overrides->storage_mode; the "desired" mode).
  - partition_mode()  the partition's own durable mode, fed from
                      partition_properties via set_partition_mode(); the
                      "actual" mode. Falls back to topic_mode() when unset.

The serving/behavior predicates (is_archival_enabled, is_remote_fetch_enabled,
is_tiered_storage, cloud_topic_enabled, is_tiered_cloud) now key on
partition_mode() instead of reading _overrides->storage_mode directly. The
legacy shadow_indexing fallback (used when the mode is unset) is preserved
unchanged, since archival-only/fetch-only SI modes have no redpanda_storage_mode
equivalent.

TS->CT migration wil lbe modelled as partition_mode lagging topic_mode
(still `tiered` while the topic config says `cloud`) until cutover, with
serving keyed on the actual mode.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… ntp_config

On partition start, read partition_properties_stm::partition_mode() and push it
into the log's ntp_config (storage::log gains a set_partition_mode method,
mirroring set_overrides), and register the STM's change callback so the value is
re-pushed whenever the STM applies a partition_mode update or restores a raft
snapshot. ntp_config::partition_mode() therefore tracks the partition's durable
mode, falling back to topic_mode() while unset.

No feature guard here: reading replicated STM state and pushing it into the
local ntp_config is always safe. Writing partition_mode (which requires the
tiered_to_cloud_migration feature) is added in the bootstrap commit; until then
the value is always unset.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Record the partition's durable storage mode (partition_mode) in
partition_properties. It is populated:
  - at partition creation, from the topic-config mode, when the migration
    feature is active (partition_manager::manage);
  - on becoming leader, for a partition that predates this (maybe_bootstrap_
    partition_mode); a node that became leader after an in-place ts->ct switch
    keys on the archival manifest (holds_archived_data) since topic_mode()
    already reads cloud;
  - on migration-feature activation, sweeping current leaders that were elected
    while the feature was inactive.
The creation-time classification (set_creation_partition_mode) is preferred by
the bootstrap over re-reading topic_mode(), so the mode does not depend on the
async config propagation a runtime re-derivation would race.

Also add partition_properties_stm::seed_partition_mode(), which pre-writes the
kvstore-backed local snapshot before the STM starts so a partition restored with
a non-empty log comes up with partition_mode already set (the seed survives
start(), whose initial cleanup only runs for an empty log). Used by the
migration-recovery path.

partition_mode stays inert here; serving/behavior accessors begin keying on it
in later PRs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@sjust-redpanda sjust-redpanda force-pushed the sjust/ts-ct-migration-pr0 branch from e6a289d to 39313f9 Compare July 2, 2026 20:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant