Skip to main content

awa_model/
unique.rs

1/// Compute a BLAKE3 unique key for job deduplication.
2///
3/// Hash inputs: kind + optional(queue) + optional(args_json) + optional(period_bucket).
4/// Truncated to 16 bytes (128 bits) — birthday bound ~18 quintillion.
5pub fn compute_unique_key(
6    kind: &str,
7    queue: Option<&str>,
8    args: Option<&serde_json::Value>,
9    period_bucket: Option<i64>,
10) -> Vec<u8> {
11    let mut hasher = blake3::Hasher::new();
12    hasher.update(kind.as_bytes());
13
14    if let Some(queue) = queue {
15        hasher.update(b"\x00");
16        hasher.update(queue.as_bytes());
17    }
18
19    if let Some(args) = args {
20        hasher.update(b"\x00");
21        // serde_json::Value uses Map<String, Value> backed by BTreeMap,
22        // so keys iterate in sorted order. This is an implementation detail
23        // of serde_json, not a guarantee. Python uses json.dumps(sort_keys=True)
24        // explicitly. Cross-language golden tests verify consistency.
25        let canonical = serde_json::to_vec(args).expect("JSON serialization should not fail");
26        hasher.update(&canonical);
27    }
28
29    if let Some(period_bucket) = period_bucket {
30        hasher.update(b"\x00");
31        hasher.update(&period_bucket.to_le_bytes());
32    }
33
34    hasher.finalize().as_bytes()[..16].to_vec()
35}
36
37#[cfg(test)]
38mod tests {
39    use super::*;
40
41    #[test]
42    fn test_unique_key_deterministic() {
43        let key1 = compute_unique_key("send_email", None, None, None);
44        let key2 = compute_unique_key("send_email", None, None, None);
45        assert_eq!(key1, key2);
46        assert_eq!(key1.len(), 16);
47    }
48
49    #[test]
50    fn test_unique_key_different_kinds() {
51        let key1 = compute_unique_key("send_email", None, None, None);
52        let key2 = compute_unique_key("process_payment", None, None, None);
53        assert_ne!(key1, key2);
54    }
55
56    #[test]
57    fn test_unique_key_with_queue() {
58        let key1 = compute_unique_key("send_email", Some("default"), None, None);
59        let key2 = compute_unique_key("send_email", Some("priority"), None, None);
60        let key3 = compute_unique_key("send_email", None, None, None);
61        assert_ne!(key1, key2);
62        assert_ne!(key1, key3);
63    }
64
65    #[test]
66    fn test_unique_key_with_args() {
67        let args1 = serde_json::json!({"to": "a@b.com"});
68        let args2 = serde_json::json!({"to": "c@d.com"});
69        let key1 = compute_unique_key("send_email", None, Some(&args1), None);
70        let key2 = compute_unique_key("send_email", None, Some(&args2), None);
71        assert_ne!(key1, key2);
72    }
73
74    #[test]
75    fn test_unique_key_with_period() {
76        let key1 = compute_unique_key("send_email", None, None, Some(1000));
77        let key2 = compute_unique_key("send_email", None, None, Some(1001));
78        assert_ne!(key1, key2);
79    }
80}