@@ -688,7 +688,10 @@ impl<Clock: LogicalClock> CheckpointStateMachine<Clock> {
688688 /// Cleanup path for I/O errors that happen while waiting on completions outside
689689 /// of `step()`. This mirrors `step()` error handling and also resets pager/WAL
690690 /// checkpoint bookkeeping.
691- pub fn cleanup_after_external_io_error ( & mut self ) {
691+ pub fn cleanup_after_external_io_error ( & mut self , err : LimboError ) -> Result < ( ) > {
692+ // run storage cleanup within proper checkpoint context (e.g. pager has pending read/write txn)
693+ let result = self . mvstore . storage . on_checkpoint_end ( Err ( err) ) ;
694+
692695 if self . lock_states . pager_write_tx {
693696 self . pager . rollback_tx ( self . connection . as_ref ( ) ) ;
694697 if self . update_transaction_state {
@@ -716,6 +719,8 @@ impl<Clock: LogicalClock> CheckpointStateMachine<Clock> {
716719 self . checkpoint_lock . unlock ( ) ;
717720 self . lock_states . blocking_checkpoint_lock_held = false ;
718721 }
722+
723+ result
719724 }
720725
721726 /// Returns all checkpointable [RowVersion]s for that `table_id`
@@ -1558,6 +1563,7 @@ impl<Clock: LogicalClock> CheckpointStateMachine<Clock> {
15581563 * special_write,
15591564 )
15601565 } ;
1566+ tracing:: debug!( "WriteRow: num_columns={num_columns}, table_id={table_id:?}, special_write={special_write:?}" ) ;
15611567
15621568 // Handle CREATE TABLE / DROP TABLE / CREATE INDEX / DROP INDEX ops
15631569 if let Some ( special_write) = special_write {
@@ -1703,6 +1709,8 @@ impl<Clock: LogicalClock> CheckpointStateMachine<Clock> {
17031709 } )
17041710 } ;
17051711
1712+ tracing:: debug!( "WriteRow: resolved root page: root_page={root_page}" ) ;
1713+
17061714 // If a table was created, it now has a real root page allocated for it, but the 'root_page' field in the sqlite_schema record is still the table id.
17071715 // So we need to rewrite the row version to use the real root page.
17081716 if let Some ( SpecialWrite :: BTreeCreate {
@@ -2255,9 +2263,11 @@ impl<Clock: LogicalClock> StateTransition for CheckpointStateMachine<Clock> {
22552263 let res = self . step_inner ( & ( ) ) ;
22562264 match res {
22572265 Err ( ref err) => {
2258- self . mvstore . storage . on_checkpoint_end ( Err ( err. clone ( ) ) ) ?;
22592266 tracing:: debug!( "Error in checkpoint state machine: {err}" ) ;
2260- self . cleanup_after_external_io_error ( ) ;
2267+ // `cleanup_after_external_io_error` already emits the paired
2268+ // `on_checkpoint_end(Err(..))`, so don't call it here too — doing both
2269+ // double-fires the hook for a single failure.
2270+ self . cleanup_after_external_io_error ( err. clone ( ) ) ?;
22612271 res
22622272 }
22632273 Ok ( TransitionResult :: Done ( ref result) ) => {
0 commit comments