Skip to content

feat(storage): add rpc.client.call.duration metric for gRPC calls - #14743

Open
nidhiii-27 wants to merge 13 commits into
feat/storage-grpc-veneer-operation-metricsfrom
feat/storage-rpc-call-duration
Open

nidhiii-27 wants to merge 13 commits into
feat/storage-grpc-veneer-operation-metricsfrom
feat/storage-rpc-call-duration

Conversation

@nidhiii-27

Copy link
Copy Markdown
Contributor

Instruments gRPC storage calls to record rpc.client.call.duration OpenTelemetry histogram metrics.

  • Adds StorageGrpcMetricsInterceptor to measure per-RPC duration and attach rpc.system.name, rpc.method, rpc.response.status_code, network.path, server.address, and error.type attributes.
  • Wires StorageGrpcMetricsInterceptor into GrpcStorageOptions and initializes rpc.client.call.duration in StorageClientMetrics.
  • Adds unit tests in StorageGrpcMetricsInterceptorTest and StorageClientMetricsTest.

Stacked on #14729.

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request introduces StorageGrpcMetricsInterceptor, a new gRPC client interceptor designed to record call metrics such as RPC duration, and integrates it into GrpcStorageOptions. It also adds comprehensive unit tests to verify its behavior. The review feedback is highly constructive, focusing on preventing a potential NullPointerException when combining interceptors, simplifying redundant server address resolution, and optimizing performance on the hot path by precomputing the DirectPath endpoint status in the constructor rather than performing string operations on every RPC completion.

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request introduces StorageGrpcMetricsInterceptor to record gRPC client call metrics (such as duration, status codes, and network paths) and integrates it into GrpcStorageOptions. It also adds comprehensive unit tests to verify the interceptor's behavior. The review feedback highlights two key improvement opportunities: adding a null check for grpcInterceptorProvider to prevent a potential NullPointerException when combining providers, and simplifying the redundant logic used to resolve the serverAddress.

…Interceptor

- Precompute defaultServerAddress and isDirectPathEndpoint in StorageGrpcMetricsInterceptor constructor
- Guard grpcInterceptorProvider and its getInterceptors() against null in GrpcStorageOptions
- Add defensive null checks and cause-aware error mapping in StorageGrpcMetricsInterceptor.onClose
- Use JSpecify @nullable and expand unit test coverage in StorageGrpcMetricsInterceptorTest

[Generated-by: AI]
…ls.extractServerAddress

Merge updated feat/storage-grpc-veneer-operation-metrics base branch and update StorageGrpcMetricsInterceptor to call OpenTelemetryBootstrappingUtils.extractServerAddress.

[Generated-by: AI]
@nidhiii-27
nidhiii-27 marked this pull request as ready for review November 5, 2026 09:00
@nidhiii-27
nidhiii-27 requested review from a team as code owners November 5, 2026 09:00
…Interceptor

- Use full gRPC method name (`method.getFullMethodName()`) for `rpc.method` and remove `extractShortMethodName`
- Capture fallback `Context.current()` in `interceptCall` on the caller thread
- Remove `isDirectPathEndpoint` and return `NETWORK_PATH_UNKNOWN` when `remoteAddr` is absent
- Restore `/18` and `/42` CIDR byte-mask check in `isDirectPathIp`

[Generated-by: AI]
… mapping

Update recordAttemptError_specificExceptions_mappedToCanonicalTypes to assert STATUS_DEADLINE_EXCEEDED (1) and STATUS_TIMEOUT (1) following the StorageErrorTypeMapper update.

[Generated-by: AI]
@nidhiii-27
nidhiii-27 requested a review from cpriti-os November 9, 2026 10:50

@cpriti-os cpriti-os 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.

Thanks for addressing the earlier comments. Mostly looks good, one real issue with the IPv6 mask plus a few cleanups inline.

@nidhiii-27
nidhiii-27 requested a review from cpriti-os November 12, 2026 08:58
if (callRecorded.compareAndSet(false, true)) {
double durationSeconds =
Math.max(0.0, (clock.nanoTime() - startTimeNanos) / 1_000_000_000.0);
String networkPath = detectNetworkPath(getAttributes());

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.

For ReadObject the retrier returns as soon as the first response arrives (GapicUnbufferedReadableByteChannel completes open in onResponseImpl), so the attempt is recorded before this onClose write and the first read attempt still falls back to cloudpath. Can we also set the path from onHeaders (peer attrs are available by then) so streaming attempts pick up the real one?

if (metricsCtx != null) {
metricsCtx.setNetworkPath(networkPath);
}
String statusCode =

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.

status and its code can't be null here per the Listener contract, so this can just be status.getCode().name() and the @Nullables can go.

}

@Test
public void interceptCall_withStorageMetricsContext_propagatesContextAndFeedsNetworkPath()

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.

Can we also assert the duration point here has network.path=directpath? Right now only the attempts side is checked.

This branch has not been deployed

No deployments
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.

2 participants