1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//! Per-topic monotonic offset cursor — spec section 13.1.
//!
//! This module provides the [`OffsetStore`] struct, which allocates
//! monotonically increasing offsets for messages published to each
//! topic. Offsets start at 1 for the first message in a topic and
//! increment by 1 for each subsequent message.
//!
//! # Design
//!
//! In a single-process deployment, this store is the same value the
//! [`TopicEntry`](crate::topic::TopicEntry) holds in its `next_offset`
//! atomic. The [`OffsetStore`] exists as a separate component so that
//! a distributed broker can replace it with a shared-storage
//! implementation (e.g. a distributed counter or a database sequence)
//! without changing the broker logic.
//!
//! # Thread safety
//!
//! Each topic's offset counter is an [`AtomicI64`], making concurrent
//! allocation from multiple threads safe without external locking. The
//! topic-to-counter map uses a [`DashMap`] for concurrent shard-level
//! access.
//!
//! # Offset semantics
//!
//! The [`alloc`](OffsetStore::alloc) method atomically increments the
//! counter and returns the *previous* value, so the first call for a
//! new topic returns 1. The [`head`](OffsetStore::head) method returns
//! the highest allocated offset (counter minus 1), or 0 if no
//! allocations have been made for the topic.
use ;
use DashMap;
/// Per-topic monotonic offset allocator.
///
/// Tracks a separate atomic counter for each topic. Offsets are
/// allocated via [`alloc`](OffsetStore::alloc) and queried via
/// [`head`](OffsetStore::head). The store is thread-safe and can be
/// shared across async tasks.
///
/// # Examples
///
/// ```ignore
/// use rifts::broker::OffsetStore;
///
/// let store = OffsetStore::new();
/// assert_eq!(store.alloc("orders"), 1);
/// assert_eq!(store.alloc("orders"), 2);
/// assert_eq!(store.alloc("orders"), 3);
/// assert_eq!(store.head("orders"), 3);
/// ```