klukai_types/
actor.rs

1use std::{
2    fmt,
3    hash::Hash,
4    net::SocketAddr,
5    ops::Deref,
6    time::{Duration, SystemTime},
7};
8
9use crate::api::SqliteValue;
10use foca::Identity;
11use rusqlite::{
12    ToSql,
13    types::{FromSql, ToSqlOutput},
14};
15use serde::{Deserialize, Serialize};
16use speedy::{Context, Readable, Reader, Writable, Writer};
17use uhlc::NTP64;
18use uuid::Uuid;
19
20use crate::broadcast::Timestamp;
21
22#[derive(
23    Debug, Default, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize,
24)]
25#[serde(transparent)]
26pub struct ActorId(pub Uuid);
27
28impl ActorId {
29    pub fn to_bytes(&self) -> [u8; 16] {
30        self.0.into_bytes()
31    }
32
33    pub fn as_bytes(&self) -> &[u8; 16] {
34        self.0.as_bytes()
35    }
36
37    pub fn from_bytes(bytes: [u8; 16]) -> Self {
38        Self(Uuid::from_bytes(bytes))
39    }
40}
41
42impl TryFrom<ActorId> for uhlc::ID {
43    type Error = uhlc::SizeError;
44
45    fn try_from(value: ActorId) -> Result<Self, Self::Error> {
46        value.as_bytes().try_into()
47    }
48}
49
50impl Deref for ActorId {
51    type Target = Uuid;
52
53    fn deref(&self) -> &Self::Target {
54        &self.0
55    }
56}
57
58impl fmt::Display for ActorId {
59    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60        self.0.as_simple().fmt(f)
61    }
62}
63
64const UUID_SIZE: usize = 16;
65
66#[derive(Debug, thiserror::Error)]
67pub enum SqliteValueToActorIdError {
68    #[error("sqlite value had wrong type")]
69    WrongType,
70    #[error("wrong number of bytes, requires exactly 16 bytes")]
71    WrongNumberOfBytes,
72}
73
74impl TryFrom<&SqliteValue> for ActorId {
75    type Error = SqliteValueToActorIdError;
76
77    fn try_from(value: &SqliteValue) -> Result<Self, Self::Error> {
78        match value.as_blob() {
79            Some(v) => {
80                if v.len() != UUID_SIZE {
81                    Err(SqliteValueToActorIdError::WrongNumberOfBytes)
82                } else {
83                    Ok(ActorId::from_bytes(v.try_into().unwrap()))
84                }
85            }
86            None => Err(SqliteValueToActorIdError::WrongType),
87        }
88    }
89}
90
91impl<'a, C> Readable<'a, C> for ActorId
92where
93    C: Context,
94{
95    #[inline]
96    fn read_from<R: Reader<'a, C>>(reader: &mut R) -> Result<Self, C::Error> {
97        Ok(ActorId(Uuid::from_bytes(reader.read_value()?)))
98    }
99
100    #[inline]
101    fn minimum_bytes_needed() -> usize {
102        UUID_SIZE
103    }
104}
105
106impl<C> Writable<C> for ActorId
107where
108    C: Context,
109{
110    #[inline]
111    fn write_to<T: ?Sized + Writer<C>>(&self, writer: &mut T) -> Result<(), C::Error> {
112        writer.write_bytes(self.0.as_bytes())
113    }
114
115    #[inline]
116    fn bytes_needed(&self) -> Result<usize, C::Error> {
117        Ok(UUID_SIZE)
118    }
119}
120
121impl ToSql for ActorId {
122    fn to_sql(&self) -> rusqlite::Result<ToSqlOutput<'_>> {
123        self.0.to_sql()
124    }
125}
126
127impl FromSql for ActorId {
128    fn column_result(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult<Self> {
129        Ok(Self(FromSql::column_result(value)?))
130    }
131}
132
133#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
134pub struct Actor {
135    id: ActorId,
136    addr: SocketAddr,
137    ts: Timestamp,
138    #[serde(default)]
139    cluster_id: ClusterId,
140}
141
142impl Hash for Actor {
143    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
144        self.id.hash(state);
145        self.addr.hash(state);
146    }
147}
148
149impl Actor {
150    pub fn new(id: ActorId, addr: SocketAddr, ts: Timestamp, cluster_id: ClusterId) -> Self {
151        Self {
152            id,
153            addr,
154            ts,
155            cluster_id,
156        }
157    }
158
159    pub fn id(&self) -> ActorId {
160        self.id
161    }
162    pub fn addr(&self) -> SocketAddr {
163        self.addr
164    }
165    pub fn ts(&self) -> Timestamp {
166        self.ts
167    }
168    pub fn cluster_id(&self) -> ClusterId {
169        self.cluster_id
170    }
171}
172
173impl From<SocketAddr> for Actor {
174    fn from(value: SocketAddr) -> Self {
175        Self::new(
176            ActorId(Uuid::nil()),
177            value,
178            Timestamp::zero(),
179            Default::default(),
180        )
181    }
182}
183
184impl Identity for Actor {
185    type Addr = SocketAddr;
186
187    fn addr(&self) -> Self::Addr {
188        self.addr
189    }
190
191    fn win_addr_conflict(&self, adversary: &Self) -> bool {
192        // Win if we have a more recent timestamp
193        self.ts > adversary.ts
194    }
195
196    // And by implementing `renew` we enable automatic rejoining:
197    // when another member declares us as down, Foca immediatelly
198    // switches to this new identity and rejoins the cluster for us
199    fn renew(&self) -> Option<Self> {
200        Some(Self {
201            id: self.id,
202            addr: self.addr,
203            ts: NTP64::from(duration_since_epoch()).into(),
204            cluster_id: self.cluster_id,
205        })
206    }
207}
208
209fn duration_since_epoch() -> Duration {
210    SystemTime::now()
211        .duration_since(SystemTime::UNIX_EPOCH)
212        .expect("could not generate duration since unix epoch")
213}
214
215#[derive(
216    Debug, Default, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize,
217)]
218#[serde(transparent)]
219pub struct ClusterId(pub u16);
220
221impl fmt::Display for ClusterId {
222    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
223        self.0.fmt(f)
224    }
225}
226
227impl<'a, C> Readable<'a, C> for ClusterId
228where
229    C: Context,
230{
231    #[inline]
232    fn read_from<R: Reader<'a, C>>(reader: &mut R) -> Result<Self, C::Error> {
233        Ok(ClusterId(u16::read_from(reader)?))
234    }
235
236    #[inline]
237    fn minimum_bytes_needed() -> usize {
238        2
239    }
240}
241
242impl<C> Writable<C> for ClusterId
243where
244    C: Context,
245{
246    #[inline]
247    fn write_to<T: ?Sized + Writer<C>>(&self, writer: &mut T) -> Result<(), C::Error> {
248        self.0.write_to(writer)
249    }
250
251    #[inline]
252    fn bytes_needed(&self) -> Result<usize, C::Error> {
253        Writable::<C>::bytes_needed(&self.0)
254    }
255}
256
257impl ToSql for ClusterId {
258    fn to_sql(&self) -> rusqlite::Result<ToSqlOutput<'_>> {
259        self.0.to_sql()
260    }
261}
262
263impl FromSql for ClusterId {
264    fn column_result(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult<Self> {
265        Ok(Self(FromSql::column_result(value)?))
266    }
267}