@Override public void update(Entry entry, Entry existingEntry) { readWriteLock.writeLock().lock(); try { Map<String, AttributeValue> keys = createKey(entry); Map<String, AttributeValueUpdate> attributes = createAttributes(entry); Map<String, ExpectedAttributeValue> expected = expectExists(existingEntry); try { executeUpdate(keys, attributes, expected); } catch (ConditionalCheckFailedException e) { throw new DoesNotExistException("Precondition to update entry in DynamoDB failed:" + keys.toString()); } } finally { readWriteLock.writeLock().unlock(); } }
@Override public void create(Entry entry) { readWriteLock.writeLock().lock(); try { Map<String, AttributeValue> keys = createKey(entry); Map<String, AttributeValueUpdate> attributes = createAttributes(entry); Map<String, ExpectedAttributeValue> expected = expectNotExists(); try { executeUpdate(keys, attributes, expected); } catch (ConditionalCheckFailedException e) { throw new AlreadyExistsException("DynamoDB store entry already exists:" + keys.toString()); } } finally { readWriteLock.writeLock().unlock(); } }
boolean createItemIfNotExists(String key, long currentTimeMillis, Context context) { LambdaLogger logger = context.getLogger(); AmazonDynamoDB client = createDynamoDBClient(cc); String functionName = context.getFunctionName(); try { // Create a record if it does not exist PutItemRequest req = new PutItemRequest().withTableName(TABLE_NAME) .addItemEntry(COL_FUNCTION_NAME, new AttributeValue(functionName)) .addItemEntry(COL_KEY, new AttributeValue(key)) .addItemEntry(COL_CREATED_TIME, new AttributeValue().withN(Long.toString(currentTimeMillis))) .addExpectedEntry(COL_FUNCTION_NAME, new ExpectedAttributeValue().withExists(false)) .addExpectedEntry(COL_KEY, new ExpectedAttributeValue().withExists(false)); client.putItem(req); return true; } catch (ConditionalCheckFailedException e) { logger.log("Record exsited. functionName[" + functionName + "] key[" + key + "]"); return false; } finally { client.shutdown(); } }
@Test public void execute() { Map<String, AttributeValue> key = new HashMap<String, AttributeValue>(); key.put("1", new AttributeValue("Key_1")); exchange.getIn().setHeader(DdbConstants.KEY, key); Map<String, ExpectedAttributeValue> updateCondition = new HashMap<String, ExpectedAttributeValue>(); updateCondition .put("name", new ExpectedAttributeValue(new AttributeValue("expected value"))); exchange.getIn().setHeader(DdbConstants.UPDATE_CONDITION, updateCondition); exchange.getIn().setHeader(DdbConstants.RETURN_VALUES, "ALL_OLD"); command.execute(); assertEquals("DOMAIN1", ddbClient.deleteItemRequest.getTableName()); assertEquals(key, ddbClient.deleteItemRequest.getKey()); assertEquals(updateCondition, ddbClient.deleteItemRequest.getExpected()); assertEquals("ALL_OLD", ddbClient.deleteItemRequest.getReturnValues()); assertEquals(new AttributeValue("attrValue"), exchange.getIn().getHeader(DdbConstants.ATTRIBUTES, Map.class).get( "attrName")); }
@Test public void execute() { Map<String, AttributeValue> attributeMap = new HashMap<String, AttributeValue>(); AttributeValue attributeValue = new AttributeValue("test value"); attributeMap.put("name", attributeValue); exchange.getIn().setHeader(DdbConstants.ITEM, attributeMap); Map<String, ExpectedAttributeValue> expectedAttributeValueMap = new HashMap<String, ExpectedAttributeValue>(); expectedAttributeValueMap.put("name", new ExpectedAttributeValue(attributeValue)); exchange.getIn().setHeader(DdbConstants.UPDATE_CONDITION, expectedAttributeValueMap); command.execute(); assertEquals("DOMAIN1", ddbClient.putItemRequest.getTableName()); assertEquals(attributeMap, ddbClient.putItemRequest.getItem()); assertEquals(expectedAttributeValueMap, ddbClient.putItemRequest.getExpected()); assertEquals(new AttributeValue("attrValue"), exchange.getIn().getHeader(DdbConstants.ATTRIBUTES, Map.class).get("attrName")); }
public Map<String, ExpectedAttributeValue> build(final KCVMutation mutation) { Preconditions.checkState(transaction != null, "Transaction must not be null"); Preconditions.checkState(key != null, "Key must not be null"); final Map<String, ExpectedAttributeValue> expected = Maps.newHashMapWithExpectedSize(mutation.getTotalMutations()); for (Entry addedColumn : mutation.getAdditions()) { final StaticBuffer columnKey = addedColumn.getColumn(); addExpectedValueIfPresent(columnKey, expected); } for (StaticBuffer deletedKey : mutation.getDeletions()) { addExpectedValueIfPresent(deletedKey, expected); } return expected; }
private void addExpectedValueIfPresent(final StaticBuffer column, final Map<String, ExpectedAttributeValue> expectedValueMap) { final String dynamoDbColumn = encodeKeyBuffer(column); if (expectedValueMap.containsKey(dynamoDbColumn)) { return; } if (transaction.contains(store, key, column)) { final StaticBuffer expectedValue = transaction.get(store, key, column); final ExpectedAttributeValue expectedAttributeValue; if (expectedValue == null) { expectedAttributeValue = new ExpectedAttributeValue().withExists(false); } else { final AttributeValue attributeValue = encodeValue(expectedValue); expectedAttributeValue = new ExpectedAttributeValue().withValue(attributeValue) .withComparisonOperator(ComparisonOperator.EQ); } expectedValueMap.put(dynamoDbColumn, expectedAttributeValue); } }
/** * Sends an update request to the service and returns true if the request is successful. */ public boolean sendUpdateRequest(Map<String, AttributeValue> primaryKey, Map<String, AttributeValueUpdate> updateItems, Map<String, ExpectedAttributeValue> expectedItems) throws Exception { if (updateItems.isEmpty()) { return false; // No update, return false } UpdateItemRequest updateItemRequest = new UpdateItemRequest().withTableName(tableName).withKey(primaryKey).withReturnValues(ReturnValue.UPDATED_NEW) .withReturnConsumedCapacity(ReturnConsumedCapacity.TOTAL).withAttributeUpdates(updateItems); if (expectedItems != null) { updateItemRequest.withExpected(expectedItems); } UpdateItemResult result = dynamoDBClient.updateItem(updateItemRequest); if(!isRunningOnDDBLocal) { // DDB Local does not support rate limiting tableWriteRateLimiter.adjustRateWithConsumedCapacity(result.getConsumedCapacity()); } return true; }
public static void addTask(String taskID, String task){ HashMap<String, AttributeValue> item = new HashMap<String, AttributeValue>(); item.put("taskID", new AttributeValue().withS(taskID)); item.put("Task", new AttributeValue(task)); ExpectedAttributeValue notExpected = new ExpectedAttributeValue(false); Map<String, ExpectedAttributeValue> expected = new HashMap<String, ExpectedAttributeValue>(); expected.put("taskID", notExpected); PutItemRequest putItemRequest = new PutItemRequest() .withTableName(TABLE_NAME) .withItem(item) .withExpected(expected); //put item only if no taskID exists! dynamoDB.putItem(putItemRequest); }
public void addFilter(Map<String, ExpectedAttributeValue> expected, FieldDescriptor fieldDescriptor, Object fieldValue) throws DataStoreException { AttributeMapping attributeMapping = getAttributeMapping(fieldDescriptor); if (attributeMapping.isHashKey || attributeMapping.isRangeKey) { // Skip; we assume that the caller will use the filter return; } if (!attributeMapping.isFilterable) { throw new DataStoreException("Field not extracted: " + fieldDescriptor.getName()); } AttributeValue attributeValue = attributeMapping.buildAttributeValue(fieldValue); expected.put(attributeMapping.attributeName, new ExpectedAttributeValue(attributeValue).withComparisonOperator(ComparisonOperator.EQ)); }
private void executeUpdate(Map<String, AttributeValue> keys, Map<String, AttributeValueUpdate> attributes, Map<String, ExpectedAttributeValue> expected) { UpdateItemRequest updateEntry = new UpdateItemRequest() .withTableName(tableName) .withKey(keys) .withAttributeUpdates(attributes) .withExpected(expected); client.updateItem(updateEntry); }
private Map<String, ExpectedAttributeValue> expectExists(Entry entry) { Map<String, ExpectedAttributeValue> expected = new HashMap<>(); ExpectedAttributeValue expectedAttributeValue = new ExpectedAttributeValue(true); expectedAttributeValue.setValue(new AttributeValue(getPartitionKeyValue(entry))); expected.put(partitionKeyName.toString(), expectedAttributeValue); // FIXME: hardcode whole file, or make generic ExpectedAttributeValue expectedSha = new ExpectedAttributeValue(true); expectedSha.setValue(new AttributeValue(sha(entry))); expected.put(OPTIMISTIC_LOCK_FIELD_NAME, expectedSha); return expected; }
private void lockRequest(DynamoDBPlanoRequest dynamoDBPlanoRequest) { AttributeValue previousLockExpireTime = DateToStringMarshaller.instance().marshall( dynamoDBPlanoRequest.getLockExpireTime()); Date lockExpireTime = new Date(System.currentTimeMillis() + lockDurationMs); dynamoDBPlanoRequest.setLockExpireTime(lockExpireTime); DynamoDBSaveExpression saveExpression = new DynamoDBSaveExpression() .withExpectedEntry("LockExpireTime", new ExpectedAttributeValue() .withComparisonOperator(ComparisonOperator.EQ) .withValue(previousLockExpireTime)); dynamoDBMapper.save(dynamoDBPlanoRequest, saveExpression); }
/** * Inserting the food into the database, dislike and like are set to 0 * @param food */ public static void insertFood(FoodReceive food){ Log.d(LOG_TAG, "Inserting: " + food.getName()); final DynamoDBMapper mapper = AWSMobileClient.defaultMobileClient().getDynamoDBMapper(); final FoodDO firstItem = new FoodDO(); firstItem.setFoodId(food.getFood_id()); firstItem.setRestaurantId(food.getLocation().getRestaurantId()); firstItem.setName(food.getName()); AmazonClientException lastException = null; DynamoDBSaveExpression saveExpression = new DynamoDBSaveExpression(); Map<String, ExpectedAttributeValue> expectedAttributes = ImmutableMapParameter.<String, ExpectedAttributeValue>builder() .put("foodId", new ExpectedAttributeValue(false)).build(); saveExpression.setExpected(expectedAttributes); try { // mapper.save(firstItem); mapper.save(firstItem, saveExpression); } catch (ConditionalCheckFailedException e){ Log.e(LOG_TAG,"The foodId exists: " + e.getMessage()); lastException = e; } catch (final AmazonClientException ex) { Log.e(LOG_TAG,"Failed saving item batch: " + ex.getMessage()); lastException = ex; } if (lastException != null) { // Re-throw the last exception encountered to alert the user. throw lastException; } Log.d(LOG_TAG, "Insert successful"); }
@Test public void execute() { Map<String, AttributeValue> key = new HashMap<String, AttributeValue>(); key.put("1", new AttributeValue("Key_1")); exchange.getIn().setHeader(DdbConstants.KEY, key); Map<String, AttributeValueUpdate> attributeMap = new HashMap<String, AttributeValueUpdate>(); AttributeValueUpdate attributeValue = new AttributeValueUpdate( new AttributeValue("new value"), AttributeAction.ADD); attributeMap.put("name", attributeValue); exchange.getIn().setHeader(DdbConstants.UPDATE_VALUES, attributeMap); Map<String, ExpectedAttributeValue> expectedAttributeValueMap = new HashMap<String, ExpectedAttributeValue>(); expectedAttributeValueMap .put("name", new ExpectedAttributeValue(new AttributeValue("expected value"))); exchange.getIn().setHeader(DdbConstants.UPDATE_CONDITION, expectedAttributeValueMap); exchange.getIn().setHeader(DdbConstants.RETURN_VALUES, "ALL_OLD"); command.execute(); assertEquals("DOMAIN1", ddbClient.updateItemRequest.getTableName()); assertEquals(attributeMap, ddbClient.updateItemRequest.getAttributeUpdates()); assertEquals(key, ddbClient.updateItemRequest.getKey()); assertEquals(expectedAttributeValueMap, ddbClient.updateItemRequest.getExpected()); assertEquals("ALL_OLD", ddbClient.updateItemRequest.getReturnValues()); assertEquals(new AttributeValue("attrValue"), exchange.getIn().getHeader(DdbConstants.ATTRIBUTES, Map.class).get( "attrName")); }
@Override public Collection<MutateWorker> createMutationWorkers(final Map<StaticBuffer, KCVMutation> mutationMap, final DynamoDbStoreTransaction txh) { final List<MutateWorker> workers = Lists.newLinkedList(); for (Map.Entry<StaticBuffer, KCVMutation> entry : mutationMap.entrySet()) { final StaticBuffer hashKey = entry.getKey(); final KCVMutation mutation = entry.getValue(); final Map<String, AttributeValue> key = new ItemBuilder().hashKey(hashKey) .build(); // Using ExpectedAttributeValue map to handle large mutations in a single request // Large mutations would require multiple requests using expressions final Map<String, ExpectedAttributeValue> expected = new SingleExpectedAttributeValueBuilder(this, txh, hashKey).build(mutation); final Map<String, AttributeValueUpdate> attributeValueUpdates = new SingleUpdateBuilder().deletions(mutation.getDeletions()) .additions(mutation.getAdditions()) .build(); final UpdateItemRequest request = super.createUpdateItemRequest() .withKey(key) .withReturnValues(ReturnValue.ALL_NEW) .withAttributeUpdates(attributeValueUpdates) .withExpected(expected); final MutateWorker worker; if (mutation.hasDeletions() && !mutation.hasAdditions()) { worker = new SingleUpdateWithCleanupWorker(request, client.getDelegate()); } else { worker = new UpdateItemWorker(request, client.getDelegate()); } workers.add(worker); } return workers; }
/** * Put expected items into the map only when it exists and the action is put */ protected Map<String, ExpectedAttributeValue> genExpectedItemsForRecord(Map<String, AttributeValueUpdate> updateItems) { Map<String, ExpectedAttributeValue> expectedItems = new HashMap<String, ExpectedAttributeValue>(); if (null != updateItems.get(options.getGsiHashKeyName())) { ExpectedAttributeValue gsiHashKeyExpectedValue = tableWriter.genExpectedAttributeValue(getNextGsiHashKey()); expectedItems.put(options.getGsiHashKeyName(), gsiHashKeyExpectedValue); } if (null != updateItems.get(options.getGsiRangeKeyName())) { ExpectedAttributeValue gsiRangeKeyExpectedValue = tableWriter.genExpectedAttributeValue(getNextGsiRangeKey()); expectedItems.put(options.getGsiRangeKeyName(), gsiRangeKeyExpectedValue); } return expectedItems; }
public MetaStore(final AmazonDynamoDB ddb, final String tableName, final DynamoDBEncryptor encryptor) { this.ddb = checkNotNull(ddb, "ddb must not be null"); this.tableName = checkNotNull(tableName, "tableName must not be null"); this.encryptor = checkNotNull(encryptor, "encryptor must not be null"); ddbCtx = new EncryptionContext.Builder().withTableName(this.tableName) .withHashKeyName(DEFAULT_HASH_KEY).withRangeKeyName(DEFAULT_RANGE_KEY).build(); final Map<String, ExpectedAttributeValue> tmpExpected = new HashMap<String, ExpectedAttributeValue>(); tmpExpected.put(DEFAULT_HASH_KEY, new ExpectedAttributeValue().withExists(false)); tmpExpected.put(DEFAULT_RANGE_KEY, new ExpectedAttributeValue().withExists(false)); doesNotExist = Collections.unmodifiableMap(tmpExpected); }
/*** * Save the book if the expected condition holds up, then fire a change * event * * @param book * @param operator * @param attributeName * @param expectedValue */ public void saveIf(final Book book, final ComparisonOperator operator, final String attributeName, final String expectedValue) { final ExpectedAttributeValue expected = new ExpectedAttributeValue(); expected.withComparisonOperator(operator); expected.withValue(new AttributeValue(expectedValue)); final Book existingBook = this.get(Key.create(book.getBookId())); this.mapper.saveIf(book, null, attributeName, expected); this.changePublisher.publishChanges(Book.class, book.getIsbn(), book, existingBook); }
/*** * Conditionally Save the item if the expected attribute is found to be true * @param item * @param conditional * @param attributeName * @param expectedAttribute */ public <T> void saveIf(final T item, ConditionalOperator conditional, final String attributeName, final ExpectedAttributeValue expectedAttribute){ final DynamoDBSaveExpression expression = new DynamoDBSaveExpression() .withExpectedEntry(attributeName, expectedAttribute); if(conditional !=null){ expression.withConditionalOperator(conditional); } this.mapper.save(item, expression); }
/** * Convert them to a map of expected values. * @return Expected values */ public Map<String, ExpectedAttributeValue> asKeys() { final ImmutableMap.Builder<String, ExpectedAttributeValue> map = new ImmutableMap.Builder<String, ExpectedAttributeValue>(); for (final Map.Entry<String, AttributeValue> attr : this.attrs.entrySet()) { map.put( attr.getKey(), new ExpectedAttributeValue(attr.getValue()) ); } return map.build(); }
/** * Attributes can build expected keys. * @throws Exception If some problem inside */ @Test public void buildsExpectedKeys() throws Exception { final String attr = "attr-13"; final String value = "some value \u20ac"; MatcherAssert.assertThat( new Attributes().with(attr, value).asKeys(), Matchers.hasEntry( attr, new ExpectedAttributeValue(new AttributeValue(value)) ) ); }
@Override public <T extends Message> void insert(T item, Modifier... modifiers) throws DataStoreException { DynamoClassMapping<T> tableInfo = getClassMapping(item); log.debug("Insert {} {}", item.getClass().getSimpleName(), item); for (Modifier modifier : modifiers) { throw new UnsupportedOperationException(); } PutItemRequest request = new PutItemRequest(); request.setTableName(tableInfo.getDynamoTableName()); Map<String, AttributeValue> itemData = tableInfo.mapToDb(item); request.setItem(itemData); Map<String, ExpectedAttributeValue> expected = Maps.newHashMap(); expected.put(FIELD_HASH_KEY, new ExpectedAttributeValue().withComparisonOperator(ComparisonOperator.NULL)); request.setExpected(expected); if (expected.size() > 1) { request.setConditionalOperator(ConditionalOperator.AND); } try { dynamoDB.putItem(request); } catch (ConditionalCheckFailedException e) { log.debug("Insert failed {}", item, e); throw new UniqueIndexViolation(null); } }
private Map<String, ExpectedAttributeValue> expectNotExists() { Map<String, ExpectedAttributeValue> expected = new HashMap<>(); expected.put(partitionKeyName.toString(), new ExpectedAttributeValue(false)); return expected; }
private UpdateItemRequest constructUpdateItemRequest(RawSecretEntry rawSecretEntry, boolean expectExists, Optional<RawSecretEntry> expectedRawSecretEntry) { // Create item key. Map<String, AttributeValue> key = new HashMap<>(); key.put(KEY_ATTRIBUTE_NAME.toString(), new AttributeValue().withS(rawSecretEntry.secretIdentifier.name)); key.put(VERSION_ATTRIBUTE_NAME.toString(), new AttributeValue().withN(String.valueOf(rawSecretEntry.version))); // Create item attributes. Map<String, AttributeValueUpdate> attributes = new HashMap<>(); attributes.put(SCHEMA_VERSION_FIELD_NAME, new AttributeValueUpdate() .withAction(AttributeAction.PUT) .withValue(new AttributeValue() .withN(SCHEMA_VERSION))); attributes.put(NOT_BEFORE_ATTRIBUTE_NAME.toString(), new AttributeValueUpdate() .withAction(AttributeAction.PUT) .withValue(new AttributeValue() .withN(FormattedTimestamp.epoch(rawSecretEntry.notBefore.get()).toString()))); attributes.put(STATE_ATTRIBUTE_NAME.toString(), new AttributeValueUpdate() .withAction(AttributeAction.PUT) .withValue(new AttributeValue() .withN(Byte.toString(rawSecretEntry.state.asByte())))); attributes.put(VALUE_ATTRIBUTE_NAME.toString(), new AttributeValueUpdate() .withAction(AttributeAction.PUT) .withValue(new AttributeValue() .withS(Encoder.base64encode(rawSecretEntry.encryptedPayload)))); attributes.put(OPTIMISTIC_LOCKING_ATTRIBUTE_NAME, new AttributeValueUpdate() .withAction(AttributeAction.PUT) .withValue(new AttributeValue() .withS(Encoder.base64encode(rawSecretEntry.sha1OfEncryptionPayload())))); // Create the expected conditions map. Map<String, ExpectedAttributeValue> expected = new HashMap<>(); if (expectExists) { expected.put(KEY_ATTRIBUTE_NAME.toString(), new ExpectedAttributeValue(true).withValue( new AttributeValue(rawSecretEntry.secretIdentifier.name))); expected.put(OPTIMISTIC_LOCKING_ATTRIBUTE_NAME, new ExpectedAttributeValue(true).withValue( new AttributeValue(Encoder.sha1(expectedRawSecretEntry.get().encryptedPayload)))); } else { expected.put(KEY_ATTRIBUTE_NAME.toString(), new ExpectedAttributeValue(false)); } return new UpdateItemRequest(tableName, key, attributes).withExpected(expected); }
@SuppressWarnings("unchecked") protected Map<String, ExpectedAttributeValue> determineUpdateCondition() { return exchange.getIn().getHeader(DdbConstants.UPDATE_CONDITION, Map.class); }
/** * For expected value, they are stored with their attribute type, so parse * them with their value. */ protected ExpectedAttributeValue genExpectedAttributeValue(String value) throws IllegalArgumentException { AttributeValue attributeValue = AttributeValueConverter.parseFromWithAttributeTypeString(value); return new ExpectedAttributeValue().withExists(true).withValue(attributeValue); }
/** * returns 'true' if output file was generated, 'false' otherwise */ public boolean updateFromFile(boolean useConditionalUpdate) throws Exception { String correctionFilePath = options.getCorrectionInputPath(); PrintHelper.printUpdateStartInfo(correctionFilePath); if (options.isCorrectionInputS3Path()) { correctionFilePath = options.getTmpCorrectionInputPath(); } loadRecordsFromCorrectionFile(correctionFilePath); checkUseConditionalUpdate(useConditionalUpdate); // Used to create correction output file only if an error occurs boolean isCorrectionOutputFileGenerated = false; try { while (correctionReader.moveToNextRecordIfHas()) { try { Map<String, AttributeValue> primaryKey = genTablePrimaryKeyForRecord(); Map<String, AttributeValueUpdate> updateItems = genUpdateItemsForRecord(); Map<String, ExpectedAttributeValue> expectedItems = null; if (useConditionalUpdate) { expectedItems = genExpectedItemsForRecord(updateItems); } if(tableWriter.sendUpdateRequest(primaryKey, updateItems, expectedItems)) { successfulUpdates++; } } catch(Exception e) { if(e instanceof ConditionalCheckFailedException) { conditionalUpdateFailures++; } else { unexpectedErrors++; } // generate output file if it does not exist if(!isCorrectionOutputFileGenerated) { createViolationWriter(); isCorrectionOutputFileGenerated = true; // Add header to the output file List<String> correctionOutputHeader = new ArrayList<String>(correctionReader.getHeader()); correctionOutputHeader.add(ViolationRecord.GSI_VALUE_UPDATE_ERROR); // Add another column for error ViolationWriter.getInstance().addViolationRecord(correctionOutputHeader); } List<String> failedRecord = new ArrayList<String>(correctionReader.getCurrentRecord()); // Add error to the record failedRecord.add(correctionReader.getHeader().size(), e.getMessage()); ViolationWriter.getInstance().addViolationRecord(failedRecord); } } } finally { // close the file if(isCorrectionOutputFileGenerated) { ViolationWriter.getInstance().flushAndCloseWriter(); } } if(useConditionalUpdate) { PrintHelper.printCorrectionSummary(violationUpdateRequests, successfulUpdates, conditionalUpdateFailures, unexpectedErrors, options.getCorrectionOutputPath()); } else { PrintHelper.printCorrectionSummary(violationUpdateRequests, successfulUpdates, unexpectedErrors, options.getCorrectionOutputPath()); } if(conditionalUpdateFailures > 0 || unexpectedErrors > 0) { return true; } return false; }
public UpdateItemResult updateConditionalValue(final AmazonDynamoDB dynamoClient, final String tableName, final UpdateKey key, final String attribute, final AggregateAttributeModification update) throws Exception { Map<String, AttributeValue> updateKey = StreamAggregatorUtils.getTableKey(key); UpdateItemResult result; final ReturnValue returnValue = ReturnValue.UPDATED_NEW; final String setAttribute = StreamAggregatorUtils.methodToColumn(attribute); // create the update that we want to write final Map<String, AttributeValueUpdate> thisCalcUpdate = new HashMap<String, AttributeValueUpdate>() { { put(setAttribute, new AttributeValueUpdate().withAction(AttributeAction.PUT).withValue( new AttributeValue().withN("" + update.getFinalValue()))); } }; // create the request UpdateItemRequest req = new UpdateItemRequest().withTableName(tableName).withKey(updateKey).withReturnValues( returnValue).withAttributeUpdates(thisCalcUpdate); Map<String, ExpectedAttributeValue> expected = new HashMap<>(); final SummaryCalculation calc = update.getCalculationApplied(); // try an update to PUT the value if NOT EXISTS, to establish if we // are the first writer for this key expected = new HashMap<String, ExpectedAttributeValue>() { { put(setAttribute, new ExpectedAttributeValue().withExists(false)); } }; req.setExpected(expected); try { result = DynamoUtils.updateWithRetries(dynamoClient, req); // yay - we were the first writer, so our value was written return result; } catch (ConditionalCheckFailedException e1) { // set the expected to the comparison contained in the update // calculation expected.clear(); expected.put( setAttribute, new ExpectedAttributeValue().withComparisonOperator( calc.getDynamoComparisonOperator()).withValue( new AttributeValue().withN("" + update.getFinalValue()))); req.setExpected(expected); // do the conditional update on the summary // calculation. this may result in no update being // applied because the new value is greater than the // current minimum for MIN, or less than the current // maximum for MAX. try { result = DynamoUtils.updateWithRetries(dynamoClient, req); return result; } catch (ConditionalCheckFailedException e2) { // no worries - we just weren't the min or max! return null; } } }
@Override public <T extends Message> boolean update(T item, Modifier... modifiers) throws DataStoreException { DynamoClassMapping<T> tableInfo = getClassMapping(item); log.debug("Update {} {} [{}]", item.getClass().getSimpleName(), item, modifiers); UpdateItemRequest request = new UpdateItemRequest(); request.setTableName(tableInfo.getDynamoTableName()); request.setKey(tableInfo.buildCompleteKey(item)); Map<String, ExpectedAttributeValue> expected = Maps.newHashMap(); expected.put(FIELD_HASH_KEY, new ExpectedAttributeValue().withComparisonOperator(ComparisonOperator.NOT_NULL)); for (Modifier modifier : modifiers) { if (modifier instanceof WhereModifier) { WhereModifier where = (WhereModifier) modifier; Map<FieldDescriptor, Object> matcherFields = where.getMatcher().getAllFields(); for (Map.Entry<FieldDescriptor, Object> matcherField : matcherFields.entrySet()) { FieldDescriptor fieldDescriptor = matcherField.getKey(); Object fieldValue = matcherField.getValue(); tableInfo.addFilter(expected, fieldDescriptor, fieldValue); } } else { throw new UnsupportedOperationException(); } } Map<String, AttributeValueUpdate> attributeUpdates = tableInfo.mapToUpdate(item); request.setAttributeUpdates(attributeUpdates); request.setExpected(expected); if (expected.size() > 1) { request.setConditionalOperator(ConditionalOperator.AND); } try { UpdateItemResult response = dynamoDB.updateItem(request); return true; } catch (ConditionalCheckFailedException e) { log.debug("Update failed (conditional check failed)"); return false; } }
/*** * Conditionally Save the items if the expected attributes are found to be true * * @param item * @param conditional * @param expectedAttributes */ public <T> void saveIf(final T item, ConditionalOperator conditional, final Map<String, ExpectedAttributeValue> expectedAttributes){ final DynamoDBSaveExpression expression = new DynamoDBSaveExpression() .withConditionalOperator(conditional) .withExpected(expectedAttributes); this.mapper.save(item, expression); }
/*** * Construct an expected attribute value * * @param operator * @param value * @param exists * * @return expectedAttributeValue */ public static ExpectedAttributeValue expected(final ComparisonOperator operator, final String value, final boolean exists){ return new ExpectedAttributeValue() .withComparisonOperator(operator) .withExists(exists) .withValue(new AttributeValue(value)); }
/*** * Construct an expected attribute value * * @param operator * @param values * @param exists * @return expectedAttributeValue */ public static ExpectedAttributeValue expected(final ComparisonOperator operator, final List<String> values, final boolean exists){ return new ExpectedAttributeValue() .withComparisonOperator(operator) .withExists(exists) .withValue(new AttributeValue(values)); }