Apply cell-level retention to the merged tile map. For each Hilbert prefix group: - Tile versions with `system_from_ms >= horizon_ms` are in-horizon and pass through unchanged. - Tile versions with `system_from_ms < horizon_ms` are out-of-horizon. Their cells are collapsed into a single synthetic ceiling tile: - For each coordinate, the newest out-of-horizon row wins. - Coordinates already covere
(
merged: BTreeMap<TileId, MergedTile>,
schema: &ArraySchema,
horizon_ms: i64,
)
| 165 | /// strictly before all in-horizon tiles within the same prefix in the output |
| 166 | /// segment (TileId order is `(prefix, system_from_ms)` ascending). |
| 167 | fn apply_retention( |
| 168 | merged: BTreeMap<TileId, MergedTile>, |
| 169 | schema: &ArraySchema, |
| 170 | horizon_ms: i64, |
| 171 | ) -> Result<BTreeMap<TileId, MergedTile>, CompactionError> { |
| 172 | // Group tile versions by hilbert_prefix. |
| 173 | let mut by_prefix: HashMap<u64, Vec<(TileId, MergedTile)>> = HashMap::new(); |
| 174 | for (tile_id, mt) in merged { |
| 175 | by_prefix |
| 176 | .entry(tile_id.hilbert_prefix) |
| 177 | .or_default() |
| 178 | .push((tile_id, mt)); |
| 179 | } |
| 180 | |
| 181 | let mut out: BTreeMap<TileId, MergedTile> = BTreeMap::new(); |
| 182 | |
| 183 | for (prefix, versions) in by_prefix { |
| 184 | // Partition into inside-horizon and outside-horizon. |
| 185 | let mut inside: Vec<(TileId, MergedTile)> = Vec::new(); |
| 186 | let mut outside: Vec<(TileId, MergedTile)> = Vec::new(); |
| 187 | for (tile_id, mt) in versions { |
| 188 | if tile_id.system_from_ms >= horizon_ms { |
| 189 | inside.push((tile_id, mt)); |
| 190 | } else { |
| 191 | outside.push((tile_id, mt)); |
| 192 | } |
| 193 | } |
| 194 | |
| 195 | // Collect coord keys present in any in-horizon version so they can |
| 196 | // be excluded from the ceiling (the in-horizon version supersedes). |
| 197 | let mut inhorizon_coord_keys: HashSet<Vec<u8>> = HashSet::new(); |
| 198 | for (_tile_id, mt) in &inside { |
| 199 | for row in &mt.rows { |
| 200 | let key = encode_coord_key(&row.coord)?; |
| 201 | inhorizon_coord_keys.insert(key); |
| 202 | } |
| 203 | } |
| 204 | |
| 205 | // Pass in-horizon tiles through unchanged. |
| 206 | for (tile_id, mt) in inside { |
| 207 | out.insert(tile_id, mt); |
| 208 | } |
| 209 | |
| 210 | // Nothing to collapse. |
| 211 | if outside.is_empty() { |
| 212 | continue; |
| 213 | } |
| 214 | |
| 215 | // Sort outside-horizon versions newest → oldest. |
| 216 | outside.sort_by_key(|(tid, _)| std::cmp::Reverse(tid.system_from_ms)); |
| 217 | |
| 218 | // Build ceiling: coord → (newest out-of-horizon row). |
| 219 | // Maps encoded coord key → MergedRow. |
| 220 | let mut ceiling_rows: HashMap<Vec<u8>, MergedRow> = HashMap::new(); |
| 221 | for (_tile_id, mt) in outside { |
| 222 | for row in mt.rows { |
| 223 | let key = encode_coord_key(&row.coord)?; |
| 224 | // Skip coords already covered by in-horizon versions. |