use of com.google.cloud.spanner.TransactionRunner.TransactionCallable in project google-cloud-java by GoogleCloudPlatform.
the class ITTransactionTest method doBasicsTest.
private void doBasicsTest(final ReadStrategy strategy) throws InterruptedException {
final String key = uniqueKey();
// Initial value.
client.write(Arrays.asList(Mutation.newInsertBuilder("T").set("K").to(key).set("V").to(0).build()));
final int numThreads = 3;
final CountDownLatch commitBarrier = new CountDownLatch(numThreads);
final CountDownLatch complete = new CountDownLatch(numThreads);
final TransactionCallable<Long> callable = new TransactionCallable<Long>() {
@Override
public Long run(TransactionContext transaction) throws SpannerException {
Struct row = strategy.read(transaction, key);
long newValue = row.getLong(0) + 1;
transaction.buffer(Mutation.newUpdateBuilder("T").set("K").to(key).set("V").to(newValue).build());
commitBarrier.countDown();
// Synchronize so that all threads attempt to commit at the same time.
Uninterruptibles.awaitUninterruptibly(commitBarrier);
return newValue;
}
};
// We start multiple threads all attempting to update the same value concurrently. We expect
// to see at least some of the corresponding transactions abort.
final Vector<Long> results = new Vector<>();
final Vector<Timestamp> commitTimestamps = new Vector<>();
class TxnThread extends Thread {
@Override
public void run() {
TransactionRunner runner = client.readWriteTransaction();
Long result = runner.run(callable);
results.add(result);
commitTimestamps.add(runner.getCommitTimestamp());
complete.countDown();
}
}
for (int i = 0; i < numThreads; ++i) {
new TxnThread().start();
}
complete.await();
assertThat(results).hasSize(numThreads);
List<Long> expectedResults = new ArrayList<>();
for (int i = 0; i < numThreads; ++i) {
expectedResults.add(i + 1L);
}
assertThat(results).containsAllIn(expectedResults);
assertThat(Sets.newHashSet(commitTimestamps)).hasSize(numThreads);
assertThat(client.singleUse(TimestampBound.strong()).readRow("T", Key.of(key), Arrays.asList("V")).getLong(0)).isEqualTo(Long.valueOf(numThreads));
}
use of com.google.cloud.spanner.TransactionRunner.TransactionCallable in project google-cloud-java by GoogleCloudPlatform.
the class ITTransactionTest method userExceptionPreventsCommit.
@Test
public void userExceptionPreventsCommit() {
class UserException extends Exception {
UserException(String message) {
super(message);
}
}
final String key = uniqueKey();
TransactionCallable<Void> callable = new TransactionCallable<Void>() {
@Override
public Void run(TransactionContext transaction) throws UserException {
transaction.buffer(Mutation.newInsertOrUpdateBuilder("T").set("K").to(key).build());
throw new UserException("User failure");
}
};
try {
client.readWriteTransaction().run(callable);
fail("Expected user exception");
} catch (SpannerException e) {
assertThat(e.getErrorCode()).isEqualTo(ErrorCode.UNKNOWN);
assertThat(e.getMessage()).contains("User failure");
assertThat(e.getCause()).isInstanceOf(UserException.class);
}
Struct row = client.singleUse(TimestampBound.strong()).readRow("T", Key.of(key), Arrays.asList("K"));
assertThat(row).isNull();
}
use of com.google.cloud.spanner.TransactionRunner.TransactionCallable in project spanner-jdbc by olavloite.
the class TransactionThread method run.
@Override
public void run() {
TransactionRunner runner = dbClient.readWriteTransaction();
synchronized (monitor) {
try {
status = runner.run(new TransactionCallable<TransactionStatus>() {
@Override
public TransactionStatus run(TransactionContext transaction) throws Exception {
long startTime = System.currentTimeMillis();
long lastTriggerTime = startTime;
boolean transactionStartedLogged = false;
boolean stackTraceLoggedForKeepAlive = false;
boolean stackTraceLoggedForLongRunning = false;
status = TransactionStatus.RUNNING;
while (!stop) {
try {
Statement statement = statements.poll(5, TimeUnit.SECONDS);
if (statement != null) {
String sql = statement.getSql();
if (!stopStatementStrings.contains(sql)) {
resultSets.put(transaction.executeQuery(statement));
}
} else {
// keep alive
transactionStartedLogged = logTransactionStarted(transactionStartedLogged, startTime);
logger.info(String.format("%s, %s", getName(), "Transaction has been inactive for more than 5 seconds and will do a keep-alive query"));
if (!stackTraceLoggedForKeepAlive) {
logStartStackTrace();
stackTraceLoggedForKeepAlive = true;
}
try (ResultSet rs = transaction.executeQuery(Statement.of("SELECT 1"))) {
rs.next();
}
}
if (!stop && logger.logInfo() && (System.currentTimeMillis() - lastTriggerTime) > CloudSpannerDriver.getLongTransactionTrigger()) {
transactionStartedLogged = logTransactionStarted(transactionStartedLogged, startTime);
logger.info(String.format("%s, %s", getName(), "Transaction has been running for " + (System.currentTimeMillis() - startTime) + "ms"));
if (!stackTraceLoggedForLongRunning) {
logStartStackTrace();
stackTraceLoggedForLongRunning = true;
}
lastTriggerTime = System.currentTimeMillis();
}
} catch (InterruptedException e) {
logDebugIfTransactionStartedLogged(transactionStartedLogged, "Transaction interrupted");
stopped = true;
exception = e;
throw e;
}
}
switch(stopStatement) {
case COMMIT:
logDebugIfTransactionStartedLogged(transactionStartedLogged, "Transaction committed");
transaction.buffer(mutations);
break;
case ROLLBACK:
// throw an exception to force a rollback
logDebugIfTransactionStartedLogged(transactionStartedLogged, "Transaction rolled back");
throw new RollbackException();
case PREPARE:
logDebugIfTransactionStartedLogged(transactionStartedLogged, "Transaction prepare called");
XATransaction.prepareMutations(transaction, xid, mutations);
break;
case COMMIT_PREPARED:
logDebugIfTransactionStartedLogged(transactionStartedLogged, "Transaction commit prepared called");
XATransaction.commitPrepared(transaction, xid);
break;
case ROLLBACK_PREPARED:
logDebugIfTransactionStartedLogged(transactionStartedLogged, "Transaction rollback prepared called");
XATransaction.rollbackPrepared(transaction, xid);
break;
}
logDebugIfTransactionStartedLogged(transactionStartedLogged, "Transaction successfully stopped");
return TransactionStatus.SUCCESS;
}
});
commitTimestamp = runner.getCommitTimestamp();
} catch (Exception e) {
if (e.getCause() instanceof RollbackException) {
status = TransactionStatus.SUCCESS;
} else {
// if statement prevents unnecessary String.format(...) call
if (logger.logDebug()) {
logger.debug(String.format("%s, %s", getName(), "Transaction threw an exception: " + e.getMessage()));
}
status = TransactionStatus.FAIL;
exception = e;
}
} finally {
stopped = true;
monitor.notifyAll();
}
}
}
use of com.google.cloud.spanner.TransactionRunner.TransactionCallable in project google-cloud-java by GoogleCloudPlatform.
the class ITTransactionTest method userExceptionIsSpannerException.
@Test
public void userExceptionIsSpannerException() {
final String key = uniqueKey();
TransactionCallable<Void> callable = new TransactionCallable<Void>() {
@Override
public Void run(TransactionContext transaction) {
transaction.buffer(Mutation.newInsertOrUpdateBuilder("T").set("K").to(key).build());
throw newSpannerException(ErrorCode.OUT_OF_RANGE, "User failure");
}
};
try {
client.readWriteTransaction().run(callable);
fail("Expected user exception");
} catch (SpannerException e) {
assertThat(e.getErrorCode()).isEqualTo(ErrorCode.OUT_OF_RANGE);
assertThat(e.getMessage()).contains("User failure");
}
Struct row = client.singleUse(TimestampBound.strong()).readRow("T", Key.of(key), Arrays.asList("K"));
assertThat(row).isNull();
}
Aggregations