mutations) throws SpannerException;
+ /**
+ * Writes the given mutations atomically to the database without replay protection.
+ *
+ * Since this method does not feature replay protection, it may attempt to apply {@code
+ * mutations} more than once; if the mutations are not idempotent, this may lead to a failure
+ * being reported when the mutation was applied once. For example, an insert may fail with {@link
+ * ErrorCode#ALREADY_EXISTS} even though the row did not exist before this method was called. For
+ * this reason, most users of the library will prefer to use {@link #write(Iterable)} instead.
+ * However, {@code writeAtLeastOnce()} requires only a single RPC, whereas {@code write()}
+ * requires two RPCs (one of which may be performed in advance), and so this method may be
+ * appropriate for latency sensitive and/or high throughput blind writing.
+ *
+ *
Example of unprotected blind write.
+ *
+ *
{@code
+ * long singerId = my_singer_id;
+ * Mutation mutation = Mutation.newInsertBuilder("Singers")
+ * .set("SingerId")
+ * .to(singerId)
+ * .set("FirstName")
+ * .to("Billy")
+ * .set("LastName")
+ * .to("Joel")
+ * .build();
+ * dbClient.writeAtLeastOnce(Collections.singletonList(mutation));
+ * }
+ *
+ * @return a response with the timestamp at which the write was committed
+ */
+ CommitResponse writeAtLeastOnceWithOptions(
+ Iterable mutations, TransactionOption... options) throws SpannerException;
+
/**
* Returns a context in which a single read can be performed using {@link TimestampBound#strong()}
* concurrency. This method will return a {@link ReadContext} that will not return the read
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseClientImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseClientImpl.java
index 4dd10001c7..f6af000d6e 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseClientImpl.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseClientImpl.java
@@ -17,6 +17,7 @@
package com.google.cloud.spanner;
import com.google.cloud.Timestamp;
+import com.google.cloud.spanner.Options.TransactionOption;
import com.google.cloud.spanner.SessionPool.PooledSessionFuture;
import com.google.cloud.spanner.SpannerImpl.ClosedException;
import com.google.common.annotations.VisibleForTesting;
@@ -81,6 +82,13 @@ public Timestamp apply(Session session) {
}
}
+ @Override
+ public CommitResponse writeWithOptions(Iterable mutations, TransactionOption... options)
+ throws SpannerException {
+ final Timestamp commitTimestamp = write(mutations);
+ return new CommitResponse(commitTimestamp);
+ }
+
@Override
public Timestamp writeAtLeastOnce(final Iterable mutations) throws SpannerException {
Span span = tracer.spanBuilder(READ_WRITE_TRANSACTION).startSpan();
@@ -101,6 +109,13 @@ public Timestamp apply(Session session) {
}
}
+ @Override
+ public CommitResponse writeAtLeastOnceWithOptions(
+ Iterable mutations, TransactionOption... options) throws SpannerException {
+ final Timestamp commitTimestamp = writeAtLeastOnce(mutations);
+ return new CommitResponse(commitTimestamp);
+ }
+
@Override
public ReadContext singleUse() {
Span span = tracer.spanBuilder(READ_ONLY_TRANSACTION).startSpan();
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Options.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Options.java
index 879b632d17..217d81b886 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Options.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Options.java
@@ -33,6 +33,9 @@ public interface ReadOption {}
/** Marker interface to mark options applicable to query operation. */
public interface QueryOption {}
+ /** Marker interface to mark options applicable to write operations */
+ public interface TransactionOption {}
+
/** Marker interface to mark options applicable to list operations in admin API. */
public interface ListOption {}
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionImpl.java
index 6a91d85fef..971dfc2ab1 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionImpl.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionImpl.java
@@ -25,6 +25,7 @@
import com.google.cloud.spanner.AbstractReadContext.MultiUseReadOnlyTransaction;
import com.google.cloud.spanner.AbstractReadContext.SingleReadContext;
import com.google.cloud.spanner.AbstractReadContext.SingleUseReadOnlyTransaction;
+import com.google.cloud.spanner.Options.TransactionOption;
import com.google.cloud.spanner.SessionClient.SessionId;
import com.google.cloud.spanner.TransactionRunnerImpl.TransactionContextImpl;
import com.google.cloud.spanner.spi.v1.SpannerRpc;
@@ -35,7 +36,6 @@
import com.google.protobuf.Empty;
import com.google.spanner.v1.BeginTransactionRequest;
import com.google.spanner.v1.CommitRequest;
-import com.google.spanner.v1.CommitResponse;
import com.google.spanner.v1.Transaction;
import com.google.spanner.v1.TransactionOptions;
import io.opencensus.common.Scope;
@@ -139,6 +139,13 @@ public Void run(TransactionContext ctx) {
return runner.getCommitTimestamp();
}
+ @Override
+ public CommitResponse writeWithOptions(Iterable mutations, TransactionOption... options)
+ throws SpannerException {
+ final Timestamp commitTimestamp = write(mutations);
+ return new CommitResponse(commitTimestamp);
+ }
+
@Override
public Timestamp writeAtLeastOnce(Iterable mutations) throws SpannerException {
setActive(null);
@@ -154,7 +161,7 @@ public Timestamp writeAtLeastOnce(Iterable mutations) throws SpannerEx
.build();
Span span = tracer.spanBuilder(SpannerImpl.COMMIT).startSpan();
try (Scope s = tracer.withSpan(span)) {
- CommitResponse response = spanner.getRpc().commit(request, options);
+ com.google.spanner.v1.CommitResponse response = spanner.getRpc().commit(request, options);
Timestamp t = Timestamp.fromProto(response.getCommitTimestamp());
return t;
} catch (IllegalArgumentException e) {
@@ -168,6 +175,13 @@ public Timestamp writeAtLeastOnce(Iterable mutations) throws SpannerEx
}
}
+ @Override
+ public CommitResponse writeAtLeastOnceWithOptions(
+ Iterable mutations, TransactionOption... options) throws SpannerException {
+ final Timestamp commitTimestamp = writeAtLeastOnce(mutations);
+ return new CommitResponse(commitTimestamp);
+ }
+
@Override
public ReadContext singleUse() {
return singleUse(TimestampBound.strong());
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPool.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPool.java
index 2512024117..8d1e44fe9a 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPool.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPool.java
@@ -47,10 +47,10 @@
import com.google.cloud.grpc.GrpcTransportOptions.ExecutorFactory;
import com.google.cloud.spanner.Options.QueryOption;
import com.google.cloud.spanner.Options.ReadOption;
+import com.google.cloud.spanner.Options.TransactionOption;
import com.google.cloud.spanner.SessionClient.SessionConsumer;
import com.google.cloud.spanner.SpannerException.ResourceNotFoundException;
import com.google.cloud.spanner.SpannerImpl.ClosedException;
-import com.google.cloud.spanner.TransactionManager.TransactionState;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.MoreObjects;
@@ -1103,6 +1103,13 @@ public Timestamp write(Iterable mutations) throws SpannerException {
}
}
+ @Override
+ public CommitResponse writeWithOptions(
+ Iterable mutations, TransactionOption... options) throws SpannerException {
+ final Timestamp commitTimestamp = write(mutations);
+ return new CommitResponse(commitTimestamp);
+ }
+
@Override
public Timestamp writeAtLeastOnce(Iterable mutations) throws SpannerException {
try {
@@ -1112,6 +1119,13 @@ public Timestamp writeAtLeastOnce(Iterable mutations) throws SpannerEx
}
}
+ @Override
+ public CommitResponse writeAtLeastOnceWithOptions(
+ Iterable mutations, TransactionOption... options) throws SpannerException {
+ final Timestamp commitTimestamp = writeAtLeastOnce(mutations);
+ return new CommitResponse(commitTimestamp);
+ }
+
@Override
public ReadContext singleUse() {
try {
@@ -1347,6 +1361,13 @@ public Timestamp write(Iterable mutations) throws SpannerException {
}
}
+ @Override
+ public CommitResponse writeWithOptions(
+ Iterable mutations, TransactionOption... options) throws SpannerException {
+ final Timestamp commitTimestamp = write(mutations);
+ return new CommitResponse(commitTimestamp);
+ }
+
@Override
public Timestamp writeAtLeastOnce(Iterable mutations) throws SpannerException {
try {
@@ -1357,6 +1378,13 @@ public Timestamp writeAtLeastOnce(Iterable mutations) throws SpannerEx
}
}
+ @Override
+ public CommitResponse writeAtLeastOnceWithOptions(
+ Iterable mutations, TransactionOption... options) throws SpannerException {
+ final Timestamp commitTimestamp = writeAtLeastOnce(mutations);
+ return new CommitResponse(commitTimestamp);
+ }
+
@Override
public long executePartitionedUpdate(Statement stmt) throws SpannerException {
try {