|
25 | 25 | #include "cluster/partition_recovery_manager.h" |
26 | 26 | #include "cluster/topic_configuration.h" |
27 | 27 | #include "cluster/types.h" |
| 28 | +#include "features/feature_table.h" |
28 | 29 | #include "model/metadata.h" |
29 | 30 | #include "raft/consensus.h" |
30 | 31 | #include "raft/consensus_utils.h" |
31 | 32 | #include "raft/fundamental.h" |
32 | 33 | #include "ssx/async-clear.h" |
| 34 | +#include "ssx/future-util.h" |
33 | 35 |
|
34 | 36 | #include <seastar/core/lowres_clock.hh> |
35 | 37 | #include <seastar/core/shared_ptr.hh> |
@@ -73,6 +75,11 @@ partition_manager::partition_manager( |
73 | 75 | if (a) { |
74 | 76 | a.value().get().notify_leadership(leader_id); |
75 | 77 | } |
| 78 | + // Bootstrap the partition's durable storage mode on becoming |
| 79 | + // leader (one-time, idempotent; no-op when not leader). The |
| 80 | + // shared_ptr keeps the partition alive across the async write. |
| 81 | + ssx::spawn_with_gate( |
| 82 | + _gate, [p] { return p->maybe_bootstrap_partition_mode(); }); |
76 | 83 | } |
77 | 84 | }); |
78 | 85 | _shutdown_watchdog.set_callback( |
@@ -100,9 +107,36 @@ partition_manager::get_topic_partition_table( |
100 | 107 |
|
101 | 108 | ss::future<> partition_manager::start() { |
102 | 109 | maybe_arm_shutdown_watchdog(); |
| 110 | + // Bootstrap partition_mode once the migration feature activates. The |
| 111 | + // leadership-notification bootstrap (maybe_bootstrap_partition_mode) is |
| 112 | + // gated on the feature being active, so a partition that became leader |
| 113 | + // while the feature was inactive is left with partition_mode == unset. |
| 114 | + // Re-run the (idempotent) bootstrap on every current leader when the |
| 115 | + // feature turns active; partitions that gain leadership after activation |
| 116 | + // are covered by the leadership notification. |
| 117 | + ssx::spawn_with_gate(_gate, [this] { |
| 118 | + return bootstrap_partition_mode_on_migration_feature(); |
| 119 | + }); |
103 | 120 | co_return; |
104 | 121 | } |
105 | 122 |
|
| 123 | +ss::future<> |
| 124 | +partition_manager::bootstrap_partition_mode_on_migration_feature() { |
| 125 | + try { |
| 126 | + co_await _feature_table.local().await_feature( |
| 127 | + features::feature::tiered_to_cloud_migration, _as); |
| 128 | + } catch (...) { |
| 129 | + // Aborted on shutdown before the feature activated. |
| 130 | + co_return; |
| 131 | + } |
| 132 | + for (auto& [_, p] : _ntp_table) { |
| 133 | + if (p->is_leader()) { |
| 134 | + ssx::spawn_with_gate( |
| 135 | + _gate, [p] { return p->maybe_bootstrap_partition_mode(); }); |
| 136 | + } |
| 137 | + } |
| 138 | +} |
| 139 | + |
106 | 140 | ss::future<consensus_ptr> partition_manager::manage( |
107 | 141 | storage::ntp_config ntp_cfg, |
108 | 142 | raft::group_id group, |
@@ -330,6 +364,17 @@ ss::future<consensus_ptr> partition_manager::manage( |
330 | 364 | read_replica_bucket, |
331 | 365 | _cloud_topics_state); |
332 | 366 |
|
| 367 | + // Populate partition_mode at creation when the feature is active, so it is |
| 368 | + // classified deterministically here from the topic config rather than via |
| 369 | + // the racy runtime bootstrap. The bootstrap remains for the feature-switch |
| 370 | + // case (a partition that predates the feature activation). Left unset when |
| 371 | + // the feature is inactive. |
| 372 | + if ( |
| 373 | + _feature_table.local().is_active( |
| 374 | + features::feature::tiered_to_cloud_migration)) { |
| 375 | + p->set_creation_partition_mode(log->config().topic_mode()); |
| 376 | + } |
| 377 | + |
333 | 378 | _ntp_table.emplace(log->config().ntp(), p); |
334 | 379 | _raft_table.emplace(group, p); |
335 | 380 |
|
|
0 commit comments