rocketmq_model/common/message/
message_client_id_setter.rs1use std::net::IpAddr;
16use std::sync::atomic::AtomicI32;
17use std::sync::atomic::AtomicI64;
18use std::sync::atomic::Ordering;
19use std::sync::LazyLock;
20
21use bytes::BufMut;
22use bytes::BytesMut;
23use cheetah_string::CheetahString;
24use chrono::DateTime;
25use chrono::Datelike;
26use chrono::Local;
27use chrono::LocalResult;
28use chrono::Months;
29use chrono::TimeZone;
30use rocketmq_error::Result;
31
32use crate::codec::bytes_to_string;
33use crate::codec::string_to_bytes;
34use crate::codec::write_int;
35use crate::codec::write_short;
36use crate::common::hasher::string_hasher::JavaStringHasher;
37use crate::common::message::MessageConst;
38use crate::common::message::MessageTrait;
39use crate::time::current_millis;
40
41static COUNTER: LazyLock<AtomicI32> = LazyLock::new(|| AtomicI32::new(0));
42static TIME_WINDOW: LazyLock<TimeWindow> = LazyLock::new(|| {
43 let (start_time, next_start_time) = compute_time_window(current_millis() as i64);
44 TimeWindow {
45 start_time: AtomicI64::new(start_time),
46 next_start_time: AtomicI64::new(next_start_time),
47 }
48});
49static LEN: LazyLock<usize> = LazyLock::new(|| {
50 let ip = get_ip().unwrap_or_else(create_fake_ip);
51 ip.len() + 2 + 4 + 4 + 2
52});
53static FIX_STRING: LazyLock<Vec<char>> = LazyLock::new(|| {
54 let ip = get_ip().unwrap_or_else(create_fake_ip);
55 let pid = std::process::id() as i16;
56 let class_loader_hash = JavaStringHasher::hash_str("MessageClientIDSetter");
57 let mut bytes = BytesMut::with_capacity(ip.len() + 2 + 4);
58 bytes.put(ip.as_slice());
59 bytes.put_i16(pid);
60 bytes.put_i32(class_loader_hash);
61 let data = bytes_to_string(bytes.freeze().as_ref()).chars().collect::<Vec<char>>();
62 data
63});
64
65struct TimeWindow {
66 start_time: AtomicI64,
67 next_start_time: AtomicI64,
68}
69
70pub fn create_fake_ip() -> Vec<u8> {
71 current_millis().to_be_bytes()[4..].to_vec()
72}
73
74fn get_ip() -> Option<Vec<u8>> {
75 match local_ip_address::local_ip().ok()? {
76 IpAddr::V4(address) => Some(address.octets().to_vec()),
77 IpAddr::V6(address) => Some(address.octets().to_vec()),
78 }
79}
80
81fn local_timestamp_millis_or_epoch(millis: i64) -> DateTime<Local> {
82 Local
83 .timestamp_millis_opt(millis)
84 .single()
85 .or_else(|| Local.timestamp_millis_opt(0).single())
86 .unwrap_or_else(Local::now)
87}
88
89fn local_month_start(dt: DateTime<Local>) -> DateTime<Local> {
90 match Local.with_ymd_and_hms(dt.year(), dt.month(), 1, 0, 0, 0) {
91 LocalResult::Single(value) => value,
92 LocalResult::Ambiguous(earliest, _) => earliest,
93 LocalResult::None => dt,
94 }
95}
96
97fn compute_time_window(millis: i64) -> (i64, i64) {
98 let start = local_month_start(local_timestamp_millis_or_epoch(millis));
99 let next = start.checked_add_months(Months::new(1)).unwrap_or(start);
100 (start.timestamp_millis(), next.timestamp_millis())
101}
102
103fn decode_uniq_id_bytes(msg_id: &str) -> Result<Vec<u8>> {
104 let msg_id = msg_id.trim();
105 if msg_id.len() != 32 && msg_id.len() != 56 {
106 return Err(crate::error::invalid_argument(format!(
107 "Invalid uniq id length: {}. Expected 32 characters (IPv4) or 56 characters (IPv6)",
108 msg_id.len()
109 )));
110 }
111 if !msg_id.bytes().all(|b| b.is_ascii_hexdigit()) {
112 return Err(crate::error::invalid_argument(
113 "Invalid uniq id: expected hexadecimal characters",
114 ));
115 }
116 string_to_bytes(msg_id).ok_or_else(|| crate::error::invalid_argument("Invalid uniq id hex"))
117}
118
119pub struct MessageClientIDSetter;
120
121impl MessageClientIDSetter {
122 #[inline]
123 pub fn get_uniq_id<T>(message: &T) -> Option<CheetahString>
124 where
125 T: MessageTrait,
126 {
127 message.property(&CheetahString::from_static_str(
128 MessageConst::PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX,
129 ))
130 }
131
132 fn set_start_time(millis: i64) {
133 let (start_time, next_start_time) = compute_time_window(millis);
134 TIME_WINDOW.start_time.store(start_time, Ordering::Release);
135 TIME_WINDOW.next_start_time.store(next_start_time, Ordering::Release);
136 }
137
138 pub fn create_uniq_id() -> String {
139 let mut sb = vec!['\0'; *LEN * 2];
140 sb[..FIX_STRING.len()].copy_from_slice(&FIX_STRING);
141
142 let current = current_millis() as i64;
143 if current >= TIME_WINDOW.next_start_time.load(Ordering::Acquire) {
144 Self::set_start_time(current);
145 }
146
147 let mut diff = (current - TIME_WINDOW.start_time.load(Ordering::Acquire)) as i32;
148 if diff < 0 && diff > -1_000_000 {
149 diff = 0;
151 }
152
153 let pos = FIX_STRING.len();
154 write_int(&mut sb, pos, diff);
155
156 let counter_val = COUNTER.fetch_add(1, Ordering::SeqCst) as i16;
157 write_short(&mut sb, pos + 8, counter_val);
158 sb.into_iter().collect()
159 }
160
161 pub fn get_nearly_time_from_id(msg_id: &str) -> Result<DateTime<Local>> {
162 let bytes = decode_uniq_id_bytes(msg_id)?;
163 let ip_length = if bytes.len() == 28 { 16 } else { 4 };
164 let diff_start = ip_length + 2 + 4;
165 let span_ms = u32::from_be_bytes([
166 bytes[diff_start],
167 bytes[diff_start + 1],
168 bytes[diff_start + 2],
169 bytes[diff_start + 3],
170 ]) as i64;
171 let now = Local::now();
172 let mut month_start = local_month_start(now).timestamp_millis();
173 if month_start.saturating_add(span_ms) >= now.timestamp_millis() {
174 month_start = local_month_start(
175 now.checked_sub_months(Months::new(1))
176 .unwrap_or_else(|| local_timestamp_millis_or_epoch(0)),
177 )
178 .timestamp_millis();
179 }
180 let timestamp = month_start.saturating_add(span_ms);
181 Local
182 .timestamp_millis_opt(timestamp)
183 .single()
184 .ok_or_else(|| crate::error::invalid_argument("Invalid uniq id timestamp"))
185 }
186
187 pub fn get_ip_from_id(msg_id: &str) -> Result<Vec<u8>> {
188 let bytes = decode_uniq_id_bytes(msg_id)?;
189 let ip_length = if bytes.len() == 28 { 16 } else { 4 };
190 Ok(bytes[..ip_length].to_vec())
191 }
192
193 pub fn get_ip_str_from_id(msg_id: &str) -> Result<String> {
194 let ip_bytes = Self::get_ip_from_id(msg_id)?;
195 match ip_bytes.len() {
196 4 => Ok(std::net::Ipv4Addr::new(ip_bytes[0], ip_bytes[1], ip_bytes[2], ip_bytes[3]).to_string()),
197 16 => {
198 let mut segments = [0u8; 16];
199 segments.copy_from_slice(&ip_bytes);
200 Ok(std::net::Ipv6Addr::from(segments).to_string())
201 }
202 len => Err(crate::error::invalid_argument(format!(
203 "Invalid uniq id IP length: {len}"
204 ))),
205 }
206 }
207
208 pub fn get_pid_from_id(msg_id: &str) -> Result<i32> {
209 let bytes = decode_uniq_id_bytes(msg_id)?;
210 let pid_start = bytes.len() - 2 - 4 - 4 - 2;
211 let pid = u16::from_be_bytes([bytes[pid_start], bytes[pid_start + 1]]);
212 Ok(i32::from(pid))
213 }
214
215 pub fn set_uniq_id<T>(message: &mut T)
216 where
217 T: MessageTrait,
218 {
219 if message
220 .property(&CheetahString::from_static_str(
221 MessageConst::PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX,
222 ))
223 .is_none()
224 {
225 let uniq_id = Self::create_uniq_id();
226 message.put_property(
227 CheetahString::from_static_str(MessageConst::PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX),
228 CheetahString::from_string(uniq_id),
229 );
230 }
231 }
232}
233
234#[cfg(test)]
235mod tests {
236 use chrono::Local;
237
238 use super::*;
239 use crate::common::message::message_single::Message;
240
241 #[test]
242 fn unique_id_is_generated_correctly() {
243 let _message = Message::default();
244 let unique_id = MessageClientIDSetter::create_uniq_id();
245 assert!(!unique_id.is_empty());
246 }
247
248 #[test]
249 fn unique_id_counter_increments_properly() {
250 let first_id = MessageClientIDSetter::create_uniq_id();
251 let second_id = MessageClientIDSetter::create_uniq_id();
252 let first_counter = &first_id[FIX_STRING.len() + 8..];
253 let second_counter = &second_id[FIX_STRING.len() + 8..];
254 assert_ne!(first_counter, second_counter);
255 }
256
257 #[test]
258 fn get_uniq_id_returns_none_when_not_set() {
259 let message = Message::default();
260 assert_eq!(MessageClientIDSetter::get_uniq_id(&message), None);
261 }
262
263 #[test]
264 fn create_fake_ip_generates_valid_ip() {
265 let fake_ip = create_fake_ip();
266 assert_eq!(fake_ip.len(), 4);
267 assert!(fake_ip.iter().all(|&_byte| true));
268 }
269
270 #[test]
271 fn start_time_is_set_correctly_for_new_month() {
272 let millis = Local::now().timestamp_millis();
273 MessageClientIDSetter::set_start_time(millis);
274 let locked_start_time = TIME_WINDOW.start_time.load(Ordering::Acquire);
275 assert!(locked_start_time <= millis);
276 }
277
278 #[test]
279 fn next_start_time_is_set_correctly_for_new_month() {
280 let millis = Local::now().timestamp_millis();
281 MessageClientIDSetter::set_start_time(millis);
282 let locked_next_start_time = TIME_WINDOW.next_start_time.load(Ordering::Acquire);
283 assert!(locked_next_start_time > millis);
284 }
285
286 #[test]
287 fn nearly_time_from_id_is_close_to_now() {
288 let before = Local::now().timestamp_millis();
289 let unique_id = MessageClientIDSetter::create_uniq_id();
290 std::thread::sleep(std::time::Duration::from_millis(2));
291 let nearly_time = MessageClientIDSetter::get_nearly_time_from_id(&unique_id)
292 .expect("generated uniq id should decode time")
293 .timestamp_millis();
294 let after = Local::now().timestamp_millis();
295
296 assert!(nearly_time >= before - 1_000);
297 assert!(nearly_time <= after + 1_000);
298 }
299
300 #[test]
301 fn ip_and_pid_decode_from_generated_id() {
302 let unique_id = MessageClientIDSetter::create_uniq_id();
303
304 let ip = MessageClientIDSetter::get_ip_from_id(&unique_id).expect("generated uniq id should decode IP");
305 assert!(ip.len() == 4 || ip.len() == 16);
306 let ip_str =
307 MessageClientIDSetter::get_ip_str_from_id(&unique_id).expect("generated uniq id should decode IP string");
308 assert!(!ip_str.is_empty());
309 let pid = MessageClientIDSetter::get_pid_from_id(&unique_id).expect("generated uniq id should decode PID");
310 assert_eq!(pid, i32::from(std::process::id() as u16));
311 }
312
313 #[test]
314 fn malformed_id_decoders_return_error() {
315 assert!(MessageClientIDSetter::get_nearly_time_from_id("bad-id").is_err());
316 assert!(MessageClientIDSetter::get_ip_from_id("bad-id").is_err());
317 assert!(MessageClientIDSetter::get_ip_str_from_id("bad-id").is_err());
318 assert!(MessageClientIDSetter::get_pid_from_id("bad-id").is_err());
319 }
320}