@@ -165,9 +165,7 @@ impl Coordinator {
165165 let mut sources_to_drop = vec ! [ ] ;
166166 let mut replication_slots_to_drop: Vec < ( PostgresConnection , String ) > = vec ! [ ] ;
167167 let mut storage_sink_gids_to_drop = vec ! [ ] ;
168- let mut indexes_to_drop = vec ! [ ] ;
169- let mut materialized_views_to_drop = vec ! [ ] ;
170- let mut continual_tasks_to_drop = vec ! [ ] ;
168+ let mut compute_gids_to_drop = vec ! [ ] ;
171169 let mut view_gids_to_drop = vec ! [ ] ;
172170 let mut secrets_to_drop = vec ! [ ] ;
173171 let mut vpc_endpoints_to_drop = vec ! [ ] ;
@@ -188,6 +186,11 @@ impl Coordinator {
188186 let mut storage_policies_to_initialize = BTreeMap :: new ( ) ;
189187 let mut execution_timestamps_to_set = BTreeSet :: new ( ) ;
190188
189+ // Replacing a materialized view causes the replacement's catalog entry
190+ // to be dropped. Its compute and storage collections are transferred to
191+ // the target MV though, so we must make sure not to drop those.
192+ let mut replacement_gids = vec ! [ ] ;
193+
191194 // We're incrementally migrating the code that manipulates the
192195 // controller from closures in the sequencer. For some types of catalog
193196 // changes we haven't done this migration yet, so there you will see
@@ -311,7 +314,7 @@ impl Coordinator {
311314 ) ;
312315 }
313316 CatalogImplication :: Index ( CatalogImplicationKind :: Dropped ( index, full_name) ) => {
314- indexes_to_drop . push ( ( index. cluster_id , index. global_id ( ) ) ) ;
317+ compute_gids_to_drop . push ( ( index. cluster_id , index. global_id ( ) ) ) ;
315318 dropped_item_names. insert ( index. global_id ( ) , full_name) ;
316319 }
317320 CatalogImplication :: MaterializedView ( CatalogImplicationKind :: Added ( mv) ) => {
@@ -321,17 +324,20 @@ impl Coordinator {
321324 prev : prev_mv,
322325 new : new_mv,
323326 } ) => {
324- tracing:: debug!(
325- ?prev_mv,
326- ?new_mv,
327- "not handling AlterMaterializedView in here yet"
328- ) ;
327+ let old_gid = prev_mv. global_id_writes ( ) ;
328+ let new_gid = new_mv. global_id_writes ( ) ;
329+ if new_gid != old_gid {
330+ replacement_gids. push ( ( new_mv. cluster_id , new_gid) ) ;
331+ }
332+
333+ self . handle_alter_materialized_view ( prev_mv, new_mv) ?;
329334 }
330335 CatalogImplication :: MaterializedView ( CatalogImplicationKind :: Dropped (
331336 mv,
332337 full_name,
333338 ) ) => {
334- materialized_views_to_drop. push ( ( mv. cluster_id , mv. global_id_writes ( ) ) ) ;
339+ compute_gids_to_drop. push ( ( mv. cluster_id , mv. global_id_writes ( ) ) ) ;
340+ sources_to_drop. extend ( mv. global_ids ( ) . map ( |gid| ( catalog_id, gid) ) ) ;
335341 dropped_item_names. insert ( mv. global_id_writes ( ) , full_name) ;
336342 }
337343 CatalogImplication :: View ( CatalogImplicationKind :: Added ( view) ) => {
@@ -364,7 +370,8 @@ impl Coordinator {
364370 ct,
365371 _full_name,
366372 ) ) => {
367- continual_tasks_to_drop. push ( ( catalog_id, ct. cluster_id , ct. global_id ( ) ) ) ;
373+ compute_gids_to_drop. push ( ( ct. cluster_id , ct. global_id ( ) ) ) ;
374+ sources_to_drop. push ( ( catalog_id, ct. global_id ( ) ) ) ;
368375 }
369376 CatalogImplication :: Secret ( CatalogImplicationKind :: Added ( secret) ) => {
370377 tracing:: debug!( ?secret, "not handling AddSecret in here yet" ) ;
@@ -506,6 +513,12 @@ impl Coordinator {
506513 }
507514 }
508515
516+ // Cancel out drops against replacements.
517+ for ( cluster_id, gid) in replacement_gids {
518+ sources_to_drop. retain ( |( _, id) | * id != gid) ;
519+ compute_gids_to_drop. retain ( |id| * id != ( cluster_id, gid) ) ;
520+ }
521+
509522 if !source_collections_to_create. is_empty ( ) {
510523 self . create_source_collections ( source_collections_to_create)
511524 . await ?;
@@ -530,9 +543,7 @@ impl Coordinator {
530543 . map ( |( _, gid) | * gid)
531544 . chain ( tables_to_drop. iter ( ) . map ( |( _, gid) | * gid) )
532545 . chain ( storage_sink_gids_to_drop. iter ( ) . copied ( ) )
533- . chain ( indexes_to_drop. iter ( ) . map ( |( _, id) | * id) )
534- . chain ( materialized_views_to_drop. iter ( ) . map ( |( _, id) | * id) )
535- . chain ( continual_tasks_to_drop. iter ( ) . map ( |( _, _, gid) | * gid) )
546+ . chain ( compute_gids_to_drop. iter ( ) . map ( |( _, gid) | * gid) )
536547 . chain ( view_gids_to_drop. iter ( ) . copied ( ) )
537548 . collect ( ) ;
538549
@@ -598,27 +609,14 @@ impl Coordinator {
598609 }
599610 }
600611
601- let storage_ids_to_drop : BTreeSet < _ > = sources_to_drop
612+ let storage_gids_to_drop : BTreeSet < _ > = sources_to_drop
602613 . iter ( )
603614 . map ( |( _id, gid) | gid)
604615 . chain ( storage_sink_gids_to_drop. iter ( ) )
605616 . chain ( tables_to_drop. iter ( ) . map ( |( _id, gid) | gid) )
606- . chain ( materialized_views_to_drop. iter ( ) . map ( |( _, id) | id) )
607- . chain ( continual_tasks_to_drop. iter ( ) . map ( |( _, _, gid) | gid) )
608617 . copied ( )
609618 . collect ( ) ;
610619
611- let compute_ids_to_drop: BTreeSet < _ > = indexes_to_drop
612- . iter ( )
613- . copied ( )
614- . chain ( materialized_views_to_drop. iter ( ) . copied ( ) )
615- . chain (
616- continual_tasks_to_drop
617- . iter ( )
618- . map ( |( _, cluster_id, gid) | ( * cluster_id, * gid) ) ,
619- )
620- . collect ( ) ;
621-
622620 // Gather resources that we have to remove from timeline state and
623621 // pre-check if any Timelines become empty, when we drop the specified
624622 // storage and compute resources.
@@ -630,13 +628,13 @@ impl Coordinator {
630628 let mut id_bundle = CollectionIdBundle :: default ( ) ;
631629
632630 for storage_id in read_holds. storage_ids ( ) {
633- if storage_ids_to_drop . contains ( & storage_id) {
631+ if storage_gids_to_drop . contains ( & storage_id) {
634632 id_bundle. storage_ids . insert ( storage_id) ;
635633 }
636634 }
637635
638636 for ( instance_id, id) in read_holds. compute_ids ( ) {
639- if compute_ids_to_drop . contains ( & ( instance_id, id) )
637+ if compute_gids_to_drop . contains ( & ( instance_id, id) )
640638 || clusters_to_drop. contains ( & instance_id)
641639 {
642640 id_bundle
@@ -719,16 +717,8 @@ impl Coordinator {
719717 }
720718 }
721719
722- if !indexes_to_drop. is_empty ( ) {
723- self . drop_indexes ( indexes_to_drop) ;
724- }
725-
726- if !materialized_views_to_drop. is_empty ( ) {
727- self . drop_materialized_views ( materialized_views_to_drop) ;
728- }
729-
730- if !continual_tasks_to_drop. is_empty ( ) {
731- self . drop_continual_tasks ( continual_tasks_to_drop) ;
720+ if !compute_gids_to_drop. is_empty ( ) {
721+ self . drop_compute_collections ( compute_gids_to_drop) ;
732722 }
733723
734724 if !vpc_endpoints_to_drop. is_empty ( ) {
@@ -1133,6 +1123,36 @@ impl Coordinator {
11331123 Ok ( ( ) )
11341124 }
11351125
1126+ #[ instrument( level = "debug" ) ]
1127+ fn handle_alter_materialized_view (
1128+ & mut self ,
1129+ prev_mv : MaterializedView ,
1130+ new_mv : MaterializedView ,
1131+ ) -> Result < ( ) , AdapterError > {
1132+ let old_gid = prev_mv. global_id_writes ( ) ;
1133+ let new_gid = new_mv. global_id_writes ( ) ;
1134+
1135+ if old_gid == new_gid {
1136+ // It's not an ALTER MATERIALIZED VIEW as far as the controller is
1137+ // concerned, because we still have the same GlobalId. This is
1138+ // likely a change from an ALTER SWAP.
1139+ return Ok ( ( ) ) ;
1140+ }
1141+
1142+ // Cut over the MV computation, by shutting down the old dataflow and allowing the
1143+ // new dataflow to start writing.
1144+ //
1145+ // Both commands are applied to their respective clusters asynchronously and they
1146+ // race, so it is possible that we allow writes by the replacement before having
1147+ // dropped the old dataflow. Thus there might be a period where the two dataflows
1148+ // are competing in trying to commit conflicting outputs. Eventually the old
1149+ // dataflow will be dropped and the replacement takes over.
1150+ self . drop_compute_collections ( vec ! [ ( prev_mv. cluster_id, old_gid) ] ) ;
1151+ self . allow_writes ( new_mv. cluster_id , new_gid) ;
1152+
1153+ Ok ( ( ) )
1154+ }
1155+
11361156 #[ instrument( level = "debug" ) ]
11371157 async fn handle_create_source (
11381158 & mut self ,
0 commit comments