Write a WAL record to the DWB without fsyncing. The data is written to the OS page cache (Buffered mode) or directly to the block device (Direct mode) but not guaranteed durable until `flush()` is called. Use this in batch mode: write all records in a group commit batch, then call `flush()` once — reducing fsync calls from N-per-batch to 1-per-batch.
(&mut self, record: &WalRecord)
| 243 | /// group commit batch, then call `flush()` once — reducing fsync calls |
| 244 | /// from N-per-batch to 1-per-batch. |
| 245 | pub fn write_record_deferred(&mut self, record: &WalRecord) -> Result<()> { |
| 246 | let total_size = HEADER_SIZE + record.payload.len(); |
| 247 | |
| 248 | // Max 64 KiB per slot — larger records skip the double-write buffer |
| 249 | // (they're multi-page and need different protection). |
| 250 | if total_size > DWB_SLOT_PAYLOAD_MAX { |
| 251 | return Ok(()); // Skip oversized records. |
| 252 | } |
| 253 | |
| 254 | let header_bytes = record.header.to_bytes(); |
| 255 | let offset = slot_offset(self.write_pos); |
| 256 | |
| 257 | match self.mode { |
| 258 | DwbMode::Off => unreachable!("Off never opens a DoubleWriteBuffer"), |
| 259 | DwbMode::Buffered => { |
| 260 | self.file |
| 261 | .seek(SeekFrom::Start(offset)) |
| 262 | .map_err(WalError::Io)?; |
| 263 | self.file |
| 264 | .write_all(&(total_size as u32).to_le_bytes()) |
| 265 | .map_err(WalError::Io)?; |
| 266 | self.file.write_all(&header_bytes).map_err(WalError::Io)?; |
| 267 | self.file.write_all(&record.payload).map_err(WalError::Io)?; |
| 268 | DWB_BYTES_WRITTEN_TOTAL.fetch_add( |
| 269 | (4 + header_bytes.len() + record.payload.len()) as u64, |
| 270 | Ordering::Relaxed, |
| 271 | ); |
| 272 | } |
| 273 | DwbMode::Direct => { |
| 274 | let buf = self |
| 275 | .slot_buf |
| 276 | .as_mut() |
| 277 | .expect("slot_buf present in Direct mode"); |
| 278 | buf.clear(); |
| 279 | buf.write(&(total_size as u32).to_le_bytes()); |
| 280 | buf.write(&header_bytes); |
| 281 | buf.write(&record.payload); |
| 282 | // Zero the tail so the full aligned slot can be written |
| 283 | // without leaking prior contents. |
| 284 | zero_tail(buf); |
| 285 | let slice = full_capacity_slice(buf); |
| 286 | debug_assert_eq!(slice.len(), DWB_SLOT_STRIDE); |
| 287 | debug_assert!(is_aligned(offset as usize, DEFAULT_ALIGNMENT)); |
| 288 | pwrite_all(&self.file, slice, offset)?; |
| 289 | DWB_BYTES_WRITTEN_TOTAL.fetch_add(slice.len() as u64, Ordering::Relaxed); |
| 290 | } |
| 291 | } |
| 292 | |
| 293 | self.write_pos = self.write_pos.wrapping_add(1); |
| 294 | self.count = self.count.saturating_add(1).min(DWB_CAPACITY as u32); |
| 295 | self.dirty = true; |
| 296 | |
| 297 | Ok(()) |
| 298 | } |
| 299 | |
| 300 | /// Flush the DWB header and fsync the file. |
| 301 | /// |