entry :
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CustomOpenTelemetryMetricsProvider.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CustomOpenTelemetryMetricsProvider.java
index bcd1fe488f..ceade9b2c0 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CustomOpenTelemetryMetricsProvider.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CustomOpenTelemetryMetricsProvider.java
@@ -25,7 +25,7 @@
* {@code
* SdkMeterProviderBuilder sdkMeterProvider = SdkMeterProvider.builder();
*
- * // register Builtin metrics on your meter provider
+ * // register Builtin metrics on your meter provider with default credentials
* BuiltinMetricsViews.registerBuiltinMetrics("project-id", sdkMeterProvider);
*
* // register other metrics reader and views
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/DefaultMetricsProvider.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/DefaultMetricsProvider.java
index 0f3ee0c98f..68e37a8f3c 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/DefaultMetricsProvider.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/DefaultMetricsProvider.java
@@ -15,6 +15,14 @@
*/
package com.google.cloud.bigtable.data.v2.stub.metrics;
+import com.google.auth.Credentials;
+import io.opentelemetry.api.OpenTelemetry;
+import io.opentelemetry.sdk.OpenTelemetrySdk;
+import io.opentelemetry.sdk.metrics.SdkMeterProvider;
+import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder;
+import java.io.IOException;
+import javax.annotation.Nullable;
+
/**
* Set {@link
* com.google.cloud.bigtable.data.v2.BigtableDataSettings.Builder#setMetricsProvider(MetricsProvider)},
@@ -26,8 +34,20 @@ public final class DefaultMetricsProvider implements MetricsProvider {
public static DefaultMetricsProvider INSTANCE = new DefaultMetricsProvider();
+ private OpenTelemetry openTelemetry;
+
private DefaultMetricsProvider() {}
+ public OpenTelemetry getOpenTelemetry(String projectId, @Nullable Credentials credentials)
+ throws IOException {
+ if (openTelemetry == null) {
+ SdkMeterProviderBuilder meterProvider = SdkMeterProvider.builder();
+ BuiltinMetricsView.registerBuiltinMetrics(projectId, credentials, meterProvider);
+ openTelemetry = OpenTelemetrySdk.builder().setMeterProvider(meterProvider.build()).build();
+ }
+ return openTelemetry;
+ }
+
@Override
public String toString() {
return "DefaultMetricsProvider";
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/ErrorCountPerConnectionMetricTracker.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/ErrorCountPerConnectionMetricTracker.java
index cab3b0bbd0..df37ee3e9d 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/ErrorCountPerConnectionMetricTracker.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/ErrorCountPerConnectionMetricTracker.java
@@ -15,12 +15,15 @@
*/
package com.google.cloud.bigtable.data.v2.stub.metrics;
+import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.METER_NAME;
+import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.PER_CONNECTION_ERROR_COUNT_NAME;
+
import com.google.api.core.InternalApi;
-import com.google.cloud.bigtable.stats.StatsRecorderWrapperForConnection;
-import com.google.cloud.bigtable.stats.StatsWrapper;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ImmutableMap;
import io.grpc.ClientInterceptor;
+import io.opentelemetry.api.OpenTelemetry;
+import io.opentelemetry.api.common.Attributes;
+import io.opentelemetry.api.metrics.LongHistogram;
+import io.opentelemetry.api.metrics.Meter;
import java.util.Collections;
import java.util.Set;
import java.util.WeakHashMap;
@@ -31,23 +34,28 @@
@InternalApi("For internal use only")
public class ErrorCountPerConnectionMetricTracker implements Runnable {
private static final Integer PER_CONNECTION_ERROR_COUNT_PERIOD_SECONDS = 60;
+
+ private final LongHistogram perConnectionErrorCountHistogram;
+ private final Attributes attributes;
+
private final Set connectionErrorCountInterceptors;
private final Object interceptorsLock = new Object();
- // This is not final so that it can be updated and mocked during testing.
- private StatsRecorderWrapperForConnection statsRecorderWrapperForConnection;
- @VisibleForTesting
- void setStatsRecorderWrapperForConnection(
- StatsRecorderWrapperForConnection statsRecorderWrapperForConnection) {
- this.statsRecorderWrapperForConnection = statsRecorderWrapperForConnection;
- }
-
- public ErrorCountPerConnectionMetricTracker(ImmutableMap builtinAttributes) {
+ public ErrorCountPerConnectionMetricTracker(OpenTelemetry openTelemetry, Attributes attributes) {
connectionErrorCountInterceptors =
Collections.synchronizedSet(Collections.newSetFromMap(new WeakHashMap<>()));
- this.statsRecorderWrapperForConnection =
- StatsWrapper.createRecorderForConnection(builtinAttributes);
+ Meter meter = openTelemetry.getMeter(METER_NAME);
+
+ perConnectionErrorCountHistogram =
+ meter
+ .histogramBuilder(PER_CONNECTION_ERROR_COUNT_NAME)
+ .ofLongs()
+ .setDescription("Distribution of counts of channels per 'error count per minute'.")
+ .setUnit("1")
+ .build();
+
+ this.attributes = attributes;
}
public void startConnectionErrorCountTracker(ScheduledExecutorService scheduler) {
@@ -75,7 +83,7 @@ public void run() {
if (errors > 0 || successes > 0) {
// TODO: add a metric to also keep track of the number of successful requests per each
// connection.
- statsRecorderWrapperForConnection.putAndRecordPerConnectionErrorCount(errors);
+ perConnectionErrorCountHistogram.record(errors, attributes);
}
}
}
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/BigtableDataClientFactoryTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/BigtableDataClientFactoryTest.java
index 9173e6f6af..fea66e82bf 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/BigtableDataClientFactoryTest.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/BigtableDataClientFactoryTest.java
@@ -320,7 +320,7 @@ public void testFeatureFlags() throws Exception {
@Test
public void testBulkMutationFlowControllerConfigured() throws Exception {
BigtableDataSettings settings =
- BigtableDataSettings.newBuilder()
+ BigtableDataSettings.newBuilderForEmulator(server.getPort())
.setProjectId("my-project")
.setInstanceId("my-instance")
.setCredentialsProvider(credentialsProvider)
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracerCallableTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracerCallableTest.java
index 14f4c698b2..3e1d59b133 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracerCallableTest.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracerCallableTest.java
@@ -135,7 +135,7 @@ public void sendHeaders(Metadata headers) {
settings.getStubSettings(),
Tags.getTagger(),
localStats.getStatsRecorder(),
- clientContext))
+ null))
.build();
attempts = settings.getStubSettings().readRowsSettings().getRetrySettings().getMaxAttempts();
stub = new EnhancedBigtableStub(settings.getStubSettings(), clientContext);
@@ -161,7 +161,7 @@ public void sendHeaders(Metadata headers) {
noHeaderSettings.getStubSettings(),
Tags.getTagger(),
localStats.getStatsRecorder(),
- noHeaderClientContext))
+ null))
.build();
noHeaderStub =
new EnhancedBigtableStub(noHeaderSettings.getStubSettings(), noHeaderClientContext);
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/ErrorCountPerConnectionTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/ErrorCountPerConnectionTest.java
index a6670182b8..dec5120b1c 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/ErrorCountPerConnectionTest.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/ErrorCountPerConnectionTest.java
@@ -23,17 +23,29 @@
import com.google.api.gax.grpc.ChannelPoolSettings;
import com.google.api.gax.grpc.InstantiatingGrpcChannelProvider;
import com.google.bigtable.v2.*;
+import com.google.cloud.bigtable.Version;
import com.google.cloud.bigtable.data.v2.BigtableDataSettings;
import com.google.cloud.bigtable.data.v2.FakeServiceBuilder;
import com.google.cloud.bigtable.data.v2.models.*;
import com.google.cloud.bigtable.data.v2.stub.EnhancedBigtableStub;
import com.google.cloud.bigtable.data.v2.stub.EnhancedBigtableStubSettings;
-import com.google.cloud.bigtable.stats.StatsRecorderWrapperForConnection;
import io.grpc.Server;
import io.grpc.Status;
import io.grpc.StatusRuntimeException;
import io.grpc.stub.StreamObserver;
+import io.opentelemetry.api.common.Attributes;
+import io.opentelemetry.sdk.OpenTelemetrySdk;
+import io.opentelemetry.sdk.metrics.InstrumentSelector;
+import io.opentelemetry.sdk.metrics.SdkMeterProvider;
+import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder;
+import io.opentelemetry.sdk.metrics.View;
+import io.opentelemetry.sdk.metrics.data.HistogramPointData;
+import io.opentelemetry.sdk.metrics.data.MetricData;
+import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import org.junit.After;
import org.junit.Before;
@@ -51,25 +63,50 @@ public class ErrorCountPerConnectionTest {
private final FakeService fakeService = new FakeService();
private EnhancedBigtableStubSettings.Builder builder;
private ArgumentCaptor runnableCaptor;
- private StatsRecorderWrapperForConnection statsRecorderWrapperForConnection;
+
+ private InMemoryMetricReader metricReader;
+
+ private Attributes attributes;
@Before
public void setup() throws Exception {
server = FakeServiceBuilder.create(fakeService).start();
ScheduledExecutorService executors = Mockito.mock(ScheduledExecutorService.class);
+
+ attributes =
+ Attributes.builder()
+ .put(BuiltinMetricsConstants.PROJECT_ID_KEY, "fake-project")
+ .put(BuiltinMetricsConstants.INSTANCE_ID_KEY, "fake-instance")
+ .put(BuiltinMetricsConstants.APP_PROFILE_KEY, "")
+ .put(BuiltinMetricsConstants.CLIENT_NAME_KEY, "bigtable-java/" + Version.VERSION)
+ .build();
+
+ metricReader = InMemoryMetricReader.create();
+
+ SdkMeterProviderBuilder meterProvider =
+ SdkMeterProvider.builder().registerMetricReader(metricReader);
+
+ for (Map.Entry entry :
+ BuiltinMetricsConstants.getAllViews().entrySet()) {
+ meterProvider.registerView(entry.getKey(), entry.getValue());
+ }
+
+ OpenTelemetrySdk otel =
+ OpenTelemetrySdk.builder().setMeterProvider(meterProvider.build()).build();
+
builder =
BigtableDataSettings.newBuilderForEmulator(server.getPort())
.stubSettings()
.setBackgroundExecutorProvider(FixedExecutorProvider.create(executors))
.setProjectId("fake-project")
- .setInstanceId("fake-instance");
+ .setInstanceId("fake-instance")
+ .setMetricsProvider(CustomOpenTelemetryMetricsProvider.create(otel));
+
runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
Mockito.when(
executors.scheduleAtFixedRate(runnableCaptor.capture(), anyLong(), anyLong(), any()))
.thenReturn(null);
-
- statsRecorderWrapperForConnection = Mockito.mock(StatsRecorderWrapperForConnection.class);
}
@After
@@ -98,14 +135,21 @@ public void readWithOneChannel() throws Exception {
// noop
}
}
- ArgumentCaptor errorCountCaptor = ArgumentCaptor.forClass(long.class);
- Mockito.doNothing()
- .when(statsRecorderWrapperForConnection)
- .putAndRecordPerConnectionErrorCount(errorCountCaptor.capture());
+
runInterceptorTasksAndAssertCount();
- List allErrorCounts = errorCountCaptor.getAllValues();
- assertThat(allErrorCounts.size()).isEqualTo(1);
- assertThat(allErrorCounts.get(0)).isEqualTo(errorCount);
+
+ Collection allMetrics = metricReader.collectAllMetrics();
+ MetricData metricData =
+ BuiltinMetricsTestUtils.getMetricData(
+ allMetrics, BuiltinMetricsConstants.PER_CONNECTION_ERROR_COUNT_NAME);
+
+ // Make sure the correct bucket is updated with the correct number of data points
+ ArrayList histogramPointData =
+ new ArrayList<>(metricData.getHistogramData().getPoints());
+ assertThat(histogramPointData.size()).isEqualTo(1);
+ HistogramPointData point = histogramPointData.get(0);
+ int index = findDataPointIndex(point.getBoundaries(), errorCount);
+ assertThat(point.getCounts().get(index)).isEqualTo(1);
}
@Test
@@ -131,28 +175,35 @@ public void readWithTwoChannels() throws Exception {
// noop
}
}
- ArgumentCaptor errorCountCaptor = ArgumentCaptor.forClass(long.class);
- Mockito.doNothing()
- .when(statsRecorderWrapperForConnection)
- .putAndRecordPerConnectionErrorCount(errorCountCaptor.capture());
runInterceptorTasksAndAssertCount();
- List allErrorCounts = errorCountCaptor.getAllValues();
- assertThat(allErrorCounts.size()).isEqualTo(2);
- // Requests get assigned to channels using a Round Robin algorithm, so half to each.
- assertThat(allErrorCounts).containsExactly(totalErrorCount / 2, totalErrorCount / 2);
+ long errorCountPerChannel = totalErrorCount / 2;
+
+ Collection allMetrics = metricReader.collectAllMetrics();
+ MetricData metricData =
+ BuiltinMetricsTestUtils.getMetricData(
+ allMetrics, BuiltinMetricsConstants.PER_CONNECTION_ERROR_COUNT_NAME);
+
+ // The 2 channels should get equal amount of errors, so the totalErrorCount / 2 bucket is
+ // updated twice.
+ ArrayList histogramPointData =
+ new ArrayList<>(metricData.getHistogramData().getPoints());
+ assertThat(histogramPointData.size()).isEqualTo(1);
+ HistogramPointData point = histogramPointData.get(0);
+ int index = findDataPointIndex(point.getBoundaries(), errorCountPerChannel);
+ assertThat(point.getCounts().get(index)).isEqualTo(2);
}
@Test
public void readOverTwoPeriods() throws Exception {
EnhancedBigtableStub stub = EnhancedBigtableStub.create(builder.build());
- long errorCount = 0;
+ long errorCount1 = 0;
for (int i = 0; i < 20; i++) {
Query query;
if (i % 3 == 0) {
query = Query.create(ERROR_TABLE_NAME);
- errorCount += 1;
+ errorCount1 += 1;
} else {
query = Query.create(SUCCESS_TABLE_NAME);
}
@@ -162,16 +213,9 @@ public void readOverTwoPeriods() throws Exception {
// noop
}
}
- ArgumentCaptor errorCountCaptor = ArgumentCaptor.forClass(long.class);
- Mockito.doNothing()
- .when(statsRecorderWrapperForConnection)
- .putAndRecordPerConnectionErrorCount(errorCountCaptor.capture());
- runInterceptorTasksAndAssertCount();
- List allErrorCounts = errorCountCaptor.getAllValues();
- assertThat(allErrorCounts.size()).isEqualTo(1);
- assertThat(allErrorCounts.get(0)).isEqualTo(errorCount);
- errorCount = 0;
+ runInterceptorTasksAndAssertCount();
+ long errorCount2 = 0;
for (int i = 0; i < 20; i++) {
Query query;
@@ -179,7 +223,7 @@ public void readOverTwoPeriods() throws Exception {
query = Query.create(SUCCESS_TABLE_NAME);
} else {
query = Query.create(ERROR_TABLE_NAME);
- errorCount += 1;
+ errorCount2 += 1;
}
try {
stub.readRowsCallable().call(query).iterator().hasNext();
@@ -187,27 +231,22 @@ public void readOverTwoPeriods() throws Exception {
// noop
}
}
- errorCountCaptor = ArgumentCaptor.forClass(long.class);
- Mockito.doNothing()
- .when(statsRecorderWrapperForConnection)
- .putAndRecordPerConnectionErrorCount(errorCountCaptor.capture());
+
runInterceptorTasksAndAssertCount();
- allErrorCounts = errorCountCaptor.getAllValues();
- assertThat(allErrorCounts.size()).isEqualTo(1);
- assertThat(allErrorCounts.get(0)).isEqualTo(errorCount);
- }
- @Test
- public void ignoreInactiveConnection() throws Exception {
- EnhancedBigtableStub stub = EnhancedBigtableStub.create(builder.build());
+ Collection allMetrics = metricReader.collectAllMetrics();
+ MetricData metricData =
+ BuiltinMetricsTestUtils.getMetricData(
+ allMetrics, BuiltinMetricsConstants.PER_CONNECTION_ERROR_COUNT_NAME);
- ArgumentCaptor errorCountCaptor = ArgumentCaptor.forClass(long.class);
- Mockito.doNothing()
- .when(statsRecorderWrapperForConnection)
- .putAndRecordPerConnectionErrorCount(errorCountCaptor.capture());
- runInterceptorTasksAndAssertCount();
- List allErrorCounts = errorCountCaptor.getAllValues();
- assertThat(allErrorCounts).isEmpty();
+ ArrayList histogramPointData =
+ new ArrayList<>(metricData.getHistogramData().getPoints());
+ assertThat(histogramPointData.size()).isEqualTo(1);
+ HistogramPointData point = histogramPointData.get(0);
+ int index1 = findDataPointIndex(point.getBoundaries(), errorCount1);
+ int index2 = findDataPointIndex(point.getBoundaries(), errorCount2);
+ assertThat(point.getCounts().get(index1)).isEqualTo(1);
+ assertThat(point.getCounts().get(index2)).isEqualTo(1);
}
@Test
@@ -221,22 +260,19 @@ public void noFailedRequests() throws Exception {
// noop
}
}
- ArgumentCaptor errorCountCaptor = ArgumentCaptor.forClass(long.class);
- Mockito.doNothing()
- .when(statsRecorderWrapperForConnection)
- .putAndRecordPerConnectionErrorCount(errorCountCaptor.capture());
runInterceptorTasksAndAssertCount();
- List allErrorCounts = errorCountCaptor.getAllValues();
- assertThat(allErrorCounts.size()).isEqualTo(1);
- assertThat(allErrorCounts.get(0)).isEqualTo(0);
+ Collection allMetrics = metricReader.collectAllMetrics();
+ MetricData metricData =
+ BuiltinMetricsTestUtils.getMetricData(
+ allMetrics, BuiltinMetricsConstants.PER_CONNECTION_ERROR_COUNT_NAME);
+ long value = BuiltinMetricsTestUtils.getAggregatedValue(metricData, attributes);
+ assertThat(value).isEqualTo(0);
}
private void runInterceptorTasksAndAssertCount() {
int actualNumOfTasks = 0;
for (Runnable runnable : runnableCaptor.getAllValues()) {
if (runnable instanceof ErrorCountPerConnectionMetricTracker) {
- ((ErrorCountPerConnectionMetricTracker) runnable)
- .setStatsRecorderWrapperForConnection(statsRecorderWrapperForConnection);
runnable.run();
actualNumOfTasks++;
}
@@ -244,6 +280,16 @@ private void runInterceptorTasksAndAssertCount() {
assertThat(actualNumOfTasks).isEqualTo(1);
}
+ private int findDataPointIndex(List boundaries, long dataPoint) {
+ int index = 0;
+ for (; index < boundaries.size(); index++) {
+ if (boundaries.get(index) >= dataPoint) {
+ break;
+ }
+ }
+ return index;
+ }
+
static class FakeService extends BigtableGrpc.BigtableImplBase {
@Override
public void readRows(
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsTracerTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsTracerTest.java
index a4e28703c8..ecf204d1e2 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsTracerTest.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsTracerTest.java
@@ -130,7 +130,7 @@ public void setUp() throws Exception {
settings.getStubSettings(),
Tags.getTagger(),
localStats.getStatsRecorder(),
- clientContext))
+ null))
.build();
stub = new EnhancedBigtableStub(settings.getStubSettings(), clientContext);
}