こんにちは、広野です。
サーバーレスアプリを開発していると、基本データベースは Amazon DynamoDB などの NoSQL を使用するのが王道なのですが、NoSQLでは対応しきれない要件のときにはやはり RDBMS を使用する局面もあります。
そんなとき、サーバーレスの RDBMS サービスである Amazon Aurora Serverless を積極的に使っていきたいのですが、AWS Lambda でデータを取得しようとするとデフォルトのままでは扱いづらい特殊なデータフォーマットになってしまいます。
この度、データフォーマットをシンプルな配列に加工する仕組みを検証しましたので紹介します。
やりたいこと
- Amazon Aurora Serverless から AWS Lambda でデータを取得する。
- データの取得には Data API を使用する。※Data API については以下リンクを参照
- 最終的なデータの取得形式は、配列にしたい。
実装方法
サンプルデータ
DB内には以下のようなテーブルがあるとしましょう。ここでは、このデータを全て取得してくるSQLを使用する想定です。
sampletable
id | name | number (数値型) |
---|---|---|
A001 | いか | 10 |
A002 | たこ | 20 |
B001 | すし | 30 |
加工前
AWS Lambda 関数 (Python)
import boto3 rds_client = boto3.client('rds-data') def lambda_handler(event, context): # Database settings database_name = 'sampledatabase' db_cluster_arn = 'arn:aws:rds:ap-northeast-1:xxxxxxxxxxxx:cluster:samplecluster' db_credentials_secrets_store_arn = 'arn:aws:secretsmanager:ap-northeast-1:xxxxxxxxxxxx:secret:rds-db-credentials/cluster-XXXXXXXXXXXXXXXXXXXXXXXXXX/xxxxxxxxxxxx' # Executing SQL def execute_statement(sql): response = rds_client.execute_statement( secretArn=db_credentials_secrets_store_arn, database=database_name, resourceArn=db_cluster_arn, sql=sql ) return response # SQL実行結果をdataに格納 data = execute_statement('select * from sampletable') # データをそのまま返す return data
結果データ
こんな感じの JSON データが返ってきます。
data[‘records’] にクエリーの結果データが格納されているのですが、データ型によって stringValue や longValue などといったキーが入った JSON データフォーマットになっており、非常に扱いづらいです。また、テーブルの列名は返ってきません。
{ "ResponseMetadata": { "RequestId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "HTTPStatusCode": 200, "HTTPHeaders": { "x-amzn-requestid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "content-type": "application/json", "content-length": "xxx", "date": "Mon, 21 Mar 2022 00:00:00 GMT" }, "RetryAttempts": 0 }, "numberOfRecordsUpdated": 0, "records": [ [ { "stringValue": "A001" }, { "stringValue": "いか" }, { "longValue": 10 } ], [ { "stringValue": "A002" }, { "stringValue": "たこ" }, { "longValue": 20 } ], [ { "stringValue": "B001" }, { "stringValue": "すし" }, { "longValue": 30 } ] ] }
これを、以下で加工します。
加工後
AWS Lambda 関数 (Python)
クエリー結果の data を加工する関数を追加し、return で返す前にフォーマットを変換しています。
import boto3 rds_client = boto3.client('rds-data') def lambda_handler(event, context): # Database settings database_name = 'sampledatabase' db_cluster_arn = 'arn:aws:rds:ap-northeast-1:xxxxxxxxxxxx:cluster:samplecluster' db_credentials_secrets_store_arn = 'arn:aws:secretsmanager:ap-northeast-1:xxxxxxxxxxxx:secret:rds-db-credentials/cluster-XXXXXXXXXXXXXXXXXXXXXXXXXX/xxxxxxxxxxxx' # Formatting query returned Field def formatField(field): return list(field.values())[0] # Formatting query returned Record def formatRecord(record): return [formatField(field) for field in record] # Formatting query returned Field def formatRecords(records): return [formatRecord(record) for record in records] # Executing SQL def execute_statement(sql): response = rds_client.execute_statement( secretArn=db_credentials_secrets_store_arn, database=database_name, resourceArn=db_cluster_arn, sql=sql ) return response # SQL実行結果をdataに格納 data = execute_statement('select * from sampletable') # 配列にフォーマットしたデータを返す return formatRecords(data['records'])
結果データ
スッキリしました!
これなら、アプリ側でも比較的扱いやすいです。連想配列ではないので、アプリで使用するときには列の並び順をきっちり押さえておく必要があります。
[ [ "A001", "いか", 10 ], [ "A002", "たこ", 20 ], [ "B001", "すし", 30 ] ]
IAM ロール
AWS Lambda 関数に付与する IAM ロールは、データを読み書きするのであればマネージドのRDSフルポリシーを付けておけばとりあえず動きますが、用途が明確になっているようでしたら Action や Resource 等をさらに制限しましょう。
マネージドポリシー
- arn:aws:iam::aws:policy/AmazonRDSDataFullAccess
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
まとめ
いかがでしたでしょうか?
エラー処理、ページネーション処理までは言及していないので実運用には物足りない内容かと思いますが、あくまでデータフォーマットの加工 TIPS としてお役に立てれば幸いです。
Amazon Aurora Serverless は今時点では RDS Proxy も未サポートなので、膨大なクエリーを受けるような環境では耐えられないかもしれません。比較的小規模で、重要性の低いユースケースであれば迷いなくオススメします。