教程:DynamoDB 事务解析器 - Amazon AppSync
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

教程:DynamoDB 事务解析器

Amazon AppSync 支持对单个区域中的一个或多个表执行 Amazon DynamoDB 事务操作。支持的操作为 TransactGetItemsTransactWriteItems。通过在 Amazon AppSync 中使用这些功能,您可以执行如下任务:

  • 在单个查询中传递键列表,并从表中返回结果

  • 在单个查询中从一个或多个表中读取记录

  • 以全有或全无方式将事务中的记录写入到一个或多个表

  • 在满足某些条件时运行事务

权限

与其他解析器一样,您需要在 Amazon AppSync 中创建数据源,并创建一个角色或使用现有的角色。由于事务操作需要具有 DynamoDB 表的不同权限,因此,您需要为配置的角色授予读取或写入操作权限:

{ "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/*" ] } ] }
注意

角色与 Amazon AppSync 中的数据源相关联,并对数据源调用字段上的解析器。配置为针对 DynamoDB 获取的数据源仅指定一个表,以使配置保持简单。因此,当在单个解析器中针对多个表执行事务操作(这是一项更高级的任务)时,您必须向该数据源的角色授予对将与解析器进行交互的任何表的访问权限。这项操作将在上面 IAM 策略中的 Resource (资源) 字段中执行。针对表的事务调用的配置是在解析器代码中完成的,我们将在下面进行介绍。

数据源

为简便起见,我们将为本教程中使用的所有解析器使用同一个数据源。

我们具有两个名为 savingAccountscheckingAccounts 的表,它们将 accountNumber 作为分区键,还有一个 transactionHistory 表,它将 transactionId 作为分区键。您可以使用下面的 CLI 命令创建表。确保将 region 替换为您的区域。

使用 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

在 Amazon AppSync 控制台的数据源中,创建一个新的 DynamoDB 数据源,并将其命名为 TransactTutorial。选择 savingAccounts 以作为表(尽管在使用事务时具体的表并不重要)。选择创建新角色和数据源。您可以检查数据源配置以查看生成的角色的名称。在 IAM 控制台中,您可以添加一个允许数据源与所有表交互的内联策略。

regionaccountID 替换为您的区域和账户 ID:

{ "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/*" ] } ] }

事务

对于本示例,上下文是一个典型的银行交易,我们将使用 TransactWriteItems 来执行以下操作:

  • 从储蓄账户转账到支票账户

  • 为每个交易生成新的交易记录

然后我们将使用 TransactGetItems 从储蓄账户和支票账户中检索详细信息。

我们按如下所示定义我们的 GraphQL 架构:

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 - 填充账户

为了在账户之间转账,我们需要用详细信息填充表格。我们将使用 GraphQL 操作 Mutation.populateAccounts 来实现此目的。

在“架构”部分中,单击 Mutation.populateAccounts 操作旁边的附加。选择 TransactTutorial 数据源,然后选择创建

现在使用以下代码:

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

保存解析器,并导航到 Amazon AppSync 控制台的查询部分以填充账户。

执行以下变更:

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

我们在一个变更中填充了三个储蓄账户和三个支票账户。

使用 DynamoDB 控制台验证是否在 savingAccountscheckingAccounts 表中显示数据。

TransactWriteItems - 转账

使用以下代码将一个解析器附加到 transferMoney 变更。对于每笔转账,我们都需要支票账户和储蓄账户具有成功修饰符,并且需要跟踪交易中的转账。

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

现在,导航到 Amazon AppSync 控制台的查询部分并执行 transferMoney 变更,如下所示:

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

我们在一个变更中发送了三笔银行交易。使用 DynamoDB 控制台验证是否在 savingAccountscheckingAccountstransactionHistory 表中显示数据。

TransactGetItems - 检索账户

为了在单个事务请求中从储蓄和支票账户中检索详细信息,我们将一个解析器附加到架构上的 Query.getAccounts GraphQL 操作。选择附加,选择在教程开始时创建的相同 TransactTutorial 数据源。使用以下代码:

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

保存解析器,并导航到 Amazon AppSync 控制台的查询部分。为了检索储蓄和支票账户,执行以下查询:

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

我们已成功说明了如何通过 Amazon AppSync 使用 DynamoDB 事务。