1+ use std:: io;
12use std:: num:: NonZeroU64 ;
23use std:: path:: { Path , PathBuf } ;
34use std:: sync:: atomic:: { AtomicBool , Ordering } ;
@@ -9,6 +10,7 @@ use parking_lot::{Condvar, Mutex};
910use rand:: Rng ;
1011use tokio:: sync:: { mpsc, Notify , Semaphore } ;
1112use tokio:: task:: JoinSet ;
13+ use uuid:: Uuid ;
1214use zerocopy:: { AsBytes , FromZeroes } ;
1315
1416use crate :: checkpointer:: CheckpointMessage ;
@@ -21,6 +23,7 @@ use crate::segment::{current::CurrentSegment, sealed::SealedSegment};
2123use crate :: shared_wal:: { SharedWal , SwapLog } ;
2224use crate :: storage:: { OnStoreCallback , Storage } ;
2325use crate :: transaction:: TxGuard ;
26+ use crate :: { LibsqlFooter , LIBSQL_PAGE_SIZE } ;
2427use libsql_sys:: name:: NamespaceName ;
2528
2629enum Slot < IO : Io > {
@@ -115,6 +118,7 @@ where
115118 current. db_size ( ) ,
116119 current. tail ( ) . clone ( ) ,
117120 salt,
121+ current. log_id ( ) ,
118122 ) ?;
119123 // sealing must the last fallible operation, because we don't want to end up in a situation
120124 // where the current log is sealed and it wasn't swapped.
@@ -271,6 +275,27 @@ where
271275 let mut header: Sqlite3DbHeader = Sqlite3DbHeader :: new_zeroed ( ) ;
272276 db_file. read_exact_at ( header. as_bytes_mut ( ) , 0 ) ?;
273277
278+ let log_id = if db_file. len ( ) ? <= LIBSQL_PAGE_SIZE as u64 && tail. is_empty ( ) {
279+ // this is a new database
280+ self . io . uuid ( )
281+ } else if let Some ( log_id) = tail. with_head ( |h| h. header ( ) . log_id . get ( ) ) {
282+ // there is a segment list, read the logid from there.
283+ let log_id = Uuid :: from_u128 ( log_id) ;
284+ #[ cfg( debug_assertions) ]
285+ {
286+ // if the main db file has footer, then the logid must match that of the segment
287+ if let Ok ( db_log_id) =
288+ read_log_id_from_footer ( & db_file, header. db_size . get ( ) as u64 )
289+ {
290+ assert_eq ! ( db_log_id, log_id) ;
291+ }
292+ }
293+
294+ log_id
295+ } else {
296+ read_log_id_from_footer ( & db_file, header. db_size . get ( ) as u64 ) ?
297+ } ;
298+
274299 let ( db_size, next_frame_no) = tail
275300 . with_head ( |segment| {
276301 let header = segment. header ( ) ;
@@ -294,6 +319,7 @@ where
294319 db_size,
295320 tail. into ( ) ,
296321 salt,
322+ log_id,
297323 ) ?) ) ;
298324
299325 let ( new_frame_notifier, _) = tokio:: sync:: watch:: channel ( next_frame_no. get ( ) - 1 ) ;
@@ -373,3 +399,13 @@ where
373399 Ok ( ( ) )
374400 }
375401}
402+
403+ fn read_log_id_from_footer < F : FileExt > ( db_file : & F , db_size : u64 ) -> io:: Result < Uuid > {
404+ let mut footer: LibsqlFooter = LibsqlFooter :: new_zeroed ( ) ;
405+ let footer_offset = LIBSQL_PAGE_SIZE as u64 * db_size;
406+ // FIXME: failing to read the footer here is a sign of corrupted database: either we
407+ // have a tail to the segment list, or we have fully checkpointed the database. Can we
408+ // recover from that?
409+ db_file. read_exact_at ( footer. as_bytes_mut ( ) , footer_offset) ?;
410+ Ok ( footer. log_id ( ) )
411+ }
0 commit comments