AuroraへのInsert TriggerでLambdaを呼びだしてServerlessで処理する


はじめに

要件

  • 世界展開を行うECシステムで、ネットワークレイテンシーを解消するために、AWSの各リージョンにフロントエンド機能を設置する。
  • 但し、個人情報は既存のDCに保管するものとして、ECで発生したオーダーデータ等の個人情報についても一定期間以上 AWSのDBに保持してはならない。
  • 尚、今回利用するECパッケージは、オーダーが入ると同一リージョンのオーダーテーブルにデータが登録される。

ソリューション

  • ECは、パッケージ利用想定なので、なるべくイジらないのがセオリー。
  • で、オーダーテーブルに登録されたレコードを普通に移動させれば良いので、以下の構成は普通に思いつくわけです。

  • 上記だと、いかんせんサーバレスじゃないので、以下のような構成が発案されます。

  • しかしながら、上記だと赤丸部分でオーダーと同時にSQSにメッセージ投げるというECパッケージの改修が必要となるのでゲンナリします。(これを通すと先輩の手を煩わせることに...)
  • 結合度も高いので、疎結合なサーバレスアーキテクチャとして、以下のような構成が発案されます。
  • 因みにRDSのエンジンは、良いことしかないのでAuroraにしましょう。

テーブルへのInsert通知をServerlessで処理する

  • 上記を踏まえてテーブルへのInsert通知をServerlessで処理する方法を紹介します。
  • 具体的には、オーダーテーブルへのAfter InsertにTrigger仕込んで、Lambda経由でSQSにメッセージを登録する方法になります。
  • SQSに伝わればその先は幾らでもやりようがあると思います。
  • Triggerは、複雑過ぎると良いことないので、シンプルに使いましょう。

IAM Roleを作成する

  • Lambda用Role
項目 設定内容
ロール名 (任意:[例] LambdaDbTriggerTestRole)
AWS サービスロール AWS Lambda
アタッチするポリシー AmazonSQSFullAccess
  • RDS用Role
項目 設定内容
ロール名 (任意:[例] RdsDbTriggerTestRole)
AWS サービスロール AWS RDS
アタッチするポリシー 無し
※超重要※ 作成後、アクセス許可タブでAWSLamdbaRoleをアタッチ

SQSでQueueを作成する

  • 任意のキュー名でキューを作成します。
  • 例)DbTriggerTestQueue

LambdaでFunctionを作成する

  • RDSからキックされ、SQSにメッセージ送信するLambdaファンクションを作成します。
項目 設定内容
Runtime Python 2.7
Handler lambda_function.lambda_handler
Role 先程作成したLambda用Role
  • コードは以下の内容です。(queueNameは、先程作成したqueueNameを指定してください)
main.py
import boto3
import json
import logging

queueName='DbTriggerTestQueue'

def lambda_handler(event, context):
  try:
    logger.info(event)
    response = boto3.resource('sqs').get_queue_by_name(
      QueueName = queueName
    ).send_message(
      MessageBody = json.dumps(event)
    )
    logger.info(response)
    return response

  except Exception as e:
    logger.error(e)
    raise e

Auroraを起動する

  • 以下の内容で起動しました。
項目 設定内容
リージョン ap-northeast-1
インスタンスクラス db.t2.medium
マルチAZ No
サブネットグループ Default
パブリックアクセス可能 No
Availability Zone ap-northeast-1a
パラメータグループ 新規のDB Parameter Group
新規のDB Cluster Parameter Group

AuroraにIAMロールを設定する

  • クラスターのIAMロールの管理から先程作成したRDS用Roleを紐付けます。

  • 先程作成&Auroraに指定したDB Cluster Parameter Groupのパラメータの編集から、aws_default_lambda_roleの値に、先程作成したRDS用RoleのARNを設定します。
  • 例)arn:aws:iam::xxxxxxxxxx:role/RdsDbTriggerTestRole

  • DBクラスタパラメータグループがpending-rebootになるはずなので、Auroraを再起動します。

Triggerを作成する

  • 再起動したらAuroraに接続して、以下のStored Procedure、Table、Triggerを作成します。

  • Stored Procedure
    • mysql.lambda_asyncの第一引数は、先程作成したLambdaのARNを設定します。
sqs_order_data_ai_message.sql
DROP PROCEDURE IF EXISTS sqs_order_data_ai_message;
DELIMITER ;;
CREATE PROCEDURE sqs_order_data_ai_message (IN id INT(11), 
                                      IN item VARCHAR(50)) LANGUAGE SQL 
BEGIN
  CALL mysql.lambda_async('arn:aws:lambda:ap-northeast-1:xxxxxxxx:function:TestFunction',  
    CONCAT('{ "id" : "', id, 
            '", "item" : "', item, '" }')
     );
END
;;
DELIMITER ;
  • Table
order_data.sql
CREATE TABLE `order_data` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `item` varchar(50) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  • Trigger
trigger_order_data_ai.sql
DELIMITER ;;
CREATE TRIGGER trigger_order_data_ai 
  AFTER INSERT ON order_data 
  FOR EACH ROW
BEGIN
  CALL sqs_order_data_ai_message(NEW.id, NEW.item);
END
;;
DELIMITER ;

動作テスト

  • 以下のSQLを実行してみます。
insert.sql
INSERT INTO order_data VALUES (NULL, 'item_001');
  • 取り急ぎ、SQSの画面で利用可能なメッセージがカウントアップされていれば成功です。

備考

  • エンジンはAurora一択です。MySQLを選んだところで以下のようにmysql.lambda_asyncがコール出来ないです。
MySQL
mysql> show create procedure mysql.lambda_async \G
ERROR 1305 (42000): PROCEDURE lambda_async does not exist

参考