11use std:: path:: { Path , PathBuf } ;
22use std:: sync:: Arc ;
33
4- use libsql_replication :: LIBSQL_PAGE_SIZE ;
4+ use libsql_sys :: ffi :: Sqlite3DbHeader ;
55use libsql_sys:: wal:: Sqlite3WalManager ;
66use libsql_wal:: io:: StdIO ;
77use libsql_wal:: replication:: injector:: Injector ;
88use libsql_wal:: segment:: { Frame , FrameHeader } ;
9- use libsql_wal:: storage:: Storage ;
10- use libsql_wal:: { registry:: WalRegistry , segment :: sealed :: SealedSegment } ;
9+ use libsql_wal:: storage:: NoStorage ;
10+ use libsql_wal:: registry:: WalRegistry ;
1111use tempfile:: TempDir ;
1212use tokio:: io:: AsyncReadExt ;
1313use tokio_stream:: StreamExt ;
14- use zerocopy:: FromZeroes ;
14+ use zerocopy:: { FromZeroes , FromBytes } ;
1515
1616#[ cfg( not( feature = "durable-wal" ) ) ]
1717use libsql_sys:: wal:: either:: Either as EitherWAL ;
@@ -27,14 +27,11 @@ use crate::namespace::NamespaceStore;
2727
2828/// The process for migrating from bottomless to libsql wal is simple:
2929/// 1) iteratate over all namespaces, and make sure that they
30- pub async fn bottomless_migrate < S > (
30+ pub async fn bottomless_migrate (
3131 meta_store : MetaStore ,
32- storage : Arc < S > ,
3332 base_config : BaseNamespaceConfig ,
3433 primary_config : PrimaryConfig ,
3534) -> anyhow:: Result < ( ) >
36- where
37- S : Storage < Segment = SealedSegment < std:: fs:: File > > ,
3835{
3936 tracing:: info!( "attempting bottomless migration to libsql-wal" ) ;
4037
4643 let configs_stream = meta_store. namespaces ( ) ;
4744 tokio:: pin!( configs_stream) ;
4845
49- let ( sender, _) = tokio:: sync:: mpsc:: channel ( 1 ) ;
50- let tmp_registry = Arc :: new ( WalRegistry :: new ( tmp. path ( ) . join ( "wals" ) , storage, sender) ?) ;
46+ let ( sender, mut rcv) = tokio:: sync:: mpsc:: channel ( 1 ) ;
47+
48+ tokio:: spawn ( async move {
49+ loop {
50+ match rcv. recv ( ) . await {
51+ Some ( libsql_wal:: checkpointer:: CheckpointMessage :: Shutdown ) => {
52+ break
53+ }
54+ Some ( _) => ( ) ,
55+ None => break ,
56+ }
57+ }
58+ } ) ;
59+
60+ let tmp_registry = Arc :: new ( WalRegistry :: new ( tmp. path ( ) . join ( "wals" ) , NoStorage . into ( ) , sender) ?) ;
5161
5262 let mut configurators = NamespaceConfigurators :: default ( ) ;
5363
7888 . await ?;
7989 }
8090
91+ tmp_registry. shutdown ( ) . await ?;
92+
93+ // FIXME: this is not atomic!!
94+ tokio:: fs:: remove_dir_all ( base_config. base_path . join ( "dbs" ) ) . await ?;
95+ tokio:: fs:: rename ( tmp. path ( ) . join ( "dbs" ) , base_config. base_path . join ( "dbs" ) ) . await ?;
96+ tokio:: fs:: rename ( tmp. path ( ) . join ( "wals" ) , base_config. base_path . join ( "wals" ) ) . await ?;
97+
98+
8199 Ok ( ( ) )
82100}
83101
@@ -88,20 +106,18 @@ where
88106/// - either the migration didn't happen
89107/// - a crash happened before we could swap the directories
90108#[ tracing:: instrument( skip_all, fields( namespace = config. namespace( ) . as_str( ) ) ) ]
91- async fn migrate_one < S > (
109+ async fn migrate_one (
92110 configurators : & NamespaceConfigurators ,
93111 config : MetaStoreHandle ,
94112 dummy_store : NamespaceStore ,
95113 tmp : & Path ,
96- tmp_registry : Arc < WalRegistry < StdIO , S > > ,
114+ tmp_registry : Arc < WalRegistry < StdIO , NoStorage > > ,
97115 base_path : & Path ,
98116) -> anyhow:: Result < ( ) >
99- where
100- S : Storage < Segment = SealedSegment < std:: fs:: File > > ,
101117{
102118 let broadcasters = BroadcasterRegistry :: default ( ) ;
103119 // TODO: check if we already have a backup for this db from storage
104- tracing:: info!( "started db migrating " ) ;
120+ tracing:: info!( "started namespace migration " ) ;
105121 // we load the namespace ensuring it's restored to the latest version
106122 configurators
107123 . configure_primary ( ) ?
@@ -118,13 +134,16 @@ where
118134 )
119135 . await ?;
120136
121- let db_path = tmp
137+ let db_dir = tmp
122138 . join ( "dbs" )
123- . join ( config. namespace ( ) . as_str ( ) )
124- . join ( "data" ) ;
139+ . join ( config. namespace ( ) . as_str ( ) ) ;
140+ tokio:: fs:: create_dir_all ( & db_dir) . await ?;
141+ let db_path = db_dir. join ( "data" ) ;
125142 let registry = tmp_registry. clone ( ) ;
126143 let namespace = config. namespace ( ) . clone ( ) ;
127- let shared = tokio:: task:: spawn_blocking ( move || registry. open ( & db_path, & namespace. into ( ) ) )
144+ let shared = tokio:: task:: spawn_blocking ( {
145+ let registry = registry. clone ( ) ;
146+ move || registry. open ( & db_path, & namespace. into ( ) ) } )
128147 . await
129148 . unwrap ( )
130149 . unwrap ( ) ;
@@ -141,18 +160,35 @@ where
141160 . join ( config. namespace ( ) . as_str ( ) )
142161 . join ( "data" ) ;
143162 let mut orig_db_file = tokio:: fs:: File :: open ( orig_db_path) . await ?;
144- let orig_db_file_len = orig_db_file. metadata ( ) . await ?. len ( ) ;
145- for i in 0 ..( orig_db_file_len / LIBSQL_PAGE_SIZE as u64 ) {
163+ let mut db_size = usize:: MAX ;
164+ let mut current = 0 ;
165+ while current < db_size {
146166 let mut frame: Box < Frame > = Frame :: new_box_zeroed ( ) ;
167+ orig_db_file. read_exact ( frame. data_mut ( ) ) . await ?;
168+ if current == 0 {
169+ let header: Sqlite3DbHeader = Sqlite3DbHeader :: read_from_prefix ( frame. data ( ) ) . unwrap ( ) ;
170+ db_size = header. db_size . get ( ) as usize ;
171+ }
172+ let size_after = if current == db_size - 1 {
173+ db_size as u32
174+ } else {
175+ 0
176+ } ;
147177 * frame. header_mut ( ) = FrameHeader {
148- page_no : ( i as u32 + 1 ) . into ( ) ,
149- size_after : 0 . into ( ) ,
150- frame_no : ( i + 1 ) . into ( ) ,
178+ page_no : ( current as u32 + 1 ) . into ( ) ,
179+ size_after : size_after . into ( ) ,
180+ frame_no : ( current as u64 + 1 ) . into ( ) ,
151181 } ;
152- orig_db_file. read_exact ( frame. data_mut ( ) ) . await ?;
153182 injector. insert_frame ( frame) . await ?;
183+ current += 1 ;
154184 }
155185
186+ drop ( injector) ;
187+
188+ tokio:: task:: spawn_blocking ( move || {
189+ shared. seal_current ( )
190+ } ) . await . unwrap ( ) ?;
191+
156192 tracing:: info!( "sucessfull migration" ) ;
157193
158194 Ok ( ( ) )
0 commit comments