Ejemplo de transacciones en DynamoDB - Amazon DynamoDB

Ejemplo de transacciones en DynamoDB

Como ejemplo de una situación en la que Amazon DynamoDB Transactions pueden ser útiles, considere esta aplicación Java de ejemplo para un mercado en línea.

La aplicación tiene tres tablas de DynamoDB en el backend:

  • Customers: en esta tabla se almacenan detalles sobre los clientes del mercado. Su clave principal es un identificador único CustomerId.

  • ProductCatalog: en esta tabla se almacenan detalles tales como el precio y la disponibilidad de los productos que se venden en el mercado. Su clave principal es un identificador único ProductId.

  • Orders: en esta tabla se almacenan detalles sobre los pedidos del mercado. Su clave principal es un identificador único OrderId.

Hacer un pedido

Los siguientes fragmentos de código ilustran cómo utilizar las transacciones de DynamoDB para coordinar los múltiples pasos necesarios para crear y procesar un pedido. El uso de una única operación todo o nada garantiza que si falla alguna parte de la transacción, no se ejecuten acciones en la transacción y no se realicen cambios.

En este ejemplo, configura un pedido de un cliente cuyo customerId es 09e8e9c8-ec48. A continuación, se ejecuta como una única transacción mediante el siguiente flujo de trabajo de procesamiento de pedidos:

  1. Determine que el ID del cliente sea válido.

  2. Asegúrese de que el producto sea IN_STOCK y actualice el estado del producto a SOLD.

  3. Asegúrese de que el pedido aún no existe y cree el pedido.

Validar al cliente

Primero, defina una acción para verificar que un cliente con customerId igual a 09e8e9c8-ec48 existe en la tabla 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 + ")");

Actualizar el estado del producto

A continuación, defina una acción para actualizar el estado del producto a SOLD si la condición en la que el estado del producto está establecido actualmente en IN_STOCK es true. Configuración del parámetro ReturnValuesOnConditionCheckFailure devuelve el elemento si el atributo de estado del producto del artículo no era 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);

Crear el pedido

Por último, cree el pedido siempre y cuando un pedido con ese OrderId no existe.

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 + ")");

Ejecutar la transacción

En el siguiente ejemplo de la se muestra cómo ejecutar las acciones definidas anteriormente como una única operación de tipo “todo o 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()); }

Leer los detalles del pedido

En el siguiente ejemplo se muestra cómo leer el pedido completado transaccionalmente a través de las tablas Orders y 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()); }