Skip to content
This repository was archived by the owner on May 8, 2026. It is now read-only.

Commit 672b78f

Browse files
committed
add outstanding metrics tracker
1 parent a7952b6 commit 672b78f

7 files changed

Lines changed: 202 additions & 18 deletions

File tree

google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableClientContext.java

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,7 @@
2626
import com.google.auth.oauth2.ServiceAccountJwtAccessCredentials;
2727
import com.google.cloud.bigtable.data.v2.BigtableDataSettings;
2828
import com.google.cloud.bigtable.data.v2.internal.JwtCredentialsWithAudience;
29-
import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants;
30-
import com.google.cloud.bigtable.data.v2.stub.metrics.CustomOpenTelemetryMetricsProvider;
31-
import com.google.cloud.bigtable.data.v2.stub.metrics.DefaultMetricsProvider;
32-
import com.google.cloud.bigtable.data.v2.stub.metrics.ErrorCountPerConnectionMetricTracker;
33-
import com.google.cloud.bigtable.data.v2.stub.metrics.MetricsProvider;
34-
import com.google.cloud.bigtable.data.v2.stub.metrics.NoopMetricsProvider;
29+
import com.google.cloud.bigtable.data.v2.stub.metrics.*;
3530
import com.google.cloud.bigtable.gaxx.grpc.BigtableTransportChannelProvider;
3631
import com.google.cloud.bigtable.gaxx.grpc.ChannelPrimer;
3732
import io.grpc.ManagedChannelBuilder;
@@ -98,7 +93,7 @@ public static BigtableClientContext create(EnhancedBigtableStubSettings settings
9893

9994
@Nullable OpenTelemetrySdk internalOtel = null;
10095
@Nullable ErrorCountPerConnectionMetricTracker errorCountPerConnectionMetricTracker = null;
101-
96+
@Nullable OutstandingRpcsMetricTracker outstandingRpcsMetricTracker = null;
10297
// Internal metrics are scoped to the connections, so we need a mutable transportProvider,
10398
// otherwise there is
10499
// no reason to build the internal OtelProvider
@@ -111,6 +106,8 @@ public static BigtableClientContext create(EnhancedBigtableStubSettings settings
111106
errorCountPerConnectionMetricTracker =
112107
setupPerConnectionErrorTracer(builder, transportProvider, internalOtel);
113108

109+
outstandingRpcsMetricTracker = new OutstandingRpcsMetricTracker(internalOtel, "LB_POLICY");
110+
114111
// Configure grpc metrics
115112
configureGrpcOtel(transportProvider, internalOtel);
116113
}
@@ -137,7 +134,9 @@ public static BigtableClientContext create(EnhancedBigtableStubSettings settings
137134

138135
BigtableTransportChannelProvider btTransportProvider =
139136
BigtableTransportChannelProvider.create(
140-
(InstantiatingGrpcChannelProvider) transportProvider.build(), channelPrimer);
137+
(InstantiatingGrpcChannelProvider) transportProvider.build(),
138+
channelPrimer,
139+
outstandingRpcsMetricTracker);
141140

142141
builder.setTransportChannelProvider(btTransportProvider);
143142
}
@@ -149,6 +148,10 @@ public static BigtableClientContext create(EnhancedBigtableStubSettings settings
149148
clientContext.getExecutor());
150149
}
151150

151+
if (outstandingRpcsMetricTracker != null) {
152+
outstandingRpcsMetricTracker.start(clientContext.getExecutor());
153+
}
154+
152155
return new BigtableClientContext(
153156
clientContext, openTelemetry, internalOtel, settings.getMetricsProvider());
154157
}

google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ public class BuiltinMetricsConstants {
7070
static final String REMAINING_DEADLINE_NAME = "remaining_deadline";
7171
static final String CLIENT_BLOCKING_LATENCIES_NAME = "throttling_latencies";
7272
static final String PER_CONNECTION_ERROR_COUNT_NAME = "per_connection_error_count";
73+
static final String OUTSTANDING_RPCS_PER_CHANNEL_NAME = "connection_pool/outstanding_rpc";
7374

7475
// Start allow list of metrics that will be exported as internal
7576
public static final Map<String, Set<String>> GRPC_METRICS =
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.google.cloud.bigtable.data.v2.stub.metrics;
17+
18+
import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.METER_NAME;
19+
import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.OUTSTANDING_RPCS_PER_CHANNEL_NAME;
20+
21+
import com.google.cloud.bigtable.gaxx.grpc.BigtableChannelInsight;
22+
import com.google.cloud.bigtable.gaxx.grpc.BigtableChannelInsightsProvider;
23+
import io.opentelemetry.api.OpenTelemetry;
24+
import io.opentelemetry.api.common.Attributes;
25+
import io.opentelemetry.api.metrics.LongHistogram;
26+
import io.opentelemetry.api.metrics.Meter;
27+
import java.util.List;
28+
import java.util.concurrent.ScheduledExecutorService;
29+
import java.util.concurrent.ScheduledFuture;
30+
import java.util.concurrent.TimeUnit;
31+
import java.util.concurrent.atomic.AtomicReference;
32+
33+
public class OutstandingRpcsMetricTracker implements Runnable {
34+
private static final int SAMPLING_PERIOD_SECONDS = 60;
35+
private final LongHistogram outstandingRpcsHistogram;
36+
private final Attributes baseAttributes;
37+
private final AtomicReference<BigtableChannelInsightsProvider>
38+
bigtableChannelInsightsProviderRef = new AtomicReference<>();
39+
40+
public OutstandingRpcsMetricTracker(OpenTelemetry openTelemetry, String lbPolicy) {
41+
Meter meter = openTelemetry.getMeter(METER_NAME);
42+
this.outstandingRpcsHistogram =
43+
meter
44+
.histogramBuilder(OUTSTANDING_RPCS_PER_CHANNEL_NAME)
45+
.ofLongs()
46+
.setDescription(
47+
"A distribution of the number of outstanding RPCs per connection in the client pool, sampled periodically.")
48+
.setUnit("1")
49+
.build();
50+
51+
this.baseAttributes =
52+
Attributes.builder().put("transport_type", "grpc").put("lb_policy", lbPolicy).build();
53+
}
54+
55+
/**
56+
* Registers the provider for the channel pool entries. This should be called by the component
57+
* that creates the BigtableChannelPool.
58+
*/
59+
public void registerChannelInsightsProvider(
60+
BigtableChannelInsightsProvider channelInsightsProvider) {
61+
this.bigtableChannelInsightsProviderRef.set(channelInsightsProvider);
62+
}
63+
64+
/** Starts the periodic collection. */
65+
public ScheduledFuture<?> start(ScheduledExecutorService scheduler) {
66+
return scheduler.scheduleAtFixedRate(
67+
this, SAMPLING_PERIOD_SECONDS, SAMPLING_PERIOD_SECONDS, TimeUnit.SECONDS);
68+
}
69+
70+
@Override
71+
public void run() {
72+
BigtableChannelInsightsProvider channelInsightsProvider =
73+
bigtableChannelInsightsProviderRef.get();
74+
if (channelInsightsProvider == null) {
75+
return; // Not registered yet
76+
}
77+
List<? extends BigtableChannelInsight> channelInsights =
78+
channelInsightsProvider.getChannelInfos();
79+
if (channelInsights == null || channelInsights.isEmpty()) {
80+
return;
81+
}
82+
for (BigtableChannelInsight info : channelInsights) {
83+
long currentOutstandingUnaryRpcs = info.getOutstandingStreamingRpcs();
84+
long currentOutstandingStreamingRpcs = info.getOutstandingStreamingRpcs();
85+
// Record outstanding unary RPCs with streaming=false
86+
Attributes unaryAttributes = baseAttributes.toBuilder().put("streaming", false).build();
87+
outstandingRpcsHistogram.record(currentOutstandingUnaryRpcs, unaryAttributes);
88+
89+
// Record outstanding streaming RPCs with streaming=true
90+
Attributes streamingAttributes = baseAttributes.toBuilder().put("streaming", true).build();
91+
outstandingRpcsHistogram.record(currentOutstandingStreamingRpcs, streamingAttributes);
92+
}
93+
}
94+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.google.cloud.bigtable.gaxx.grpc;
17+
18+
import com.google.api.core.InternalApi;
19+
20+
/** Provides insights about a single channel in the channel pool. */
21+
@InternalApi
22+
public interface BigtableChannelInsight {
23+
/** Gets the current number of outstanding Unary RPCs on this channel. */
24+
int getOutstandingUnaryRpcs();
25+
26+
int getOutstandingStreamingRpcs();
27+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.google.cloud.bigtable.gaxx.grpc;
17+
18+
import com.google.api.core.InternalApi;
19+
import java.util.List;
20+
21+
@InternalApi
22+
@FunctionalInterface
23+
public interface BigtableChannelInsightsProvider {
24+
/** Gets the current list of BigtableChannelInsight objects. */
25+
List<? extends BigtableChannelInsight> getChannelInfos();
26+
}

google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/gaxx/grpc/BigtableChannelPool.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
* <p>Internal API
5858
*/
5959
@InternalApi
60-
public class BigtableChannelPool extends ManagedChannel {
60+
public class BigtableChannelPool extends ManagedChannel implements BigtableChannelInsightsProvider {
6161
@VisibleForTesting
6262
static final Logger LOG = Logger.getLogger(BigtableChannelPool.class.getName());
6363

@@ -507,8 +507,14 @@ private Entry getEntry(int affinity) {
507507
return localEntries.get(index);
508508
}
509509

510+
/** Gets the current list of BigtableChannelInsight objects. */
511+
@Override
512+
public List<? extends BigtableChannelInsight> getChannelInfos() {
513+
return entries.get();
514+
}
515+
510516
/** Bundles a gRPC {@link ManagedChannel} with some usage accounting. */
511-
static class Entry {
517+
static class Entry implements BigtableChannelInsight {
512518
private final ManagedChannel channel;
513519

514520
/**
@@ -628,6 +634,17 @@ private void shutdown() {
628634
channel.shutdown();
629635
}
630636
}
637+
638+
/** Gets the current number of outstanding Unary RPCs on this channel. */
639+
@Override
640+
public int getOutstandingUnaryRpcs() {
641+
return outstandingUnaryRpcs.get();
642+
}
643+
644+
@Override
645+
public int getOutstandingStreamingRpcs() {
646+
return 0;
647+
}
631648
}
632649

633650
/** Thin wrapper to ensure that new calls are properly reference counted. */

google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/gaxx/grpc/BigtableTransportChannelProvider.java

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,14 @@
2323
import com.google.api.gax.rpc.TransportChannel;
2424
import com.google.api.gax.rpc.TransportChannelProvider;
2525
import com.google.auth.Credentials;
26+
import com.google.cloud.bigtable.data.v2.stub.metrics.OutstandingRpcsMetricTracker;
2627
import com.google.common.base.Preconditions;
2728
import io.grpc.ManagedChannel;
2829
import java.io.IOException;
2930
import java.util.Map;
3031
import java.util.concurrent.Executor;
3132
import java.util.concurrent.ScheduledExecutorService;
33+
import javax.annotation.Nullable;
3234

3335
/**
3436
* An instance of TransportChannelProvider that provides a TransportChannel through a supplied
@@ -39,12 +41,15 @@ public final class BigtableTransportChannelProvider implements TransportChannelP
3941

4042
private final InstantiatingGrpcChannelProvider delegate;
4143
private final ChannelPrimer channelPrimer;
44+
@Nullable private final OutstandingRpcsMetricTracker outstandingRpcsMetricTracker;
4245

4346
private BigtableTransportChannelProvider(
4447
InstantiatingGrpcChannelProvider instantiatingGrpcChannelProvider,
45-
ChannelPrimer channelPrimer) {
48+
ChannelPrimer channelPrimer,
49+
OutstandingRpcsMetricTracker outstandingRpcsMetricTracker) {
4650
delegate = Preconditions.checkNotNull(instantiatingGrpcChannelProvider);
4751
this.channelPrimer = channelPrimer;
52+
this.outstandingRpcsMetricTracker = outstandingRpcsMetricTracker;
4853
}
4954

5055
@Override
@@ -66,7 +71,8 @@ public BigtableTransportChannelProvider withExecutor(ScheduledExecutorService ex
6671
public BigtableTransportChannelProvider withExecutor(Executor executor) {
6772
InstantiatingGrpcChannelProvider newChannelProvider =
6873
(InstantiatingGrpcChannelProvider) delegate.withExecutor(executor);
69-
return new BigtableTransportChannelProvider(newChannelProvider, channelPrimer);
74+
return new BigtableTransportChannelProvider(
75+
newChannelProvider, channelPrimer, outstandingRpcsMetricTracker);
7076
}
7177

7278
@Override
@@ -78,7 +84,8 @@ public boolean needsHeaders() {
7884
public BigtableTransportChannelProvider withHeaders(Map<String, String> headers) {
7985
InstantiatingGrpcChannelProvider newChannelProvider =
8086
(InstantiatingGrpcChannelProvider) delegate.withHeaders(headers);
81-
return new BigtableTransportChannelProvider(newChannelProvider, channelPrimer);
87+
return new BigtableTransportChannelProvider(
88+
newChannelProvider, channelPrimer, outstandingRpcsMetricTracker);
8289
}
8390

8491
@Override
@@ -90,7 +97,8 @@ public boolean needsEndpoint() {
9097
public TransportChannelProvider withEndpoint(String endpoint) {
9198
InstantiatingGrpcChannelProvider newChannelProvider =
9299
(InstantiatingGrpcChannelProvider) delegate.withEndpoint(endpoint);
93-
return new BigtableTransportChannelProvider(newChannelProvider, channelPrimer);
100+
return new BigtableTransportChannelProvider(
101+
newChannelProvider, channelPrimer, outstandingRpcsMetricTracker);
94102
}
95103

96104
@Deprecated
@@ -104,7 +112,8 @@ public boolean acceptsPoolSize() {
104112
public TransportChannelProvider withPoolSize(int size) {
105113
InstantiatingGrpcChannelProvider newChannelProvider =
106114
(InstantiatingGrpcChannelProvider) delegate.withPoolSize(size);
107-
return new BigtableTransportChannelProvider(newChannelProvider, channelPrimer);
115+
return new BigtableTransportChannelProvider(
116+
newChannelProvider, channelPrimer, outstandingRpcsMetricTracker);
108117
}
109118

110119
/** Expected to only be called once when BigtableClientContext is created */
@@ -136,6 +145,10 @@ public TransportChannel getTransportChannel() throws IOException {
136145
BigtableChannelPool btChannelPool =
137146
BigtableChannelPool.create(btPoolSettings, channelFactory, channelPrimer);
138147

148+
if (outstandingRpcsMetricTracker != null) {
149+
outstandingRpcsMetricTracker.registerChannelInsightsProvider(btChannelPool::getChannelInfos);
150+
}
151+
139152
return GrpcTransportChannel.create(btChannelPool);
140153
}
141154

@@ -153,13 +166,16 @@ public boolean needsCredentials() {
153166
public TransportChannelProvider withCredentials(Credentials credentials) {
154167
InstantiatingGrpcChannelProvider newChannelProvider =
155168
(InstantiatingGrpcChannelProvider) delegate.withCredentials(credentials);
156-
return new BigtableTransportChannelProvider(newChannelProvider, channelPrimer);
169+
return new BigtableTransportChannelProvider(
170+
newChannelProvider, channelPrimer, outstandingRpcsMetricTracker);
157171
}
158172

159173
/** Creates a BigtableTransportChannelProvider. */
160174
public static BigtableTransportChannelProvider create(
161175
InstantiatingGrpcChannelProvider instantiatingGrpcChannelProvider,
162-
ChannelPrimer channelPrimer) {
163-
return new BigtableTransportChannelProvider(instantiatingGrpcChannelProvider, channelPrimer);
176+
ChannelPrimer channelPrimer,
177+
OutstandingRpcsMetricTracker outstandingRpcsMetricTracke) {
178+
return new BigtableTransportChannelProvider(
179+
instantiatingGrpcChannelProvider, channelPrimer, outstandingRpcsMetricTracke);
164180
}
165181
}

0 commit comments

Comments
 (0)