Exemplo de transações do DynamoDB - Amazon DynamoDB

Exemplo de transações do DynamoDB

Como exemplo de uma situação em que as Amazon DynamoDB Transactions podem ser úteis, considere este exemplo de aplicação Java para um marketplace online.

A aplicação tem três tabelas do DynamoDB no backend:

  • Customers: esta tabela armazena detalhes sobre os clientes do marketplace. Sua chave primária é um identificador exclusivo CustomerId.

  • ProductCatalog: esta tabela armazena detalhes como preço e disponibilidade sobre os produtos para venda no mercado. Sua chave primária é um identificador exclusivo ProductId.

  • Orders: esta tabela armazena detalhes sobre os pedidos do marketplace. Sua chave primária é um identificador exclusivo OrderId.

Fazer um pedido

Os trechos de código a seguir ilustram como usar transações do DynamoDB para coordenar as várias etapas necessárias para criar e processar um pedido. O uso de uma única operação de tudo ou nada garante que, se qualquer parte da transação falhar, nenhuma ação na transação será executada e nenhuma alteração será feita.

Neste exemplo, você configura um pedido de um cliente cujo customerId é 09e8e9c8-ec48. Em seguida, execute-o como uma única transação usando o seguinte fluxo de trabalho simples de processamento de pedidos:

  1. Determine se o ID do cliente é válido.

  2. Verifique se o produto é IN_STOCK e atualize o status do produto para SOLD.

  3. Certifique-se de que o pedido ainda não exista e crie-o.

Validar o cliente

Primeiro, defina uma ação para verificar se um cliente com customerId igual a 09e8e9c8-ec48 existe na tabela de clientes.

final String CUSTOMER_TABLE_NAME = "Customers"; final String CUSTOMER_PARTITION_KEY = "CustomerId"; final String customerId = "09e8e9c8-ec48"; final HashMap<String, AttributeValue> customerItemKey = new HashMap<>(); customerItemKey.put(CUSTOMER_PARTITION_KEY, new AttributeValue(customerId)); ConditionCheck checkCustomerValid = new ConditionCheck() .withTableName(CUSTOMER_TABLE_NAME) .withKey(customerItemKey) .withConditionExpression("attribute_exists(" + CUSTOMER_PARTITION_KEY + ")");

Atualizar o status do produto

Em seguida, defina uma ação para atualizar o status do produto para SOLD se a condição do status atual do produto IN_STOCK for true. Configurar a opção ReturnValuesOnConditionCheckFailure retornará o item se o atributo de status do produto do item não for igual a IN_STOCK.

final String PRODUCT_TABLE_NAME = "ProductCatalog"; final String PRODUCT_PARTITION_KEY = "ProductId"; HashMap<String, AttributeValue> productItemKey = new HashMap<>(); productItemKey.put(PRODUCT_PARTITION_KEY, new AttributeValue(productKey)); Map<String, AttributeValue> expressionAttributeValues = new HashMap<>(); expressionAttributeValues.put(":new_status", new AttributeValue("SOLD")); expressionAttributeValues.put(":expected_status", new AttributeValue("IN_STOCK")); Update markItemSold = new Update() .withTableName(PRODUCT_TABLE_NAME) .withKey(productItemKey) .withUpdateExpression("SET ProductStatus = :new_status") .withExpressionAttributeValues(expressionAttributeValues) .withConditionExpression("ProductStatus = :expected_status") .withReturnValuesOnConditionCheckFailure(ReturnValuesOnConditionCheckFailure.ALL_OLD);

Criar o pedido

Por último, crie o pedido, desde que um pedido com esse OrderId ainda não exista.

final String ORDER_PARTITION_KEY = "OrderId"; final String ORDER_TABLE_NAME = "Orders"; HashMap<String, AttributeValue> orderItem = new HashMap<>(); orderItem.put(ORDER_PARTITION_KEY, new AttributeValue(orderId)); orderItem.put(PRODUCT_PARTITION_KEY, new AttributeValue(productKey)); orderItem.put(CUSTOMER_PARTITION_KEY, new AttributeValue(customerId)); orderItem.put("OrderStatus", new AttributeValue("CONFIRMED")); orderItem.put("OrderTotal", new AttributeValue("100")); Put createOrder = new Put() .withTableName(ORDER_TABLE_NAME) .withItem(orderItem) .withReturnValuesOnConditionCheckFailure(ReturnValuesOnConditionCheckFailure.ALL_OLD) .withConditionExpression("attribute_not_exists(" + ORDER_PARTITION_KEY + ")");

Executar a transação

O exemplo a seguir ilustra como executar as ações definidas anteriormente como uma única operação tudo ou nada.

Collection<TransactWriteItem> actions = Arrays.asList( new TransactWriteItem().withConditionCheck(checkCustomerValid), new TransactWriteItem().withUpdate(markItemSold), new TransactWriteItem().withPut(createOrder)); TransactWriteItemsRequest placeOrderTransaction = new TransactWriteItemsRequest() .withTransactItems(actions) .withReturnConsumedCapacity(ReturnConsumedCapacity.TOTAL); // Run the transaction and process the result. try { client.transactWriteItems(placeOrderTransaction); System.out.println("Transaction Successful"); } catch (ResourceNotFoundException rnf) { System.err.println("One of the table involved in the transaction is not found" + rnf.getMessage()); } catch (InternalServerErrorException ise) { System.err.println("Internal Server Error" + ise.getMessage()); } catch (TransactionCanceledException tce) { System.out.println("Transaction Canceled " + tce.getMessage()); }

Ler os detalhes do pedido

O exemplo a seguir mostra como ler a ordem concluída transacionalmente entre as tabelas Orders e ProductCatalog.

HashMap<String, AttributeValue> productItemKey = new HashMap<>(); productItemKey.put(PRODUCT_PARTITION_KEY, new AttributeValue(productKey)); HashMap<String, AttributeValue> orderKey = new HashMap<>(); orderKey.put(ORDER_PARTITION_KEY, new AttributeValue(orderId)); Get readProductSold = new Get() .withTableName(PRODUCT_TABLE_NAME) .withKey(productItemKey); Get readCreatedOrder = new Get() .withTableName(ORDER_TABLE_NAME) .withKey(orderKey); Collection<TransactGetItem> getActions = Arrays.asList( new TransactGetItem().withGet(readProductSold), new TransactGetItem().withGet(readCreatedOrder)); TransactGetItemsRequest readCompletedOrder = new TransactGetItemsRequest() .withTransactItems(getActions) .withReturnConsumedCapacity(ReturnConsumedCapacity.TOTAL); // Run the transaction and process the result. try { TransactGetItemsResult result = client.transactGetItems(readCompletedOrder); System.out.println(result.getResponses()); } catch (ResourceNotFoundException rnf) { System.err.println("One of the table involved in the transaction is not found" + rnf.getMessage()); } catch (InternalServerErrorException ise) { System.err.println("Internal Server Error" + ise.getMessage()); } catch (TransactionCanceledException tce) { System.err.println("Transaction Canceled" + tce.getMessage()); }