1use crate::utils::address::RowAddress;
48use crate::{Error, Result};
49use roaring::{RoaringBitmap, RoaringTreemap};
50use std::collections::HashMap;
51
52#[derive(Clone)]
59pub enum RowAddrRemap {
60 Compact(CompactRowAddrRemap),
63 Direct(HashMap<u64, Option<u64>>),
65}
66
67impl RowAddrRemap {
68 pub fn compact(groups: impl IntoIterator<Item = GroupInput>) -> Result<Self> {
69 Ok(Self::Compact(CompactRowAddrRemap::new(groups)?))
70 }
71
72 pub fn direct(map: HashMap<u64, Option<u64>>) -> Self {
74 Self::Direct(map)
75 }
76
77 pub fn empty() -> Self {
79 Self::Direct(HashMap::new())
80 }
81
82 #[inline]
84 pub fn get(&self, addr: u64) -> Option<Option<u64>> {
85 match self {
86 Self::Compact(c) => c.get(addr),
87 Self::Direct(m) => m.get(&addr).copied(),
88 }
89 }
90
91 pub fn is_empty(&self) -> bool {
92 match self {
93 Self::Compact(c) => c.is_empty(),
94 Self::Direct(m) => m.is_empty(),
95 }
96 }
97
98 pub fn affected_fragments(&self) -> RoaringBitmap {
99 match self {
100 Self::Compact(c) => RoaringBitmap::from_iter(c.frag_to_group.keys().copied()),
101 Self::Direct(m) => RoaringBitmap::from_iter(m.keys().map(|addr| (addr >> 32) as u32)),
102 }
103 }
104
105 pub fn fully_deleted_fragments(&self) -> Option<RoaringBitmap> {
106 match self {
107 Self::Compact(c) => c.fully_deleted_fragments(),
108 Self::Direct(m) => {
109 if m.values().all(|v| v.is_none()) {
110 Some(RoaringBitmap::from_iter(
111 m.keys().map(|addr| (addr >> 32) as u32),
112 ))
113 } else {
114 None
115 }
116 }
117 }
118 }
119}
120
121pub struct GroupInput {
124 pub rewritten_old_row_addrs: RoaringTreemap,
126 pub old_frag_ids: Vec<u32>,
128 pub new_frags: Vec<(u32, u32)>,
130}
131
132#[derive(Clone)]
133struct GroupRemap {
134 frags: HashMap<u32, (RoaringBitmap, u64)>,
137 new_frag_row_ranges: Vec<(u32, u64, u32)>,
140}
141
142impl GroupRemap {
143 fn new(input: GroupInput) -> Result<Self> {
144 let mut new_frag_row_ranges = Vec::with_capacity(input.new_frags.len());
151 let mut rewritten_rows_before = 0u64;
152 let mut prev_frag_id: Option<u32> = None;
153 for (frag_id, physical_rows) in input.new_frags {
154 if physical_rows == 0 {
155 continue;
156 }
157 if let Some(prev) = prev_frag_id
158 && frag_id <= prev
159 {
160 return Err(Error::invalid_input(format!(
161 "compaction new fragments must be in ascending id (write) order, but fragment {frag_id} follows {prev}",
162 )));
163 }
164 prev_frag_id = Some(frag_id);
165 new_frag_row_ranges.push((frag_id, rewritten_rows_before, physical_rows));
166 rewritten_rows_before += physical_rows as u64;
167 }
168 let total_new_rows = rewritten_rows_before;
169
170 let mut per_frag: HashMap<u32, RoaringBitmap> = input
171 .rewritten_old_row_addrs
172 .bitmaps()
173 .map(|(frag_id, bitmap)| (frag_id, bitmap.clone()))
174 .collect();
175 let mut frags = HashMap::new();
176 let mut rewritten_rows_before = 0u64;
177 for &frag_id in &input.old_frag_ids {
178 if let Some(bitmap) = per_frag.remove(&frag_id) {
181 let num_rewritten_rows = bitmap.len();
182 frags.insert(frag_id, (bitmap, rewritten_rows_before));
183 rewritten_rows_before += num_rewritten_rows;
184 }
185 }
186 if !per_frag.is_empty() {
188 return Err(Error::invalid_input(format!(
189 "compaction rewritten old row addresses reference fragments {:?} not in the rewrite group's old fragments {:?}",
190 per_frag.keys().collect::<Vec<_>>(),
191 input.old_frag_ids,
192 )));
193 }
194
195 let total_rewritten_old_rows = input.rewritten_old_row_addrs.len();
198 if total_new_rows != total_rewritten_old_rows {
199 return Err(Error::invalid_input(format!(
200 "compaction rewrote {total_rewritten_old_rows} old rows from fragments {:?} but the new fragments hold {total_new_rows} rows",
201 input.old_frag_ids,
202 )));
203 }
204
205 Ok(Self {
206 frags,
207 new_frag_row_ranges,
208 })
209 }
210
211 fn compute_new_addr(&self, rewritten_row_index: u64) -> u64 {
212 let idx =
213 match self
214 .new_frag_row_ranges
215 .binary_search_by(|(_, rewritten_rows_before, _)| {
216 rewritten_rows_before.cmp(&rewritten_row_index)
217 }) {
218 Ok(i) => i,
219 Err(i) => i - 1,
220 };
221 let (frag_id, rewritten_rows_before, _rows) = self.new_frag_row_ranges[idx];
222 let offset = (rewritten_row_index - rewritten_rows_before) as u32;
223 u64::from(RowAddress::new_from_parts(frag_id, offset))
224 }
225
226 #[inline]
229 fn get(&self, frag: u32, offset: u32) -> Option<u64> {
230 match self.frags.get(&frag) {
231 Some((bitmap, rewritten_rows_before)) if bitmap.contains(offset) => {
232 let rewritten_row_index = rewritten_rows_before + bitmap.rank(offset) - 1;
233 Some(self.compute_new_addr(rewritten_row_index))
234 }
235 _ => None,
236 }
237 }
238}
239
240#[derive(Clone)]
242pub struct CompactRowAddrRemap {
243 groups: Vec<GroupRemap>,
244 frag_to_group: HashMap<u32, usize>,
246}
247
248impl CompactRowAddrRemap {
249 fn new(groups: impl IntoIterator<Item = GroupInput>) -> Result<Self> {
250 let mut frag_to_group = HashMap::new();
251 let mut group_remaps = Vec::new();
252 for input in groups {
253 let gi = group_remaps.len();
254 for &frag_id in &input.old_frag_ids {
255 frag_to_group.insert(frag_id, gi);
256 }
257 group_remaps.push(GroupRemap::new(input)?);
258 }
259 Ok(Self {
260 groups: group_remaps,
261 frag_to_group,
262 })
263 }
264
265 #[inline]
266 pub fn get(&self, addr: u64) -> Option<Option<u64>> {
267 let frag = (addr >> 32) as u32;
268 let gi = *self.frag_to_group.get(&frag)?;
270 Some(self.groups[gi].get(frag, addr as u32))
271 }
272
273 pub fn is_empty(&self) -> bool {
274 self.groups.is_empty()
275 }
276
277 fn fully_deleted_fragments(&self) -> Option<RoaringBitmap> {
278 if self.groups.iter().any(|g| !g.frags.is_empty()) {
280 return None;
281 }
282 Some(RoaringBitmap::from_iter(self.frag_to_group.keys().copied()))
283 }
284}
285
286#[cfg(test)]
287mod tests {
288 use super::*;
289
290 fn addr(frag: u32, offset: u32) -> u64 {
291 u64::from(RowAddress::new_from_parts(frag, offset))
292 }
293
294 #[test]
295 fn test_compact_lookup() {
296 let group_a = GroupInput {
301 rewritten_old_row_addrs: RoaringTreemap::from_iter([
302 addr(4, 0),
303 addr(4, 2),
304 addr(4, 4),
305 addr(3, 0),
306 addr(3, 1),
307 ]),
308 old_frag_ids: vec![4, 3],
309 new_frags: vec![(10, 2), (11, 0), (12, 3)],
310 };
311 let group_b = GroupInput {
312 rewritten_old_row_addrs: RoaringTreemap::new(),
313 old_frag_ids: vec![7],
314 new_frags: vec![],
315 };
316 let remap = RowAddrRemap::compact([group_a, group_b]).unwrap();
317
318 assert_eq!(remap.get(addr(4, 0)), Some(Some(addr(10, 0))));
320 assert_eq!(remap.get(addr(4, 2)), Some(Some(addr(10, 1))));
321 assert_eq!(remap.get(addr(4, 4)), Some(Some(addr(12, 0))));
323 assert_eq!(remap.get(addr(3, 0)), Some(Some(addr(12, 1))));
324 assert_eq!(remap.get(addr(3, 1)), Some(Some(addr(12, 2))));
325 assert_eq!(remap.get(addr(4, 1)), Some(None));
327 assert_eq!(remap.get(addr(4, 3)), Some(None));
328 assert_eq!(remap.get(addr(7, 0)), Some(None));
330 assert_eq!(remap.get(addr(9, 0)), None);
332 assert!(!remap.is_empty());
333 }
334
335 #[test]
336 fn test_fragment_sets() {
337 let dead = RowAddrRemap::compact([GroupInput {
339 rewritten_old_row_addrs: RoaringTreemap::new(),
340 old_frag_ids: vec![3, 7],
341 new_frags: vec![],
342 }])
343 .unwrap();
344 assert_eq!(
345 dead.fully_deleted_fragments(),
346 Some(RoaringBitmap::from_iter([3u32, 7u32]))
347 );
348 assert_eq!(
349 dead.affected_fragments(),
350 RoaringBitmap::from_iter([3u32, 7u32])
351 );
352
353 let alive = RowAddrRemap::compact([GroupInput {
356 rewritten_old_row_addrs: RoaringTreemap::from_iter([addr(0, 0)]),
357 old_frag_ids: vec![0, 1],
358 new_frags: vec![(10, 1)],
359 }])
360 .unwrap();
361 assert!(alive.fully_deleted_fragments().is_none());
362 assert_eq!(
363 alive.affected_fragments(),
364 RoaringBitmap::from_iter([0u32, 1u32])
365 );
366 }
367
368 #[test]
369 fn test_compact_rejects_rewritten_addrs_outside_old_frags() {
370 let input = GroupInput {
373 rewritten_old_row_addrs: RoaringTreemap::from_iter([addr(0, 0), addr(5, 0)]),
374 old_frag_ids: vec![0],
375 new_frags: vec![(10, 2)],
376 };
377 assert!(RowAddrRemap::compact([input]).is_err());
378 }
379
380 #[test]
381 fn test_compact_rejects_new_frags_out_of_write_order() {
382 let input = GroupInput {
386 rewritten_old_row_addrs: RoaringTreemap::from_iter([addr(0, 0), addr(0, 1)]),
387 old_frag_ids: vec![0],
388 new_frags: vec![(12, 1), (11, 1)],
389 };
390 assert!(RowAddrRemap::compact([input]).is_err());
391 }
392
393 #[test]
394 fn test_direct_and_empty() {
395 let mut map = HashMap::new();
397 map.insert(addr(2, 0), Some(addr(9, 9)));
398 map.insert(addr(5, 1), None);
399 let remap = RowAddrRemap::direct(map);
400 assert_eq!(remap.get(addr(2, 0)), Some(Some(addr(9, 9))));
401 assert_eq!(remap.get(addr(5, 1)), Some(None));
402 assert_eq!(remap.get(addr(2, 1)), None);
403 assert_eq!(
405 remap.affected_fragments(),
406 RoaringBitmap::from_iter([2u32, 5u32])
407 );
408
409 let empty = RowAddrRemap::empty();
410 assert!(empty.is_empty());
411 assert_eq!(empty.get(addr(0, 0)), None);
412 }
413}