@@ -190,6 +190,7 @@ impl Default for MigrationResult {
190190/// required migrations, respectively.
191191pub ( super ) async fn run (
192192 build_info : & BuildInfo ,
193+ deploy_generation : u64 ,
193194 txn : & mut Transaction < ' _ > ,
194195 config : BuiltinItemMigrationConfig ,
195196) -> Result < MigrationResult , Error > {
@@ -223,6 +224,7 @@ pub(super) async fn run(
223224 let migration = Migration :: new (
224225 durable_version. clone ( ) ,
225226 build_version. clone ( ) ,
227+ deploy_generation,
226228 txn,
227229 builtins,
228230 config,
@@ -243,6 +245,7 @@ pub(super) async fn run(
243245struct Migration < ' a , ' b > {
244246 source_version : Version ,
245247 target_version : Version ,
248+ deploy_generation : u64 ,
246249 txn : & ' a mut Transaction < ' b > ,
247250 builtins : BTreeMap < SystemObjectDescription , & ' static Builtin < NameReference > > ,
248251 object_ids : BTreeMap < SystemObjectDescription , SystemObjectUniqueIdentifier > ,
@@ -253,6 +256,7 @@ impl<'a, 'b> Migration<'a, 'b> {
253256 fn new (
254257 source_version : Version ,
255258 target_version : Version ,
259+ deploy_generation : u64 ,
256260 txn : & ' a mut Transaction < ' b > ,
257261 builtins : BTreeMap < SystemObjectDescription , & ' static Builtin < NameReference > > ,
258262 config : BuiltinItemMigrationConfig ,
@@ -265,6 +269,7 @@ impl<'a, 'b> Migration<'a, 'b> {
265269 Self {
266270 source_version,
267271 target_version,
272+ deploy_generation,
268273 txn,
269274 builtins,
270275 object_ids,
@@ -600,7 +605,7 @@ impl<'a, 'b> Migration<'a, 'b> {
600605 }
601606
602607 /// Try to get or insert replacement shards for the given IDs into the migration shard, at
603- /// `target_version`.
608+ /// `target_version` and `deploy_generation` .
604609 ///
605610 /// This method looks for existing entries in the migration shards and returns those if they
606611 /// are present. Otherwise it generates new shard IDs and tries to insert them.
@@ -617,13 +622,13 @@ impl<'a, 'b> Migration<'a, 'b> {
617622 let upper = persist_write. fetch_recent_upper ( ) . await ;
618623 let write_ts = * upper. as_option ( ) . expect ( "migration shard not sealed" ) ;
619624
620- // Another process might already have done a shard replacement at our version, in which
621- // case we can directly reuse the replacement shards.
622- //
623- // TODO: We should check for deploy generation as well as build version. Storing the deploy
624- // generation as well requires changing the key schema of the migration shard.
625+ // Another process might already have done a shard replacement at our version and
626+ // generation, in which case we can directly reuse the replacement shards.
625627 if let Some ( read_ts) = write_ts. step_back ( ) {
626- let pred = |key : & migration_shard:: Key | key. build_version == self . target_version ;
628+ let pred = |key : & migration_shard:: Key | {
629+ key. build_version == self . target_version
630+ && key. deploy_generation == Some ( self . deploy_generation )
631+ } ;
627632 if let Some ( entries) = read_migration_shard ( persist_read, read_ts, pred) . await {
628633 let replaced_shards: BTreeMap < _ , _ > = entries
629634 . into_iter ( )
@@ -659,6 +664,7 @@ impl<'a, 'b> Migration<'a, 'b> {
659664 let key = migration_shard:: Key {
660665 global_id,
661666 build_version : self . target_version . clone ( ) ,
667+ deploy_generation : Some ( self . deploy_generation ) ,
662668 } ;
663669 updates. push ( ( ( key, shard_id) , write_ts, 1 ) ) ;
664670 }
@@ -939,23 +945,41 @@ mod migration_shard {
939945 use mz_persist_types:: columnar:: Schema ;
940946 use mz_persist_types:: stats:: NoneStats ;
941947 use semver:: Version ;
948+ use serde:: { Deserialize , Serialize } ;
942949
943- #[ derive( Debug , Clone , Eq , Ord , PartialEq , PartialOrd ) ]
950+ #[ derive( Debug , Clone , Eq , Ord , PartialEq , PartialOrd , Serialize , Deserialize ) ]
944951 pub ( super ) struct Key {
945952 pub ( super ) global_id : u64 ,
946953 pub ( super ) build_version : Version ,
954+ // Versions < 26.0 didn't include the deploy generation. As long as we still might
955+ // encounter migration shard entries that don't have it, we need to keep this an `Option`
956+ // and keep supporting both key formats.
957+ pub ( super ) deploy_generation : Option < u64 > ,
947958 }
948959
949960 impl fmt:: Display for Key {
950961 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
951- write ! ( f, "{}-{}" , self . global_id, self . build_version)
962+ if self . deploy_generation . is_some ( ) {
963+ // current format
964+ let s = serde_json:: to_string ( self ) . expect ( "JSON serializable" ) ;
965+ f. write_str ( & s)
966+ } else {
967+ // pre-26.0 format
968+ write ! ( f, "{}-{}" , self . global_id, self . build_version)
969+ }
952970 }
953971 }
954972
955973 impl FromStr for Key {
956974 type Err = String ;
957975
958976 fn from_str ( s : & str ) -> Result < Self , String > {
977+ // current format
978+ if let Ok ( key) = serde_json:: from_str ( s) {
979+ return Ok ( key) ;
980+ } ;
981+
982+ // pre-26.0 format
959983 let parts: Vec < _ > = s. splitn ( 2 , '-' ) . collect ( ) ;
960984 let & [ global_id, build_version] = parts. as_slice ( ) else {
961985 return Err ( format ! ( "invalid Key '{s}'" ) ) ;
@@ -967,6 +991,7 @@ mod migration_shard {
967991 Ok ( Key {
968992 global_id,
969993 build_version,
994+ deploy_generation : None ,
970995 } )
971996 }
972997 }
@@ -976,6 +1001,7 @@ mod migration_shard {
9761001 Self {
9771002 global_id : Default :: default ( ) ,
9781003 build_version : Version :: new ( 0 , 0 , 0 ) ,
1004+ deploy_generation : Some ( 0 ) ,
9791005 }
9801006 }
9811007 }
0 commit comments