Serialize all segments for checkpointing. When `kek` is `Some`, the MessagePack payload is wrapped in an AES-256-GCM encrypted envelope with a `SEGV` preamble. When `None`, raw MessagePack bytes are returned (existing plaintext format). Returns an empty `Vec` on serialization failure (callers treat this as a skip signal, consistent with the pre-existing error handling).
(
&self,
kek: Option<&nodedb_wal::crypto::WalEncryptionKey>,
)
| 132 | /// Returns an empty `Vec` on serialization failure (callers treat this as a |
| 133 | /// skip signal, consistent with the pre-existing error handling). |
| 134 | pub fn checkpoint_to_bytes( |
| 135 | &self, |
| 136 | kek: Option<&nodedb_wal::crypto::WalEncryptionKey>, |
| 137 | ) -> Vec<u8> { |
| 138 | let snapshot = CollectionSnapshot { |
| 139 | dim: self.dim, |
| 140 | params_m: self.params.m, |
| 141 | params_m0: self.params.m0, |
| 142 | params_ef_construction: self.params.ef_construction, |
| 143 | params_metric: self.params.metric as u8, |
| 144 | next_id: self.next_id, |
| 145 | growing_base_id: self.growing_base_id, |
| 146 | growing_vectors: (0..self.growing.len() as u32) |
| 147 | .filter_map(|i| self.growing.get_vector_raw(i).map(|v| v.to_vec())) |
| 148 | .collect(), |
| 149 | growing_deleted: (0..self.growing.len() as u32) |
| 150 | .map(|i| self.growing.is_deleted(i)) |
| 151 | .collect(), |
| 152 | sealed_segments: self |
| 153 | .sealed |
| 154 | .iter() |
| 155 | .map(|s| { |
| 156 | let (pq_bytes, pq_codes) = match &s.pq { |
| 157 | Some((codec, codes)) => (codec.to_bytes().ok(), Some(codes.clone())), |
| 158 | None => (None, None), |
| 159 | }; |
| 160 | // Only serialize SQ8 when PQ is absent — a segment never carries both. |
| 161 | let (sq8_bytes, sq8_codes) = if pq_bytes.is_none() { |
| 162 | match &s.sq8 { |
| 163 | Some((codec, codes)) => (Some(codec.to_bytes()), Some(codes.clone())), |
| 164 | None => (None, None), |
| 165 | } |
| 166 | } else { |
| 167 | (None, None) |
| 168 | }; |
| 169 | SealedSnapshot { |
| 170 | base_id: s.base_id, |
| 171 | hnsw_bytes: s.index.checkpoint_to_bytes(), |
| 172 | pq_bytes, |
| 173 | pq_codes, |
| 174 | sq8_bytes, |
| 175 | sq8_codes, |
| 176 | } |
| 177 | }) |
| 178 | .collect(), |
| 179 | building_segments: self |
| 180 | .building |
| 181 | .iter() |
| 182 | .map(|b| BuildingSnapshot { |
| 183 | base_id: b.base_id, |
| 184 | vectors: (0..b.flat.len() as u32) |
| 185 | .filter_map(|i| b.flat.get_vector_raw(i).map(|v| v.to_vec())) |
| 186 | .collect(), |
| 187 | deleted: (0..b.flat.len() as u32) |
| 188 | .map(|i| b.flat.is_deleted(i)) |
| 189 | .collect(), |
| 190 | }) |
| 191 | .collect(), |