Skip to content

Commit fc19c2e

Browse files
authored
---
yaml --- r: 8679 b: refs/heads/master c: 32a6648 h: refs/heads/master i: 8677: d8c6035 8675: aed4144 8671: 13162eb
1 parent 9e020e1 commit fc19c2e

9 files changed

Lines changed: 381 additions & 207 deletions

File tree

[refs]

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
refs/heads/master: f9140a6661278532f5c21cebfaf53ab99aeb0df0
2+
refs/heads/master: 32a66489d9a9828b1466b7a6c8723fb25cb17091
33
refs/heads/travis: 47e4fee4fd5af9b2a8ce46f23c72ec95f9b195b2
44
refs/heads/gh-pages: 6daca92127d91b7c2c99490080ecf8a13fa94cde
55
refs/tags/0.0.9: 22f1839238f66c39e67ed4dfdcd273b1ae2e8444

trunk/google-cloud-datastore/src/main/java/com/google/cloud/datastore/Datastore.java

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
package com.google.cloud.datastore;
1818

1919
import com.google.cloud.Service;
20-
20+
import com.google.datastore.v1.TransactionOptions;
2121
import java.util.Iterator;
2222
import java.util.List;
2323

@@ -26,6 +26,14 @@
2626
*/
2727
public interface Datastore extends Service<DatastoreOptions>, DatastoreReaderWriter {
2828

29+
/**
30+
* Returns a new Datastore transaction.
31+
*
32+
* @param options a transaction option indicating the mode of the transaction (read-only or read-write)
33+
* @throws DatastoreException upon failure
34+
*/
35+
Transaction newTransaction(TransactionOptions options);
36+
2937
/**
3038
* Returns a new Datastore transaction.
3139
*
@@ -69,6 +77,40 @@ interface TransactionCallable<T> {
6977
*/
7078
<T> T runInTransaction(TransactionCallable<T> callable);
7179

80+
81+
/**
82+
* Invokes the callback's {@link Datastore.TransactionCallable#run} method with a
83+
* {@link DatastoreReaderWriter} that is associated with a new transaction.
84+
* The transaction will be committed upon successful invocation.
85+
* Any thrown exception will cause the transaction to rollback and will be propagated
86+
* as a {@link DatastoreException} with the original exception as its root cause.
87+
* If {@link TransactionOptions} is set to read-write mode, previous transaction Id
88+
* in the options will be automatically populated each time a transaction is retried.
89+
*
90+
* <p>Example of running in a transaction.
91+
* <pre> {@code
92+
* String callableResult = "my_callable_result";
93+
* TransactionCallable<String> callable = new TransactionCallable<String>() {
94+
* public String run(DatastoreReaderWriter readerWriter) {
95+
* // use readerWriter to run in transaction
96+
* return callableResult;
97+
* }
98+
* };
99+
*
100+
* TransactionOptions options = TransactionOptions.newBuilder()
101+
* .setReadWrite(TransactionOptions.ReadWrite
102+
* .getDefaultInstance())
103+
* .build();
104+
*
105+
* String result = datastore.runInTransaction(callable, options);
106+
* }</pre>
107+
*
108+
* @param callable the callback to call with a newly created transactional readerWriter
109+
* @param options the Transaction options indicating whether the transaction mode is Read-only or Read-Write
110+
* @throws DatastoreException upon failure
111+
*/
112+
<T> T runInTransaction(TransactionCallable<T> callable, TransactionOptions options);
113+
72114
/**
73115
* Returns a new Batch for processing multiple write operations in one request.
74116
*

trunk/google-cloud-datastore/src/main/java/com/google/cloud/datastore/DatastoreHelper.java

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818

1919
import com.google.common.collect.Iterators;
2020
import com.google.common.collect.Maps;
21-
2221
import java.util.ArrayList;
2322
import java.util.Arrays;
2423
import java.util.Collections;
@@ -88,20 +87,4 @@ private static List<Entity> compileEntities(Key[] keys, Iterator<Entity> entitie
8887
}
8988
return list;
9089
}
91-
92-
static <T> T runInTransaction(Datastore datastore, Datastore.TransactionCallable<T> callable) {
93-
Transaction transaction = datastore.newTransaction();
94-
try {
95-
T value = callable.run(transaction);
96-
transaction.commit();
97-
return value;
98-
} catch (Exception ex) {
99-
transaction.rollback();
100-
throw DatastoreException.propagateUserException(ex);
101-
} finally {
102-
if (transaction.isActive()) {
103-
transaction.rollback();
104-
}
105-
}
106-
}
10790
}

trunk/google-cloud-datastore/src/main/java/com/google/cloud/datastore/DatastoreImpl.java

Lines changed: 73 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import com.google.common.collect.Iterables;
3232
import com.google.common.collect.Sets;
3333
import com.google.datastore.v1.ReadOptions.ReadConsistency;
34+
import com.google.datastore.v1.TransactionOptions;
3435
import com.google.protobuf.ByteString;
3536
import java.util.ArrayList;
3637
import java.util.Arrays;
@@ -62,22 +63,74 @@ public Batch newBatch() {
6263
return new BatchImpl(this);
6364
}
6465

66+
@Override
67+
public Transaction newTransaction(TransactionOptions transactionOptions) {
68+
return new TransactionImpl(this, transactionOptions);
69+
}
70+
6571
@Override
6672
public Transaction newTransaction() {
6773
return new TransactionImpl(this);
6874
}
6975

76+
static class ReadWriteTransactionCallable<T> implements Callable<T> {
77+
private final Datastore datastore;
78+
private final TransactionCallable<T> callable;
79+
private volatile TransactionOptions options;
80+
private volatile Transaction transaction;
81+
82+
ReadWriteTransactionCallable(Datastore datastore, TransactionCallable<T> callable, TransactionOptions options) {
83+
this.datastore = datastore;
84+
this.callable = callable;
85+
this.options = options;
86+
this.transaction = null;
87+
}
88+
89+
Datastore getDatastore() {
90+
return datastore;
91+
}
92+
93+
TransactionOptions getOptions() {
94+
return options;
95+
}
96+
97+
Transaction getTransaction() {
98+
return transaction;
99+
}
100+
101+
void setPrevTransactionId(ByteString transactionId) {
102+
TransactionOptions.ReadWrite readWrite =
103+
TransactionOptions.ReadWrite.newBuilder().setPreviousTransaction(transactionId).build();
104+
options = options.toBuilder().setReadWrite(readWrite).build();
105+
}
106+
107+
@Override
108+
public T call() throws DatastoreException {
109+
transaction = datastore.newTransaction(options);
110+
try {
111+
T value = callable.run(transaction);
112+
transaction.commit();
113+
return value;
114+
} catch (Exception ex) {
115+
transaction.rollback();
116+
throw DatastoreException.propagateUserException(ex);
117+
} finally {
118+
if (transaction.isActive()) {
119+
transaction.rollback();
120+
}
121+
if (options != null && options.getModeCase().equals(TransactionOptions.ModeCase.READ_WRITE)) {
122+
setPrevTransactionId(transaction.getTransactionId());
123+
}
124+
}
125+
}
126+
}
127+
70128
@Override
71129
public <T> T runInTransaction(final TransactionCallable<T> callable) {
72130
final DatastoreImpl self = this;
73131
try {
74132
return RetryHelper.runWithRetries(
75-
new Callable<T>() {
76-
@Override
77-
public T call() throws DatastoreException {
78-
return DatastoreHelper.runInTransaction(self, callable);
79-
}
80-
},
133+
new ReadWriteTransactionCallable<T>(self, callable, null),
81134
retrySettings,
82135
TRANSACTION_EXCEPTION_HANDLER,
83136
getOptions().getClock());
@@ -86,6 +139,20 @@ public T call() throws DatastoreException {
86139
}
87140
}
88141

142+
@Override
143+
public <T> T runInTransaction(final TransactionCallable<T> callable, TransactionOptions transactionOptions) {
144+
final DatastoreImpl self = this;
145+
try {
146+
return RetryHelper.runWithRetries(
147+
new ReadWriteTransactionCallable<T>(self, callable, transactionOptions),
148+
retrySettings,
149+
TRANSACTION_EXCEPTION_HANDLER,
150+
getOptions().getClock());
151+
} catch (RetryHelperException e) {
152+
throw DatastoreException.translateAndThrow(e);
153+
}
154+
}
155+
89156
@Override
90157
public <T> QueryResults<T> run(Query<T> query) {
91158
return run(null, query);

trunk/google-cloud-datastore/src/main/java/com/google/cloud/datastore/Transaction.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package com.google.cloud.datastore;
1818

19+
import com.google.protobuf.ByteString;
1920
import java.util.Iterator;
2021
import java.util.List;
2122

@@ -445,4 +446,6 @@ interface Response {
445446
* Returns the transaction associated {@link Datastore}.
446447
*/
447448
Datastore getDatastore();
449+
450+
ByteString getTransactionId();
448451
}

trunk/google-cloud-datastore/src/main/java/com/google/cloud/datastore/TransactionImpl.java

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,16 @@
1616

1717
package com.google.cloud.datastore;
1818

19+
import com.google.datastore.v1.TransactionOptions;
1920
import com.google.protobuf.ByteString;
20-
2121
import java.util.ArrayList;
2222
import java.util.Iterator;
2323
import java.util.List;
2424

2525
final class TransactionImpl extends BaseDatastoreBatchWriter implements Transaction {
2626

2727
private final DatastoreImpl datastore;
28-
private final ByteString transaction;
28+
private final ByteString transactionId;
2929
private boolean rolledback;
3030

3131
static class ResponseImpl implements Transaction.Response {
@@ -52,11 +52,20 @@ public List<Key> getGeneratedKeys() {
5252
}
5353

5454
TransactionImpl(DatastoreImpl datastore) {
55+
this(datastore, null);
56+
}
57+
58+
TransactionImpl(DatastoreImpl datastore, TransactionOptions options) {
5559
super("transaction");
5660
this.datastore = datastore;
5761
com.google.datastore.v1.BeginTransactionRequest.Builder requestPb =
58-
com.google.datastore.v1.BeginTransactionRequest.newBuilder();
59-
transaction = datastore.requestTransactionId(requestPb);
62+
com.google.datastore.v1.BeginTransactionRequest.newBuilder();
63+
64+
if (options != null) {
65+
requestPb.setTransactionOptions(options);
66+
}
67+
68+
transactionId = datastore.requestTransactionId(requestPb);
6069
}
6170

6271
@Override
@@ -69,7 +78,7 @@ public Iterator<Entity> get(Key... keys) {
6978
validateActive();
7079
com.google.datastore.v1.ReadOptions.Builder readOptionsPb =
7180
com.google.datastore.v1.ReadOptions.newBuilder();
72-
readOptionsPb.setTransaction(transaction);
81+
readOptionsPb.setTransaction(transactionId);
7382
return datastore.get(readOptionsPb.build(), keys);
7483
}
7584

@@ -84,7 +93,7 @@ public <T> QueryResults<T> run(Query<T> query) {
8493
validateActive();
8594
com.google.datastore.v1.ReadOptions.Builder readOptionsPb =
8695
com.google.datastore.v1.ReadOptions.newBuilder();
87-
readOptionsPb.setTransaction(transaction);
96+
readOptionsPb.setTransaction(transactionId);
8897
return datastore.run(readOptionsPb.build(), query);
8998
}
9099

@@ -95,7 +104,7 @@ public Transaction.Response commit() {
95104
com.google.datastore.v1.CommitRequest.Builder requestPb =
96105
com.google.datastore.v1.CommitRequest.newBuilder();
97106
requestPb.setMode(com.google.datastore.v1.CommitRequest.Mode.TRANSACTIONAL);
98-
requestPb.setTransaction(transaction);
107+
requestPb.setTransaction(transactionId);
99108
requestPb.addAllMutations(mutationsPb);
100109
com.google.datastore.v1.CommitResponse responsePb = datastore.commit(requestPb.build());
101110
deactivate();
@@ -108,7 +117,7 @@ public void rollback() {
108117
return;
109118
}
110119
validateActive();
111-
datastore.rollbackTransaction(transaction);
120+
datastore.rollbackTransaction(transactionId);
112121
deactivate();
113122
rolledback = true;
114123
}
@@ -118,4 +127,9 @@ public void rollback() {
118127
public Datastore getDatastore() {
119128
return datastore;
120129
}
130+
131+
@Override
132+
public ByteString getTransactionId() {
133+
return transactionId;
134+
}
121135
}

trunk/google-cloud-datastore/src/test/java/com/google/cloud/datastore/DatastoreHelperTest.java

Lines changed: 1 addition & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,12 @@
2323
import static org.easymock.EasyMock.verify;
2424
import static org.junit.Assert.assertEquals;
2525
import static org.junit.Assert.assertNull;
26-
import static org.junit.Assert.assertSame;
27-
import static org.junit.Assert.assertTrue;
28-
import static org.junit.Assert.fail;
2926

30-
import com.google.cloud.datastore.Datastore.TransactionCallable;
3127
import com.google.common.collect.ImmutableList;
3228
import com.google.common.collect.Iterators;
33-
34-
import org.easymock.EasyMock;
35-
import org.junit.Test;
36-
3729
import java.util.Collections;
3830
import java.util.List;
31+
import org.junit.Test;
3932

4033
public class DatastoreHelperTest {
4134

@@ -160,52 +153,4 @@ public void testFetchWithTransaction() throws Exception {
160153
assertEquals(entity2, values.get(1));
161154
verify(transaction);
162155
}
163-
164-
@Test
165-
public void testRunInTransaction() throws Exception {
166-
final Datastore datastore = createStrictMock(Datastore.class);
167-
final Transaction transaction = createStrictMock(Transaction.class);
168-
expect(datastore.newTransaction()).andReturn(transaction).once();
169-
expect(transaction.isActive()).andReturn(true).once();
170-
expect(transaction.commit()).andReturn(null).once();
171-
expect(transaction.isActive()).andReturn(false).once();
172-
replay(datastore, transaction);
173-
String value = DatastoreHelper.runInTransaction(datastore,
174-
new TransactionCallable<String>() {
175-
@Override
176-
public String run(DatastoreReaderWriter readerWriter) {
177-
assertTrue(transaction.isActive());
178-
assertSame(transaction, readerWriter);
179-
return "done";
180-
}
181-
});
182-
verify(datastore, transaction);
183-
assertEquals("done", value);
184-
}
185-
186-
@Test
187-
public void testRunInTransactionWithException() throws Exception {
188-
final Datastore datastore = createStrictMock(Datastore.class);
189-
final Transaction transaction = createStrictMock(Transaction.class);
190-
expect(datastore.newTransaction()).andReturn(transaction).once();
191-
expect(transaction.isActive()).andReturn(true).once();
192-
transaction.rollback();
193-
EasyMock.expectLastCall().once();
194-
expect(transaction.isActive()).andReturn(false).once();
195-
replay(datastore, transaction);
196-
try {
197-
DatastoreHelper.runInTransaction(datastore, new TransactionCallable<Void>() {
198-
@Override
199-
public Void run(DatastoreReaderWriter readerWriter) throws Exception {
200-
assertTrue(transaction.isActive());
201-
assertSame(transaction, readerWriter);
202-
throw new Exception("Bla");
203-
}
204-
});
205-
fail("DatastoreException was expected");
206-
} catch (DatastoreException ex) {
207-
assertEquals("Bla", ex.getCause().getMessage());
208-
}
209-
verify(datastore, transaction);
210-
}
211156
}

0 commit comments

Comments
 (0)