Compact a single segment by removing deleted rows. Reads the segment, skips rows marked in the delete bitmap, and writes a new segment with only live rows. Returns `None` segment if all rows were deleted. When `kek` is `Some`, the output segment is wrapped in an AES-256-GCM SEGC envelope. The input segment must be plaintext (the caller is responsible for decrypting before passing to this functio
(
segment_data: &[u8],
deletes: &DeleteBitmap,
schema: &ColumnarSchema,
profile_tag: u8,
governor: Option<&Arc<MemoryGovernor>>,
kek: Option<&nodedb_wal::crypto::WalEncryptionK
| 42 | /// tracked against the `Columnar` engine budget. Pass `None` in embedded |
| 43 | /// (Lite) deployments where no governor is configured. |
| 44 | pub fn compact_segment( |
| 45 | segment_data: &[u8], |
| 46 | deletes: &DeleteBitmap, |
| 47 | schema: &ColumnarSchema, |
| 48 | profile_tag: u8, |
| 49 | governor: Option<&Arc<MemoryGovernor>>, |
| 50 | kek: Option<&nodedb_wal::crypto::WalEncryptionKey>, |
| 51 | ) -> Result<CompactionResult, ColumnarError> { |
| 52 | let reader = SegmentReader::open(segment_data)?; |
| 53 | let total_rows = reader.row_count() as usize; |
| 54 | let deleted = deletes.deleted_count() as usize; |
| 55 | let live = total_rows.saturating_sub(deleted); |
| 56 | |
| 57 | if live == 0 { |
| 58 | return Ok(CompactionResult { |
| 59 | segment: None, |
| 60 | live_rows: 0, |
| 61 | removed_rows: total_rows, |
| 62 | }); |
| 63 | } |
| 64 | |
| 65 | // Read all columns without delete masking — we'll filter manually. |
| 66 | let col_count = reader.column_count(); |
| 67 | // Reserve budget for the decoded-column pointer vec (each entry is a fat pointer). |
| 68 | let _cols_guard = governor |
| 69 | .map(|g| { |
| 70 | g.reserve( |
| 71 | EngineId::Columnar, |
| 72 | col_count * std::mem::size_of::<usize>() * 3, |
| 73 | ) |
| 74 | }) |
| 75 | .transpose()?; |
| 76 | let mut decoded_cols = Vec::with_capacity(col_count); |
| 77 | for i in 0..col_count { |
| 78 | decoded_cols.push(reader.read_column(i)?); |
| 79 | } |
| 80 | |
| 81 | // Build a new memtable with only live rows. |
| 82 | let mut memtable = ColumnarMemtable::new(schema); |
| 83 | let col_len = schema.columns.len(); |
| 84 | let _row_guard = governor |
| 85 | .map(|g| { |
| 86 | g.reserve( |
| 87 | EngineId::Columnar, |
| 88 | col_len * std::mem::size_of::<usize>() * 3, |
| 89 | ) |
| 90 | }) |
| 91 | .transpose()?; |
| 92 | let mut row_values = Vec::with_capacity(col_len); |
| 93 | |
| 94 | for row_idx in 0..total_rows { |
| 95 | if deletes.is_deleted(row_idx as u32) { |
| 96 | continue; |
| 97 | } |
| 98 | |
| 99 | row_values.clear(); |
| 100 | for (col_idx, decoded) in decoded_cols.iter().enumerate() { |
| 101 | let col = &schema.columns[col_idx]; |