laminar_connectors/kafka/partitioner/
mod.rs1pub trait KafkaPartitioner: Send + Sync {
14 fn partition(&mut self, key: Option<&[u8]>, num_partitions: i32) -> Option<i32>;
19}
20
21#[derive(Debug, Default)]
26pub struct KeyHashPartitioner;
27
28impl KeyHashPartitioner {
29 #[must_use]
31 pub fn new() -> Self {
32 Self
33 }
34}
35
36impl KafkaPartitioner for KeyHashPartitioner {
37 fn partition(&mut self, key: Option<&[u8]>, num_partitions: i32) -> Option<i32> {
38 key.map(|k| {
39 let hash = murmur2(k) & 0x7fff_ffff;
40 #[allow(
41 clippy::cast_possible_truncation,
42 clippy::cast_possible_wrap,
43 clippy::cast_sign_loss
44 )]
45 let partition = (hash % num_partitions as u32) as i32;
46 partition
47 })
48 }
49}
50
51#[derive(Debug)]
53pub struct RoundRobinPartitioner {
54 counter: u64,
55}
56
57impl RoundRobinPartitioner {
58 #[must_use]
60 pub fn new() -> Self {
61 Self { counter: 0 }
62 }
63}
64
65impl Default for RoundRobinPartitioner {
66 fn default() -> Self {
67 Self::new()
68 }
69}
70
71impl KafkaPartitioner for RoundRobinPartitioner {
72 #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
73 fn partition(&mut self, _key: Option<&[u8]>, num_partitions: i32) -> Option<i32> {
74 let partition = (self.counter % num_partitions as u64) as i32;
75 self.counter += 1;
76 Some(partition)
77 }
78}
79
80#[derive(Debug)]
85pub struct StickyPartitioner {
86 current_partition: i32,
87 records_in_batch: usize,
88 batch_threshold: usize,
89}
90
91impl StickyPartitioner {
92 #[must_use]
94 pub fn new(batch_threshold: usize) -> Self {
95 Self {
96 current_partition: 0,
97 records_in_batch: 0,
98 batch_threshold,
99 }
100 }
101}
102
103impl KafkaPartitioner for StickyPartitioner {
104 fn partition(&mut self, _key: Option<&[u8]>, num_partitions: i32) -> Option<i32> {
105 if self.records_in_batch >= self.batch_threshold {
106 self.current_partition = (self.current_partition + 1) % num_partitions;
107 self.records_in_batch = 0;
108 }
109 self.records_in_batch += 1;
110 Some(self.current_partition)
111 }
112}
113
114fn murmur2(data: &[u8]) -> u32 {
118 let seed: u32 = 0x9747_b28c;
119 let m: u32 = 0x5bd1_e995;
120 let r: u32 = 24;
121
122 let len = data.len();
123 #[allow(clippy::cast_possible_truncation)] let mut h: u32 = seed ^ (len as u32);
125
126 let chunks = len / 4;
127 for i in 0..chunks {
128 let offset = i * 4;
129 let mut k = u32::from_le_bytes([
130 data[offset],
131 data[offset + 1],
132 data[offset + 2],
133 data[offset + 3],
134 ]);
135 k = k.wrapping_mul(m);
136 k ^= k >> r;
137 k = k.wrapping_mul(m);
138 h = h.wrapping_mul(m);
139 h ^= k;
140 }
141
142 let remainder = len % 4;
143 let tail_start = chunks * 4;
144 if remainder >= 3 {
145 h ^= u32::from(data[tail_start + 2]) << 16;
146 }
147 if remainder >= 2 {
148 h ^= u32::from(data[tail_start + 1]) << 8;
149 }
150 if remainder >= 1 {
151 h ^= u32::from(data[tail_start]);
152 h = h.wrapping_mul(m);
153 }
154
155 h ^= h >> 13;
156 h = h.wrapping_mul(m);
157 h ^= h >> 15;
158
159 h
160}
161
162#[cfg(test)]
163mod tests;