enhance: support idempotent requests for DML - #3784
Conversation
Milvus accepts a client idempotency key as transport metadata: the REST header Idempotency-Key and the gRPC metadata key idempotency-key (milvus-io/milvus#52544). A retry carrying the same key resolves to the original request instead of doing the work again. This exposes it from pymilvus. - MilvusClient / AsyncMilvusClient insert, upsert and delete accept idempotency_key as a keyword argument, following the client_request_id precedent: CallContext carries it and emits the idempotency-key gRPC metadata only when set, so a keyless call is byte-identical to today. - The ORM path picks it up through connections._generate_call_context. - bulk_import(idempotency_key=...) sends the Idempotency-Key header. Bulk import honors the key on merged servers. Insert honors it once milvus-io/milvus#50007 lands. Delete and upsert carry the key but the proxy does not act on it yet. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WQihG8uqLQb8LWW5aVivjY Signed-off-by: bigsheeper <yihao.dai@zilliz.com>
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: bigsheeper The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
|
Tick the box to add this pull request to the merge queue (same as
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #3784 +/- ##
=======================================
Coverage 94.22% 94.22%
=======================================
Files 77 77
Lines 16204 16211 +7
=======================================
+ Hits 15268 15275 +7
Misses 936 936 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Adversarial review found no issues requiring changes. Verified:
Low — non-blocking
|
|
Reviewed against the server side of milvus-io/milvus#50007 and #52544. The plumbing is The wire name matches the server constants exactly, and empty-vs-absent is handled the way Must fix1. // task_insert.go:192 -> task_insert_idempotency.go:65
if it.idempotencyKey != "" && !globalIdempotencyEnabled {
return merr.WrapErrParameterInvalidMsg("idempotency key is not accepted when idempotent write is globally disabled")
}
if it.idempotencyKey != "" && !collectionIdempotencyEnabled {
return merr.WrapErrParameterInvalidMsg("idempotency key is not accepted when collection idempotent write is disabled")
}Both gates default off: 2.
Worth fixing3. Nothing validates the key before it reaches the server. 4.
Relatedly: 5. The Go SDK and this PR disagree on Optional6. 7. Only async |
Milvus accepts a client idempotency key as transport metadata: the REST header
Idempotency-Keyand the gRPC metadata keyidempotency-key(milvus-io/milvus#52544). A retry carrying the same key resolves to the original request instead of doing the work again. This PR exposes it from pymilvus.Changes
MilvusClient/AsyncMilvusClientinsert,upsertanddeleteacceptidempotency_keyas a keyword argument, following theclient_request_idprecedent:CallContextcarries it and emits theidempotency-keygRPC metadata only when set, so a keyless call is byte-identical to today.connections._generate_call_context, soCollection.insert(..., idempotency_key=...)works too.bulk_import(idempotency_key=...)sends theIdempotency-Keyheader.Server-side status
Tests
tests/unit/test_idempotency_key.py(new):CallContextmetadata emission, sync and async clientinsert/upsert/deleteforwarding, ORM context generation.tests/unit/test_bulk_import.py: header emission at_http_headers,_post_requestandbulk_import.tests/unitrun: 4718 passed;ruffandblackclean.🤖 Generated with Claude Code
https://claude.ai/code/session_01WQihG8uqLQb8LWW5aVivjY