MCPcopy Create free account
hub / github.com/NodeDB-Lab/nodedb / recover_record

Method recover_record

nodedb-wal/src/double_write.rs:355–417  ·  view source on GitHub ↗

Try to recover a WAL record by LSN from the double-write buffer. Scans **all** DWB_CAPACITY slots for a record matching the given LSN with valid CRC. We scan every slot rather than relying on `count` or `write_pos` because the header itself may be stale or corrupted after a crash. Each slot is self-describing: the record's own CRC validates whether the slot contains usable data.

(&mut self, target_lsn: u64)

Source from the content-addressed store, hash-verified

353 /// a crash. Each slot is self-describing: the record's own CRC validates
354 /// whether the slot contains usable data.
355 pub fn recover_record(&mut self, target_lsn: u64) -> Result<Option<WalRecord>> {
356 #[cfg(target_arch = "wasm32")]
357 {
358 let _ = target_lsn;
359 return Ok(None);
360 }
361
362 #[cfg(not(target_arch = "wasm32"))]
363 {
364 use std::os::unix::io::AsRawFd as _;
365
366 // Under O_DIRECT, reads must also use aligned buffers and aligned
367 // lengths. Read one full aligned slot at a time, then parse.
368 let mut slot = AlignedBuf::new(DWB_SLOT_STRIDE, DEFAULT_ALIGNMENT)?;
369
370 for i in 0..DWB_CAPACITY as u32 {
371 let offset = slot_offset(i);
372 // SAFETY: slot.as_mut_ptr is valid for `capacity()` bytes.
373 let read = unsafe {
374 libc::pread(
375 self.file.as_raw_fd(),
376 slot.as_mut_ptr() as *mut libc::c_void,
377 DWB_SLOT_STRIDE,
378 offset as libc::off_t,
379 )
380 };
381 if read <= 0 {
382 continue;
383 }
384 // SAFETY: the kernel populated `read` bytes starting at the buffer.
385 let bytes: &[u8] =
386 unsafe { std::slice::from_raw_parts(slot.as_ptr(), read as usize) };
387 if bytes.len() < 4 + HEADER_SIZE {
388 continue;
389 }
390
391 let mut arr4 = [0u8; 4];
392 arr4.copy_from_slice(&bytes[0..4]);
393 let total_size = u32::from_le_bytes(arr4) as usize;
394 if !(HEADER_SIZE..=DWB_SLOT_PAYLOAD_MAX).contains(&total_size)
395 || bytes.len() < 4 + total_size
396 {
397 continue;
398 }
399
400 let mut header_buf = [0u8; HEADER_SIZE];
401 header_buf.copy_from_slice(&bytes[4..4 + HEADER_SIZE]);
402 let header = RecordHeader::from_bytes(&header_buf);
403 if header.magic != WAL_MAGIC || header.lsn != target_lsn {
404 continue;
405 }
406
407 let payload_len = total_size - HEADER_SIZE;
408 let payload = bytes[4 + HEADER_SIZE..4 + HEADER_SIZE + payload_len].to_vec();
409 let record = WalRecord { header, payload };
410 if record.verify_checksum().is_ok() {
411 return Ok(Some(record));
412 }

Callers 8

write_and_recoverFunction · 0.80
survives_reopenFunction · 0.80
recover_after_wraparoundFunction · 0.80
next_recordMethod · 0.80
read_payloadMethod · 0.80

Calls 9

slot_offsetFunction · 0.85
is_okMethod · 0.80
verify_checksumMethod · 0.80
as_raw_fdMethod · 0.45
as_mut_ptrMethod · 0.45
as_ptrMethod · 0.45
lenMethod · 0.45
containsMethod · 0.45
to_vecMethod · 0.45

Tested by 6

write_and_recoverFunction · 0.64
survives_reopenFunction · 0.64
recover_after_wraparoundFunction · 0.64