Skip to main content

Cycle de vie d’une ligne

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

Encodages de vecteurs

Par ligne, l’un des deux encodages sur le fil (envoyer les deux est un 400) :
vector_b64 est le base64 du vecteur sous forme de tableau f32 little-endian — environ 4× plus petit sur le fil et décodé avec un memcpy plutôt qu’un parsing de nombres JSON. Encodeur Python :
Les deux encodages produisent des vecteurs stockés identiques au bit près 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 header JSON (dims, label, ids, props partagés) suivi des valeurs fp16 L2-normalisées en row-major. Comparé à JSON+base64, cela évite de ré-encoder plusieurs fois des payloads de plusieurs Go.

Chargement safe-restart

Les loaders 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à validée → 200 avec "replayed": true et le request_fingerprint correspondant — pas de lignes en doublon.
  • Payload différent à la même position → 409 write_conflict.
  • Les écritures conditionnelles réussies renvoient request_fingerprint, rows_before, rows_after — validez votre lot uniquement après avoir reçu l’un de ces reçus.
Cela fait de « crash, redémarre, ré-envoie » toute l’histoire de reprise : aucune tâche d’import à surveiller.

Compaction

L’auto-compaction est géométrique, de sorte que le travail total de compaction reste linéaire par rapport aux octets ingérés. Terminez toute charge en masse par une compaction explicite — envoyez "compact": true au niveau supérieur de la dernière requête d’écriture :
La réponse rapporte "compacted": true/false. La compaction construit également l’index ANN une fois la table suffisamment grande ; les lignes écrites après le snapshot d’index sont fusionnées en brute-force dans les résultats, donc 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 porte des ids en double les insère silencieusement, et les doublons consomment des slots de top-k. {"DedupN": {}} réconcilie un magasin avec le contrat d’unicité d’id externe (conserve la dernière ligne écrite par id), et {"DedupN": {"dry_run": true}} est l’audit de recensement uniquement que toute charge en masse devrait exécuter lorsque sa source ne peut pas prouver l’unicité des ids — un total de lignes correspondant ne peut pas révéler des doublons cachés à l’intérieur.

Règles de retry

Les lectures peuvent toujours être réessayées après un échec de transport. Pour les écritures, seules les trames en masse conditionnelles 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.