【Tips】Amazon Athenaの “GENERIC_INTERNAL_ERROR: No partition found with values” を解決する

先日、Amazon Athenaで以下のエラーに遭遇したので、原因と解決方法を記しておきます。

GENERIC_INTERNAL_ERROR: No partition found with values [20211031]

ちなみに2021年11月2日現在、上記に関する公式ドキュメントは見つかっておりません。

原因

エラーメッセージには「"20211031"の値のあるパーティションが見つかりません」という意です。Amazon AthenaでPartition Projectionを使用している時に表示されるエラーの模様で、Partition Projectionの設定の範囲外のパーティションに対してINSERTが実行されようとすると、「そのパーティションは存在しない」と怒られるわけです。

私の手元では、以下のようなDDLを設定していました。projection.yyyymmdd.rangeで、日付パーティションの範囲を2018年3月1日現在時刻として定義しています。

CREATE EXTERNAL TABLE IF NOT EXISTS `cm-haruta`.`partition_error` (
  `id` string COMMENT '',
  `time` string COMMENT ''
)
PARTITIONED BY (
  `date_created` string COMMENT '')
ROW FORMAT SERDE
  'org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe'
STORED AS INPUTFORMAT
  'org.apache.hadoop.hive.ql.io.parquet.MapredParquetInputFormat'
OUTPUTFORMAT
  'org.apache.hadoop.hive.ql.io.parquet.MapredParquetOutputFormat'
LOCATION
  's3://cm-haruta/partition_error'
TBLPROPERTIES (
  'has_encrypted_data' = 'false',
  'projection.enabled' = 'true',
  'projection.date_created.type' = 'date',
  'projection.date_created.range' = '2018-03-01,NOW',
  'projection.date_created.format' = 'yyyy-MM-dd',
  'projection.date_created.interval' = '1',
  'projection.date_created.interval.unit' = 'DAYS'
)

この範囲指定で気をつけなければいけないのが、 日付型のパーティションカラムは、クエリ実行時間の協定世界時(UTC)を基準に生成される という点です。こちらは公式ドキュメントに注意書きがありました。

Projected date columns are generated in Coordinated Universal Time (UTC) at query execution time.

Supported Types for Partition Projection - Amazon Athena

要は日本時間で00時00分〜08時59分の間にクエリが実行されると、そのタイミングのNOWは前日になってしまう、ということです。午前中にバッチジョブを動かすケースをよくあると思うので、Partition Projectionを使用する際は注意が必要ですね。

念のため先ほどのテーブルへINSERTクエリを発行するLambda関数を作成し、EventBridgeで1時間ごとに起動して挙動を確認してみました。AthenaとS3への権限を付与したIAM Roleをアタッチし、タイムアウトを5分に設定したLambdaで、以下のPython関数を実行します。

import boto3
import uuid
import time

from datetime import datetime as dt, timezone as tz, timedelta as td

client = boto3.client('athena')
database = 'cm-haruta'
output='s3://cm-haruta/athena_results'
workgroup = 'primary'

def lambda_handler(event, context):
    id = uuid.uuid4()
    dt_now = dt.now(tz(td(hours=9)))
    today = dt_now.date()
    query = f'''
INSERT INTO "cm-haruta"."partition_error"
VALUES ('{id}', '{dt_now}', '{today}')
'''
    print("following query excecuted: ")
    print(query)

    # Execution
    exec_id = client.start_query_execution(
        QueryString=query,
        QueryExecutionContext={
            'Database': database
        },
        ResultConfiguration={
            'OutputLocation': output,
        },
        WorkGroup=workgroup
    )["QueryExecutionId"]
    
    status = client.get_query_execution(QueryExecutionId=exec_id)["QueryExecution"]["Status"]
    print("Athena Execution ID: {}".format(exec_id))
    print("Workgroup: {}".format(workgroup))

    while status["State"] not in ["SUCCEEDED", "FAILED", "CANCELLED"]:
        print('{}: wait query running...'.format(status["State"]))
        time.sleep(5)
        status = client.get_query_execution(QueryExecutionId=exec_id)["QueryExecution"]["Status"]
        print(status["State"])
    if not status["State"] == 'SUCCEEDED':
        print('Execution ID: ' + exec_id)
        print('Detail: ' + status['StateChangeReason'])
        raise Exception('Athena Query Failed')
    print(status)

    return 'OK'

結果、やはり00時00分〜08時59分間の実行は、全てGENERIC_INTERNAL_ERRORとなっていました。09時52分の実行からパーティションへのインサートに成功しています。

id time date_created
9f18ccb8-087d-4207-9325-7d1f2e314cb0 2021-11-01 17:52:32.098482+09:00 2021-11-01
feae989b-3fec-47ef-8cab-26a2fa3407b8 2021-11-01 18:52:31.927325+09:00 2021-11-01
9b938cec-c567-4c9e-bf6d-e4d5207970e8 2021-11-01 19:52:31.962782+09:00 2021-11-01
785de808-cf6c-4b82-a99a-077aefb116ec 2021-11-01 20:52:32.248542+09:00 2021-11-01
a1c367c2-fd30-4678-a28f-534bf5c6a8f9 2021-11-01 21:52:32.271044+09:00 2021-11-01
49f46a41-8d03-4ce1-a157-e4c6cd62e51d 2021-11-01 22:52:32.237517+09:00 2021-11-01
a13758d8-634c-4ab3-a9c1-919ab8ab8609 2021-11-01 23:52:32.088840+09:00 2021-11-01
1c1ac2ef-e43a-4199-8213-d853d6fa980b 2021-11-02 09:52:32.219154+09:00 2021-11-02

解決方法

解決方法は至って簡単で、パーティションの範囲を1日伸ばしておけばOKです。具体的には、'projection.date_created.range' = '2018-03-01,NOW+1DAY'とするだけ。この辺り、Partition Projectionはかなり柔軟に設定が可能です。

TBLPROPERTIES (
  'has_encrypted_data' = 'false',
  'projection.enabled' = 'true',
  'projection.date_created.type' = 'date',
  'projection.date_created.range' = '2018-03-01,NOW+1DAY',
  'projection.date_created.format' = 'yyyy-MM-dd',
  'projection.date_created.interval' = '1',
  'projection.date_created.interval.unit' = 'DAYS'
)

設定項目の細かい詳細については、公式ドキュメントをご覧ください。

Supported Types for Partition Projection - Amazon Athena

気になった点

そもそもPartition ProjectionはMSCK REPAIRなどで更新する実体のパーティション情報とは別の存在だと思っていたので、Partition Projectionの設定がINSERT時のパーティションに影響を及ぼしている点がちょっと不可解です。このケースにおいてエラーを出すかどうかは、Athenaの開発チームの匙加減で決められたんですかねー。