[AWS Glue]データカタログのパーティションをAWS CLIで更新してみた

2021.01.06

こんにちは、CX事業本部の若槻です。

AWS Glueでは、パーティション分割を行うことによりデータの整理や効率的なクエリ実行を行うことが可能です。

今回は、AWS GlueのデータカタログのパーティションをAWS CLIで更新してみました。

やってみた

環境準備

CloudFormationスタックをデプロイします。これによりyearmonthdayのパーティションキーを持ったデータカタログテーブルを作成します。

template.yaml

AWSTemplateFormatVersion: '2010-09-09'

Resources:
  DevicesRawDataBucket:
    Type: AWS::S3::Bucket
    Properties: 
      BucketName: !Sub devices-raw-data-${AWS::AccountId}-${AWS::Region}

  DevicesDataAnalyticsGlueDatabase:
    Type: AWS::Glue::Database
    Properties: 
      CatalogId: !Ref AWS::AccountId
      DatabaseInput:
        Name: devices_data_analystics

  DevicesRawDataGlueTable:
    Type: AWS::Glue::Table
    Properties:
      CatalogId: !Ref AWS::AccountId
      DatabaseName: !Ref DevicesDataAnalyticsGlueDatabase
      TableInput:
        Name: devices_raw_data
        TableType: EXTERNAL_TABLE
        Parameters:
          has_encrypted_data: false
          serialization.encoding: utf-8
          EXTERNAL: true
        StorageDescriptor:
          OutputFormat: org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat
          Columns:
            - Name: device_id
              Type: string
            - Name: timestamp
              Type: bigint
            - Name: state
              Type: boolean
          InputFormat: org.apache.hadoop.mapred.TextInputFormat
          Location: !Sub s3://${DevicesRawDataBucket}/raw-data
          SerdeInfo:
            Parameters:
              paths: "device_id, timestamp, state"
            SerializationLibrary: org.apache.hive.hcatalog.data.JsonSerDe
        PartitionKeys:
          - Name: year
            Type: string
          - Name: month
            Type: string
          - Name: day
            Type: string

  DevicesAthenaWorkGroup:
    Type: AWS::Athena::WorkGroup
    Properties:
      Name: devices-athena-work-group
      WorkGroupConfiguration:
        ResultConfiguration:
          OutputLocation: !Sub s3://${DevicesRawDataBucket}/athena-query-result
        EnforceWorkGroupConfiguration: true
        PublishCloudWatchMetricsEnabled: true
% aws cloudformation deploy \
  --template-file template.yaml \
  --stack-name devices-data-analytics-stack \
  --capabilities CAPABILITY_NAMED_IAM \
  --no-fail-on-empty-changeset

以降のコマンド実行で使用する変数を定義します。

% AWS_REGION=ap-northeast-1
% ACCOUNT_ID=$(aws sts get-caller-identity | jq -r ".Account")
% RAW_DATA_BUCKET=s3://devices-raw-data-${ACCOUNT_ID}-${AWS_REGION}
% GLUE_DATABASE_NAME=devices_data_analystics
% GLUE_TABLE_NAME=devices_raw_data

パーティションとなるパスを持つデータをS3バケットに作成します。

raw-data.json

{"device_id": "3ff9c44a", "timestamp": 1609348014, "state": true}
% aws s3 cp raw-data.json \
    ${RAW_DATA_BUCKET}/raw-data/year=2021/month=01/day=06/raw-data.json

この時点ではデータカタログにパーティションはまだ作成されていません。

% aws glue get-partitions \
    --database-name ${GLUE_DATABASE_NAME} \
    --table-name ${GLUE_TABLE_NAME}

{
    "Partitions": []
}

athena start-query-executionコマンドによる方法

athena start-query-executionコマンドでMSCK REPAIR TABLEクエリを実行する方法でパーティションを更新してみます。

% aws athena start-query-execution \
    --query-string "MSCK REPAIR TABLE ${GLUE_TABLE_NAME}" \
    --work-group devices-athena-work-group \
    --query-execution-context Database=${GLUE_DATABASE_NAME},Catalog=AwsDataCatalog

パーティション一覧を取得すると、パーティションが作成されていますね。

% aws glue get-partitions \
    --database-name ${GLUE_DATABASE_NAME} \
    --table-name ${GLUE_TABLE_NAME}

{
    "Partitions": [
        {
            "Values": [
                "2021",
                "01",
                "06"
            ],
            "DatabaseName": "devices_data_analystics",
            "TableName": "devices_raw_data",
            "CreationTime": "2021-01-06T21:29:24+09:00",
            "LastAccessTime": "1970-01-01T09:00:00+09:00",
            "StorageDescriptor": {
                "Columns": [
                    {
                        "Name": "device_id",
                        "Type": "string"
                    },
                    {
                        "Name": "timestamp",
                        "Type": "bigint"
                    },
                    {
                        "Name": "state",
                        "Type": "boolean"
                    }
                ],
                "Location": "s3://devices-raw-data-XXXXXXXXXXXX-ap-northeast-1/raw-data/year=2021/month=01/day=06",
                "InputFormat": "org.apache.hadoop.mapred.TextInputFormat",
                "OutputFormat": "org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat",
                "Compressed": false,
                "NumberOfBuckets": 0,
                "SerdeInfo": {
                    "SerializationLibrary": "org.apache.hive.hcatalog.data.JsonSerDe",
                    "Parameters": {
                        "paths": "device_id, timestamp, state"
                    }
                },
                "BucketColumns": [],
                "SortColumns": [],
                "Parameters": {},
                "StoredAsSubDirectories": false
            }
        }
    ]
}

その他の方法

glue create-partitionglue batch-create-partitionコマンドでもパーティションの作成は可能です。ただし前述の方法とは異なり、作成したいパーティションの仕様を明示的に指定する必要があります。

オブジェクトが一つも作成されていない場合

S3バケットのデータロケーションにオブジェクトが一つも作成されていない場合、MSCK REPAIR TABLEクエリは失敗します。

先程S3バケットに作成したオブジェクトを削除します。

% aws s3 rm \
    ${RAW_DATA_BUCKET}/raw-data/year=2021/month=01/day=06/raw-data.json

athena start-query-executionコマンドでパーティションを更新するクエリを実行します。

% aws athena start-query-execution \
    --query-string "MSCK REPAIR TABLE ${GLUE_TABLE_NAME}" \
    --work-group devices-athena-work-group \
    --query-execution-context Database=${GLUE_DATABASE_NAME},Catalog=AwsDataCatalog
{
    "QueryExecutionId": "949737b5-2514-4dd2-abf6-c997d8f5143e"
}

クエリの実行結果を取得すると、Tables missing on filesystem:\tdevices_raw_dataというエラーとなっています。

% aws athena get-query-results \
    --query-execution-id 949737b5-2514-4dd2-abf6-c997d8f5143e

{
    "ResultSet": {
        "Rows": [
            {
                "Data": [
                    {
                        "VarCharValue": "Tables missing on filesystem:\tdevices_raw_data"
                    }
                ]
            }
        ],
        "ResultSetMetadata": {
            "ColumnInfo": []
        }
    },
    "UpdateCount": null

そもそもオブジェクトをすべて削除する状況があまり無いですし、その状況になったとしてパーティション更新がエラーになったからと言ってすぐに困ることは無いですが、私はこの検証を行う際にバケットにデータを上げ忘れたままパーティション更新を行おうとして今回のエラーが出たため、「なんでテーブルが無いなんて言ってるんだ?」と少し混乱しました。

おわりに

AWS GlueのデータカタログのパーティションをAWS CLIで更新してみました。

Glueで構築したETLシステムにおいてはパーティション更新はクローラーやLambdaで行うことが多いと思いますが、これを応用すればGlueジョブの中でBoto3を使ってジョブ冒頭でパーティション更新させて構成を単純化させる、なんてこともできそうですね。

参考

以上