use crate::double_write::{DwbDegradation, DwbMirror, DwbProtection, DwbSkipReason, metrics};
use crate::record::WalRecord;
use super::config::{open_dwb_at, resolve_dwb_mode};
use super::core::WalWriter;
impl WalWriter {
pub fn dwb_protection(&self) -> DwbProtection {
self.dwb_protection
}
pub fn dwb_unprotected_records(&self) -> u64 {
self.dwb_unprotected_records
}
pub fn reattach_double_write(&mut self) -> DwbProtection {
let Some(dwb_path) = self.dwb_path.clone() else {
return self.dwb_protection;
};
match open_dwb_at(&dwb_path, resolve_dwb_mode(&self.config)) {
Some(dwb) => {
self.double_write = Some(dwb);
self.dwb_protection = DwbProtection::Active;
}
None => self.degrade_dwb(DwbDegradation::OpenFailed),
}
self.dwb_protection
}
pub(super) fn mirror_into_dwb(&mut self, lsn: u64, record: &WalRecord) {
let mirrored = self
.double_write
.as_mut()
.map(|dwb| dwb.write_record_deferred(record));
match mirrored {
Some(Ok(DwbMirror::Mirrored)) => {}
Some(Ok(DwbMirror::Skipped(reason))) => self.note_unprotected_record(lsn, reason),
Some(Err(e)) => {
tracing::warn!(lsn = lsn, error = %e, "DWB write failed, detaching DWB");
self.double_write = None;
self.degrade_dwb(DwbDegradation::WriteFailed);
self.note_unprotected();
}
None => {
if self.dwb_protection.is_degraded() {
self.note_unprotected();
}
}
}
}
pub(super) fn flush_dwb(&mut self) {
let staged = self
.double_write
.as_ref()
.map_or(0, |dwb| dwb.staged_slots());
let written = self.double_write.as_mut().map(|dwb| dwb.write_staged());
if let Some(Err(e)) = written {
tracing::warn!(error = %e, staged, "DWB slot write failed, detaching DWB");
self.double_write = None;
self.degrade_dwb(DwbDegradation::WriteFailed);
for _ in 0..staged {
self.note_unprotected();
}
return;
}
let flushed = self.double_write.as_mut().map(|dwb| dwb.flush());
if let Some(Err(e)) = flushed {
tracing::warn!(error = %e, "DWB flush failed, detaching DWB");
self.double_write = None;
self.degrade_dwb(DwbDegradation::FlushFailed);
}
}
fn note_unprotected_record(&mut self, lsn: u64, reason: DwbSkipReason) {
tracing::warn!(
lsn = lsn,
reason = %reason,
"record appended without torn-write protection"
);
self.note_unprotected();
}
fn note_unprotected(&mut self) {
self.dwb_unprotected_records = self.dwb_unprotected_records.saturating_add(1);
metrics::add_unprotected_record();
}
fn degrade_dwb(&mut self, degradation: DwbDegradation) {
if !self.dwb_protection.is_degraded() {
metrics::add_degradation();
}
self.dwb_protection = DwbProtection::Degraded(degradation);
}
}