11use etl:: destination:: Destination ;
22use etl:: error:: { ErrorKind , EtlError , EtlResult } ;
33use etl:: store:: schema:: SchemaStore ;
4- use etl:: store:: state:: { DestinationSchemaState , DestinationSchemaStateType , StateStore } ;
4+ use etl:: store:: state:: { DestinationSchemaState , StateStore } ;
55use etl:: types:: {
66 Cell , Event , ReplicatedTableSchema , SchemaDiff , TableId , TableName , TableRow ,
77 generate_sequence_number,
@@ -346,6 +346,18 @@ where
346346 // Note that if the table is deleted outside ETL and the cache marks it as created, the
347347 // inserts will fail because the table will be missing and won't be created.
348348 if !inner. created_tables . contains ( & sequenced_bigquery_table_id) {
349+ let snapshot_id = replicated_table_schema. get_inner ( ) . snapshot_id ;
350+ let replication_mask = replicated_table_schema. replication_mask ( ) . clone ( ) ;
351+
352+ // Mark the state as applying BEFORE creating the table. This ensures that if the
353+ // process crashes during table creation, we'll detect the incomplete state on restart.
354+ self . state_store
355+ . store_destination_schema_state (
356+ table_id,
357+ DestinationSchemaState :: applying ( snapshot_id, replication_mask. clone ( ) ) ,
358+ )
359+ . await ?;
360+
349361 self . client
350362 . create_table_if_missing (
351363 & self . dataset_id ,
@@ -356,17 +368,11 @@ where
356368 )
357369 . await ?;
358370
359- // Store the initial destination schema state with the current snapshot and mask.
360- // This is needed so that when schema changes occur, we can reconstruct the old
361- // ReplicatedTableSchema for accurate diffing.
371+ // Mark as applied after successful table creation.
362372 self . state_store
363373 . store_destination_schema_state (
364374 table_id,
365- DestinationSchemaState {
366- state : DestinationSchemaStateType :: Applied ,
367- snapshot_id : replicated_table_schema. get_inner ( ) . snapshot_id ,
368- replication_mask : replicated_table_schema. replication_mask ( ) . clone ( ) ,
369- } ,
375+ DestinationSchemaState :: applied ( snapshot_id, replication_mask) ,
370376 )
371377 . await ?;
372378
@@ -572,7 +578,7 @@ where
572578 )
573579 ) ;
574580 }
575- Some ( state) if state. state == DestinationSchemaStateType :: Applying => {
581+ Some ( state) if state. is_applying ( ) => {
576582 // A previous schema change was interrupted, require manual intervention.
577583 // The previous valid snapshot_id can be derived from table_schemas table
578584 // by finding the second-highest snapshot_id for this table.
@@ -587,7 +593,7 @@ where
587593 )
588594 ) ;
589595 }
590- Some ( state) if state. state == DestinationSchemaStateType :: Applied => {
596+ Some ( state) if state. is_applied ( ) => {
591597 let current_snapshot_id = state. snapshot_id ;
592598 let current_replication_mask = state. replication_mask . clone ( ) ;
593599
@@ -626,15 +632,16 @@ where
626632 let old_schema =
627633 ReplicatedTableSchema :: from_mask ( old_table_schema, current_replication_mask) ;
628634
635+ let new_replication_mask = new_schema. replication_mask ( ) . clone ( ) ;
636+
629637 // Mark as applying before making changes (with the NEW snapshot_id and mask).
630638 self . state_store
631639 . store_destination_schema_state (
632640 table_id,
633- DestinationSchemaState {
634- state : DestinationSchemaStateType :: Applying ,
635- snapshot_id : new_snapshot_id,
636- replication_mask : new_schema. replication_mask ( ) . clone ( ) ,
637- } ,
641+ DestinationSchemaState :: applying (
642+ new_snapshot_id,
643+ new_replication_mask. clone ( ) ,
644+ ) ,
638645 )
639646 . await ?;
640647
@@ -652,11 +659,7 @@ where
652659 self . state_store
653660 . store_destination_schema_state (
654661 table_id,
655- DestinationSchemaState {
656- state : DestinationSchemaStateType :: Applied ,
657- snapshot_id : new_snapshot_id,
658- replication_mask : new_schema. replication_mask ( ) . clone ( ) ,
659- } ,
662+ DestinationSchemaState :: applied ( new_snapshot_id, new_replication_mask) ,
660663 )
661664 . await ?;
662665
0 commit comments