tests/rptest: expose L0 GC unreadability after a storage-mode flip#31065
Open
Lazin wants to merge 6 commits into
Open
tests/rptest: expose L0 GC unreadability after a storage-mode flip#31065Lazin wants to merge 6 commits into
Lazin wants to merge 6 commits into
Conversation
Switch the flag from explicit_only to always so it activates as soon as every node runs v26.2, with no admin action. The create and alter-config gates keep rejecting cloud-architecture tiered topics (and cloud -> tiered_cloud conversions) while the cluster is only partially upgraded, and open automatically once the upgrade completes. set_feature_active now tolerates always-policy features jumping straight from unavailable to active without an observable available state, and the real-upgrade finalization test expects the flag to auto-activate after finalize. Signed-off-by: Evgeny Lazin <4lazin@gmail.com>
The set_min_allowed_local_threshold stm command is new in v26.2, so the compaction sink must not replicate it while pre-v26.2 replicas may still be in the raft group. Gate the notifier on the tiered_cloud_topics feature flag: until it activates (the cluster is fully upgraded to v26.2) every notification attempt reports success without replicating. This is safe because no cloud-architecture tiered topic -- the only reader of local data below the floor -- can exist while the flag is inactive. Signed-off-by: Evgeny Lazin <4lazin@gmail.com>
The tiered_cloud storage mode ships in v26.2 as the second implementation of tiered storage, so the user surface is reworked around the 'tiered' name instead of exposing the internal enum: - redpanda.storage.mode accepts unset/local/tiered/cloud; the 'tiered_cloud' spelling is no longer a valid value. Both tiered variants display as 'tiered' in DescribeConfigs. - The new default_redpanda_storage_mode_tiered_impl cluster config (tiered_v1 = classic Tiered Storage, tiered_v2 = Cloud Topics based) selects which implementation 'tiered' refers to. It defaults to tiered_v1 for all clusters, so an upgrade never changes what 'tiered' means and tiered_v2 is an explicit opt-in. - The new read-only redpanda.storage.mode.impl topic property reports the exact implementation of every topic's storage mode (unset/local/tiered_v1/tiered_v2/cloud), making the describe output unambiguous regardless of the reader's cluster config. On creation it selects the storage mode precisely, alone or together with a matching redpanda.storage.mode; mismatched pairs are rejected. The property is not alterable, although sets that match the current value are tolerated as no-ops so that describe-then-alter round trips (which replay every config) keep working. It is reported with the default config source: the value is derived from the storage mode rather than being an explicit override, so config backup/replay tooling must not treat it as user-set. Storage-mode transition errors print the impl names, so a rejected tiered_v1 to tiered_v2 transition does not read as 'tiered to tiered'. The tiered_v2 impl keeps the tiered_cloud_topics feature and cloud storage gates. Signed-off-by: Evgeny Lazin <4lazin@gmail.com>
The synced describe output shows 'tiered' for both tiered impls, so the mode string alone is ambiguous. Request the read-only redpanda.storage.mode.impl property alongside the mode and apply it first (it is exact and reported for every topic), reconstructing the source topic's storage mode on the target instead of re-interpreting 'tiered' through the target's own tiered-impl config - with different configs on the two clusters that could silently turn a cloud-architecture topic into a classic tiered one and start an archiver on a partition that has no archival stm. The mode string remains as a fallback for sources that predate the impl property. Mirror-topic creation and property updates bypass the kafka handlers, so this path enforces the guards itself: storage-mode transition rules (a bad sync becomes a rejected update instead of an applied forbidden transition) and the tiered_cloud_topics upgrade gate - no mirror topic is created with or moved to the tiered_v2 impl until the cluster is fully upgraded to v26.2; creation is skipped and retried once the feature activates. Signed-off-by: Evgeny Lazin <4lazin@gmail.com>
TieredCloudUpgradeTest checks the mixed-version behavior: creating a tiered_v2 topic or converting a cloud topic to it is rejected (or, via a v26.1 controller that drops the unknown impl property, degrades to a classic tiered topic) while the cluster is partially upgraded, and both operations work once every node runs v26.2. StorageModeAliasMatrixTest covers every mode/impl input combination under both tiered impls, including impl-only creation, mode/impl consistency rejections, read-only enforcement and the always-present describe entry. ShadowLinkStorageModeSyncTest syncs cloud and tiered topics across all four combinations of source/target tiered impls, including a mid-flight cloud to tiered_v2 conversion. TopicSpec spells tiered variants through the new storage_mode_config helper (mode=tiered plus the impl property, which keeps v26.1 brokers on a tiered mode instead of silently falling back to the cluster default), kafka-cli describe output round-trips through the new impl kwarg, and the describe-topics parser accepts the asterisk kcl appends to read-only properties. The storage-mode toggle and rnot flipping tests point the 'tiered' alias at tiered_v2 explicitly, since the alter path resolves it through the cluster config. Signed-off-by: Evgeny Lazin <4lazin@gmail.com>
Rework the storage-mode toggle test to reproduce the mode-flip / L0-GC interaction: after a cloud -> tiered_v2 flip the tiered read branch serves everything at or above the local log start from the local log, ignoring the last-reconciled offset. The local log still holds placeholders for the already-reconciled region, L0 GC deletes the objects they reference once their epoch goes inactive and the minimum age passes, and reading the region then fails to materialize with no L1 fallback - even though L1 holds a complete copy. The flip also freezes local prefix-truncation, so the log never self-heals. The test shrinks the GC minimum object age and the epoch/housekeeping intervals that gate GC eligibility, disables the batch cache, ends the toggling in tiered_v2, trickle-produces until the reconciled L0 objects are collected, wipes the cloud-storage disk cache with a rolling restart and then reads the whole log back. The read currently wedges on the placeholder region (download_not_found materialization failures) instead of returning every acked record. The test is expected to FAIL until the read path falls back to L1 (or prefix-truncation trims the placeholder region) after a mode flip. Signed-off-by: Evgeny Lazin <4lazin@gmail.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR reworks storage-mode configuration to introduce an explicit, unambiguous tiered implementation selector (tiered_v1 vs tiered_v2) via a new read-only topic property redpanda.storage.mode.impl, updates cluster-linking to sync that property safely across clusters with different defaults, and adjusts ducktape coverage (including a draft reproducer expected to fail) to expose an L0-GC unreadability after a cloud -> tiered_v2 flip.
Changes:
- Add
default_redpanda_storage_mode_tiered_implcluster config andredpanda.storage.mode.impltopic property; update create/describe/alter validation and parsing to use the new(mode, impl)semantics. - Update cluster-linking topic reconciler and property sync to preserve tiered variant correctly across differing defaults and enforce upgrade gating for tiered_v2.
- Update/extend rptest coverage and add an end-to-end mode-flip/L0-GC reproducer (expected to fail until the underlying bug is fixed).
Reviewed changes
Copilot reviewed 59 out of 59 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/rptest/tests/topic_creation_test.py | Normalize kcl output by stripping * from read-only config keys. |
| tests/rptest/tests/tiered_cloud_local_retention_test.py | Switch tiered-cloud usage to tiered_v2 via storage_mode_config. |
| tests/rptest/tests/storage_mode_test.py | Add upgrade-gate + alias/impl matrix coverage for new storage mode semantics. |
| tests/rptest/tests/shadow_linking_rnot_test.py | Pin tiered default to v2 for flip workload; use storage_mode_config. |
| tests/rptest/tests/random_node_operations_smoke_test.py | Use storage_mode_config for tiered_v2 topic creation. |
| tests/rptest/tests/follower_fetching_test.py | Use storage_mode_config for tiered_v2 fetch-from case. |
| tests/rptest/tests/describe_topics_test.py | Document/parse new redpanda.storage.mode.impl (and optional * marker). |
| tests/rptest/tests/cluster_linking_test_base.py | Sync redpanda.storage.mode.impl and update storage-mode sets used by tests. |
| tests/rptest/tests/cluster_linking_storage_mode_sync_test.py | New test verifying storage-mode variant survives sync across differing defaults. |
| tests/rptest/tests/cluster_linking_e2e_test.py | Update cloud-topic replication tests to use tiered_v2/impl semantics. |
| tests/rptest/tests/cluster_features_test.py | Adjust tiered_cloud_topics expectations for available_policy::always. |
| tests/rptest/tests/cloud_topics/retention_test.py | Use tiered_v2 storage-mode config helper in retention tests. |
| tests/rptest/tests/cloud_topics/iceberg_test.py | Use tiered_v2 storage-mode config helper in Iceberg tests. |
| tests/rptest/tests/cloud_topics/e2e_test.py | Rework mode-toggle test into L0-GC unreadability reproducer (expected fail). |
| tests/rptest/tests/cloud_topics/debug_rows_test.py | Update test matrix to include tiered_v2. |
| tests/rptest/tests/cloud_topics/compaction_stress_test.py | Update tiered-cloud references to tiered_v2/impl. |
| tests/rptest/tests/cloud_topics_test.py | Update tiered-cloud references to tiered_v2/impl. |
| tests/rptest/services/redpanda.py | Allow await_feature to accept {available,active} for always-auto-active features. |
| tests/rptest/clients/types.py | Add storage_mode_config() helper + PROPERTY_STORAGE_MODE_IMPL. |
| src/v/model/tests/storage_mode_alias_test.cc | New unit tests for user/impl parsing and naming behavior. |
| src/v/model/tests/BUILD | Register new storage_mode_alias gtest target. |
| src/v/model/model.cc | Implement tiered-impl parsing + user-vs-impl vocabulary and naming helpers. |
| src/v/model/metadata.h | Define redpanda_storage_mode_tiered_impl and helper APIs. |
| src/v/kafka/server/tests/alter_config_test.cc | Ensure new redpanda.storage.mode.impl appears in describe-configs coverage. |
| src/v/kafka/server/handlers/topics/validators.h | Extend create-topic validation for (mode, impl) semantics. |
| src/v/kafka/server/handlers/topics/types.cc | Prefer impl property over alias-resolved mode when building topic configs. |
| src/v/kafka/server/handlers/incremental_alter_configs.cc | Enforce impl read-only; parse storage mode via user-string semantics. |
| src/v/kafka/server/handlers/create_topics.cc | Allow redpanda.storage.mode.impl as supported create-topic config. |
| src/v/kafka/server/handlers/configs/config_utils.h | Improve transition error messages to use impl names. |
| src/v/kafka/server/handlers/configs/config_response_utils.cc | Describe storage.mode as user name; add read-only storage.mode.impl. |
| src/v/kafka/server/handlers/alter_configs.cc | Enforce impl read-only; parse storage mode via user-string semantics. |
| src/v/kafka/protocol/topic_properties.h | Define constant for redpanda.storage.mode.impl. |
| src/v/features/feature_table.h | Change tiered_cloud_topics to available_policy::always (auto-activate). |
| src/v/config/rjson_serialization.h | Add JSON serialization for tiered-impl enum. |
| src/v/config/rjson_serialization.cc | Implement JSON serialization for tiered-impl enum. |
| src/v/config/property.h | Treat tiered-impl enum as string-typed property. |
| src/v/config/convert.h | Add YAML conversion for tiered-impl enum. |
| src/v/config/configuration.h | Add default_redpanda_storage_mode_tiered_impl cluster config binding. |
| src/v/config/configuration.cc | Define default_redpanda_storage_mode_tiered_impl with docs and defaults. |
| src/v/cluster_link/utils/topic_properties_utils.h | Add helper to combine (mode, impl) into a safe storage-mode update. |
| src/v/cluster_link/utils/topic_properties_utils.cc | Implement (mode, impl) combination + gating + transition validation. |
| src/v/cluster_link/utils/BUILD | Link cluster_link utils against features for gating logic. |
| src/v/cluster_link/topic_reconciler.h | Inject feature_table into reconciler for upgrade gating and mode updates. |
| src/v/cluster_link/topic_reconciler.cc | Apply storage mode from (mode, impl) pair and gate tiered_v2 on upgrades. |
| src/v/cluster_link/tests/topic_reconciler_test.cc | Add tests for gating behavior in storage-mode updates. |
| src/v/cluster_link/tests/link_test.cc | Update manager construction for new feature_table dependency. |
| src/v/cluster_link/tests/deps.h | Add feature_table to test fixture wiring. |
| src/v/cluster_link/tests/deps.cc | Start/activate feature_table in test fixture wiring. |
| src/v/cluster_link/tests/BUILD | Add features + topic_properties_utils deps for cluster_link tests. |
| src/v/cluster_link/service.cc | Pass controller feature_table into cluster_link manager. |
| src/v/cluster_link/model/types.h | Sync redpanda.storage.mode.impl in default-synced topic properties. |
| src/v/cluster_link/manager.h | Plumb feature_table pointer into manager. |
| src/v/cluster_link/manager.cc | Plumb feature_table pointer into topic_reconciler construction. |
| src/v/cloud_topics/level_zero/notifier/tests/level_zero_notifier_test.cc | Add feature-gated no-op coverage for notifier calls pre-upgrade. |
| src/v/cloud_topics/level_zero/notifier/tests/BUILD | Add features dependency for notifier tests. |
| src/v/cloud_topics/level_zero/notifier/level_zero_notifier.h | Gate notifications on tiered_cloud_topics feature activation. |
| src/v/cloud_topics/level_zero/notifier/level_zero_notifier.cc | Implement gating logic (no-op until feature active). |
| src/v/cloud_topics/level_zero/notifier/BUILD | Add features dependency for notifier component. |
| src/v/cloud_topics/app.cc | Pass controller feature_table to the L0 notifier. |
Comment on lines
+82
to
+85
| // Read-only companion of redpanda.storage.mode: the resolved variant | ||
| // (tiered_v1/tiered_v2) of a topic in the tiered storage mode. Accepted on | ||
| // topic creation (only together with redpanda.storage.mode=tiered) to pick a | ||
| // non-default variant; not alterable. |
Comment on lines
+552
to
+553
| * The optional redpanda.storage.mode.impl property picks the tiered | ||
| * variant and is only valid together with redpanda.storage.mode=tiered. |
Comment on lines
+272
to
+276
| if ( | ||
| cfg.properties.storage_mode | ||
| == ::model::redpanda_storage_mode::tiered_cloud | ||
| && !_feature_table->local().is_active( | ||
| features::feature::tiered_cloud_topics)) { |
Comment on lines
+337
to
+347
| try { | ||
| if ( | ||
| utils::maybe_append_storage_mode_update( | ||
| update, | ||
| find_config(kafka::topic_property_redpanda_storage_mode), | ||
| find_config(kafka::topic_property_redpanda_storage_mode_impl), | ||
| local_topic_config, | ||
| _feature_table->local())) { | ||
| config_updated = true; | ||
| } | ||
| } catch (const std::exception& e) { |
Collaborator
Retry command for Build#86956please wait until all jobs are finished before running the slash command |
Collaborator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Reworks the storage-mode toggle ducktape test so it reproduces a data
unreadability bug in the mode-flip / L0-GC interaction (report:
https://gist.github.com/nvartolomei/fdb274b629a2184ec63709eb740d8c5d).
After a
cloud→tiered_v2storage-mode flip, the tiered read branchserves everything at or above the local log's start offset from the local
log, ignoring the last-reconciled offset. The local log still holds
placeholders for the already-reconciled region, and L0 GC is allowed to
delete the objects those placeholders reference once their epoch goes
inactive and the minimum object age passes. Reading that region then has
to materialize placeholders against deleted objects, which fails with
download_not_foundand no L1 fallback — even though L1 holds a completecopy of the data. The flip also freezes local prefix-truncation
(tiered_v2 honors full topic retention), so the log never self-heals by
trimming the placeholder region.
The test shrinks
cloud_topics_short_term_gc_minimum_object_age(12h →30s) and the epoch/housekeeping intervals that gate GC eligibility,
disables the batch cache, ends the toggling in
tiered_v2,trickle-produces until the reconciled L0 objects are collected, wipes the
cloud-storage disk cache with a rolling restart, and reads the whole log
back. The read wedges on the placeholder region (~94k
Failed to materialize sorted run: download_not_foundwarnings in alocal run) instead of returning every acked record.
This PR is a reproducer: the altered test is expected to FAIL until the
bug is fixed. It is opened as a draft to anchor the discussion of the
fix (make the tiered read branch respect the last-reconciled offset, fall
back to L1 on materialization failure, or resume prefix-truncation of the
reconciled region after a flip).
Depends on #30966 (the storage-mode impl rework), whose commits are
included underneath.
Backports Required
Release Notes