Skip to content

feat: add client telemetry support - #3770

Open
xiaofan-luan wants to merge 1 commit into
milvus-io:masterfrom
xiaofan-luan:feat/client-telemetry
Open

feat: add client telemetry support#3770
xiaofan-luan wants to merge 1 commit into
milvus-io:masterfrom
xiaofan-luan:feat/client-telemetry

Conversation

@xiaofan-luan

@xiaofan-luan xiaofan-luan commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add client telemetry heartbeat, command handling, bounded metrics history, recent-error reporting, and server-driven configuration
  • give every sync/async logical MilvusClient its own telemetry manager, client ID, configuration, history, command cursor, replies, and heartbeat
  • keep pooled GrpcHandler/AsyncGrpcHandler instances transport-only so independent clients can share an authenticated channel without sharing telemetry state
  • preserve one legacy fallback telemetry manager for direct ORM/handler connections
  • keep the same logical telemetry manager across use_database, reconnect, and global-primary failover; candidate failures leave the previous handler and telemetry binding intact
  • generation- and lease-token-fence transport rebinding so late reconnects and stale heartbeat responses cannot restore a retired stub or mutate current state
  • report one outcome per canonical public logical operation across validation, retry, and result processing; Get, iterators, and internal attempts are not counted as Query
  • preserve arbitrary non-empty legacy request IDs on the RPC wire while accepting only valid trace IDs in telemetry records
  • keep at most one hour / 4096 history windows and aggregate p99 from 128 bounded per-window quantile samples rather than averaging window p99 values
  • keep the command heartbeat alive after runtime disable, while an initial user opt-out starts no worker
  • isolate unexpected telemetry exceptions from business RPCs and client close; saturate unsupported-RPC backoff without overflow

Related work

Verification

  • full unit suite: 4802 passed, 3 skipped
  • focused telemetry / connection lifecycle suite: 306 passed
  • deterministic register-vs-reconnect and custom-command lifecycle race tests
  • Black (197 files), Ruff, and git diff --check

@sre-ci-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: xiaofan-luan
To complete the pull request process, please assign longjiquan after the PR has been reviewed.
You can assign the PR to them by writing /assign @longjiquan in a comment when ready.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@codecov

codecov Bot commented Aug 24, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.40283% with 129 lines in your changes missing coverage. Please review.
✅ Project coverage is 94.16%. Comparing base (64e53d1) to head (a9e449e).

Files with missing lines Patch % Lines
pymilvus/client/telemetry.py 96.60% 36 Missing ⚠️
pymilvus/milvus_client/async_milvus_client.py 80.64% 24 Missing ⚠️
pymilvus/client/connection_manager.py 71.01% 20 Missing ⚠️
pymilvus/client/async_grpc_handler.py 84.61% 18 Missing ⚠️
pymilvus/client/grpc_handler.py 87.78% 16 Missing ⚠️
pymilvus/client/asynch.py 90.24% 8 Missing ⚠️
pymilvus/milvus_client/milvus_client.py 92.13% 7 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #3770      +/-   ##
==========================================
- Coverage   94.22%   94.16%   -0.07%     
==========================================
  Files          77       78       +1     
  Lines       16204    17813    +1609     
==========================================
+ Hits        15268    16773    +1505     
- Misses        936     1040     +104     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@mergify

mergify Bot commented Aug 24, 2026

Copy link
Copy Markdown

Tick the box to add this pull request to the merge queue (same as @mergifyio queue).

  • Queue this pull request

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6f60fe4601

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread pymilvus/client/connection_manager.py Outdated
Comment thread pymilvus/client/grpc_handler.py Outdated
Comment thread pymilvus/client/telemetry.py Outdated
@mergify mergify Bot added ci-passed and removed ci-passed labels Aug 24, 2026
@xiaofan-luan
xiaofan-luan force-pushed the feat/client-telemetry branch from b3b64a8 to e3281fe Compare August 25, 2026 20:08
@mergify mergify Bot added ci-passed and removed ci-passed labels Aug 25, 2026

@pymilvus-bot pymilvus-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes for the telemetry correctness and lifecycle issues called out inline. The most serious issue is that persistent configs are passed through the one-time command watermark, so a newly relevant config can be acknowledged and hashed without ever being applied. Iterator-internal RPCs and sync Future parsing also violate the stated one-final-outcome contract, and sync shutdown can return while its worker is still running. These were verified against the current head with focused reproductions.

Comment thread pymilvus/client/telemetry.py Outdated
Comment thread pymilvus/client/telemetry.py
Comment thread pymilvus/client/telemetry.py
Comment thread pymilvus/client/grpc_handler.py
Comment thread pymilvus/client/telemetry.py Outdated
Comment thread pymilvus/client/telemetry.py Outdated
Comment thread tests/e2e/test_client_telemetry.py Outdated
@xiaofan-luan
xiaofan-luan force-pushed the feat/client-telemetry branch from e3281fe to c874b2e Compare August 26, 2026 18:18
@mergify mergify Bot added ci-passed and removed ci-passed labels Aug 26, 2026
@xiaofan-luan
xiaofan-luan force-pushed the feat/client-telemetry branch from c874b2e to 0c846be Compare August 31, 2026 03:44
@mergify mergify Bot added ci-passed and removed ci-passed labels Aug 31, 2026

@pymilvus-bot pymilvus-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inline review comments for the current head. The existing config-hash discussion already covers the acknowledged protocol gap, so it is not duplicated here.

Comment thread pymilvus/client/grpc_handler.py
Comment thread pymilvus/client/telemetry.py
Comment thread pymilvus/client/telemetry.py
Comment thread tests/unit/client/test_telemetry.py Outdated
Comment thread pymilvus/client/async_grpc_handler.py
Comment thread pymilvus/client/telemetry.py
Signed-off-by: xiaofanluan <xiaofan.luan@zilliz.com>
@mergify mergify Bot added ci-passed and removed ci-passed labels Sep 1, 2026
xiaofan-luan added a commit to milvus-io/milvus that referenced this pull request Sep 2, 2026
issue: #53099

## What problem does this PR solve?

After the last persistent telemetry config matching a client is deleted,
the client continues to report its old non-empty config hash. The
heartbeat response contains only commands, so an empty command list
cannot distinguish an already-matching non-empty config set from an
authoritative empty set. Existing SDKs therefore preserve the old hash
and never converge.

Related design:
https://github.com/milvus-io/milvus/blob/master/docs/design-docs/design_docs/20260131-client_side_telemetry.md

PyMilvus discussion:
milvus-io/pymilvus#3770 (comment)

## What is changed and how does it work?

- Synthesize a stable persistent no-op push_config sentinel only when
the effective config set is empty and the client reports another
non-empty hash.
- Accept both the original empty hash and the sentinel hash as converged
empty states, so fresh clients receive no extra command.
- Keep the sentinel out of etcd and command List/Delete APIs, and ignore
its ACK in ordinary reply history.
- Document the compatibility behavior and its cross-SDK hash vector.

This is a server-only rolling-upgrade fix. It requires no protobuf or
SDK change because all telemetry SDKs already accept an empty
push_config object as a no-op and calculate the same persistent-command
hash.

## Verification

- go test ./internal/rootcoord/telemetry
- go test -race ./internal/rootcoord/telemetry
- go vet ./internal/rootcoord/telemetry
- git diff --check

A broader local go test ./internal/rootcoord/... reaches an unrelated
existing Loon CGo ABI mismatch in internal/storagev2/packed; the
telemetry package and race suite pass, and PR CI will provide the clean
broader build signal.

Signed-off-by: xiaofanluan <xf@hjjaq.com>
Co-authored-by: xiaofanluan <xf@hjjaq.com>
if self._database_bound
else self._database_provider() or ""
)
if database:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Report the implicit default database in heartbeats

MilvusClient and AsyncMilvusClient default db_name to "", and this branch omits reserved["db_name"] when that value is empty. RootCoord only adds non-empty database names to AccessedDatabases, while database:default command matching consults that set. Consequently, a normal MilvusClient() or AsyncMilvusClient() never receives commands or persistent configs scoped to database:default, even though its operations run in that database. Please normalize the logical empty name to "default" in telemetry identity (or handle the convention server-side) and cover database:default in an end-to-end test.


def _queue_reply(self, reply: CommandReply) -> None:
with self._pending_lock:
self._pending_replies.append(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Enforce the built-in reply limit at a common boundary

The linked design caps built-in reply payloads at 1 MiB, but this queue accepts every payload unchanged. Both the no-payload collection_metrics response and get_config serialize the unbounded enabled_collections set, which can grow across repeated commands and exceed the limit. If an oversized reply then makes the heartbeat fail, it remains in _pending_replies and can poison every later heartbeat. Please enforce the limit for all built-in handlers before enqueueing and replace an oversized result with a bounded failure reply.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants