Realizar transacciones de DynamoDB en AWS AppSync - AWS AppSync

Las traducciones son generadas a través de traducción automática. En caso de conflicto entre la traducción y la version original de inglés, prevalecerá la version en inglés.

Realizar transacciones de DynamoDB en AWS AppSync

AWS AppSync admite el uso de operaciones de transacciones de Amazon DynamoDB en una o más tablas de una sola región. Las operaciones admitidas son TransactGetItems y TransactWriteItems. Al utilizar estas funciones en AWS AppSync, puede realizar tareas como las siguientes:

  • Transferir una lista de claves en una sola consulta y devolver los resultados desde una tabla

  • Leer registros desde una o varias tablas en una única consulta

  • Escribir los registros de las transacciones en una o más tablas de all-or-nothing alguna manera

  • Ejecutar transacciones cuando se cumplan algunas condiciones

Permisos

Al igual que otros solucionadores, debe crear una fuente de datos AWS AppSync y crear un rol o usar uno existente. Dado que las operaciones de transacciones requieren diferentes permisos para las tablas de DynamoDB, debe conceder los permisos de rol configurados para las acciones de lectura o escritura:

{ "Version": "2012-10-17", "Statement": [ { "Action": [ "dynamodb:DeleteItem", "dynamodb:GetItem", "dynamodb:PutItem", "dynamodb:Query", "dynamodb:Scan", "dynamodb:UpdateItem" ], "Effect": "Allow", "Resource": [ "arn:aws:dynamodb:region:accountId:table/TABLENAME", "arn:aws:dynamodb:region:accountId:table/TABLENAME/*" ] } ] }
nota

Los roles están vinculados a las fuentes de datos en AWS AppSync una fuente de datos y los resolutores de los campos se invocan en función de una fuente de datos. Los orígenes de datos configurados para recuperar información de DynamoDB solo tienen especificada una tabla para que la configuración siga siendo sencilla. Por lo tanto, al realizar una operación de transacciones en varias tablas con un único solucionador, que es una tarea más avanzada, debe conceder al rol de ese origen de datos acceso a cualquier tabla con la que el solucionador vaya a interactuar. Esto se haría en el campo Recursos de la IAM política anterior. La configuración de las tablas en las que se realizan llamadas de transacciones se lleva a cabo en el código del solucionador, que se describe a continuación.

Origen de datos

En aras de la simplicidad, vamos a utilizar el mismo origen de datos para todos los solucionadores que se utilizan en este tutorial.

Tendremos dos tablas llamadas savingAccountsy checkingAccounts, ambas con accountNumber como clave de partición y una transactionHistorytabla con transactionId como clave de partición. Puedes usar los siguientes CLI comandos para crear tus tablas. Asegúrese de reemplazar region por la región.

Con el CLI

aws dynamodb create-table --table-name savingAccounts \ --attribute-definitions AttributeName=accountNumber,AttributeType=S \ --key-schema AttributeName=accountNumber,KeyType=HASH \ --provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5 \ --table-class STANDARD --region region aws dynamodb create-table --table-name checkingAccounts \ --attribute-definitions AttributeName=accountNumber,AttributeType=S \ --key-schema AttributeName=accountNumber,KeyType=HASH \ --provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5 \ --table-class STANDARD --region region aws dynamodb create-table --table-name transactionHistory \ --attribute-definitions AttributeName=transactionId,AttributeType=S \ --key-schema AttributeName=transactionId,KeyType=HASH \ --provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5 \ --table-class STANDARD --region region

En la AWS AppSync consola, en Fuentes de datos, cree una nueva fuente de datos de DynamoDB y asígnele un nombre. TransactTutorial Seleccione savingAccountscomo tabla (aunque la tabla específica no importa cuando se utilizan transacciones). Elija crear un nuevo rol y el origen de datos. Puede revisar la configuración del origen de datos para ver el nombre del rol generado. En la IAM consola, puede añadir una política en línea que permita que la fuente de datos interactúe con todas las tablas.

Sustituya region y accountID por su región y su identificador de cuenta:

{ "Version": "2012-10-17", "Statement": [ { "Action": [ "dynamodb:DeleteItem", "dynamodb:GetItem", "dynamodb:PutItem", "dynamodb:Query", "dynamodb:Scan", "dynamodb:UpdateItem" ], "Effect": "Allow", "Resource": [ "arn:aws:dynamodb:region:accountId:table/savingAccounts", "arn:aws:dynamodb:region:accountId:table/savingAccounts/*", "arn:aws:dynamodb:region:accountId:table/checkingAccounts", "arn:aws:dynamodb:region:accountId:table/checkingAccounts/*", "arn:aws:dynamodb:region:accountId:table/transactionHistory", "arn:aws:dynamodb:region:accountId:table/transactionHistory/*" ] } ] }

Transacciones

Para este ejemplo, el contexto es una transacción bancaria clásica, en la que usaremos TransactWriteItems para:

  • Transferir dinero de cuentas de ahorro a cuentas corrientes

  • Generar nuevos registros de transacciones para cada transacción

Y, a continuación, usaremos TransactGetItems para recuperar los detalles de las cuentas de ahorro y las cuentas corrientes.

aviso

TransactWriteItemsno se admite cuando se utiliza con la detección y resolución de conflictos. Esta configuración debe estar deshabilitada para evitar posibles errores.

Definimos nuestro esquema GraphQL de la siguiente manera:

type SavingAccount { accountNumber: String! username: String balance: Float } type CheckingAccount { accountNumber: String! username: String balance: Float } type TransactionHistory { transactionId: ID! from: String to: String amount: Float } type TransactionResult { savingAccounts: [SavingAccount] checkingAccounts: [CheckingAccount] transactionHistory: [TransactionHistory] } input SavingAccountInput { accountNumber: String! username: String balance: Float } input CheckingAccountInput { accountNumber: String! username: String balance: Float } input TransactionInput { savingAccountNumber: String! checkingAccountNumber: String! amount: Float! } type Query { getAccounts(savingAccountNumbers: [String], checkingAccountNumbers: [String]): TransactionResult } type Mutation { populateAccounts(savingAccounts: [SavingAccountInput], checkingAccounts: [CheckingAccountInput]): TransactionResult transferMoney(transactions: [TransactionInput]): TransactionResult }

TransactWriteItems - Rellenar cuentas

Para transferir dinero entre cuentas, tenemos que rellenar la tabla con los detalles. Para ello, utilizaremos la operación Mutation.populateAccounts de GraphQL.

En la sección Esquema, haga clic en Asociar junto a la operación Mutation.populateAccounts. Elija el origen de datos TransactTutorial y, a continuación, seleccione Crear.

Ahora utilice el siguiente código:

import { util } from '@aws-appsync/utils' export function request(ctx) { const { savingAccounts, checkingAccounts } = ctx.args const savings = savingAccounts.map(({ accountNumber, ...rest }) => { return { table: 'savingAccounts', operation: 'PutItem', key: util.dynamodb.toMapValues({ accountNumber }), attributeValues: util.dynamodb.toMapValues(rest), } }) const checkings = checkingAccounts.map(({ accountNumber, ...rest }) => { return { table: 'checkingAccounts', operation: 'PutItem', key: util.dynamodb.toMapValues({ accountNumber }), attributeValues: util.dynamodb.toMapValues(rest), } }) return { version: '2018-05-29', operation: 'TransactWriteItems', transactItems: [...savings, ...checkings], } } export function response(ctx) { if (ctx.error) { util.error(ctx.error.message, ctx.error.type, null, ctx.result.cancellationReasons) } const { savingAccounts: sInput, checkingAccounts: cInput } = ctx.args const keys = ctx.result.keys const savingAccounts = sInput.map((_, i) => keys[i]) const sLength = sInput.length const checkingAccounts = cInput.map((_, i) => keys[sLength + i]) return { savingAccounts, checkingAccounts } }

Guarde la resolución y vaya a la sección Consultas de la AWS AppSync consola para rellenar las cuentas.

Ejecute la mutación siguiente:

mutation populateAccounts { populateAccounts ( savingAccounts: [ {accountNumber: "1", username: "Tom", balance: 100}, {accountNumber: "2", username: "Amy", balance: 90}, {accountNumber: "3", username: "Lily", balance: 80}, ] checkingAccounts: [ {accountNumber: "1", username: "Tom", balance: 70}, {accountNumber: "2", username: "Amy", balance: 60}, {accountNumber: "3", username: "Lily", balance: 50}, ]) { savingAccounts { accountNumber } checkingAccounts { accountNumber } } }

Hemos rellenado tres cuentas de ahorro y tres cuentas corrientes en una mutación.

Utilice la consola de DynamoDB para validar que los datos se muestran en las tablas y. savingAccountscheckingAccounts

TransactWriteItems - Transfiere dinero

Asocie un solucionador a la mutación transferMoney con el siguiente código. Para cada transferencia, necesitamos un modificador de éxito tanto en la cuenta corriente como en la de ahorros, y debemos hacer un seguimiento de la transferencia en las transacciones.

import { util } from '@aws-appsync/utils' export function request(ctx) { const transactions = ctx.args.transactions const savings = [] const checkings = [] const history = [] transactions.forEach((t) => { const { savingAccountNumber, checkingAccountNumber, amount } = t savings.push({ table: 'savingAccounts', operation: 'UpdateItem', key: util.dynamodb.toMapValues({ accountNumber: savingAccountNumber }), update: { expression: 'SET balance = balance - :amount', expressionValues: util.dynamodb.toMapValues({ ':amount': amount }), }, }) checkings.push({ table: 'checkingAccounts', operation: 'UpdateItem', key: util.dynamodb.toMapValues({ accountNumber: checkingAccountNumber }), update: { expression: 'SET balance = balance + :amount', expressionValues: util.dynamodb.toMapValues({ ':amount': amount }), }, }) history.push({ table: 'transactionHistory', operation: 'PutItem', key: util.dynamodb.toMapValues({ transactionId: util.autoId() }), attributeValues: util.dynamodb.toMapValues({ from: savingAccountNumber, to: checkingAccountNumber, amount, }), }) }) return { version: '2018-05-29', operation: 'TransactWriteItems', transactItems: [...savings, ...checkings, ...history], } } export function response(ctx) { if (ctx.error) { util.error(ctx.error.message, ctx.error.type, null, ctx.result.cancellationReasons) } const tInput = ctx.args.transactions const tLength = tInput.length const keys = ctx.result.keys const savingAccounts = tInput.map((_, i) => keys[tLength * 0 + i]) const checkingAccounts = tInput.map((_, i) => keys[tLength * 1 + i]) const transactionHistory = tInput.map((_, i) => keys[tLength * 2 + i]) return { savingAccounts, checkingAccounts, transactionHistory } }

Ahora, dirígete a la sección de consultas de la AWS AppSync consola y ejecuta la transferMoneymutación de la siguiente manera:

mutation write { transferMoney( transactions: [ {savingAccountNumber: "1", checkingAccountNumber: "1", amount: 7.5}, {savingAccountNumber: "2", checkingAccountNumber: "2", amount: 6.0}, {savingAccountNumber: "3", checkingAccountNumber: "3", amount: 3.3} ]) { savingAccounts { accountNumber } checkingAccounts { accountNumber } transactionHistory { transactionId } } }

Hemos enviado tres transacciones bancarias en una mutación. Utilice la consola de DynamoDB para validar que los datos aparecen en savingAccountslas tablas checkingAccounts, y. transactionHistory

TransactGetItems - Recuperar cuentas

Con el fin de recuperar los detalles de las cuentas de ahorro y corriente en una sola solicitud transaccional, vamos a asociar un solucionador a la operación Query.getAccounts de GraphQL en nuestro esquema. Seleccione Asociar y elija el origen de datos TransactTutorial creado al inicio del tutorial. Utilice el siguiente código:

import { util } from '@aws-appsync/utils' export function request(ctx) { const { savingAccountNumbers, checkingAccountNumbers } = ctx.args const savings = savingAccountNumbers.map((accountNumber) => { return { table: 'savingAccounts', key: util.dynamodb.toMapValues({ accountNumber }) } }) const checkings = checkingAccountNumbers.map((accountNumber) => { return { table: 'checkingAccounts', key: util.dynamodb.toMapValues({ accountNumber }) } }) return { version: '2018-05-29', operation: 'TransactGetItems', transactItems: [...savings, ...checkings], } } export function response(ctx) { if (ctx.error) { util.error(ctx.error.message, ctx.error.type, null, ctx.result.cancellationReasons) } const { savingAccountNumbers: sInput, checkingAccountNumbers: cInput } = ctx.args const items = ctx.result.items const savingAccounts = sInput.map((_, i) => items[i]) const sLength = sInput.length const checkingAccounts = cInput.map((_, i) => items[sLength + i]) return { savingAccounts, checkingAccounts } }

Guarde el solucionador y navegue hasta las secciones de consultas de la AWS AppSync consola. Para recuperar las cuentas de ahorro y corriente, ejecute la siguiente consulta:

query getAccounts { getAccounts( savingAccountNumbers: ["1", "2", "3"], checkingAccountNumbers: ["1", "2"] ) { savingAccounts { accountNumber username balance } checkingAccounts { accountNumber username balance } } }

Hemos demostrado con éxito el uso de transacciones de DynamoDB mediante. AWS AppSync