DynamoDBテーブルのAthena参照をServerlessFrameworkで構築

以前、参考記事通りにCDKで組んだことがあったのですが、CDKでできるならCFでできないか？エクスポート周りもCLI（シェル）でやってたので、SDKで組めないか？

そこから今回のを組みました。

クラメソさんの記事が思惑に完全一致でした。大感謝。

https://dev.classmethod.jp/articles/dynamodb-table-export-service/

https://dev.classmethod.jp/articles/joining-data-from-s3-and-dynamodb-in-aws-glue-job/

Exportのコスト回りや仕組みについては、下記に詳しくまとめている方がいたので、こちらを見ると非常にわかりやすいです。

https://qiita.com/bassaaaaa/items/5114bfd28bd3832d1f6a#fn5



# 全体設計

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247263/feba3aa5-dfc5-2bfe-69d4-e69dc56bd833.png)


- dynamoのエクスポートが毎回新しいファイルを新しい階層に作る
- 参照不要なファイルが多々ある

って形でしたので、

- エクスポート→必要なファイルだけコピー→コピー先を参照

ってことしてます。

## API設計

### API（３つ）
- [GET]対応リージョン取得
- [GET]テーブル一覧取得(query: region)
- [POST]エクスポート実行(body: region & table)
    - 1: エクスポート処理中チェック　（DynamoDB: listExports）
    - 2: 過去データ削除 （S3: listObjectsV2 ＆ deleteObject）
    - 3: ポイントインタイムリカバリ、オン（DynamoDB: updateContinuousBackups）
    - 4: DynamoDBエクスポート開始（DynamoDB: exportTableToPointInTime）
    - 5: ポイントインタイムリカバリ、オフ（DynamoDB: updateContinuousBackups）

※ロジックは割愛。上述のAPI使えばいけます。

### S3イベント（１つ）
- ファイルコピー用Lambda
    - Exportされたら発火。ファイルをコピーする

# 環境
nodejs14.x
Serverless Framework: 2.57.0

# 実装

serverless.yaml
※ポイントのみ、記載

```
・・・
iamRoleStatements:
    - Effect: 'Allow'
      Action:
        - "dynamodb:ListTables"
        - "dynamodb:ExportTableToPointInTime"
        - 'dynamodb:ListExports'
        - 'dynamodb:UpdateContinuousBackups'
        - 'dynamodb:ExportTableToPointInTime'
      Resource: '*'
    - Effect: 'Allow'
      Action:
        - 's3:GetObject'
        - 's3:CopyObject'
        - 's3:DeleteObject'
        - 's3:AbortMultipartUpload'
        - 's3:PutObject'
        - 's3:PutObjectAcl'
      Resource:
        - "arn:aws:s3:::${env:DYNAMO_ANALYTICS_BUCKET}/*"
    - Effect: 'Allow'
      Action:
        - 's3:ListBucket'
      Resource:
        - "arn:aws:s3:::${env:DYNAMO_ANALYTICS_BUCKET}"
・・・
layers:
  node:
    path: layer/modules/node
    name: ${self:service.name}-${sls:stage}-nodeLayer
    description: Description of what the lambda layer does 
    compatibleRuntimes:
      - nodejs14.x
    package:
      patterns:
        - node_modules/**
・・・
functions:
  api-export:
    handler: dist/table-export/src/index.handler
    events:
      - http:
          method: ANY
          path: '{proxy+}'
          private: true
      - http:
          method: ANY
          path: '/'
    layers:
      - { Ref: NodeLambdaLayer }
  dynamoExport:
    handler: dist/table-export/src/standalone/main.dynamoExportHndler
    layers:
      - { Ref: NodeLambdaLayer }
    events:
      - s3:
          bucket: ${env:DYNAMO_ANALYTICS_BUCKET}
          event: s3:ObjectCreated:*
          rules:
            - prefix: exports/
            - suffix: .json.gz
・・・
resources:
  Resources:
    GlueDatabase:
      Type: AWS::Glue::Database
      Properties:
        CatalogId: !Ref AWS::AccountId  
        DatabaseInput:
          Name: !Sub glue-database-${sls:stage}
    GlueTable:
      Type: AWS::Glue::Table
      Properties:
        CatalogId: !Ref AWS::AccountId
        DatabaseName: !Ref GlueDatabase
        TableInput: 
          Name: !Sub glue-table-${sls:stage}
          TableType: EXTERNAL_TABLE
          StorageDescriptor:
            Location: !Sub s3://${env:DYNAMO_ANALYTICS_BUCKET}/athena/data
            InputFormat: org.apache.hadoop.mapred.TextInputFormat
            OutputFormat: org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat
            Parameters:
              EXTERNAL: true
            Columns:
              - Comment: "id test"
                Name: 'Item'
                Type: 'struct<id:struct<S:string>,name:struct<S:string>>'
            SerdeInfo:
              Parameters:
                paths: "Item"
              SerializationLibrary: org.apache.hive.hcatalog.data.JsonSerDe

```

- Glueテーブルは必要に応じて拡張させる。（Typeに増やすの微妙ですが、そこに追加していけばです。）

# 確認
連携がうまくいけば、画像の感じに確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247263/6429a508-e89f-42a5-d02b-7e3c2f61ca8d.png)

参照が、Item.id.sと微妙ですが、view作っちゃえば綺麗に使えます。

# まとめ
Glueの定義で正解がわからず、少し苦労しました。
Blackbelt調べたらあったので、そのうち見てみようかなと。

あと、ListBucket（削除時に利用）。どの権限が必要かで詰まったのと、フェッチ量が多いと1回で取れないってのが気付くまでに時間食いました。（1回目がうまくいってたが故にハマった。。。）
そう言うのは経験積んで減らしていきたいです。

Athenaでも、使いすぎるとお金かかるかもですが、小規模ならほぼ無料みたいなものなので、そういうところはAWS様様です。

# 参考
https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/DataExport.Requesting.html