Skip to content

Conversation

@jy-tan
Copy link
Contributor

@jy-tan jy-tan commented Jan 18, 2026

Summary

Adds comprehensive gRPC client instrumentation for the Python SDK, enabling record and replay of all 4 gRPC communication patterns. This mirrors and extends the Node SDK's gRPC support, with the Python SDK now covering more patterns (client streaming, bidirectional streaming) than the Node SDK.

Changes

  • New gRPC instrumentation (drift/instrumentation/grpc/)

    • Supports all 4 gRPC patterns: unary, server streaming, client streaming, bidirectional streaming
    • Supports all invocation variants: direct call, with_call(), and future() (async)
    • Patches grpc._channel.Channel (concrete implementation, not abstract base class) for proper method resolution
    • Handles protobuf binary data serialization via placeholder-based buffer maps
  • Auto-initialization: SDK automatically initializes gRPC instrumentation when grpcio is installed

  • E2E tests: Docker-based test suite covering all gRPC patterns with a Flask gateway + gRPC backend architecture

  • Documentation: Added notes.md with gRPC communication patterns, Python API details, use cases in Python ecosystem, and implementation notes

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

5 issues found across 21 files

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="drift/instrumentation/grpc/e2e-tests/Dockerfile">

<violation number="1" location="drift/instrumentation/grpc/e2e-tests/Dockerfile:1">
P2: Pin the base image to a specific version/digest instead of `latest` to keep e2e builds reproducible and avoid unexpected upstream changes.</violation>
</file>

<file name="drift/instrumentation/grpc/e2e-tests/src/app.py">

<violation number="1" location="drift/instrumentation/grpc/e2e-tests/src/app.py:145">
P2: `has_initial_metadata` will always be true because `len(...) >= 0` is always true. Use a positive check so the flag reflects whether metadata is present.</violation>

<violation number="2" location="drift/instrumentation/grpc/e2e-tests/src/app.py:146">
P2: `has_trailing_metadata` will always be true because `len(...) >= 0` is always true. Use a positive check so the flag reflects whether metadata is present.</violation>
</file>

<file name="drift/instrumentation/grpc/e2e-tests/requirements.txt">

<violation number="1" location="drift/instrumentation/grpc/e2e-tests/requirements.txt:5">
P2: The e2e test requirements allow protobuf 4.x even though the SDK’s supported minimum is protobuf>=6.0. This can run tests against an unsupported version and miss compatibility issues. Align the test requirement with the SDK minimum.</violation>
</file>

<file name="drift/instrumentation/grpc/instrumentation.py">

<violation number="1" location="drift/instrumentation/grpc/instrumentation.py:881">
P2: Converting the streaming request iterator to a list consumes the whole stream up front, which breaks client/bidi streaming semantics (no incremental sending) and can cause unbounded memory usage for large or infinite streams. Consider lazily wrapping the iterator to record elements while allowing the RPC to stream normally.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

try:
# For client streaming, we need to consume the iterator to capture all requests
# This changes the behavior slightly - the iterator is consumed upfront
requests_list = list(request_iterator)
Copy link

@cubic-dev-ai cubic-dev-ai bot Jan 18, 2026

Choose a reason for hiding this comment

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

P2: Converting the streaming request iterator to a list consumes the whole stream up front, which breaks client/bidi streaming semantics (no incremental sending) and can cause unbounded memory usage for large or infinite streams. Consider lazily wrapping the iterator to record elements while allowing the RPC to stream normally.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At drift/instrumentation/grpc/instrumentation.py, line 881:

<comment>Converting the streaming request iterator to a list consumes the whole stream up front, which breaks client/bidi streaming semantics (no incremental sending) and can cause unbounded memory usage for large or infinite streams. Consider lazily wrapping the iterator to record elements while allowing the RPC to stream normally.</comment>

<file context>
@@ -0,0 +1,1976 @@
+        try:
+            # For client streaming, we need to consume the iterator to capture all requests
+            # This changes the behavior slightly - the iterator is consumed upfront
+            requests_list = list(request_iterator)
+            metadata = kwargs.get("metadata")
+            input_value = self._build_stream_input_value(method, requests_list, metadata)
</file context>
Fix with Cubic

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Documented limitation in notes.md. Required for record/replay - we need full input to serialize and match. Lazy wrapper won't work for replay mode.

Copy link

Choose a reason for hiding this comment

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

Thanks for the feedback! I've saved this as a new learning to improve future reviews.

@jy-tan jy-tan merged commit 1d785d9 into main Jan 18, 2026
18 checks passed
@jy-tan jy-tan deleted the grpc-instrumentation branch January 18, 2026 22:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants