Skip to main content

Cycle de vie des lignes

Trois types d’étapes couvrent le cycle de vie clé sur id, tous via POST /v1/namespaces/{ns}/query :
  • UpsertN — remplacement de ligne complet clé sur l’id externe. 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 par NWhere) ; une valeur null supprime la clé. id lui-même ne peut être modifié — c’est un UpsertN. Approprié pour des réécritures d’attributs occasionnelles, inapproprié pour des compteurs par requête.
  • DeleteN — supprime un flux correspondant (NWhereDeleteN), ou passe ids pour des suppressions par id externe direct.

Encodages de vecteurs

Par ligne, l’un des deux encodages sur fil (envoyer les deux est un 400) :
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 :
Les deux encodages produisent des vecteurs stockés identiques au bit et des résultats de recherche identiques. La dimension est fixée par la première écriture vectorisée ; une incompatibilité est un 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 → 200 avec "replayed": true et le request_fingerprint correspondant — 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.
Cela fait de « crash, redémarrage, renvoi » toute l’histoire de reprise : pas de job d’import à surveiller.

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 :
La réponse indique "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 ou replayed: true). Ne rejouez jamais aveuglément une écriture non conditionnelle après une réponse perdue — réconciliez d’abord l’état.