From cb88b391853a7e84f8041cdf7304c2276b2ae881 Mon Sep 17 00:00:00 2001 From: Igor Bernstein Date: Fri, 29 Sep 2023 10:56:57 -0400 Subject: [PATCH 1/3] fix: a rare race condition in the row merger (#1939) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: a rare race condition in the row merger This would manifest as a hang when iterating over a ServerStream from ReadRows Change-Id: I74533c6714b40a68ec0ef81dadac747e10bee39d * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --------- Co-authored-by: Owl Bot --- .../reframing/ReframingResponseObserver.java | 2 +- .../ReframingResponseObserverTest.java | 121 ++++++++++++++++++ 2 files changed, 122 insertions(+), 1 deletion(-) diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/gaxx/reframing/ReframingResponseObserver.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/gaxx/reframing/ReframingResponseObserver.java index 6f2440fff7..3a5458836f 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/gaxx/reframing/ReframingResponseObserver.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/gaxx/reframing/ReframingResponseObserver.java @@ -277,7 +277,7 @@ private void deliverUnsafe() { // Optimization: the inner loop will eager process any accumulated state, so reset the lock // for just this iteration. (If another event occurs during processing, it can increment the // lock to enqueue another iteration). - lock.lazySet(1); + lock.set(1); // Process the upstream message if one exists. pollUpstream(); diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/gaxx/reframing/ReframingResponseObserverTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/gaxx/reframing/ReframingResponseObserverTest.java index 426c27f5a3..23df3726d3 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/gaxx/reframing/ReframingResponseObserverTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/gaxx/reframing/ReframingResponseObserverTest.java @@ -15,9 +15,12 @@ */ package com.google.cloud.bigtable.gaxx.reframing; +import static com.google.common.truth.Truth.assertWithMessage; + import com.google.api.gax.rpc.StreamController; import com.google.cloud.bigtable.gaxx.testing.FakeStreamingApi.ServerStreamingStashCallable; import com.google.cloud.bigtable.gaxx.testing.FakeStreamingApi.ServerStreamingStashCallable.StreamControllerStash; +import com.google.cloud.bigtable.gaxx.testing.MockStreamingApi; import com.google.cloud.bigtable.gaxx.testing.MockStreamingApi.MockResponseObserver; import com.google.cloud.bigtable.gaxx.testing.MockStreamingApi.MockServerStreamingCall; import com.google.cloud.bigtable.gaxx.testing.MockStreamingApi.MockServerStreamingCallable; @@ -27,9 +30,13 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Queues; import com.google.common.truth.Truth; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.Queue; +import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; @@ -431,6 +438,120 @@ public String pop() { Truth.assertThat(finalError.getSuppressed()[0].getCause()).isSameInstanceAs(fakeCancelError); } + /** + * Test race between a request() and onComplete (b/295866356). This will stress the concurrency + * primitives in deliver() by running a many iterations across many threads. Some race conditions + * are very subtle and are very rare, so bugs in the implementation would present themselves as + * flakes in this test. All flakes of this test should be investigated as a failure. + */ + @Test + public void testRequestAndCompleteRaceCondition() throws Throwable { + int concurrency = 20; + int iterations = 20_000; + + ExecutorService executor = Executors.newFixedThreadPool(concurrency); + + List> results = new ArrayList<>(); + + for (int i = 0; i < concurrency; i++) { + Future result = + executor.submit( + (Callable) + () -> { + for (int j = 0; j < iterations; j++) { + requestAndCompleteRaceConditionIteration(); + } + return null; + }); + results.add(result); + } + + executor.shutdown(); + + for (Future result : results) { + try { + result.get(); + } catch (ExecutionException e) { + throw e.getCause(); + } + } + } + + private static void requestAndCompleteRaceConditionIteration() + throws InterruptedException, ExecutionException { + MockStreamingApi.MockResponseObserver observer = + new MockStreamingApi.MockResponseObserver<>(false); + ReframingResponseObserver underTest = + new ReframingResponseObserver<>( + observer, new ReframingResponseObserverTest.DasherizingReframer(1)); + + // This is intentionally not a Phaser, the Phaser seems to drastically reduce the reproduction + // rate of the + // original race condition. + CountDownLatch readySignal = new CountDownLatch(2); + CompletableFuture startSignal = new CompletableFuture<>(); + + ExecutorService executor = Executors.newFixedThreadPool(2); + + Future f1 = + executor.submit( + () -> { + // no setup, tell controller thread we are ready and wait for the start signal + readySignal.countDown(); + startSignal.get(); + + // Race start + underTest.onComplete(); + // Race end + + return null; + }); + + Future f2 = + executor.submit( + () -> { + // Setup before race - simulate that the ServerStream iterator got one row and is now + // checking if there + // is another. This is the lead up to the race with grpc's onComplete + underTest.onStart( + new StreamController() { + @Override + public void cancel() {} + + @Override + public void disableAutoInboundFlowControl() {} + + @Override + public void request(int count) {} + }); + observer.getController().request(1); + underTest.onResponse("moo"); + + // Setup complete, tell controller thread we are ready and wait for the start signal + readySignal.countDown(); + startSignal.get(); + + // Race start + observer.getController().request(1); + // Race end + + return null; + }); + executor.shutdown(); + + // Wait for worker setup + readySignal.await(); + // Tell workers to race + startSignal.complete(null); + + // Wait workers to finish + f1.get(); + f2.get(); + + // the outer observer should be told of the completion of rpc + assertWithMessage("outer observer should not hang").that(observer.isDone()).isTrue(); + } + /** * A simple implementation of a {@link Reframer}. The input string is split by dash, and the * output is concatenated by dashes. The test can verify M:N behavior by adjusting the From db3df292783fa24ef49afe811de4f22c5bf8ad33 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Wed, 8 Nov 2023 19:51:39 +0000 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot=20po?= =?UTF-8?q?st-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- .github/workflows/ci.yaml | 145 +++++++++++++++++++++----------------- README.md | 8 +-- 2 files changed, 83 insertions(+), 70 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index c13eb8a836..e27b2c5756 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1,8 +1,23 @@ -'on': +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# Github action job to test core java library features on +# downstream client libraries before they are released. +on: push: branches: - - 2.25.x - pull_request: null + - main + pull_request: name: ci jobs: units: @@ -10,21 +25,20 @@ jobs: strategy: fail-fast: false matrix: - java: - - 11 - - 17 + java: [11, 17] steps: - - uses: actions/checkout@v3 - - uses: actions/setup-java@v3 - with: - distribution: temurin - java-version: ${{matrix.java}} - - run: java -version - - run: .kokoro/build.sh - env: - JOB_TYPE: test + - uses: actions/checkout@v3 + - uses: actions/setup-java@v3 + with: + distribution: temurin + java-version: ${{matrix.java}} + - run: java -version + - run: .kokoro/build.sh + env: + JOB_TYPE: test units-java8: - name: units (8) + # Building using Java 17 and run the tests with Java 8 runtime + name: "units (8)" runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -32,9 +46,9 @@ jobs: with: java-version: 8 distribution: temurin - - name: >- - Set jvm system property environment variable for surefire plugin (unit - tests) + - name: "Set jvm system property environment variable for surefire plugin (unit tests)" + # Maven surefire plugin (unit tests) allows us to specify JVM to run the tests. + # https://maven.apache.org/surefire/maven-surefire-plugin/test-mojo.html#jvm run: echo "SUREFIRE_JVM_OPT=-Djvm=${JAVA_HOME}/bin/java" >> $GITHUB_ENV shell: bash - uses: actions/setup-java@v3 @@ -47,64 +61,63 @@ jobs: windows: runs-on: windows-latest steps: - - name: Support longpaths - run: git config --system core.longpaths true - - uses: actions/checkout@v3 - - uses: actions/setup-java@v3 - with: - distribution: temurin - java-version: 8 - - run: java -version - - run: .kokoro/build.bat - env: - JOB_TYPE: test + - name: Support longpaths + run: git config --system core.longpaths true + - uses: actions/checkout@v3 + - uses: actions/setup-java@v3 + with: + distribution: temurin + java-version: 8 + - run: java -version + - run: .kokoro/build.bat + env: + JOB_TYPE: test dependencies: runs-on: ubuntu-latest strategy: matrix: - java: - - 17 + java: [17] steps: - - uses: actions/checkout@v3 - - uses: actions/setup-java@v3 - with: - distribution: temurin - java-version: ${{matrix.java}} - - run: java -version - - run: .kokoro/dependencies.sh + - uses: actions/checkout@v3 + - uses: actions/setup-java@v3 + with: + distribution: temurin + java-version: ${{matrix.java}} + - run: java -version + - run: .kokoro/dependencies.sh javadoc: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-java@v3 - with: - distribution: temurin - java-version: 17 - - run: java -version - - run: .kokoro/build.sh - env: - JOB_TYPE: javadoc + - uses: actions/checkout@v3 + - uses: actions/setup-java@v3 + with: + distribution: temurin + java-version: 17 + - run: java -version + - run: .kokoro/build.sh + env: + JOB_TYPE: javadoc lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-java@v3 - with: - distribution: temurin - java-version: 11 - - run: java -version - - run: .kokoro/build.sh - env: - JOB_TYPE: lint + - uses: actions/checkout@v3 + - uses: actions/setup-java@v3 + with: + distribution: temurin + java-version: 11 + - run: java -version + - run: .kokoro/build.sh + env: + JOB_TYPE: lint clirr: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-java@v3 - with: - distribution: temurin - java-version: 8 - - run: java -version - - run: .kokoro/build.sh - env: - JOB_TYPE: clirr + - uses: actions/checkout@v3 + - uses: actions/setup-java@v3 + with: + distribution: temurin + java-version: 8 + - run: java -version + - run: .kokoro/build.sh + env: + JOB_TYPE: clirr diff --git a/README.md b/README.md index c556fc94e8..08ae8b92c2 100644 --- a/README.md +++ b/README.md @@ -50,20 +50,20 @@ If you are using Maven without the BOM, add this to your dependencies: If you are using Gradle 5.x or later, add this to your dependencies: ```Groovy -implementation platform('com.google.cloud:libraries-bom:26.18.0') +implementation platform('com.google.cloud:libraries-bom:26.26.0') implementation 'com.google.cloud:google-cloud-bigtable' ``` If you are using Gradle without BOM, add this to your dependencies: ```Groovy -implementation 'com.google.cloud:google-cloud-bigtable:2.24.1' +implementation 'com.google.cloud:google-cloud-bigtable:2.29.1' ``` If you are using SBT, add this to your dependencies: ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-bigtable" % "2.24.1" +libraryDependencies += "com.google.cloud" % "google-cloud-bigtable" % "2.29.1" ``` @@ -609,7 +609,7 @@ Java is a registered trademark of Oracle and/or its affiliates. [kokoro-badge-link-5]: http://storage.googleapis.com/cloud-devrel-public/java/badges/java-bigtable/java11.html [stability-image]: https://img.shields.io/badge/stability-stable-green [maven-version-image]: https://img.shields.io/maven-central/v/com.google.cloud/google-cloud-bigtable.svg -[maven-version-link]: https://central.sonatype.com/artifact/com.google.cloud/google-cloud-bigtable/2.24.1 +[maven-version-link]: https://central.sonatype.com/artifact/com.google.cloud/google-cloud-bigtable/2.29.1 [authentication]: https://github.com/googleapis/google-cloud-java#authentication [auth-scopes]: https://developers.google.com/identity/protocols/oauth2/scopes [predefined-iam-roles]: https://cloud.google.com/iam/docs/understanding-roles#predefined_roles From c7de6c653df4fdd506781018720f530de83d4258 Mon Sep 17 00:00:00 2001 From: Steven Niemitz Date: Thu, 27 Jul 2023 16:14:15 -0400 Subject: [PATCH 3/3] chore: Fix flaky metrics tests (#1865) This fixes a few flaky unit tests that relied on `Thread.sleep` to ensure that all metrics processing was done. Rather than using `Thread.sleep`, we can instead use an inline event queue in the OpenCensus stats component to execute all work inline, removing the need to wait for anything to finish. --- .../metrics/BigtableTracerCallableTest.java | 11 +++---- .../metrics/BuiltinMetricsTracerTest.java | 7 ++++- .../v2/stub/metrics/MetricsTracerTest.java | 31 +++++-------------- .../v2/stub/metrics/SimpleStatsComponent.java | 27 ++++++++++++++++ 4 files changed, 46 insertions(+), 30 deletions(-) create mode 100644 google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/SimpleStatsComponent.java 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 5896eb38de..d8e3402b84 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 @@ -19,6 +19,7 @@ import static org.junit.Assert.fail; import com.google.api.gax.rpc.ClientContext; +import com.google.api.gax.rpc.ServerStream; import com.google.api.gax.rpc.UnavailableException; import com.google.bigtable.v2.BigtableGrpc.BigtableImplBase; import com.google.bigtable.v2.CheckAndMutateRowRequest; @@ -54,7 +55,6 @@ import io.grpc.Status; import io.grpc.StatusRuntimeException; import io.grpc.stub.StreamObserver; -import io.opencensus.impl.stats.StatsComponentImpl; import io.opencensus.stats.StatsComponent; import io.opencensus.tags.TagKey; import io.opencensus.tags.TagValue; @@ -74,7 +74,7 @@ public class BigtableTracerCallableTest { private FakeService fakeService = new FakeService(); - private final StatsComponent localStats = new StatsComponentImpl(); + private final StatsComponent localStats = new SimpleStatsComponent(); private EnhancedBigtableStub stub; private EnhancedBigtableStub noHeaderStub; private int attempts; @@ -157,10 +157,9 @@ public void tearDown() { } @Test - public void testGFELatencyMetricReadRows() throws InterruptedException { - stub.readRowsCallable().call(Query.create(TABLE_ID)); - - Thread.sleep(WAIT_FOR_METRICS_TIME_MS); + public void testGFELatencyMetricReadRows() { + ServerStream call = stub.readRowsCallable().call(Query.create(TABLE_ID)); + call.forEach(r -> {}); long latency = StatsTestUtils.getAggregationValueAsLong( diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java index f0fba2164a..95d8514588 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java @@ -109,6 +109,7 @@ public class BuiltinMetricsTracerTest { private static final long FAKE_SERVER_TIMING = 50; private static final long SERVER_LATENCY = 100; private static final long APPLICATION_LATENCY = 200; + private static final long SLEEP_VARIABILITY = 15; private static final long CHANNEL_BLOCKING_LATENCY = 75; @@ -353,7 +354,11 @@ public void onComplete() { .recordOperation(status.capture(), tableId.capture(), zone.capture(), cluster.capture()); assertThat(counter.get()).isEqualTo(fakeService.getResponseCounter().get()); - assertThat(applicationLatency.getValue()).isAtLeast(APPLICATION_LATENCY * counter.get()); + // Thread.sleep might not sleep for the requested amount depending on the interrupt period + // defined by the OS. + // On linux this is ~1ms but on windows may be as high as 15-20ms. + assertThat(applicationLatency.getValue()) + .isAtLeast((APPLICATION_LATENCY - SLEEP_VARIABILITY) * counter.get()); assertThat(applicationLatency.getValue()) .isAtMost(operationLatency.getValue() - SERVER_LATENCY); } 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 6bd0628554..33f225ccd8 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 @@ -55,7 +55,7 @@ import io.grpc.Status; import io.grpc.StatusRuntimeException; import io.grpc.stub.StreamObserver; -import io.opencensus.impl.stats.StatsComponentImpl; +import io.opencensus.stats.StatsComponent; import io.opencensus.tags.TagKey; import io.opencensus.tags.TagValue; import io.opencensus.tags.Tags; @@ -85,6 +85,7 @@ public class MetricsTracerTest { private static final String INSTANCE_ID = "fake-instance"; private static final String APP_PROFILE_ID = "default"; private static final String TABLE_ID = "fake-table"; + private static final long SLEEP_VARIABILITY = 15; private static final ReadRowsResponse DEFAULT_READ_ROWS_RESPONSES = ReadRowsResponse.newBuilder() @@ -105,7 +106,7 @@ public class MetricsTracerTest { @Mock(answer = Answers.CALLS_REAL_METHODS) private BigtableGrpc.BigtableImplBase mockService; - private final StatsComponentImpl localStats = new StatsComponentImpl(); + private final StatsComponent localStats = new SimpleStatsComponent(); private EnhancedBigtableStub stub; private BigtableDataSettings settings; @@ -157,9 +158,6 @@ public Object answer(InvocationOnMock invocation) throws Throwable { Lists.newArrayList(stub.readRowsCallable().call(Query.create(TABLE_ID))); long elapsed = stopwatch.elapsed(TimeUnit.MILLISECONDS); - // Give OpenCensus a chance to update the views asynchronously. - Thread.sleep(100); - long opLatency = StatsTestUtils.getAggregationValueAsLong( localStats, @@ -193,9 +191,6 @@ public Object answer(InvocationOnMock invocation) { Lists.newArrayList(stub.readRowsCallable().call(Query.create(TABLE_ID))); Lists.newArrayList(stub.readRowsCallable().call(Query.create(TABLE_ID))); - // Give OpenCensus a chance to update the views asynchronously. - Thread.sleep(100); - long opLatency = StatsTestUtils.getAggregationValueAsLong( localStats, @@ -247,8 +242,6 @@ public void testReadRowsFirstRow() throws InterruptedException { } long elapsed = stopwatch.elapsed(TimeUnit.MILLISECONDS); - // Give OpenCensus a chance to update the views asynchronously. - Thread.sleep(100); executor.shutdown(); long firstRowLatency = @@ -260,7 +253,10 @@ public void testReadRowsFirstRow() throws InterruptedException { INSTANCE_ID, APP_PROFILE_ID); - assertThat(firstRowLatency).isIn(Range.closed(beforeSleep, elapsed - afterSleep)); + assertThat(firstRowLatency) + .isIn( + Range.closed( + beforeSleep - SLEEP_VARIABILITY, elapsed - afterSleep + SLEEP_VARIABILITY)); } @Test @@ -292,9 +288,6 @@ public Object answer(InvocationOnMock invocation) { Lists.newArrayList(stub.readRowsCallable().call(Query.create(TABLE_ID))); - // Give OpenCensus a chance to update the views asynchronously. - Thread.sleep(100); - long opLatency = StatsTestUtils.getAggregationValueAsLong( localStats, @@ -341,9 +334,6 @@ public Object answer(InvocationOnMock invocation) throws Throwable { Lists.newArrayList(stub.readRowsCallable().call(Query.create(TABLE_ID))); long elapsed = stopwatch.elapsed(TimeUnit.MILLISECONDS); - // Give OpenCensus a chance to update the views asynchronously. - Thread.sleep(100); - long attemptLatency = StatsTestUtils.getAggregationValueAsLong( localStats, @@ -360,12 +350,11 @@ public Object answer(InvocationOnMock invocation) throws Throwable { } @Test - public void testInvalidRequest() throws InterruptedException { + public void testInvalidRequest() { try { stub.bulkMutateRowsCallable().call(BulkMutation.create(TABLE_ID)); Assert.fail("Invalid request should throw exception"); } catch (IllegalStateException e) { - Thread.sleep(100); // Verify that the latency is recorded with an error code (in this case UNKNOWN) long attemptLatency = StatsTestUtils.getAggregationValueAsLong( @@ -403,9 +392,6 @@ public Object answer(InvocationOnMock invocation) { batcher.add(ByteString.copyFromUtf8("row1")); batcher.sendOutstanding(); - // Give OpenCensus a chance to update the views asynchronously. - Thread.sleep(100); - long throttledTimeMetric = StatsTestUtils.getAggregationValueAsLong( localStats, @@ -476,7 +462,6 @@ public Object answer(InvocationOnMock invocation) { batcher.add(RowMutationEntry.create("key")); batcher.sendOutstanding(); - Thread.sleep(100); long throttledTimeMetric = StatsTestUtils.getAggregationValueAsLong( localStats, diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/SimpleStatsComponent.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/SimpleStatsComponent.java new file mode 100644 index 0000000000..99aed9c3b4 --- /dev/null +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/SimpleStatsComponent.java @@ -0,0 +1,27 @@ +/* + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.cloud.bigtable.data.v2.stub.metrics; + +import io.opencensus.implcore.common.MillisClock; +import io.opencensus.implcore.internal.SimpleEventQueue; +import io.opencensus.implcore.stats.StatsComponentImplBase; + +/** A StatsComponent implementation for testing that executes all events inline. */ +public class SimpleStatsComponent extends StatsComponentImplBase { + public SimpleStatsComponent() { + super(new SimpleEventQueue(), MillisClock.getInstance()); + } +}