use crate::vfs::SyncKind;
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum DurabilityMode {
#[default]
Derived,
Durable,
Attached,
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum CommitTier {
None,
#[default]
Ordered,
Durable,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum SyncRequirement {
Skip,
Sync(SyncKind),
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct DurabilityPolicy {
data_file: SyncRequirement,
directory: SyncRequirement,
}
impl DurabilityPolicy {
pub const fn new(
mode: DurabilityMode,
tier: CommitTier,
) -> Result<Self, DurabilityPolicyError> {
let requirement = match (mode, tier) {
(DurabilityMode::Attached, _) => {
return Err(DurabilityPolicyError::AttachedNotYetSupported);
}
(DurabilityMode::Derived, _) | (DurabilityMode::Durable, CommitTier::None) => {
SyncRequirement::Skip
}
(DurabilityMode::Durable, CommitTier::Ordered) => {
SyncRequirement::Sync(SyncKind::Barrier)
}
(DurabilityMode::Durable, CommitTier::Durable) => SyncRequirement::Sync(SyncKind::Full),
};
Ok(Self {
data_file: requirement,
directory: requirement,
})
}
#[must_use]
pub const fn data_file_sync(self) -> SyncRequirement {
self.data_file
}
#[must_use]
pub const fn directory_sync(self) -> SyncRequirement {
self.directory
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum DurabilityPolicyError {
AttachedNotYetSupported,
}
impl std::fmt::Display for DurabilityPolicyError {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::AttachedNotYetSupported => {
formatter.write_str("attached durability mode is not yet supported")
}
}
}
}
impl std::error::Error for DurabilityPolicyError {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[allow(clippy::expect_used)]
fn default_policy_skips_every_synchronization() {
let policy = DurabilityPolicy::new(DurabilityMode::default(), CommitTier::default())
.expect("default policy");
assert_eq!(policy.data_file_sync(), SyncRequirement::Skip);
assert_eq!(policy.directory_sync(), SyncRequirement::Skip);
}
#[test]
#[allow(clippy::expect_used)]
fn derived_and_durable_none_resolve_to_the_same_policy() {
let durable_none = DurabilityPolicy::new(DurabilityMode::Durable, CommitTier::None)
.expect("durable none policy");
for tier in [CommitTier::None, CommitTier::Ordered, CommitTier::Durable] {
let derived =
DurabilityPolicy::new(DurabilityMode::Derived, tier).expect("derived policy");
assert_eq!(
derived, durable_none,
"Derived with {tier:?} must match Durable with None"
);
}
}
#[test]
#[allow(clippy::expect_used)]
fn maps_every_mode_and_tier_without_attached_fallback() {
let cases = [
(
DurabilityMode::Derived,
CommitTier::None,
SyncRequirement::Skip,
),
(
DurabilityMode::Derived,
CommitTier::Ordered,
SyncRequirement::Skip,
),
(
DurabilityMode::Derived,
CommitTier::Durable,
SyncRequirement::Skip,
),
(
DurabilityMode::Durable,
CommitTier::None,
SyncRequirement::Skip,
),
(
DurabilityMode::Durable,
CommitTier::Ordered,
SyncRequirement::Sync(SyncKind::Barrier),
),
(
DurabilityMode::Durable,
CommitTier::Durable,
SyncRequirement::Sync(SyncKind::Full),
),
];
for (mode, tier, expected) in cases {
let policy = DurabilityPolicy::new(mode, tier).expect("supported policy");
assert_eq!(
(policy.data_file_sync(), policy.directory_sync()),
(expected, expected),
"policy mismatch for ({mode:?}, {tier:?})"
);
}
for tier in [CommitTier::None, CommitTier::Ordered, CommitTier::Durable] {
let error = DurabilityPolicy::new(DurabilityMode::Attached, tier)
.expect_err("Attached must fail rather than fall back");
assert_eq!(error, DurabilityPolicyError::AttachedNotYetSupported);
assert_eq!(
error.to_string(),
"attached durability mode is not yet supported"
);
}
assert_eq!(DurabilityMode::default(), DurabilityMode::Derived);
assert_eq!(CommitTier::default(), CommitTier::Ordered);
}
}