げっとシステムログ

WEB開発メモ

AWS Lambda から DynamoDB にアクセスする

Slack Bot でデプロイする」で、1回の発言に対して複数回リクエストが来た。 処理は1回にしたいので、処理済みの発言を DynamoDB に登録しておく。

CONTENTS
  1. DynamoDB で二重処理を防ぐ
  2. Lambda からアクセスする
  3. テーブルを作成する
  4. 項目を追加する
  5. 項目を取得する
  6. まとめ
  7. 参考資料
ENVIRONMENTS
  • Node.js : 10.16.0

DynamoDB で二重処理を防ぐ

Slack Bot でデプロイする」で、1回の発言に対して複数回リクエストが来た。 以下の手順で DynamoDB に項目を登録し、複数回処理が走らないようにする。

  • team, channel, timestamp をプライマリキーとして登録
  • progress_id に uuid を生成して登録
  • 生成した uuid で検索することで登録完了を確認

複数回のリクエストについては詳しく調べていないが、おそらく、Lambda のレスポンスが 200 ではなかった時に Slack がリクエストを再送しているのだろう。 そっちを対応するのが本筋の気はする。

TOP

Lambda からアクセスする

今回必要となるポリシーは以下の通り。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "dynamodb:PutItem",
        "dynamodb:Query"
      ],
      "Resource": [
        "arn:aws:dynamodb:REGION:ACCOUNT:table/TABLE_NAME"
      ]
    }
  ]
}

REGIONACCOUNTTABLE_NAME を適当な値に設定する。

TOP

テーブルを作成する

今回はテーブルの作成は AWS コンソールからぽちぽちすることにした。 「テーブルの作成」から作成すれば良い。

キャパシティモードはオンデマンドで作成した。 Slack Bot への会話は現在のところリリースだけなので、それほどのコストにはならないだろうと判断した。 ただ、プロビジョンドの方がコストを削減できそうなので、月々のコストをみて調べることにする。

ここでは sample-table をパーティションキー team、ソートキー conversation で作成したことにして進める。

TOP

項目を追加する

以下のコードを Lambda で実行すると、sample-table に項目が追加される。

const AWS = require("aws-sdk");
const uuid = require("uuid/v4"); // npm install --save uuid

const client = new AWS.DynamoDB.DocumentClient({
  region: "us-east-1",
});

const table = "sample-table";

const team = "TEAM";
const conversation = "CHANNEL:TIMESTAMP";
const progress_id = uuid();

const params = {
  TableName: table,
  Item: {
    team,
    conversation,
    progress_id,
  }
  ConditionExpression: "attribute_not_exists(team) and attribute_not_exists(conversation)",
};

client.put(params, (err, data) => {
  if (err) {
    console.error("PUT Error JSON:", JSON.stringify(err, null, 2));
  } else {
    console.log("put item:", JSON.stringify(data, null, 2));
  }
});

各項目については以下の通り。

  • team : 発言した channel の team
  • conversation : 発言の channel と timestamp を繋げたもの
  • uuid : 処理ごとに生成される uuid

ConditionExpressionattribute_not_exists を指定すると、重複した put でエラーになる。 しかし、これだけでは重複を防げなかった。 同時に put すると例外が発生しないこともあるようだ。

詳しく追えていないが、ここでは uuid を生成して登録することで重複処理を防ぐことにする。

TOP

項目を取得する

以下のコードを Lambda で実行すると、sample-table から項目を取得できる。

const AWS = require("aws-sdk");

const client = new AWS.DynamoDB.DocumentClient({
  region: "us-east-1",
});

const table = "sample-table";

const team = "TEAM";
const conversation = "CHANNEL:TIMESTAMP";
const uuid = "UUID";

const params = {
  TableName,
  KeyConditionExpression: "team = :team and conversation = :conversation",
  FilterExpression: "progress_id = :progress_id",
  ExpressionAttributeValues: {
    ":team": team,
    ":conversation": conversation,
    ":progress_id": progress_id,
  },
  ProjectionExpression: "progress_id",
};

client.put(params, (err, data) => {
  if (err) {
    console.error("QUERY Error JSON:", JSON.stringify(err, null, 2));
  } else {
    console.log("query result", JSON.stringify(data, null, 2));
  }
});

テーブルに存在する teamconversationprogress_id で検索すると、結果を取得できる。

  • KeyConditionExpression : プライマリキーの条件を指定
  • FilterExpression : 追加の絞り込み
  • ProjectionExpression : 取得する項目の指定

progress_id は処理ごとに生成される uuid なので、これで項目が取得できた場合は正しく put できている。 この場合に限って処理を行うことで重複処理を防ぐ。

Slack からのリクエストは同時に 3件程度なので、uuid がぶつかる可能性はほぼ 0 だと考えて良い。

ドキュメントには、put してから query できるまでに時間がかかる、と書いてある。 今回試したところ必要なかったが、多少待つか、何回か query しないといけない可能性はある。

TOP

まとめ

DynamoDB で重複処理を防いでみた。 とりあえず、リリースの処理が連続で走ってしまっていたのを防ぐことができている。

今後、コストがどの程度かかるのかを追跡したい。

TOP

参考資料

TOP