Skip to content

Commit fbf140e

Browse files
authored
chore: bump java-bigtable to 2.85.0, beam to 2.77.0, emulator to 0.222.0 (#4640)
* chore: bump java-bigtable to 2.85.0, beam to 2.77.0, emulator to 0.222.0 - java-bigtable: 2.81.0 → 2.85.0 - google-cloud-bigtable-emulator: 0.218.0 → 0.222.0 - beam: 2.72.0 → 2.77.0 - conscrypt: 2.6.0 → 2.6.2 (to match bigtable 2.85.0 deps-bom) - maven.compiler.source/target: 1.8 → 11 (beam 2.77+ requires Java 11) Add dev.cel relocation to 1.x-shaded and 2.x-shaded: bigtable 2.85.0 pulls in grpc-xds which transitively introduces dev.cel:0.13.0; without relocation those classes leak un-relocated into the shaded jars and trip verify-shaded-jar-entries. Upgrade kokoro java8 CI jobs to use the java11 docker image (presubmit, continuous, nightly). Move bigtable-dataflow-parent into a JDK-activated profile so it is excluded from any remaining Java 8 build environments. Change-Id: I558368876e3e46827097598f426abc8469e503cf * test: deflake replication endpoint and buffered mutator tests Replication endpoint tests (TestUtils, shared by 1.x and 2.x): - waitForReplication polled 5 times with a doubling sleep, but the final 8s sleep was not followed by another check, so the effective budget was 7.5s and a failure cost 8s of dead time. Replace it with a deadline based poll (60s, every 500ms) that always re-checks after sleeping. - The replicated-entry counter is static and shared by every test in the class. Resetting it in @before is not enough because replication is asynchronous: entries still in flight from the previous test arrive after the reset and count towards the next test's target. This is worst after the 10k-row test. Drain replication before resetting the counter, and retry the table comparison in assertTableEventuallyEquals so the assertion waits on the replicated data rather than the counter. - Close the ResultScanners in assertTableEquals; they were leaked, which matters now that the comparison runs in a retry loop. TestBufferedMutator: - testAutoFlushOff/testAutoFlushOn write on one Connection and read back on another, asserting on the first response. flush() makes the write durable but does not guarantee a read on a separate connection observes it immediately, so the read could return 0 cells. Retry the read; it only absorbs that delay and still fails if the write never appears. - Close the ResultScanner in testBulkMutation. Change-Id: I5a1936b0037965f6306f545164c014a0a6306c3d * test: raise modifyTableAsync timeout in TestModifyTable testModifyTable_HBase2_Async timed out waiting 60s on modifyTableAsync. modifyTableAsync chains a plain thenApply off getDescriptor, and because ApiFutureUtils completes its futures with a directExecutor, that continuation runs on a gRPC callback thread -- where it then blocks on the modifyFamilies RPC. With the suite running admin operations across four classes in parallel these pile up and starve the callback pool, so the call can take well over a minute. Raise the wait to 5 minutes; the 1800s fork timeout still bounds a genuine hang. This only stops the test failing. The blocking continuation is still there and can stall modifyTable/modifyTableAsync for callers. Change-Id: Ibdd83e80ad47f429791d8e189dead12d2f96ec54
1 parent 292cfa5 commit fbf140e

11 files changed

Lines changed: 155 additions & 49 deletions

File tree

‎.kokoro/continuous/java8.cfg‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# Configure the docker image for kokoro-trampoline.
44
env_vars: {
55
key: "TRAMPOLINE_IMAGE"
6-
value: "gcr.io/cloud-devrel-kokoro-resources/java8"
6+
value: "gcr.io/cloud-devrel-kokoro-resources/java11"
77
}
88

99
env_vars: {

‎.kokoro/nightly/java8.cfg‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# Configure the docker image for kokoro-trampoline.
44
env_vars: {
55
key: "TRAMPOLINE_IMAGE"
6-
value: "gcr.io/cloud-devrel-kokoro-resources/java8"
6+
value: "gcr.io/cloud-devrel-kokoro-resources/java11"
77
}
88

99
env_vars: {

‎.kokoro/presubmit/java8.cfg‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# Configure the docker image for kokoro-trampoline.
44
env_vars: {
55
key: "TRAMPOLINE_IMAGE"
6-
value: "gcr.io/cloud-devrel-kokoro-resources/java8"
6+
value: "gcr.io/cloud-devrel-kokoro-resources/java11"
77
}
88

99
env_vars: {

‎bigtable-client-core-parent/bigtable-hbase-integration-tests-common/src/test/java/com/google/cloud/bigtable/hbase/TestBufferedMutator.java‎

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@
2121
import static org.junit.Assert.assertNotNull;
2222
import static org.junit.Assert.assertNull;
2323

24+
import java.io.IOException;
2425
import java.util.ArrayList;
2526
import java.util.Collections;
2627
import java.util.List;
28+
import java.util.concurrent.TimeUnit;
2729
import org.apache.commons.lang.RandomStringUtils;
2830
import org.apache.hadoop.hbase.client.BufferedMutator;
2931
import org.apache.hadoop.hbase.client.BufferedMutatorParams;
@@ -61,7 +63,7 @@ public void testAutoFlushOff() throws Exception {
6163
// Bigtable pushes the change right away. This test would be flaky.
6264
// Assert.assertEquals("Expecting no results", 0, tableForRead.get(get).size());
6365
mutator.flush();
64-
Assert.assertEquals("Expecting one result", 1, tableForRead.get(get).size());
66+
assertEventuallyHasCells(tableForRead, get, 1);
6567
}
6668
}
6769

@@ -71,7 +73,7 @@ public void testAutoFlushOn() throws Exception {
7173
Connection c = createNewConnection();
7274
Table tableForRead = c.getTable(sharedTestEnv.getDefaultTableName()); ) {
7375
mutator.put(getPut());
74-
Assert.assertEquals("Expecting one result", 1, tableForRead.get(getGet()).size());
76+
assertEventuallyHasCells(tableForRead, getGet(), 1);
7577
}
7678
}
7779

@@ -190,8 +192,10 @@ public void testBulkMutation() throws Exception {
190192

191193
Scan scan = new Scan();
192194
scan.setRowPrefixFilter(Bytes.toBytes(rowKeyPrefix));
193-
ResultScanner resultScanner = tableForRead.getScanner(scan);
194-
Result[] results = resultScanner.next(20);
195+
Result[] results;
196+
try (ResultScanner resultScanner = tableForRead.getScanner(scan)) {
197+
results = resultScanner.next(20);
198+
}
195199

196200
assertEquals("mutations should have been applied now", mutations.size(), results.length);
197201
for (int i = 0; i < results.length; i++) {
@@ -204,6 +208,30 @@ public void testBulkMutation() throws Exception {
204208
final byte[] rowKey = dataHelper.randomData("testrow-");
205209
final byte[] value = dataHelper.randomData("testValue-");
206210

211+
/**
212+
* Reads until {@code get} returns {@code expectedCells} cells, or fails once the timeout expires.
213+
*
214+
* <p>These tests read back through a second {@link Connection} to prove the write reached
215+
* Bigtable rather than a client side buffer. A write is durable once the mutator returns, but a
216+
* read issued on a different connection is not guaranteed to observe it immediately, so asserting
217+
* on the very first response makes the test flaky. Retrying only absorbs that delay: if the write
218+
* never becomes visible, this still fails with the same expected/actual mismatch.
219+
*/
220+
private static void assertEventuallyHasCells(Table table, Get get, int expectedCells)
221+
throws IOException, InterruptedException {
222+
long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(30);
223+
int actualCells;
224+
do {
225+
actualCells = table.get(get).size();
226+
if (actualCells == expectedCells) {
227+
return;
228+
}
229+
Thread.sleep(250);
230+
} while (System.nanoTime() < deadline);
231+
232+
assertEquals("Expecting " + expectedCells + " result(s)", expectedCells, actualCells);
233+
}
234+
207235
private Get getGet() {
208236
Get get = new Get(rowKey);
209237
get.addColumn(COLUMN_FAMILY, qualifier);

‎bigtable-hbase-1.x-parent/bigtable-hbase-1.x-shaded/pom.xml‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,10 @@ limitations under the License.
389389
<pattern>org.jspecify.annotations</pattern>
390390
<shadedPattern>com.google.bigtable.repackaged.org.jspecify.annotations</shadedPattern>
391391
</relocation>
392+
<relocation>
393+
<pattern>dev.cel</pattern>
394+
<shadedPattern>com.google.bigtable.repackaged.dev.cel</shadedPattern>
395+
</relocation>
392396
</relocations>
393397
</configuration>
394398
</execution>

‎bigtable-hbase-2.x-parent/bigtable-hbase-2.x-integration-tests/src/test/java/com/google/cloud/bigtable/hbase/TestModifyTable.java‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,9 @@ public void testModifyTable_HBase2_Async() throws IOException {
7070
@Override
7171
public Void apply(@Nullable HTableDescriptor descriptor) {
7272
try (Admin admin = getConnection().getAdmin()) {
73-
admin.modifyTableAsync(descriptor).get(60, TimeUnit.SECONDS);
73+
// Generous: modifyTableAsync blocks a gRPC callback thread while it waits on the
74+
// modifyFamilies RPC, so it can stall when the suite runs admin ops in parallel.
75+
admin.modifyTableAsync(descriptor).get(5, TimeUnit.MINUTES);
7476
} catch (Exception e) {
7577
throw new RuntimeException(e);
7678
}

‎bigtable-hbase-2.x-parent/bigtable-hbase-2.x-shaded/pom.xml‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,10 @@ limitations under the License.
380380
<pattern>org.jspecify.annotations</pattern>
381381
<shadedPattern>com.google.bigtable.repackaged.org.jspecify.annotations</shadedPattern>
382382
</relocation>
383+
<relocation>
384+
<pattern>dev.cel</pattern>
385+
<shadedPattern>com.google.bigtable.repackaged.dev.cel</shadedPattern>
386+
</relocation>
383387
</relocations>
384388
</configuration>
385389
</execution>

‎hbase-migration-tools/bigtable-hbase-replication/bigtable-hbase-1.x-replication/src/test/java/com/google/cloud/bigtable/hbase1_x/replication/HbaseToCloudBigtableReplicationEndpointTest.java‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,11 @@ public static void setUpCluster() throws Exception {
149149
}
150150

151151
@Before
152-
public void setupTestCase() throws IOException {
152+
public void setupTestCase() throws IOException, InterruptedException {
153+
154+
// Drain any replication still in flight from the previous test, so that it is not counted
155+
// against this test's target by the reset below.
156+
TestUtils.waitForReplicationToQuiesce(TestReplicationEndpoint.replicatedEntries::get);
153157

154158
// Create and set the empty tables
155159
TableName table1 = TableName.valueOf(UUID.randomUUID().toString());

‎hbase-migration-tools/bigtable-hbase-replication/bigtable-hbase-2.x-replication/src/test/java/com/google/cloud/bigtable/hbase2_x/replication/HbaseToCloudBigtableReplicationEndpointTest.java‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,11 @@ public static void tearDown() throws Exception {
157157
}
158158

159159
@Before
160-
public void setupTestCase() throws IOException {
160+
public void setupTestCase() throws IOException, InterruptedException {
161+
162+
// Drain any replication still in flight from the previous test, so that it is not counted
163+
// against this test's target by the reset below.
164+
TestUtils.waitForReplicationToQuiesce(TestReplicationEndpoint.replicatedEntries::get);
161165

162166
// Create and set the empty tables
163167
TableName table1 = TableName.valueOf(UUID.randomUUID().toString());

‎hbase-migration-tools/bigtable-hbase-replication/bigtable-hbase-replication-core/src/main/java/com/google/cloud/bigtable/hbase/replication/utils/TestUtils.java‎

Lines changed: 83 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@
2525
import java.util.NavigableMap;
2626
import java.util.concurrent.CompletableFuture;
2727
import java.util.concurrent.Future;
28+
import java.util.concurrent.TimeUnit;
2829
import java.util.function.BooleanSupplier;
30+
import java.util.function.IntSupplier;
2931
import org.apache.hadoop.hbase.Cell;
3032
import org.apache.hadoop.hbase.CellUtil;
3133
import org.apache.hadoop.hbase.TableName;
@@ -65,6 +67,15 @@ public class TestUtils {
6567
public static final byte[] VALUE = "Value".getBytes();
6668
public static final long TIMESTAMP = 1000l;
6769

70+
/** How long to wait for asynchronous replication before giving up. */
71+
private static final long REPLICATION_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(60);
72+
73+
/** How long to wait between polls while waiting on asynchronous replication. */
74+
private static final long POLL_INTERVAL_MILLIS = 500;
75+
76+
/** Consecutive unchanged polls required before replication is considered idle. */
77+
private static final int QUIESCE_STABLE_POLLS = 3;
78+
6879
// ONLY STATIC METHODS
6980
private TestUtils() {}
7081

@@ -180,52 +191,77 @@ public static void assertEquals(RowMutations expected, RowMutations actual) {
180191
/** Scans the 2 Tables and compares the data. Fails assertion in case of a mismatch. */
181192
public static void assertTableEquals(Table expected, Table actual) throws IOException {
182193

183-
ResultScanner expectedScanner = expected.getScanner(new Scan().setMaxVersions());
184-
ResultScanner actualScanner = actual.getScanner(new Scan().setMaxVersions());
194+
try (ResultScanner expectedScanner = expected.getScanner(new Scan().setMaxVersions());
195+
ResultScanner actualScanner = actual.getScanner(new Scan().setMaxVersions())) {
185196

186-
Iterator<Result> expectedIterator = expectedScanner.iterator();
187-
Iterator<Result> actualIterator = actualScanner.iterator();
197+
Iterator<Result> expectedIterator = expectedScanner.iterator();
198+
Iterator<Result> actualIterator = actualScanner.iterator();
188199

189-
// Both databases should return exact same results in exact same order.
190-
while (expectedIterator.hasNext()) {
191-
// Check size mismatch
192-
if (!actualIterator.hasNext()) {
200+
// Both databases should return exact same results in exact same order.
201+
while (expectedIterator.hasNext()) {
202+
// Check size mismatch
203+
if (!actualIterator.hasNext()) {
204+
Assert.fail(
205+
"Expected more rows. CBT missing results starting at "
206+
+ Bytes.toStringBinary(expectedIterator.next().getRow()));
207+
}
208+
209+
// Both the databases have next, so compare the results
210+
assertEquals(expectedIterator.next(), actualIterator.next());
211+
}
212+
// Verify that actual has no more data left
213+
if (actualIterator.hasNext()) {
193214
Assert.fail(
194-
"Expected more rows. CBT missing results starting at "
195-
+ Bytes.toStringBinary(expectedIterator.next().getRow()));
215+
"Expected less rows, extra results in CBT starting from "
216+
+ actualIterator.next().toString());
196217
}
197-
198-
// Both the databases have next, so compare the results
199-
assertEquals(expectedIterator.next(), actualIterator.next());
200-
}
201-
// Verify that actual has no more data left
202-
if (actualIterator.hasNext()) {
203-
Assert.fail(
204-
"Expected less rows, extra results in CBT starting from "
205-
+ actualIterator.next().toString());
206218
}
207219
}
208220

209221
/** Waits for replication to replicate numReplicatedEntries */
210222
public static void waitForReplication(BooleanSupplier isReplicationCurrent)
211223
throws InterruptedException {
212-
int sleepTimeInMillis = 500;
213-
for (int i = 0; i < 5; i++) {
214-
215-
// First check if replication has already happened
224+
long start = System.nanoTime();
225+
long deadline = start + TimeUnit.MILLISECONDS.toNanos(REPLICATION_TIMEOUT_MILLIS);
226+
while (true) {
216227
if (isReplicationCurrent.getAsBoolean()) {
217-
LOG.info("Replication is caught up after " + i + " sleep attempts");
228+
LOG.info("Replication caught up after " + elapsedMillis(start) + "ms");
218229
return;
219230
}
220-
221-
// Give time for replication to catch up
222-
LOG.info("sleeping for " + sleepTimeInMillis);
223-
Thread.sleep(sleepTimeInMillis);
224-
// Exponential backoff
225-
sleepTimeInMillis = sleepTimeInMillis * 2;
231+
if (System.nanoTime() >= deadline) {
232+
Assert.fail(
233+
"Timed out waiting for replication to catch up after " + elapsedMillis(start) + "ms");
234+
}
235+
Thread.sleep(POLL_INTERVAL_MILLIS);
226236
}
237+
}
227238

228-
Assert.fail("Timed out waiting for replication to catch up.");
239+
private static long elapsedMillis(long startNanos) {
240+
return TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNanos);
241+
}
242+
243+
/**
244+
* Waits until {@code replicatedEntries} stops advancing, draining entries left in flight by an
245+
* earlier test.
246+
*
247+
* <p>The replication endpoint's entry counter is static and shared by every test in a class.
248+
* Resetting it between tests is not enough on its own: replication is asynchronous, so entries
249+
* belonging to the previous test keep arriving after the reset and count towards the next test's
250+
* target. Call this before resetting the counter so it starts from a genuinely idle state.
251+
*
252+
* <p>This is best effort and does not fail on timeout; callers still verify the data itself.
253+
*/
254+
public static void waitForReplicationToQuiesce(IntSupplier replicatedEntries)
255+
throws InterruptedException {
256+
long deadline = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(REPLICATION_TIMEOUT_MILLIS);
257+
int previous = -1;
258+
int stablePolls = 0;
259+
while (stablePolls < QUIESCE_STABLE_POLLS && System.nanoTime() < deadline) {
260+
int current = replicatedEntries.getAsInt();
261+
stablePolls = (current == previous) ? stablePolls + 1 : 0;
262+
previous = current;
263+
Thread.sleep(POLL_INTERVAL_MILLIS);
264+
}
229265
}
230266

231267
/**
@@ -236,7 +272,22 @@ public static void assertTableEventuallyEquals(
236272
Table expected, Table actual, BooleanSupplier isReplicationCurrent)
237273
throws InterruptedException, IOException {
238274
waitForReplication(isReplicationCurrent);
239-
TestUtils.assertTableEquals(expected, actual);
275+
276+
// The entry counter the predicate reads is only a coarse progress signal: it is shared across
277+
// tests and counts entries for every table, so it can be satisfied before this table's own
278+
// writes land. Retry the comparison so the assertion waits on the replicated data itself.
279+
long deadline = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(REPLICATION_TIMEOUT_MILLIS);
280+
while (true) {
281+
try {
282+
TestUtils.assertTableEquals(expected, actual);
283+
return;
284+
} catch (AssertionError e) {
285+
if (System.nanoTime() >= deadline) {
286+
throw e;
287+
}
288+
}
289+
Thread.sleep(POLL_INTERVAL_MILLIS);
290+
}
240291
}
241292

242293
/**

0 commit comments

Comments
 (0)