Skip to content

Commit 98fc243

Browse files
committed
feat(bigtable): add client uptime gauge metric
1 parent e0bdb6e commit 98fc243

4 files changed

Lines changed: 109 additions & 1 deletion

File tree

java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricRegistry.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import com.google.cloud.bigtable.data.v2.internal.csm.metrics.ClientSessionDuration;
2727
import com.google.cloud.bigtable.data.v2.internal.csm.metrics.ClientSessionOpenLatency;
2828
import com.google.cloud.bigtable.data.v2.internal.csm.metrics.ClientSessionUptime;
29+
import com.google.cloud.bigtable.data.v2.internal.csm.metrics.ClientUptime;
2930
import com.google.cloud.bigtable.data.v2.internal.csm.metrics.ClientTransportLatency;
3031
import com.google.cloud.bigtable.data.v2.internal.csm.metrics.CustomAttemptLatency;
3132
import com.google.cloud.bigtable.data.v2.internal.csm.metrics.GrpcMetric;
@@ -80,6 +81,7 @@ public class MetricRegistry {
8081
final ClientTransportLatency transportLatencyMetric;
8182

8283
final ClientSessionUptime sessionUptimeMetric;
84+
final ClientUptime clientUptimeMetric;
8385
final ClientSessionDuration sessionDurationMetric;
8486
final ClientSessionOpenLatency sessionOpenLatencyMetric;
8587

@@ -111,6 +113,7 @@ public MetricRegistry() {
111113
batchWriteFlowControlTargetQpsMetric = register(new ClientBatchWriteFlowControlTargetQps());
112114

113115
sessionUptimeMetric = register(new ClientSessionUptime());
116+
clientUptimeMetric = register(new ClientUptime());
114117
sessionDurationMetric = register(new ClientSessionDuration());
115118
sessionOpenLatencyMetric = register(new ClientSessionOpenLatency());
116119
transportLatencyMetric = register(new ClientTransportLatency());
@@ -222,6 +225,7 @@ public class RecorderRegistry {
222225
public final ClientTransportLatency.Recorder transportLatency;
223226

224227
public final ClientSessionUptime.Recorder sessionUptime;
228+
public final ClientUptime.Recorder clientUptime;
225229
public final ClientSessionDuration.Recorder sessionDuration;
226230
public final ClientSessionOpenLatency.Recorder sessionOpenLatency;
227231

@@ -260,6 +264,7 @@ private RecorderRegistry(Meter meter, boolean disableInternalMetrics) {
260264
transportLatency = transportLatencyMetric.newRecorder(meter);
261265

262266
sessionUptime = sessionUptimeMetric.newRecorder(meter);
267+
clientUptime = clientUptimeMetric.newRecorder(meter);
263268
sessionDuration = sessionDurationMetric.newRecorder(meter);
264269
sessionOpenLatency = sessionOpenLatencyMetric.newRecorder(meter);
265270

java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricsImpl.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
import com.google.cloud.opentelemetry.metric.GoogleCloudMetricExporter;
5757
import com.google.cloud.opentelemetry.metric.MetricConfiguration;
5858
import com.google.common.base.Preconditions;
59+
import com.google.common.base.Stopwatch;
5960
import com.google.common.base.Splitter;
6061
import com.google.common.base.Suppliers;
6162
import com.google.common.collect.ImmutableList;
@@ -93,6 +94,7 @@ public class MetricsImpl implements Metrics, Closeable {
9394
.map(Boolean::parseBoolean)
9495
.orElse(false);
9596

97+
private final ClientInfo clientInfo;
9698
private final ApiTracerFactory userTracerFactory;
9799
private final @Nullable OpenTelemetrySdk internalOtel;
98100
private final @Nullable MetricRegistry.RecorderRegistry internalRecorder;
@@ -113,6 +115,7 @@ public class MetricsImpl implements Metrics, Closeable {
113115
private final List<SessionTracer> sessionTracers = new ArrayList<>();
114116

115117
private final List<ScheduledFuture<?>> tasks = new ArrayList<>();
118+
private final Stopwatch clientUptimeStopwatch = Stopwatch.createUnstarted();
116119

117120
public MetricsImpl(
118121
MetricRegistry metricRegistry,
@@ -123,6 +126,7 @@ public MetricsImpl(
123126
Tagger ocTagger,
124127
StatsRecorder ocRecorder,
125128
ScheduledExecutorService executor) {
129+
this.clientInfo = clientInfo;
126130
this.userTracerFactory = Preconditions.checkNotNull(userTracerFactory);
127131

128132
this.internalOtel = internalOtel;
@@ -174,13 +178,18 @@ public void close() {
174178
for (ScheduledFuture<?> task : tasks) {
175179
task.cancel(false);
176180
}
181+
if (clientUptimeStopwatch.isRunning()) {
182+
clientUptimeStopwatch.stop();
183+
recordAsyncClientMetrics();
184+
}
177185
if (internalOtel != null) {
178186
internalOtel.close();
179187
}
180188
}
181189

182190
@Override
183191
public void start() {
192+
clientUptimeStopwatch.start();
184193
if (channelPoolMetricsTracer != null) {
185194
tasks.add(channelPoolMetricsTracer.start(executor));
186195
}
@@ -189,7 +198,7 @@ public void start() {
189198
}
190199
if (internalOtel != null) {
191200
tasks.add(
192-
executor.scheduleAtFixedRate(this::recordAsyncSessionMetrics, 1, 1, TimeUnit.MINUTES));
201+
executor.scheduleAtFixedRate(this::recordAsyncMetrics, 1, 1, TimeUnit.MINUTES));
193202
}
194203
}
195204

@@ -250,6 +259,17 @@ private void recordAsyncSessionMetrics() {
250259
}
251260
}
252261

262+
private void recordAsyncMetrics() {
263+
recordAsyncSessionMetrics();
264+
recordAsyncClientMetrics();
265+
}
266+
267+
private void recordAsyncClientMetrics() {
268+
if (internalRecorder != null) {
269+
internalRecorder.clientUptime.record(clientInfo, clientUptimeStopwatch.elapsed());
270+
}
271+
}
272+
253273
@Override
254274
public PoolFallbackListener getPoolFallbackListener() {
255275
return poolFallbackListener;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright 2026 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+
17+
package com.google.cloud.bigtable.data.v2.internal.csm.metrics;
18+
19+
import com.google.cloud.bigtable.data.v2.internal.csm.attributes.ClientInfo;
20+
import com.google.cloud.bigtable.data.v2.internal.csm.metrics.Constants.Units;
21+
import com.google.cloud.bigtable.data.v2.internal.csm.schema.ClientSchema;
22+
import io.opentelemetry.api.common.Attributes;
23+
import io.opentelemetry.api.metrics.LongGauge;
24+
import io.opentelemetry.api.metrics.Meter;
25+
import java.time.Duration;
26+
27+
public class ClientUptime extends MetricWrapper<ClientSchema> {
28+
private static final String NAME = "bigtable.googleapis.com/internal/client/uptime";
29+
30+
public ClientUptime() {
31+
super(ClientSchema.INSTANCE, NAME);
32+
}
33+
34+
public Recorder newRecorder(Meter meter) {
35+
return new Recorder(meter);
36+
}
37+
38+
public class Recorder {
39+
private final LongGauge instrument;
40+
41+
private Recorder(Meter meter) {
42+
instrument =
43+
meter
44+
.gaugeBuilder(NAME)
45+
.setDescription("The uptime of the client")
46+
.setUnit(Units.MILLISECOND)
47+
.ofLongs()
48+
.build();
49+
}
50+
51+
public void record(ClientInfo clientInfo, Duration duration) {
52+
Attributes attributes =
53+
getSchema()
54+
.createResourceAttrs(clientInfo)
55+
.build();
56+
57+
instrument.set(duration.toMillis(), attributes);
58+
}
59+
}
60+
}

java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/csm/MetricRegistryExportTest.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -751,6 +751,29 @@ void testSessionUptime() {
751751
.build());
752752
}
753753

754+
@Test
755+
void testClientUptime() {
756+
registry.clientUptime.record(clientInfo, Duration.ofMinutes(10));
757+
metricReader.forceFlush().join(1, TimeUnit.MINUTES);
758+
759+
TimeSeries timeSeries =
760+
metricService.getSingleTimeSeriesByName(
761+
"bigtable.googleapis.com/internal/client/uptime");
762+
763+
assertThat(timeSeries.getResource()).isEqualTo(expectedClientMonitoredResource);
764+
765+
assertThat(timeSeries.getMetric().getLabelsMap()).isEmpty();
766+
767+
assertThat(timeSeries.getPointsList())
768+
.comparingExpectedFieldsOnly()
769+
.containsExactly(
770+
Point.newBuilder()
771+
.setValue(
772+
TypedValue.newBuilder()
773+
.setInt64Value(Duration.ofMinutes(10).toMillis()))
774+
.build());
775+
}
776+
754777
@Test
755778
void testPacemaker() {
756779
registry.pacemakerDelay.record(clientInfo, "background", Duration.ofMillis(1));

0 commit comments

Comments
 (0)