protected Map lockItem()

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);
    }