Skip to content

Commit b922257

Browse files
committed
read logid from db footer
1 parent 5b694b8 commit b922257

2 files changed

Lines changed: 46 additions & 1 deletion

File tree

libsql-wal/src/lib.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ const LIBSQL_MAGIC: u64 = u64::from_be_bytes(*b"LIBSQL\0\0");
1515
const LIBSQL_PAGE_SIZE: u16 = 4096;
1616
const LIBSQL_WAL_VERSION: u16 = 1;
1717

18-
use zerocopy::byteorder::big_endian::{U16 as bu16, U64 as bu64};
18+
use uuid::Uuid;
19+
use zerocopy::byteorder::big_endian::{U128 as bu128, U16 as bu16, U64 as bu64};
1920
/// LibsqlFooter is located at the end of the libsql file. I contains libsql specific metadata,
2021
/// while remaining fully compatible with sqlite (which just ignores that footer)
2122
///
@@ -29,6 +30,14 @@ pub struct LibsqlFooter {
2930
/// only valid if there are no outstanding segments to checkpoint, since a checkpoint could be
3031
/// partial.
3132
pub replication_index: bu64,
33+
/// Id of the log for this this database
34+
pub log_id: bu128,
35+
}
36+
37+
impl LibsqlFooter {
38+
pub fn log_id(&self) -> Uuid {
39+
Uuid::from_u128(self.log_id.get())
40+
}
3241
}
3342

3443
#[cfg(any(debug_assertions, test))]

libsql-wal/src/registry.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::io;
12
use std::num::NonZeroU64;
23
use std::path::{Path, PathBuf};
34
use std::sync::atomic::{AtomicBool, Ordering};
@@ -9,6 +10,7 @@ use parking_lot::{Condvar, Mutex};
910
use rand::Rng;
1011
use tokio::sync::{mpsc, Notify, Semaphore};
1112
use tokio::task::JoinSet;
13+
use uuid::Uuid;
1214
use zerocopy::{AsBytes, FromZeroes};
1315

1416
use crate::checkpointer::CheckpointMessage;
@@ -21,6 +23,7 @@ use crate::segment::{current::CurrentSegment, sealed::SealedSegment};
2123
use crate::shared_wal::{SharedWal, SwapLog};
2224
use crate::storage::{OnStoreCallback, Storage};
2325
use crate::transaction::TxGuard;
26+
use crate::{LibsqlFooter, LIBSQL_PAGE_SIZE};
2427
use libsql_sys::name::NamespaceName;
2528

2629
enum 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

Comments
 (0)