Cycle de vie des lignes
Trois types d’étapes couvrent le cycle de vie clé sur id, tous viaPOST /v1/namespaces/{ns}/query :
UpsertN— remplacement de ligne complet clé sur l’idexterne. Les lignes existantes avec cet id sont supprimées, puis la nouvelle ligne est insérée. Remplacement, pas fusion : un upsert sans vecteur d’une ligne vectorisée supprime le vecteur.SetProps— écrase des clés spécifiques sur un flux correspondant (débutez la requête parNWhere) ; une valeurnullsupprime la clé.idlui-même ne peut être modifié — c’est unUpsertN. Approprié pour des réécritures d’attributs occasionnelles, inapproprié pour des compteurs par requête.DeleteN— supprime un flux correspondant (NWhere→DeleteN), ou passeidspour des suppressions par id externe direct.
Encodages de vecteurs
Par ligne, l’un des deux encodages sur fil (envoyer les deux est un400) :
vector_b64 est le base64 du vecteur en tant que tableau f32 little-endian — ~4× plus petit sur le fil et décodé par une memcpy plutôt qu’un parsing JSON de nombres. Encodeur Python :
400.
Ingestion en masse : POST /v1/namespaces/{ns}/bulk-vectors
Le chemin d’amorçage à haut débit — une trame binaire par requête (Content-Type: application/octet-stream) : un petit en-tête JSON (dims, label, ids, props partagés) suivi de valeurs fp16 L2-normalisées en ligne par ligne. Comparé à JSON+base64, cela évite de ré-encoder des charges de plusieurs Go plusieurs fois.
Chargement sûr au redémarrage
Les chargeurs reprenables passent?expected_generation=<G>&expected_rows=<N>. La trame n’est acceptée que lorsque le namespace est exactement à cet état :
- Même trame déjà committée →
200avec"replayed": trueet lerequest_fingerprintcorrespondant — pas de lignes dupliquées. - Charge différente à la même position →
409 write_conflict. - Les écritures conditionnelles réussies renvoient
request_fingerprint,rows_before,rows_after— validez votre batch uniquement après avoir reçu l’un de ces reçus.
Compaction
L’auto-compaction est géométrique, donc le travail total de compaction reste linéaire en octets ingérés. Terminez tout chargement en masse par une compaction explicite — envoyez"compact": true au niveau supérieur de la dernière requête d’écriture :
"compacted": true/false. La compaction construit également l’index ANN lorsque la table est suffisamment grande ; les lignes écrites après le snapshot d’index sont fusionnées en brute-force dans les résultats, si bien que la fraîcheur n’est jamais sacrifiée.
Déduplication d’une source en masse
Les trames en masse ajoutent aveuglément par conception — une source qui contient des ids dupliqués les stocke silencieusement, et les doublons occupent des places dans le top-k.{"DedupN": {}} réconcilie un magasin avec le contrat d’unicité par id externe (garde la dernière ligne écrite par id), et {"DedupN": {"dry_run": true}} est l’audit de recensement seul que tout chargement en masse devrait exécuter lorsque sa source ne peut prouver l’unicité des ids — un nombre total de lignes correspondant ne peut révéler les doublons cachés à l’intérieur.
Règles de réessai
Les lectures peuvent toujours être réessayées après un échec de transport. Pour les écritures, seules les trames conditionnelles en masse sont automatiquement réessayables (trame identique + paramètres CAS → reçu original oureplayed: true). Ne rejouez jamais aveuglément une écriture non conditionnelle après une réponse perdue — réconciliez d’abord l’état.