hyperactor/mailbox/
headers.rs1use std::any::type_name;
15use std::time::SystemTime;
16
17use hyperactor_config::Flattrs;
18use hyperactor_config::attrs::OPERATION_CONTEXT_HEADER;
19use hyperactor_config::attrs::declare_attrs;
20use hyperactor_config::global;
21use hyperactor_telemetry::hash_to_u64;
22
23use crate::ActorAddr;
24use crate::PortAddr;
25use crate::metrics::MESSAGE_LATENCY_MICROS;
26use crate::ordering::SeqInfo;
27
28declare_attrs! {
29 pub attr SEND_TIMESTAMP: SystemTime;
31
32 pub attr RUST_MESSAGE_TYPE: String;
34
35 pub attr SENDER_ACTOR_ID_HASH: u64;
37
38 pub attr SENDER_ACTOR_ID: ActorAddr;
53
54 pub attr TELEMETRY_MESSAGE_ID: u64;
56
57 pub attr TELEMETRY_PORT_INDEX: u64;
59
60 @meta(OPERATION_CONTEXT_HEADER = true)
79 pub attr OPERATION_ENDPOINT: String;
80
81 @meta(OPERATION_CONTEXT_HEADER = true)
85 pub attr OPERATION_ADVERB: String;
86}
87
88pub fn set_send_timestamp(headers: &mut Flattrs) {
93 if !headers.contains_key(SEND_TIMESTAMP) {
94 let time = std::time::SystemTime::now();
95 headers.set(SEND_TIMESTAMP, time);
96 }
97}
98
99pub fn set_rust_message_type<M>(headers: &mut Flattrs) {
101 headers.set(RUST_MESSAGE_TYPE, type_name::<M>().to_string());
102}
103
104#[doc(hidden)]
109pub fn stamp_sender_actor_id_hash(headers: &mut Flattrs, sender: &ActorAddr) {
110 headers.set(SENDER_ACTOR_ID_HASH, hash_to_u64(sender.id()));
111}
112
113#[doc(hidden)]
123pub fn stamp_sender_actor_id(
124 headers: &mut Flattrs,
125 seq_info: &SeqInfo,
126 dest: &PortAddr,
127 owner: &ActorAddr,
128) {
129 if let SeqInfo::Session { seq, .. } = seq_info {
130 let early_session = *seq <= 4;
131 let caller_set_stale = headers.contains_key(SENDER_ACTOR_ID);
132 if (early_session || caller_set_stale) && dest.is_handler_port() {
133 headers.set(SENDER_ACTOR_ID, owner.clone());
134 }
135 }
136}
137
138pub(crate) fn stamp_sender_actor_id_fresh(
142 headers: &mut Flattrs,
143 seq: u64,
144 dest: &PortAddr,
145 owner: &ActorAddr,
146) {
147 if seq <= 4 && dest.is_handler_port() {
148 headers.set(SENDER_ACTOR_ID, owner.clone());
149 }
150}
151
152pub fn log_message_latency_if_sampling(headers: &Flattrs, actor_addr: &ActorAddr) {
159 if fastrand::f32() > global::get(crate::config::MESSAGE_LATENCY_SAMPLING_RATE) {
160 return;
161 }
162
163 let actor_id = actor_addr.to_string();
164 if !headers.contains_key(SEND_TIMESTAMP) {
165 tracing::debug!(
166 actor_id = actor_id,
167 "SEND_TIMESTAMP missing from message headers, cannot measure latency"
168 );
169 return;
170 }
171
172 let metric_pairs = hyperactor_telemetry::kv_pairs!(
173 "actor_id" => actor_id
174 );
175 let Some(send_timestamp) = headers.get(SEND_TIMESTAMP) else {
176 return;
177 };
178 let now = std::time::SystemTime::now();
179 let latency = now.duration_since(send_timestamp).unwrap_or_default();
180 MESSAGE_LATENCY_MICROS.record(latency.as_micros() as f64, metric_pairs);
181}
182
183#[cfg(test)]
184mod tests {
185 use uuid::Uuid;
186
187 use super::*;
188 use crate::port::ControlPort;
189 use crate::port::Port;
190 use crate::testing::ids::test_actor_id;
191
192 fn session(seq: u64) -> SeqInfo {
193 SeqInfo::Session {
194 session_id: Uuid::now_v7(),
195 seq,
196 }
197 }
198
199 fn handler_port(actor_name: &str) -> (ActorAddr, PortAddr) {
200 let addr: ActorAddr = test_actor_id(actor_name, "worker");
201 let port = addr.port_addr(Port::handler::<TestHandlerMsg>());
202 (addr, port)
203 }
204
205 fn non_handler_port(actor_name: &str) -> (ActorAddr, PortAddr) {
206 let addr: ActorAddr = test_actor_id(actor_name, "worker");
207 let port = addr.port_addr(Port::from(1));
211 (addr, port)
212 }
213
214 fn control_port(actor_name: &str) -> (ActorAddr, PortAddr) {
215 let addr: ActorAddr = test_actor_id(actor_name, "worker");
216 let port = addr.port_addr(Port::control(ControlPort::Introspect));
217 (addr, port)
218 }
219
220 #[derive(typeuri::Named)]
223 struct TestHandlerMsg;
224
225 #[test]
226 fn test_stamp_sender_actor_id_hash_overwrites_existing_value() {
227 let owner: ActorAddr = test_actor_id("test_0", "worker");
228 let mut headers = Flattrs::new();
229 headers.set(SENDER_ACTOR_ID_HASH, 123);
230
231 stamp_sender_actor_id_hash(&mut headers, &owner);
232
233 assert_eq!(
234 headers.get(SENDER_ACTOR_ID_HASH),
235 Some(hash_to_u64(owner.id()))
236 );
237 }
238
239 #[test]
240 fn test_stamp_helper_sets_sender_on_seq_1() {
241 let (owner, dest) = handler_port("test_0");
242 let mut headers = Flattrs::new();
243 stamp_sender_actor_id(&mut headers, &session(1), &dest, &owner);
244 assert_eq!(headers.get(SENDER_ACTOR_ID), Some(owner));
245 }
246
247 #[test]
248 fn test_stamp_helper_sets_sender_on_seq_4() {
249 let (owner, dest) = handler_port("test_0");
250 let mut headers = Flattrs::new();
251 stamp_sender_actor_id(&mut headers, &session(4), &dest, &owner);
252 assert_eq!(headers.get(SENDER_ACTOR_ID), Some(owner));
253 }
254
255 #[test]
256 fn test_stamp_helper_skips_seq_5_no_stale() {
257 let (owner, dest) = handler_port("test_0");
258 let mut headers = Flattrs::new();
259 stamp_sender_actor_id(&mut headers, &session(5), &dest, &owner);
260 assert_eq!(headers.get(SENDER_ACTOR_ID), None);
261 }
262
263 #[test]
264 fn test_stamp_helper_overwrites_stale_at_seq_5() {
265 let (owner, dest) = handler_port("test_0");
266 let fake_owner: ActorAddr = test_actor_id("fake_0", "imposter");
267 let mut headers = Flattrs::new();
268 headers.set(SENDER_ACTOR_ID, fake_owner.clone());
269 stamp_sender_actor_id(&mut headers, &session(5), &dest, &owner);
270 assert_eq!(headers.get(SENDER_ACTOR_ID), Some(owner));
271 }
272
273 #[test]
274 fn test_stamp_helper_skips_non_handler_port() {
275 let (owner, dest) = non_handler_port("test_0");
276 let mut headers = Flattrs::new();
277 stamp_sender_actor_id(&mut headers, &session(1), &dest, &owner);
278 assert_eq!(headers.get(SENDER_ACTOR_ID), None);
279 }
280
281 #[test]
282 fn test_stamp_helper_skips_control_port() {
283 let (owner, dest) = control_port("test_0");
284 let mut headers = Flattrs::new();
285 stamp_sender_actor_id(&mut headers, &session(1), &dest, &owner);
286 assert_eq!(headers.get(SENDER_ACTOR_ID), None);
287 }
288
289 #[test]
290 fn test_stamp_helper_skips_seq_info_direct() {
291 let (owner, dest) = handler_port("test_0");
292 let mut headers = Flattrs::new();
293 stamp_sender_actor_id(&mut headers, &SeqInfo::Direct, &dest, &owner);
294 assert_eq!(headers.get(SENDER_ACTOR_ID), None);
295 }
296
297 #[test]
298 fn test_stamp_fresh_helper_sets_on_seq_4() {
299 let (owner, dest) = handler_port("test_0");
300 let mut headers = Flattrs::new();
301 stamp_sender_actor_id_fresh(&mut headers, 4, &dest, &owner);
302 assert_eq!(headers.get(SENDER_ACTOR_ID), Some(owner));
303 }
304
305 #[test]
306 fn test_stamp_fresh_helper_skips_on_seq_5() {
307 let (owner, dest) = handler_port("test_0");
308 let mut headers = Flattrs::new();
309 stamp_sender_actor_id_fresh(&mut headers, 5, &dest, &owner);
310 assert_eq!(headers.get(SENDER_ACTOR_ID), None);
311 }
312}