S3イベント通知が失敗する?マルチパートアップロードが原因だった

はじめに
こんにちは!
クラウドインフラグループの松尾です。
早いもので今年の8月で入社3年目に突入してしまいました。
今回は、S3イベント通知について、ちょっとした躓きがあったので
知識のアウトプットとして簡単にまとめようと思いました。
同じような問題に直面した方の参考になれば幸いです。
きっかけ
📋LambdaがファイルをS3にアップロードし、アップロードをトリガーとしてSQSにメッセージを送信したい
とあるシステムで依頼があり、S3イベント通知を、ObjectCreated:Putの場合SQSに送信するように設定し実現しました。
しかし、設定後しばらくしてSQSにメッセージが届いていないという指摘があり、その調査を行いました。
S3イベント通知とは?
そもそもS3イベント通知とは、ざっくりいうとS3バケット内でイベントが発生した際に
イベント駆動で他のAWSサービスに通知を送信できるS3の機能です。
通知先は(Lambda/SNS/SQS)が選択できます。
📝 補足
今回は直接SQSへ通知するためS3イベントを採用していますが、Amazon EventBridgeを経由した通知も可能です。
複数サービスとの連携や複雑な設定が必要な場合はEventBridge連携も検討する必要があります。
最初の仮説と対応
実は通知先のSQSは一度名前を変更しており、併せてS3イベント通知の設定も更新していました。
設定後すぐに依頼者には確認いただき問題ないと連絡をいただいていたため、以下の仮説を立てました。
💭 SQSやS3イベント通知の設定がAWSの内部的な問題で更新できていなかった可能性があるのではなかろうか?
そのためS3イベント通知/SQSを一度削除し、再作成し適当なtxtファイルをS3にPUTしたところ、イベントを正常に受信できました。
この時点では「問題は解決した」と考えました。
問題の未解決と新たな発見
調査及び対応で行ったことを伝え、しばらくすると、
再度依頼者からまだ問題が解決していないという旨の連絡がありました。
その際依頼者から
💬拡張子.txtのファイルで試してみたところ、メッセージ受信しました。.csvでメッセージ受信できるようになっていない可能性がありそう
とのメッセージも添えられていました。
おやおや?と思いS3の中身を確認すると、あっ!と思った箇所が!
拡張子 | サイズ |
---|---|
.txt | 数バイト |
.csv | 約20Mバイト |
拡張子の問題ではなくファイルサイズが原因の可能性が高そうだと判断して再度調査してみました!
原因の特定
ファイルアップロード処理を行っているLambda関数のコードを確認したところ、S3.upload_fileを見つけました。
def upload_file(temp_file_path: str, S3_bucket_name: str, S3_file_name: str):
"""
ファイルアップロード
"""
logger.info('---- Upload ----')
S3 = boto3.client('s3')
res = S3.upload_file(temp_file_path, S3_bucket_name, S3_file_name)
logger.info(f"ファイル {S3_file_name} をアップロードしました")
boto3のupload_file
メソッドは、ファイルサイズが一定の閾値(8Mバイト)を超えると、自動的にマルチパートアップロードを実行します。[1]
原因はここにありそうです🔍
S3イベントタイプが違った
S3の設定イベントタイプとして設定していたのは
ObjectCreated:Put
この設定では、ObjectCreated:Put
による作成のみが通知対象となります。
しかし、マルチパートアップロードで発生するイベントは
ObjectCreated:CompleteMultipartUpload
マルチパートアップロードでは、ObjectCreated:Put
とは全く別のイベントが発生することになります!
つまり大きなファイルの場合、このような流れでイベントが起こらなかったのです
- LambdaがS3にファイルをアップロードする
- その際、
upload_file
メソッドにより、自動的にマルチパートアップロードを実行 - そのため発生したイベントは
ObjectCreated:CompleteMultipartUpload
- 結果としてイベント通知設定が
ObjectCreated:Put
のみだったため、SQSに通知されない
ちなみにマルチパートアップロードとは?
マルチパートアップロードは、大きなファイルを複数の部分(パート)に分割してアップロードする仕組みです。以下の利点があります。[2]
高速: 複数パートを並列でアップロード
信頼性: 失敗したパートのみ再送信
中断の再開: ネットワーク障害時でも途中から再開が可能
解決方法
S3イベント通知の設定変更
イベントタイプの設定を以下のように変更しました
すべてのオブジェクト作成イベント (ObjectCreated:*)
この設定により、以下のすべてのイベントが通知対象となります
ObjectCreated:Put
ObjectCreated:Post
ObjectCreated:Copy
ObjectCreated:CompleteMultipartUpload
設定変更後、大きなCSVファイルでもSQSにS3イベント通知が正常に送信されることを確認できました!
今回のケースでは、ファイルのアップロード元がLambda関数のみであることが明確だったため、
ObjectCreated:*
(すべてのオブジェクト作成イベント)に設定しました
他の解決方法もある
S3イベント通知の設定変更以外にも、boto3のコードでput_objectメソッドを使用することでも対応することは可能です。
ただし、今回はS3イベント通知の設定変更を選択しました。理由は
- アプリケーションコードを変更する必要がない
- 将来的な変更に対しても安定している
- 他のアップロード方法でも対応可能
S3イベント設定変更が最も汎用的で確実だと思いますが、状況によってはコード側での対応も有効だと思います。
学んだこと
1. ファイルサイズを意識したテスト
小さなテストファイルでの検証では、マルチパートアップロードが発生せず、問題を見落とす可能性があります。S3を挟む処理の場合は本番環境で想定されるファイルサイズでのテストを実施することが重要です。
2. boto3の内部動作への理解
boto3のupload_file
メソッドは便利ですが、内部でマルチパートアップロードを自動実行する場合があります。
この動作を理解して、適切なイベント設定を行う必要があります。
3. イベント通知設定の考慮点
今回は要件上、アップロード元がLambda関数のみと限定されていたため、s3:ObjectCreated:*
を選択しましたが、一般的には必要最小限のイベントタイプに絞ることが推奨されます。
重要なのは、boto3などのSDKが内部でどのようなアップロード方法を使用するかを把握し、それに応じた適切なイベント設定を行うことです。
まとめ
今回の問題は、以下の要因が重なって発生しました。
1. boto3が自動的にマルチパートアップロードを実行
2. S3イベント通知が`ObjectCreated:Put`イベントのみを対象に設定されていた
3. 小さなテストファイルでは問題が再現されない
小さなファイルでテストして安心していたら、実際の運用で問題が発覚するというのは、よくある落とし穴だと思います。
同様の問題で困っている方は、以下を確認してみてください。
- S3にアップロードしたファイルのサイズ
- S3イベント通知の対象イベントタイプ
このブログが少しでも参考になれば幸いです。
関連記事 | Related Posts
We are hiring!
【クラウドエンジニア】Cloud Infrastructure G/東京・大阪・福岡
KINTO Tech BlogWantedlyストーリーCloud InfrastructureグループについてAWSを主としたクラウドインフラの設計、構築、運用を主に担当しています。
【クラウドプラットフォームエンジニア】プラットフォームG/東京・大阪・福岡
プラットフォームグループについてAWS を中心とするインフラ上で稼働するアプリケーション運用改善のサポートを担当しています。