r-epaxos icon indicating copy to clipboard operation
r-epaxos copied to clipboard

ID types

Open Nugine opened this issue 3 years ago • 2 comments

A ID type should not export its raw value implicitly. (Do not impl Deref)

The arithmetics of a ID type should be explict methods with overflow/underflow checks instead of built-in operators.

Using the raw value of ReplicaId can lead to unexpected issues when adding members to the cluster.

Nugine avatar Apr 02 '22 14:04 Nugine

All the IDs in EPaxos are natural numbers or their tuples. They can be sorted unstably, which is a notable property.

Nugine avatar Apr 02 '22 15:04 Nugine

#![deny(clippy::missing_inline_in_public_items, clippy::missing_const_for_fn)]

use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct ReplicaId(u64);

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct LocalInstanceId(u64);

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct Seq(u64);

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct Epoch(u64);

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct InstanceId(pub ReplicaId, pub LocalInstanceId);

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct Round(u64);

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct Ballot(pub Epoch, pub Round, pub ReplicaId);

macro_rules! impl_newtype {
    ($($ty: ident($inner: ident),)+) => {
        $(
            impl From<$inner> for $ty {
                #[inline]
                #[must_use]
                #[track_caller]
                fn from(val: $inner) -> Self {
                    assert!(val != 0, concat!("Zero ", stringify!($ty), " is reserved"));
                    Self(val)
                }
            }

            impl $ty {
                pub const ZERO: Self = Self(0);

                #[inline]
                #[must_use]
                pub const fn raw_value(self) -> $inner {
                    self.0
                }
            }
        )+
    };
}

impl_newtype!(
    ReplicaId(u64),
    LocalInstanceId(u64),
    Epoch(u64),
    Seq(u64),
    Round(u64),
);

macro_rules! impl_add_one {
    ($($ty: ident,)+) => {
        $(
            impl $ty {
                #[inline]
                #[must_use]
                #[track_caller]
                pub fn add_one(self) -> Self {
                    Self(self.0.checked_add(1).expect(concat!(stringify!($ty), " overflow")))
                }
            }
        )+
    };
}

impl_add_one!(LocalInstanceId, Seq, Round,);

macro_rules! impl_sub_one {
    ($($ty: ident,)+) => {
        $(
            impl $ty {
                #[inline]
                #[must_use]
                #[track_caller]
                pub fn sub_one(self) -> Self {
                    Self(self.0.checked_sub(1).expect(concat!(stringify!($ty), " underflow")))
                }
            }
        )+
    };
}

impl_sub_one!(LocalInstanceId,);

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    #[should_panic]
    fn overflow() {
        let _ = Seq::from(u64::MAX).add_one();
    }

    #[test]
    #[should_panic]
    fn underflow() {
        let _ = LocalInstanceId::ZERO.sub_one();
    }

    #[test]
    #[should_panic]
    fn nonzero() {
        let _ = ReplicaId::from(0);
    }
}

Nugine avatar Apr 02 '22 15:04 Nugine