in src/main/java/com/amazonaws/services/dynamodbv2/transactions/Transaction.java [998:1074]
protected Map<String, AttributeValue> lockItem(Request callerRequest, boolean expectExists, int attempts) throws ItemNotLockedException, TransactionException {
Map<String, AttributeValue> key = callerRequest.getKey(txManager);
if(attempts <= 0) {
throw new TransactionException(txId, "Unable to acquire item lock for item " + key); // This won't trigger a rollback, it's really just a case of contention and needs more redriving
}
// Create Expected and Updates maps.
// - If we expect the item TO exist, we only update the lock
// - If we expect the item NOT to exist, we update both the transient attribute and the lock.
// In both cases we expect the txid not to be set
Map<String, AttributeValueUpdate> updates = new HashMap<String, AttributeValueUpdate>();
updates.put(AttributeName.TXID.toString(), new AttributeValueUpdate().withAction(AttributeAction.PUT).withValue(new AttributeValue(txId)));
updates.put(AttributeName.DATE.toString(), new AttributeValueUpdate().withAction(AttributeAction.PUT).withValue(txManager.getCurrentTimeAttribute()));
Map<String, ExpectedAttributeValue> expected;
if(expectExists) {
expected = callerRequest.getExpectExists(txManager);
expected.put(AttributeName.TXID.toString(), new ExpectedAttributeValue().withExists(false));
} else {
expected = new HashMap<String, ExpectedAttributeValue>(1);
updates.put(AttributeName.TRANSIENT.toString(), new AttributeValueUpdate()
.withAction(AttributeAction.PUT)
.withValue(new AttributeValue().withS(BOOLEAN_TRUE_ATTR_VAL)));
}
expected.put(AttributeName.TXID.toString(), new ExpectedAttributeValue().withExists(false));
// Do a conditional update on NO transaction id, and that the item DOES exist
UpdateItemRequest updateRequest = new UpdateItemRequest()
.withTableName(callerRequest.getTableName())
.withExpected(expected)
.withKey(key)
.withReturnValues(ReturnValue.ALL_NEW)
.withAttributeUpdates(updates);
String owner = null;
boolean nextExpectExists = false;
Map<String, AttributeValue> item = null;
try {
item = txManager.getClient().updateItem(updateRequest).getAttributes();
owner = getOwner(item);
} catch (ConditionalCheckFailedException e) {
// If the check failed, it means there is either:
// 1) a different transaction currently locking the item
// 2) this transaction already is attempting to lock the item.
// 3) the item does not exist
// Get the item and see which is the case
item = getItem(callerRequest.getTableName(), key);
if(item == null) {
nextExpectExists = false;
} else {
nextExpectExists = true;
owner = getOwner(item);
}
}
// Try the write again if the item is unowned (but not if it is owned)
if(owner != null) {
if(txId.equals(owner)) {
return item;
}
// For now, always roll back / complete the other transaction in the case of a conflict.
if(attempts > 1) {
try {
Transaction otherTransaction = txManager.resumeTransaction(owner);
otherTransaction.rollback();
} catch (TransactionCompletedException e) {
// no-op
} catch (TransactionNotFoundException e) {
releaseReadLock(owner, txManager, callerRequest.getTableName(), key);
}
} else {
throw new ItemNotLockedException(txId, owner, callerRequest.getTableName(), key);
}
}
return lockItem(callerRequest, nextExpectExists, attempts - 1);
}