re_types_core/id.rs
1/// A unique ID for a `Chunk`.
2///
3/// `Chunk`s are the atomic unit of ingestion, transport, storage, events and GC in Rerun.
4///
5/// Internally, a `Chunk` is made up of rows, which are themselves uniquely identified by
6/// their [`RowId`].
7///
8/// There is no relationship whatsoever between a [`ChunkId`] and the [`RowId`]s within that chunk.
9///
10/// ### Uniqueness
11///
12/// [`ChunkId`] are assumed unique within a single Recording.
13///
14/// The chunk store will treat two chunks with the same [`ChunkId`] as the same, and only keep one
15/// of them (which one is kept is an arbitrary and unstable implementation detail).
16///
17/// This makes it easy to build and maintain secondary indices around [`RowId`]s with few to no
18/// extraneous state tracking.
19///
20/// ### Garbage collection
21///
22/// Garbage collection is handled at the chunk level by first ordering the chunks based on the minimum
23/// [`RowId`] present in each of them.
24/// Garbage collection therefore happens (roughly) in the logger's wall-clock order.
25///
26/// This has very important implications when inserting data far into the past or into the future:
27/// think carefully about your `RowId`s in these cases.
28#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
29#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
30pub struct ChunkId(pub(crate) re_tuid::Tuid);
31
32impl std::fmt::Display for ChunkId {
33 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34 self.0.fmt(f)
35 }
36}
37
38impl ChunkId {
39 pub const ZERO: Self = Self(re_tuid::Tuid::ZERO);
40 pub const MAX: Self = Self(re_tuid::Tuid::MAX);
41
42 /// Create a new unique [`ChunkId`] based on the current time.
43 #[allow(clippy::new_without_default)]
44 #[inline]
45 pub fn new() -> Self {
46 Self(re_tuid::Tuid::new())
47 }
48
49 /// Returns the next logical [`ChunkId`].
50 ///
51 /// Beware: wrong usage can easily lead to conflicts.
52 /// Prefer [`ChunkId::new`] when unsure.
53 #[must_use]
54 #[inline]
55 pub fn next(&self) -> Self {
56 Self(self.0.next())
57 }
58
59 /// Returns the `n`-next logical [`ChunkId`].
60 ///
61 /// This is equivalent to calling [`ChunkId::next`] `n` times.
62 /// Wraps the monotonically increasing back to zero on overflow.
63 ///
64 /// Beware: wrong usage can easily lead to conflicts.
65 /// Prefer [`ChunkId::new`] when unsure.
66 #[must_use]
67 #[inline]
68 pub fn incremented_by(&self, n: u64) -> Self {
69 Self(self.0.incremented_by(n))
70 }
71
72 /// When the `ChunkId` was created, in nanoseconds since unix epoch.
73 #[inline]
74 pub fn nanoseconds_since_epoch(&self) -> u64 {
75 self.0.nanoseconds_since_epoch()
76 }
77
78 #[inline]
79 pub fn from_u128(id: u128) -> Self {
80 Self(re_tuid::Tuid::from_u128(id))
81 }
82
83 #[inline]
84 pub fn as_u128(&self) -> u128 {
85 self.0.as_u128()
86 }
87}
88
89impl re_byte_size::SizeBytes for ChunkId {
90 #[inline]
91 fn heap_size_bytes(&self) -> u64 {
92 0
93 }
94
95 #[inline]
96 fn is_pod() -> bool {
97 true
98 }
99}
100
101impl std::ops::Deref for ChunkId {
102 type Target = re_tuid::Tuid;
103
104 #[inline]
105 fn deref(&self) -> &Self::Target {
106 &self.0
107 }
108}
109
110impl std::ops::DerefMut for ChunkId {
111 #[inline]
112 fn deref_mut(&mut self) -> &mut Self::Target {
113 &mut self.0
114 }
115}
116
117crate::delegate_arrow_tuid!(ChunkId as "rerun.controls.ChunkId");
118
119// ---
120
121/// A unique ID for a row's worth of data within a chunk.
122///
123/// There is no relationship whatsoever between a [`ChunkId`] and the [`RowId`]s within that chunk.
124///
125/// ### Uniqueness
126///
127/// Duplicated [`RowId`]s within a single recording is considered undefined behavior.
128///
129/// While it is benign in most cases, care has to be taken when manually crafting [`RowId`]s.
130/// Ideally: don't do so and stick to [`RowId::new`] instead to avoid bad surprises.
131///
132/// This makes it easy to build and maintain secondary indices around [`RowId`]s with few to no
133/// extraneous state tracking.
134///
135/// ### Query
136///
137/// Queries (both latest-at & range semantics) will defer to `RowId` order as a tie-breaker when
138/// looking at several rows worth of data that rest at the exact same timestamp.
139///
140/// In pseudo-code:
141/// ```text
142/// rr.set_time_sequence("frame", 10)
143///
144/// rr.log("my_entity", point1, row_id=#1)
145/// rr.log("my_entity", point2, row_id=#0)
146///
147/// rr.query("my_entity", at=("frame", 10)) # returns `point1`
148/// ```
149///
150/// Think carefully about your `RowId`s when logging a lot of data at the same timestamp.
151///
152/// ### Garbage collection
153///
154/// Garbage collection is handled at the chunk level by first ordering the chunks based on the minimum
155/// [`RowId`] present in each of them.
156/// Garbage collection therefore happens (roughly) in the logger's wall-clock order.
157///
158/// This has very important implications when inserting data far into the past or into the future:
159/// think carefully about your `RowId`s in these cases.
160#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
161#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
162pub struct RowId(pub(crate) re_tuid::Tuid);
163
164impl std::fmt::Display for RowId {
165 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
166 self.0.fmt(f)
167 }
168}
169
170impl RowId {
171 pub const ZERO: Self = Self(re_tuid::Tuid::ZERO);
172 pub const MAX: Self = Self(re_tuid::Tuid::MAX);
173
174 /// Create a new unique [`RowId`] based on the current time.
175 #[allow(clippy::new_without_default)]
176 #[inline]
177 pub fn new() -> Self {
178 Self(re_tuid::Tuid::new())
179 }
180
181 #[inline]
182 pub fn from_tuid(tuid: re_tuid::Tuid) -> Self {
183 Self(tuid)
184 }
185
186 /// Returns the next logical [`RowId`].
187 ///
188 /// Beware: wrong usage can easily lead to conflicts.
189 /// Prefer [`RowId::new`] when unsure.
190 #[must_use]
191 #[inline]
192 pub fn next(&self) -> Self {
193 Self(self.0.next())
194 }
195
196 /// Returns the `n`-next logical [`RowId`].
197 ///
198 /// This is equivalent to calling [`RowId::next`] `n` times.
199 /// Wraps the monotonically increasing back to zero on overflow.
200 ///
201 /// Beware: wrong usage can easily lead to conflicts.
202 /// Prefer [`RowId::new`] when unsure.
203 #[must_use]
204 #[inline]
205 pub fn incremented_by(&self, n: u64) -> Self {
206 Self(self.0.incremented_by(n))
207 }
208
209 /// When the `RowId` was created, in nanoseconds since unix epoch.
210 #[inline]
211 pub fn nanoseconds_since_epoch(&self) -> u64 {
212 self.0.nanoseconds_since_epoch()
213 }
214
215 #[inline]
216 pub fn from_u128(id: u128) -> Self {
217 Self(re_tuid::Tuid::from_u128(id))
218 }
219
220 #[inline]
221 pub fn as_u128(&self) -> u128 {
222 self.0.as_u128()
223 }
224}
225
226impl re_byte_size::SizeBytes for RowId {
227 #[inline]
228 fn heap_size_bytes(&self) -> u64 {
229 0
230 }
231
232 #[inline]
233 fn is_pod() -> bool {
234 true
235 }
236}
237
238impl std::ops::Deref for RowId {
239 type Target = re_tuid::Tuid;
240
241 #[inline]
242 fn deref(&self) -> &Self::Target {
243 &self.0
244 }
245}
246
247impl std::ops::DerefMut for RowId {
248 #[inline]
249 fn deref_mut(&mut self) -> &mut Self::Target {
250 &mut self.0
251 }
252}
253
254crate::delegate_arrow_tuid!(RowId as "rerun.controls.RowId");