use std::fmt;
use std::time::Duration;
use openraft_macros::since;
use crate::raft::ReadPolicy;
#[since(version = "0.10.0", change = "renamed from `LinearizableReadRequest`")]
#[since(version = "0.10.0")]
#[derive(Debug, Clone)]
pub struct LinearizerOption {
pub(crate) max_quorum_ack_age: Option<Duration>,
pub(crate) heartbeat_if_quorum_ack_stale: bool,
pub(crate) wait_timeout: Option<Duration>,
}
impl fmt::Display for LinearizerOption {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"LinearizerOption {{ max_quorum_ack_age: {:?}, heartbeat_if_quorum_ack_stale: {}, wait_timeout: {:?} }}",
self.max_quorum_ack_age, self.heartbeat_if_quorum_ack_stale, self.wait_timeout
)
}
}
impl LinearizerOption {
#[since(version = "0.10.0")]
pub fn new(max_quorum_ack_age: Option<Duration>, heartbeat_if_quorum_ack_stale: bool) -> Self {
Self {
max_quorum_ack_age,
heartbeat_if_quorum_ack_stale,
wait_timeout: None,
}
}
#[since(version = "0.10.0")]
pub fn with_wait_timeout(mut self, wait_timeout: Duration) -> Self {
self.wait_timeout = Some(wait_timeout);
self
}
pub(crate) fn effective_max_quorum_ack_age(&self, leader_lease: Duration) -> Duration {
let requested_age = self.max_quorum_ack_age.unwrap_or(leader_lease);
requested_age.min(leader_lease)
}
pub(crate) fn effective_wait_timeout(&self, leader_lease: Duration) -> Duration {
self.wait_timeout.unwrap_or(leader_lease)
}
pub(crate) fn from_read_policy(read_policy: ReadPolicy) -> Self {
match read_policy {
ReadPolicy::LeaseRead => Self::new(None, false).with_wait_timeout(Duration::ZERO),
ReadPolicy::ReadIndex => Self::new(Some(Duration::ZERO), true),
}
}
}
impl From<ReadPolicy> for LinearizerOption {
fn from(read_policy: ReadPolicy) -> Self {
Self::from_read_policy(read_policy)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_effective_max_quorum_ack_age() {
let leader_lease = Duration::from_millis(500);
let cases = [
(None, leader_lease),
(Some(Duration::ZERO), Duration::ZERO),
(Some(Duration::from_millis(300)), Duration::from_millis(300)),
(Some(leader_lease), leader_lease),
(Some(Duration::from_millis(700)), leader_lease),
];
for (max_quorum_ack_age, want) in cases {
let linearizer_option = LinearizerOption::new(max_quorum_ack_age, false);
let got = linearizer_option.effective_max_quorum_ack_age(leader_lease);
assert_eq!(want, got);
}
}
#[test]
fn test_effective_wait_timeout() {
let leader_lease = Duration::from_millis(500);
let cases = [
(None, leader_lease),
(Some(Duration::ZERO), Duration::ZERO),
(Some(Duration::from_millis(300)), Duration::from_millis(300)),
(Some(Duration::from_millis(700)), Duration::from_millis(700)),
];
for (wait_timeout, want) in cases {
let mut linearizer_option = LinearizerOption::new(None, false);
if let Some(wait_timeout) = wait_timeout {
linearizer_option = linearizer_option.with_wait_timeout(wait_timeout);
}
let got = linearizer_option.effective_wait_timeout(leader_lease);
assert_eq!(want, got);
}
}
#[test]
fn test_from_read_policy() {
let lease_read = LinearizerOption::from_read_policy(ReadPolicy::LeaseRead);
assert_eq!(None, lease_read.max_quorum_ack_age);
assert!(!lease_read.heartbeat_if_quorum_ack_stale);
assert_eq!(Some(Duration::ZERO), lease_read.wait_timeout);
let read_index = LinearizerOption::from_read_policy(ReadPolicy::ReadIndex);
assert_eq!(Some(Duration::ZERO), read_index.max_quorum_ack_age);
assert!(read_index.heartbeat_if_quorum_ack_stale);
assert_eq!(None, read_index.wait_timeout);
}
}