Step FunctionsステートマシンからDynamoDBテーブル上の複数のアイテムをUpdateItemしたい(AWS CDK)

2021.11.15

こんにちは、CX事業本部 IoT事業部の若槻です。

今回は、Step FunctionsステートマシンからDynamoDBテーブル上の複数のアイテムをUpdateItemする方法を確認し、AWS CDKで実装してみました。

PutItemとUpdateItemの違い

本題に入る前に、まずDynamoDBのPutItemとUpdateItemの違いについて確認します。

PutItemUpdateItemは、どちらもDynamoDBテーブル上のアイテムを1つ更新するAPI操作ですが、その更新時の違いは以下のようになります。

説明
PutItem 入力で指定したアイテムで、既存のアイテムを置き換える。
UpdateItem 入力で指定した属性情報で、既存のアイテムの属性情報を更新する。

例えばDynamoDBテーブルに下記の既存アイテムがあるとします。deviceIdがPartition Keyです。

//既存アイテム
{
  "deviceId": "d001",
  "deviceName": "デバイス001",
  "temperature": 25
}

この時以下の入力を指定して、PutItemを行った場合とUpdateItemを行った場合だと、

//入力
{
  "deviceId": "d001",
  "temperature": 8
}

アイテムの更新結果はそれぞれ以下のようになります。PutItemは入力で指定されていない属性は削除されますが、UpdateItemは残されます。

//PutItemの結果
{
  "deviceId": "d001",
  "temperature": 8
}

//UpdateItemの結果
{
  "deviceId": "d001",
  "deviceName": "デバイス001",
  "temperature": 8
}

またいずれのAPI操作も既定では指定のキーのアイテムが存在しない場合はアイテム新規作成が行われます。

BatchWriteItemではUpdateItemができない

一方で、BatchWriteItemはDynamoDBテーブル上のアイテムを複数更新する操作ですが、行える更新操作はPutItemまたはDeleteItemのみです。UpdateItemは行なえません。

ドキュメントにも代わりにUpdateItemを直接使用するように書いてあります。

BatchWriteItem cannot update items. To update items, use the UpdateItem action.

Step FunctionsステートマシンからDynamoDBテーブル上の複数のアイテムをUpdateItemしたい

以上を踏まえると、Step FunctionsステートマシンからDynamoDBテーブル上の複数のアイテムをUpdateItemしたい場合、以下の方法が考えられます。

  • MapステートでUpdateItemを使用する
  • TransactWriteItemsを使用する

TransactWriteItemsではPutItem、UpdateItemまたはDeleteItemの更新操作が可能で、25個までのアイテムであれば一度のAPI操作で更新が可能です。また状態遷移の回数も一度で済みます。そこで今回はTransactWriteItemsで複数アイテムのUpdateを行ってみます。

やってみた

CDKコード

AWS CDKで実装します。DynamoDBテーブルとステートマシンを下記の通り定義します。

lib/aws-cdk-app-stack.ts

import * as cdk from '@aws-cdk/core';
import * as sfn from '@aws-cdk/aws-stepfunctions';
import * as tasks from '@aws-cdk/aws-stepfunctions-tasks';
import * as dynamodb from '@aws-cdk/aws-dynamodb';

export class AwsCdkAppStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const deviceTable = new dynamodb.Table(this, 'deviceTable', {
      tableName: 'deviceTable',
      partitionKey: { name: 'deviceId', type: dynamodb.AttributeType.STRING },
      billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
    });

    const updateDeviceData = new tasks.CallAwsService(
      this,
      'updateDeviceData',
      {
        service: 'dynamodb',
        action: 'transactWriteItems',
        parameters: {
          'TransactItems.$': '$.TransactItems',
        },
        iamResources: [deviceTable.tableArn],
        iamAction: 'dynamodb:*',
      }
    );

    new sfn.StateMachine(this, 'updateDeviceDataStateMachine', {
      stateMachineName: 'updateDeviceDataStateMachine',
      definition: updateDeviceData,
    });
  }
}

cdk deployでデプロイします。

動作

準備としてDynamoDBテーブルに以下のようにデータを作成しておきます。

以下のJsonを入力としてステートマシンを実行してみます。

{
  "TransactItems": [
    {
      "Update": {
        "TableName": "deviceTable",
        "Key": { "deviceId": { "S": "d001" } },
        "ExpressionAttributeValues": {
          ":temperature": { "N": "10" }
        },
        "ExpressionAttributeNames": {
          "#temperature": "temperature"
        },
        "UpdateExpression": "SET #temperature = :temperature"
      }
    },
    {
      "Update": {
        "TableName": "deviceTable",
        "Key": { "deviceId": { "S": "d003" } },
        "ExpressionAttributeValues": {
          ":temperature": { "N": "15" },
          ":deviceName": { "S": "デバイス003" }
        },
        "ExpressionAttributeNames": {
          "#deviceName": "deviceName",
          "#temperature": "temperature"
        },
        "UpdateExpression": "SET #deviceName = :deviceName, #temperature = :temperature"
      }
    }
  ]
}

ステートマシンの実行が成功しました。

テーブル上のデータが指定通りに更新されています。

参考

以上