diff --git a/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/Mutation.java b/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/Mutation.java index dc55756241e9..46686261335b 100644 --- a/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/Mutation.java +++ b/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/Mutation.java @@ -23,6 +23,7 @@ import com.google.bigtable.v2.Mutation.DeleteFromRow; import com.google.bigtable.v2.Mutation.MergeToCell; import com.google.bigtable.v2.Mutation.SetCell; +import com.google.bigtable.v2.Mutation.TimestampOrigin; import com.google.cloud.bigtable.data.v2.models.Range.TimestampRange; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; @@ -32,6 +33,7 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; +import java.time.Instant; import java.util.List; import javax.annotation.Nonnull; @@ -154,9 +156,10 @@ public Mutation setCell( @Override public Mutation setCell( @Nonnull String familyName, @Nonnull ByteString qualifier, @Nonnull ByteString value) { - long timestamp = System.currentTimeMillis() * 1_000; + Instant now = Instant.now(); + long timestamp = now.getEpochSecond() * 1_000_000L + now.getNano() / 1_000; - return setCell(familyName, qualifier, timestamp, value); + return setCell(familyName, qualifier, timestamp, value, TimestampOrigin.CLIENT_AUTO_GENERATED); } @Override @@ -165,6 +168,15 @@ public Mutation setCell( @Nonnull ByteString qualifier, long timestamp, @Nonnull ByteString value) { + return setCell(familyName, qualifier, timestamp, value, TimestampOrigin.USER_SPECIFIED); + } + + private Mutation setCell( + @Nonnull String familyName, + @Nonnull ByteString qualifier, + long timestamp, + @Nonnull ByteString value, + @Nonnull TimestampOrigin timestampOrigin) { Validations.validateFamily(familyName); Preconditions.checkNotNull(qualifier, "qualifier can't be null."); Preconditions.checkNotNull(value, "value can't be null."); @@ -182,6 +194,7 @@ public Mutation setCell( .setTimestampMicros(timestamp) .setValue(value) .build()) + .setTimestampOrigin(timestampOrigin) .build()); return this; diff --git a/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java b/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java index 1b3d4f2dee14..4b0e91724777 100644 --- a/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java +++ b/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java @@ -660,7 +660,8 @@ private Builder() { .setDirectAccessRequested(isDirectPathRequested) .setTrafficDirectorEnabled(isDirectPathRequested) .setPeerInfo(true) - .setSessionsCompatible(true); + .setSessionsCompatible(true) + .setMicrosecondTimestamp(true); } private Builder(EnhancedBigtableStubSettings settings) { @@ -1008,6 +1009,7 @@ public EnhancedBigtableStubSettings build() { featureFlags.setRoutingCookie(true); featureFlags.setRetryInfo(true); + featureFlags.setMicrosecondTimestamp(true); // client_Side_metrics_enabled feature flag is only set when a user is running with a // DefaultMetricsProvider. This may cause false negatives when a user registered the // metrics on their CustomOpenTelemetryMetricsProvider. diff --git a/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BulkMutateIT.java b/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BulkMutateIT.java index aaf32decfe17..6b9d320486eb 100644 --- a/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BulkMutateIT.java +++ b/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BulkMutateIT.java @@ -53,6 +53,11 @@ public class BulkMutateIT { @Test(timeout = 60 * 1000) public void test() throws IOException, InterruptedException { + assume() + .withMessage("Emulator does not support microsecond timestamp granularity") + .that(testEnvRule.env()) + .isNotInstanceOf(EmulatorEnv.class); + BigtableDataSettings settings = testEnvRule.env().getDataClientSettings(); String rowPrefix = UUID.randomUUID().toString(); // Set target latency really low so it'll trigger adjusting thresholds diff --git a/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/CheckAndMutateIT.java b/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/CheckAndMutateIT.java index a3570f5e5bfb..1ca62c7db5bc 100644 --- a/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/CheckAndMutateIT.java +++ b/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/CheckAndMutateIT.java @@ -49,6 +49,11 @@ public class CheckAndMutateIT { @Test public void test() throws Exception { + assume() + .withMessage("Emulator does not support microsecond timestamp granularity") + .that(testEnvRule.env()) + .isNotInstanceOf(EmulatorEnv.class); + TableId tableId = testEnvRule.env().getTableId(); String familyId = testEnvRule.env().getFamilyId(); String rowKey = UUID.randomUUID().toString(); diff --git a/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/MutateRowIT.java b/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/MutateRowIT.java index a54c7268276f..ac96a10f72e0 100644 --- a/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/MutateRowIT.java +++ b/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/MutateRowIT.java @@ -44,6 +44,11 @@ public class MutateRowIT { @Test public void test() throws Exception { + assume() + .withMessage("Emulator does not support microsecond timestamp granularity") + .that(testEnvRule.env()) + .isNotInstanceOf(EmulatorEnv.class); + String rowKey = UUID.randomUUID().toString(); String familyId = testEnvRule.env().getFamilyId(); diff --git a/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/ReadIT.java b/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/ReadIT.java index e5af176e2172..0c86fb3c85de 100644 --- a/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/ReadIT.java +++ b/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/ReadIT.java @@ -92,6 +92,11 @@ public void setUp() { @Test public void isRowExists() throws Exception { + assume() + .withMessage("Emulator does not support microsecond timestamp granularity") + .that(testEnvRule.env()) + .isNotInstanceOf(EmulatorEnv.class); + String rowKey = prefix + "-test-row-key"; TableId tableId = testEnvRule.env().getTableId(); testEnvRule diff --git a/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/SampleRowsIT.java b/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/SampleRowsIT.java index 063d0d1f5095..118895867004 100644 --- a/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/SampleRowsIT.java +++ b/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/SampleRowsIT.java @@ -50,6 +50,11 @@ public class SampleRowsIT { @Test public void test() throws InterruptedException, ExecutionException, TimeoutException { + assume() + .withMessage("Emulator does not support microsecond timestamp granularity") + .that(testEnvRule.env()) + .isNotInstanceOf(EmulatorEnv.class); + BigtableDataClient client = testEnvRule.env().getDataClient(); String rowPrefix = UUID.randomUUID().toString(); diff --git a/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/BulkMutationTest.java b/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/BulkMutationTest.java index 412959eb11f0..498e3ee034c5 100644 --- a/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/BulkMutationTest.java +++ b/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/BulkMutationTest.java @@ -73,6 +73,7 @@ public void test() throws ParseException { + " timestamp_micros: 1000" + " value: 'fake-value1'" + " }" + + " timestamp_origin: USER_SPECIFIED" + " }" + " mutations {" + " set_cell {" @@ -81,6 +82,7 @@ public void test() throws ParseException { + " timestamp_micros: 2000" + " value: 'fake-value2'" + " }" + + " timestamp_origin: USER_SPECIFIED" + " }" + "}" + "entries {" @@ -92,6 +94,7 @@ public void test() throws ParseException { + " timestamp_micros: 3000" + " value: 'fake-value3'" + " }" + + " timestamp_origin: USER_SPECIFIED" + " }" + "}", expected); diff --git a/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/MutationTest.java b/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/MutationTest.java index 3ba1de67011e..cd23dd896750 100644 --- a/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/MutationTest.java +++ b/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/MutationTest.java @@ -22,6 +22,7 @@ import com.google.bigtable.v2.Mutation.DeleteFromFamily; import com.google.bigtable.v2.Mutation.DeleteFromRow; import com.google.bigtable.v2.Mutation.MergeToCell; +import com.google.bigtable.v2.Mutation.TimestampOrigin; import com.google.cloud.bigtable.data.v2.models.Range.TimestampRange; import com.google.common.primitives.Longs; import com.google.protobuf.ByteString; @@ -30,6 +31,7 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +import java.time.Instant; import java.util.List; import org.junit.Before; import org.junit.Test; @@ -48,7 +50,8 @@ public void setUp() { @Test public void setCellTest() { - long minTimestamp = System.currentTimeMillis() * 1_000; + Instant minInstant = Instant.now(); + long minTimestamp = minInstant.getEpochSecond() * 1_000_000L + minInstant.getNano() / 1_000; mutation .setCell( @@ -65,7 +68,8 @@ public void setCellTest() { List actual = mutation.getMutations(); - long maxTimestamp = System.currentTimeMillis() * 1_000; + Instant maxInstant = Instant.now(); + long maxTimestamp = maxInstant.getEpochSecond() * 1_000_000L + maxInstant.getNano() / 1_000; com.google.common.collect.Range expectedTimestampRange = com.google.common.collect.Range.closed(minTimestamp, maxTimestamp); @@ -77,6 +81,7 @@ public void setCellTest() { assertThat(actual.get(0).getSetCell().getValue()) .isEqualTo(ByteString.copyFromUtf8("fake-value")); assertThat(actual.get(0).getSetCell().getTimestampMicros()).isEqualTo(1_000); + assertThat(actual.get(0).getTimestampOrigin()).isEqualTo(TimestampOrigin.USER_SPECIFIED); assertThat(actual.get(1).getSetCell().getFamilyName()).isEqualTo("fake-family"); assertThat(actual.get(1).getSetCell().getColumnQualifier()) @@ -84,6 +89,7 @@ public void setCellTest() { assertThat(actual.get(1).getSetCell().getValue()) .isEqualTo(ByteString.copyFromUtf8("fake-value")); assertThat(actual.get(1).getSetCell().getTimestampMicros()).isIn(expectedTimestampRange); + assertThat(actual.get(1).getTimestampOrigin()).isEqualTo(TimestampOrigin.CLIENT_AUTO_GENERATED); assertThat(actual.get(2).getSetCell().getFamilyName()).isEqualTo("fake-family2"); assertThat(actual.get(2).getSetCell().getColumnQualifier()) @@ -91,6 +97,7 @@ public void setCellTest() { assertThat(actual.get(2).getSetCell().getValue()) .isEqualTo(ByteString.copyFromUtf8("fake-value2")); assertThat(actual.get(2).getSetCell().getTimestampMicros()).isEqualTo(1_000); + assertThat(actual.get(2).getTimestampOrigin()).isEqualTo(TimestampOrigin.USER_SPECIFIED); assertThat(actual.get(3).getSetCell().getFamilyName()).isEqualTo("fake-family2"); assertThat(actual.get(3).getSetCell().getColumnQualifier()) @@ -98,6 +105,7 @@ public void setCellTest() { assertThat(actual.get(3).getSetCell().getValue()) .isEqualTo(ByteString.copyFromUtf8("fake-value2")); assertThat(actual.get(3).getSetCell().getTimestampMicros()).isIn(expectedTimestampRange); + assertThat(actual.get(3).getTimestampOrigin()).isEqualTo(TimestampOrigin.CLIENT_AUTO_GENERATED); assertThat(Mutation.fromProtoUnsafe(actual).getMutations()).isEqualTo(actual); } @@ -113,6 +121,7 @@ public void setCellWithServerSideTimestamp() { List actual = mutation.getMutations(); assertThat(actual.get(0).getSetCell().getTimestampMicros()) .isEqualTo(Mutation.SERVER_SIDE_TIMESTAMP); + assertThat(actual.get(0).getTimestampOrigin()).isEqualTo(TimestampOrigin.USER_SPECIFIED); } @Test @@ -276,6 +285,10 @@ public void testWithLongValue() { assertThat(actualMutation.getSetCell().getValue()) .isEqualTo(ByteString.copyFrom(Longs.toByteArray(100_000L))); + assertThat(mutations.get(0).getTimestampOrigin()) + .isEqualTo(TimestampOrigin.CLIENT_AUTO_GENERATED); + assertThat(mutations.get(1).getTimestampOrigin()) + .isEqualTo(TimestampOrigin.CLIENT_AUTO_GENERATED); assertThat(mutations.get(2).getSetCell()) .isEqualTo( @@ -285,6 +298,7 @@ public void testWithLongValue() { .setTimestampMicros(30_000L) .setValue(ByteString.copyFrom(Longs.toByteArray(20_000L))) .build()); + assertThat(mutations.get(2).getTimestampOrigin()).isEqualTo(TimestampOrigin.USER_SPECIFIED); } @Test diff --git a/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/RowMutationEntryTest.java b/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/RowMutationEntryTest.java index 4f616d937937..d3016fea28a5 100644 --- a/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/RowMutationEntryTest.java +++ b/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/RowMutationEntryTest.java @@ -19,6 +19,7 @@ import com.google.bigtable.v2.MutateRowsRequest; import com.google.bigtable.v2.Mutation; +import com.google.bigtable.v2.Mutation.TimestampOrigin; import com.google.common.collect.ImmutableList; import com.google.common.primitives.Longs; import com.google.protobuf.ByteString; @@ -71,7 +72,8 @@ public void toProtoTest() { .setFamilyName("fake-family") .setColumnQualifier(ByteString.copyFromUtf8("qualifier-1")) .setTimestampMicros(timestamp) - .setValue(ByteString.copyFromUtf8("fake-values")))) + .setValue(ByteString.copyFromUtf8("fake-values"))) + .setTimestampOrigin(TimestampOrigin.USER_SPECIFIED)) .build()); } @@ -92,6 +94,7 @@ public void multipleMutationTest() { .setColumnQualifier(ByteString.copyFromUtf8("qualifier-1")) .setTimestampMicros(10_000L) .setValue(ByteString.copyFromUtf8("fake-values"))) + .setTimestampOrigin(TimestampOrigin.USER_SPECIFIED) .build(), Mutation.newBuilder() .setDeleteFromColumn( @@ -135,6 +138,7 @@ public void unsafeMutationTest() { .setColumnQualifier(ByteString.copyFromUtf8("qualifier-1")) .setTimestampMicros(10_000L) .setValue(ByteString.copyFromUtf8("fake-values"))) + .setTimestampOrigin(TimestampOrigin.USER_SPECIFIED) .build(), Mutation.newBuilder() .setDeleteFromFamily( diff --git a/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/RowMutationTest.java b/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/RowMutationTest.java index 16569ef97e71..e0f4fdd3926d 100644 --- a/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/RowMutationTest.java +++ b/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/RowMutationTest.java @@ -30,6 +30,7 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +import java.time.Instant; import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; @@ -49,7 +50,7 @@ public class RowMutationTest { @Test public void toProtoTest() { - long timestampMin = System.currentTimeMillis() * 1_000; + Instant minInstant = Instant.now(); // Test RowMutation on a table. RowMutation rowMutation = @@ -58,7 +59,7 @@ public void toProtoTest() { MutateRowRequest actualRowMutation = rowMutation.toProto(REQUEST_CONTEXT); com.google.common.collect.Range timestampRange = - com.google.common.collect.Range.closed(timestampMin, System.currentTimeMillis() * 1_000); + com.google.common.collect.Range.closed(toMicros(minInstant), toMicros(Instant.now())); assertThat(actualRowMutation.getTableName()) .isEqualTo(NameUtil.formatTableName(PROJECT_ID, INSTANCE_ID, TABLE_ID)); @@ -77,7 +78,7 @@ public void toProtoTest() { actualRowMutation = rowMutation.toProto(REQUEST_CONTEXT); timestampRange = - com.google.common.collect.Range.closed(timestampMin, System.currentTimeMillis() * 1_000); + com.google.common.collect.Range.closed(toMicros(minInstant), toMicros(Instant.now())); assertThat(actualRowMutation.getTableName()).isEmpty(); assertThat(actualRowMutation.getAuthorizedViewName()) @@ -92,7 +93,7 @@ public void toProtoTest() { @Test public void toBulkProtoTest() { - long timestampMin = System.currentTimeMillis() * 1_000; + Instant minInstant = Instant.now(); // Test RowMutation on a table. RowMutation rowMutation = @@ -102,7 +103,7 @@ public void toBulkProtoTest() { MutateRowsRequest actualRowMutation = rowMutation.toBulkProto(REQUEST_CONTEXT); com.google.common.collect.Range timestampRange = - com.google.common.collect.Range.closed(timestampMin, System.currentTimeMillis() * 1_000); + com.google.common.collect.Range.closed(toMicros(minInstant), toMicros(Instant.now())); assertThat(actualRowMutation.getTableName()) .isEqualTo(NameUtil.formatTableName(PROJECT_ID, INSTANCE_ID, TABLE_ID)); @@ -124,7 +125,7 @@ public void toBulkProtoTest() { actualRowMutation = rowMutation.toBulkProto(REQUEST_CONTEXT); timestampRange = - com.google.common.collect.Range.closed(timestampMin, System.currentTimeMillis() * 1_000); + com.google.common.collect.Range.closed(toMicros(minInstant), toMicros(Instant.now())); assertThat(actualRowMutation.getTableName()).isEmpty(); assertThat(actualRowMutation.getAuthorizedViewName()) @@ -300,4 +301,8 @@ public void toSessionProtoTest() { assertThat(sessionProto).isEqualTo(expected); } + + private static long toMicros(Instant instant) { + return instant.getEpochSecond() * 1_000_000L + instant.getNano() / 1_000; + } } diff --git a/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/mutaterows/MaybePointWriteCallableTest.java b/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/mutaterows/MaybePointWriteCallableTest.java index 1115fa1eb269..9a0c2fe4f972 100644 --- a/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/mutaterows/MaybePointWriteCallableTest.java +++ b/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/mutaterows/MaybePointWriteCallableTest.java @@ -56,7 +56,7 @@ public void singleEntry_routesToPointWriter() throws Exception { ApiFuture future = callable.futureCall(request, null); pointWriter.response.set(null); - assertThat(future.get()).isNull(); + assertThat((Object) future.get()).isNull(); assertThat(classic.request).isNull(); assertThat(pointWriter.request).isNotNull(); // The single entry is converted back into a RowMutation targeting the same row.