krabka_replicator/ids.rs
1//! Domain newtypes for the replicator crate.
2//!
3//! MirrorMaker-2 offset translation juggles three different `i64` offsets that
4//! all live in different address spaces:
5//!
6//! - [`UpstreamOffset`][]: an offset on the **source** cluster, as recorded in an
7//! offset-sync or checkpoint.
8//! - [`DownstreamOffset`][]: the corresponding offset on the **target** cluster.
9//! - [`CommittedOffset`][]: a consumer group's committed **source** offset that
10//! translation moves to the target.
11//!
12//! A call site can transpose these values. For example, it can build an
13//! [`crate::mm2::OffsetSync`] as
14//! `{ upstream: downstream_val, downstream: upstream_val }`, or write the
15//! `translate()` math as `up + (committed - down)`. Such code compiles cleanly
16//! when every value is a bare `i64`, and it silently corrupts offset
17//! translation. A separate type for each meaning turns those swaps into compile
18//! errors.
19//!
20//! The internal MM2 wire codec (`Writer` / `Reader`) still
21//! reads and writes raw `i64`/`i32`, so byte-exactness with the JVM MM2 codecs is
22//! preserved: these newtypes wrap only the in-memory domain values and unwrap via
23//! `.0` at the codec boundary.
24//!
25//! See the [newtype guidance] in the style guide.
26//!
27//! [newtype guidance]: ../../../docs/style_guides/code_style_guide.md
28
29use std::cmp::Ordering;
30
31use derive_more::{Display, From, Into};
32/// The canonical cross-crate `PartitionIndex`, as carried in offset-syncs,
33/// checkpoints, and replicated records. The MM2 offset newtypes below stay
34/// crate-local. They encode source-cluster and target-cluster distinctions that
35/// are specific to MirrorMaker-2 and have no meaning outside this crate.
36pub use krabka_ids::{Offset, PartitionIndex};
37
38/// An offset on the **source** cluster, as recorded in an offset-sync or
39/// checkpoint (`upstream` in the JVM MM2 codecs).
40#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Display, From, Into)]
41pub struct UpstreamOffset(pub i64);
42
43/// An offset on the **target** cluster, paired with an [`UpstreamOffset`] in an
44/// offset-sync or checkpoint (`downstream` in the JVM MM2 codecs).
45#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Display, From, Into)]
46pub struct DownstreamOffset(pub i64);
47
48/// A consumer group's committed **source** offset that translation moves to the
49/// target. It lives in the same address space as [`UpstreamOffset`], because
50/// both are source-cluster offsets. It is a distinct concept at the translation
51/// call site, so it gets its own type.
52#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Display, From, Into)]
53pub struct CommittedOffset(pub i64);
54
55/// A record timestamp in epoch milliseconds.
56#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Display, From, Into)]
57pub struct Timestamp(pub i64);
58
59macro_rules! impl_primitive_cmp {
60 ($ty:ty, $inner:ty) => {
61 impl PartialEq<$inner> for $ty {
62 #[inline]
63 fn eq(&self, other: &$inner) -> bool {
64 self.0 == *other
65 }
66 }
67
68 impl PartialEq<$ty> for $inner {
69 #[inline]
70 fn eq(&self, other: &$ty) -> bool {
71 *self == other.0
72 }
73 }
74
75 impl PartialOrd<$inner> for $ty {
76 #[inline]
77 fn partial_cmp(&self, other: &$inner) -> Option<Ordering> {
78 self.0.partial_cmp(other)
79 }
80 }
81
82 impl PartialOrd<$ty> for $inner {
83 #[inline]
84 fn partial_cmp(&self, other: &$ty) -> Option<Ordering> {
85 self.partial_cmp(&other.0)
86 }
87 }
88 };
89}
90
91impl_primitive_cmp!(UpstreamOffset, i64);
92impl_primitive_cmp!(DownstreamOffset, i64);
93impl_primitive_cmp!(CommittedOffset, i64);
94impl_primitive_cmp!(Timestamp, i64);
95
96impl From<Offset> for UpstreamOffset {
97 #[inline]
98 fn from(value: Offset) -> Self {
99 Self(value.0)
100 }
101}
102
103impl From<CommittedOffset> for UpstreamOffset {
104 #[inline]
105 fn from(value: CommittedOffset) -> Self {
106 Self(value.0)
107 }
108}