faces: avoid 40 MB row clone in /faces/embeddings

list_embeddings cloned the full FaceDetectionRow inside the filter_map
just to pair it with the base64-encoded embedding. The 2 KB BLOB was
already on the row — at 20k unassigned faces that's 40 MB of pointless
heap traffic per Apollo cluster-suggest run. Move the bytes out via
Option::take() so the row drops the BLOB instead of duplicating it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Cameron Cordes
2026-05-01 19:00:55 -04:00
parent 7998a0c9b0
commit 1d9b9a0bc4

View File

@@ -884,14 +884,18 @@ impl FaceDao for SqliteFaceDao {
// Pair with the base64-encoded embedding string so the handler // Pair with the base64-encoded embedding string so the handler
// doesn't need to know the wire format. Skip rows with NULL // doesn't need to know the wire format. Skip rows with NULL
// embedding (shouldn't happen on detected rows, but defensive). // embedding (shouldn't happen on detected rows, but defensive).
// `embedding.take()` moves the bytes out of the row so we can
// hand the (now-empty-embedding) row plus the encoded string
// back to the caller without cloning the whole row — at 20k
// rows × 2 KB that clone was 40 MB of pointless heap traffic
// per cluster-suggest run.
use base64::Engine; use base64::Engine;
Ok(rows Ok(rows
.into_iter() .into_iter()
.filter_map(|r| { .filter_map(|mut r| {
r.embedding.as_ref().map(|bytes| { let bytes = r.embedding.take()?;
let b64 = base64::engine::general_purpose::STANDARD.encode(bytes); let b64 = base64::engine::general_purpose::STANDARD.encode(&bytes);
(r.clone(), b64) Some((r, b64))
})
}) })
.collect()) .collect())
}) })