Skip to main content

Row-Lifecycle

Drei Step-Arten decken den id-basierten Lifecycle ab, alle über POST /v1/namespaces/{ns}/query:
  • UpsertN – vollständiger Zeilenersatz mit externer id als Schlüssel. Bestehende Zeilen mit dieser id werden gelöscht, danach wird die neue Zeile eingefügt. Ersetzen, nicht mergen: Ein vektorloser Upsert einer vektorisierten Zeile lässt den Vektor fallen.
  • SetProps – überschreibt bestimmte Keys auf einem gematchten Stream (Query mit NWhere starten); ein null-Wert entfernt den Key. id selbst kann nicht geändert werden – dafür ist ein UpsertN da. Richtig für gelegentliches Umschreiben von Attributen, falsch für Zähler pro Query.
  • DeleteN – löscht einen gematchten Stream (NWhereDeleteN) oder übergibt ids für direkte externe-id-Löschungen.

Vektor-Encodings

Pro Zeile eine von zwei Wire-Encodings (beide zu senden ergibt ein 400):
vector_b64 ist Base64 des Vektors als Little-Endian f32-Array – ca. 4× kleiner auf der Leitung und mit einem memcpy statt JSON-Zahlen-Parsing dekodiert. Python-Encoder:
Beide Encodings erzeugen bitgleiche gespeicherte Vektoren und identische Suchergebnisse. Die Dimension wird durch den ersten vektorisierten Schreibvorgang festgelegt; ein Mismatch ergibt ein 400.

Bulk-Ingest: POST /v1/namespaces/{ns}/bulk-vectors

Der Hochdurchsatz-Seed-Pfad – ein binärer Frame pro Request (Content-Type: application/octet-stream): ein kleiner JSON-Header (dims, label, ids, geteilte props) gefolgt von row-major L2-normalisierten fp16-Werten. Verglichen mit JSON+Base64 vermeidet das mehrfaches Neucodieren von Multi-GB-Payloads.

Restart-sicheres Laden

Wiederaufnehmbare Loader übergeben ?expected_generation=<G>&expected_rows=<N>. Der Frame wird nur akzeptiert, wenn der Namespace genau in diesem Zustand ist:
  • Derselbe Frame ist bereits committet → 200 mit "replayed": true und passendem request_fingerprint – keine Duplikatzeilen.
  • Andere Payload an derselben Position → 409 write_conflict.
  • Erfolgreiche konditionale Writes liefern request_fingerprint, rows_before, rows_after zurück – checkpoint deinen Batch erst nach einer solchen Quittung.
Damit ist „Crash, Restart, erneut senden” die vollständige Resume-Story: keine Import-Jobs zu babysitten.

Compaction

Auto-Compaction ist geometrisch, sodass die gesamte Compaction-Arbeit linear zu den ingested Bytes bleibt. Schließe jeden Bulk-Load mit einem expliziten Compact ab – sende "compact": true auf oberster Ebene des letzten Write-Requests:
Die Response meldet "compacted": true/false. Compaction baut auch den ANN-Index, sobald die Tabelle groß genug ist; Zeilen, die nach dem Index-Snapshot geschrieben wurden, werden per Brute Force in die Ergebnisse gemerged, damit Frische nie geopfert wird.

Eine Bulk-Quelle deduplizieren

Bulk-Frames hängen bewusst blind an – eine Quelle mit doppelten ids landet sie stumm, und Duplikate verbrennen Top-k-Slots. {"DedupN": {}} gleicht einen Store zur externen id-Eindeutigkeitszusage ab (behält die zuletzt geschriebene Zeile pro id), und {"DedupN": {"dry_run": true}} ist der reine Zensus-Audit, den jeder Bulk-Load ausführen sollte, wenn seine Quelle keine id-Eindeutigkeit garantieren kann – eine passende Gesamt-Zeilenzahl kann darin verborgene Duplikate nicht enttarnen.

Retry-Regeln

Lesevorgänge können nach einem Transportfehler stets wiederholt werden. Bei Writes sind nur konditionale Bulk-Frames automatisch retry-fähig (identischer Frame + CAS-Params → ursprüngliche Quittung oder replayed: true). Wiederhole einen unkonditionalen Write nie blind nach verlorener Response – gleiche zuerst den Zustand ab.