Skip to main content

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
184pub struct ActorAddr {
185    id: ActorId,
186    addr: SocketAddr,
187}
188
189impl PartialEq for ActorAddr {
190    fn eq(&self, other: &Self) -> bool {
191        // this happens if we're announcing ourselves to another node
192        // we don't yet have any info about them, except their gossip addr
193        if self.id.is_nil() || other.id.is_nil() {
194            self.addr.eq(&other.addr)
195        } else {
196            self.id.eq(&other.id)
197        }
198    }
199}
200
201impl Identity for Actor {
202    type Addr = ActorAddr;
203
204    // Since a client outside the cluster will not be aware of our
205    // `bump` field, we implement the optional trait method
206    // `has_same_prefix` to allow anyone that knows our `addr`
207    // to join our cluster.
208    fn addr(&self) -> Self::Addr {
209        ActorAddr {
210            id: self.id,
211            addr: self.addr,
212        }
213    }
214
215    // And by implementing `renew` we enable automatic rejoining:
216    // when another member declares us as down, Foca immediatelly
217    // switches to this new identity and rejoins the cluster for us
218    fn renew(&self) -> Option<Self> {
219        Some(Self {
220            id: self.id,
221            addr: self.addr,
222            ts: NTP64::from(duration_since_epoch()).into(),
223            cluster_id: self.cluster_id,
224        })
225    }
226
227    fn win_addr_conflict(&self, adversary: &Self) -> bool {
228        self.ts > adversary.ts
229    }
230}
231
232fn duration_since_epoch() -> Duration {
233    SystemTime::now()
234        .duration_since(SystemTime::UNIX_EPOCH)
235        .expect("could not generate duration since unix epoch")
236}
237
238#[derive(
239    Debug, Default, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize,
240)]
241#[serde(transparent)]
242pub struct ClusterId(pub u16);
243
244impl fmt::Display for ClusterId {
245    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
246        self.0.fmt(f)
247    }
248}
249
250impl<'a, C> Readable<'a, C> for ClusterId
251where
252    C: Context,
253{
254    #[inline]
255    fn read_from<R: Reader<'a, C>>(reader: &mut R) -> Result<Self, C::Error> {
256        Ok(ClusterId(u16::read_from(reader)?))
257    }
258
259    #[inline]
260    fn minimum_bytes_needed() -> usize {
261        2
262    }
263}
264
265impl<C> Writable<C> for ClusterId
266where
267    C: Context,
268{
269    #[inline]
270    fn write_to<T: ?Sized + Writer<C>>(&self, writer: &mut T) -> Result<(), C::Error> {
271        self.0.write_to(writer)
272    }
273
274    #[inline]
275    fn bytes_needed(&self) -> Result<usize, C::Error> {
276        Writable::<C>::bytes_needed(&self.0)
277    }
278}
279
280impl ToSql for ClusterId {
281    fn to_sql(&self) -> rusqlite::Result<ToSqlOutput<'_>> {
282        self.0.to_sql()
283    }
284}
285
286impl FromSql for ClusterId {
287    fn column_result(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult<Self> {
288        Ok(Self(FromSql::column_result(value)?))
289    }
290}