以下の内容はhttps://www.getto.systems/entry/2019/08/22/021817より取得しました。


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




以上の内容はhttps://www.getto.systems/entry/2019/08/22/021817より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14