[AWS][Aurora]テーブルにレコードが挿入された際にLambdaを呼び出す方法

##はじめに
"Aurora DBのテーブルにレコードが挿入されたらLambda関数を呼び出す"という方法に関して情報が少なかったため、備忘録として残しておきます。

##参考
[Amazon Aurora MySQL DB クラスターからの Lambda 関数の呼び出し](https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/AuroraMySQL.Integrating.Lambda.html)
[AuroraへのInsert TriggerでLambdaを呼びだしてServerlessで処理する](https://qiita.com/ukitiyan/items/bdd25946bbbf7a70903e)
[ストアドプロシージャの基本的ななにか](https://qiita.com/setsuna82001/items/e742338eb93e3a48ba46)



##何がしたかったか
以下の処理を実現させるために、AuroraのInsert Triggerを利用してLambdaを呼び出す実装を行いました。

①DBのテーブルAにレコードが挿入されたら、そのレコードのidをAPIに渡す。
②レコードの内容を使ってAPIがある処理を行い、レスポンスを返す。
③レスポンスに含まれるデータをテーブルBに挿入する。



![f523a897de8061af74e3dcf14ab07f23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151361/1d38d7e6-2eb1-233d-90fb-eb8017dc014c.png)




##実装手順
DBエンジンにAuroraを使用したデータベースが構築されていることに加え、RDSとLambdaのIAM RoleおよびLambda関数が作成されていることを前提とします。作成手順が不明な方は[こちらの記事](https://qiita.com/ukitiyan/items/bdd25946bbbf7a70903e)を参照。

###1.プロシージャを作成
プロシージャとはデータベース上で行う一連の処理(複数のSQL文)を関数として保存することで、それらの処理をいつでも呼び出せるようにしたものです。一般的な関数と違って戻り値はありませんが、引数を渡すことはできます。

ここでは「test_procedure」という名前のプロシージャを作成します。

```sql
DROP PROCEDURE IF EXISTS test_procedure;
DELIMITER ;;
CREATE PROCEDURE test_procedure (IN id INT) LANGUAGE SQL 
BEGIN
  CALL mysql.lambda_async('arn:aws:lambda:ap-northeast-1:9xxxxxxxxxx0:function:test_lambda_function',  
    CONCAT('{ "target_id" : "', id, '" }')
     );
END
;;
DELIMITER ;
```

以下、詳しく解説します。
まずは「test_procedure」という名前のプロシージャが既に存在しないかチェックし、存在する場合は削除します。

```sql
DROP PROCEDURE IF EXISTS test_procedure;
```

次にDELIMITER を使って, 区切り文字を一時的に変更します。なぜ変更するのかというとSQL文は通常はセミコロン(;)が処理の区切りとされますが、もし複数の処理を定義したい場合はこれでは都合が悪くなってしまいます。そのため、DELIMITER を使って区切り文字を一時的に変更し、後述する`BEGIN`〜`END`内に複数の処理を定義します。

```sql
DELIMITER ;;
```

これでセミコロン２つ`;;`が区切り文字として扱われます。

`CREATE PROCEDURE`でプロシージャを作成します。`(IN id INT)`は引数を意味します。ここではint型のidを引数にするという定義になります。もし引数が複数ある場合は`(IN 〜, IN 〜)`という形にします。

```sql
CREATE PROCEDURE test_procedure (IN id INT) LANGUAGE SQL 
```

`BEGIN`〜`END`内に行いたい処理(SQL文)を定義します。

```sql
BEGIN
  CALL mysql.lambda_async('arn:aws:lambda:ap-northeast-1:9xxxxxxxxxx0:function:test_lambda_function',  
    CONCAT('{ "target_id" : "', id, '" }')
     );
END
;;
DELIMITER ;
```

`CALL`によってプロシージャを呼び出します。ここで呼び出している`mysql.lambda_async`はAurora上でデフォルトで定義されているプロシージャです。引数に指定のLambda関数のリソースネーム(ARN)を渡すことで、その関数を呼び出す処理を行ってくれます。また、`CONCAT()`で Lambda関数に渡すパラメータを指定（上記の場合は、挿入されたレコードのID）することができます。 

このパラメータはLambda関数で呼び出す際は
`lambda_handler(event, context)`
の`event`に格納されているので `event['target_id']` みたいな感じで呼び出します。

なお、ここでは１つのSQL文しか書いていませんが、`;`区切りで複数のSQL文を定義できます。

```sql
BEGIN
  SQL文１;
  SQL文２;
END
```
最後に先ほど定義した区切り文字`;;`を入れてプロシージャの定義を完了させ、`DELIMITER ;`で本来の区切り文字に戻します。

```
;;
DELIMITER ;
```



###2.トリガーを設定
`CREATE TRIGGER`によって、テーブル(table_a)にレコードが挿入された際に上記で作成したプロシージャ(test_procedure)を呼び出す設定を行います。


```sql
DELIMITER ;;
CREATE TRIGGER trigger_test_procedure
  AFTER INSERT ON table_a
  FOR EACH ROW
BEGIN
  CALL test_procedure(NEW.id);
END
;;
DELIMITER ;
```
`AFTER INSERT ON table_a`でプロシージャの実行タイミングを定義しています。もし`BEFORE INSERT ON table_a`とした場合は、テーブルAにレコードが挿入される前にプロシージャが実行されます。

プロシージャの引数にある`NEW`とは、挿入されたレコードを指します。つまり、`NEW.id`とは挿入されたレコードのidを意味します。

###3.動作確認
あとはテーブルにレコードを挿入し、Lambdaの実行が確認できれば完了です。