📖

re:Invent 2024: AWS LambdaのPowertoolsの主要機能と活用法

2024/01/01に公開

はじめに

海外の様々な講演を日本語記事に書き起こすことで、隠れた良質な情報をもっと身近なものに。そんなコンセプトで進める本企画で今回取り上げるプレゼンテーションはこちら!

📖 AWS re:Invent 2024 - Gain expert-level knowledge about Powertools for AWS Lambda (OPN402)

この動画では、AWS Senior SA EngineerのAndrea AmorosiがPowertools for AWS Lambdaの主要機能について解説しています。週間20億回以上のLambda関数で使用され、機能の40%がコミュニティ貢献によるこのツールキットは、Structured Logging、API Event Handling、Idempotency、Batch Processingなど、Lambda開発における一般的な課題を解決します。特にStructured LoggingではCloudWatch Insightsとの連携を容易にし、Event HandlerではAPI Gateway等との統合を簡素化。また、Idempotencyでは重複実行を防ぎ、Batch ProcessingではSQSやKinesisからの部分的な失敗に対応できます。2025年第1四半期にはPowertools for TypeScriptバージョンでもEvent Handler機能が利用可能になる予定です。
https://www.youtube.com/watch?v=kxJTq8FTkDA
※ 動画から自動生成した記事になります。誤字脱字や誤った内容が記載される可能性がありますので、正確な情報は動画本編をご覧ください。
※ 画像をクリックすると、動画中の該当シーンに遷移します。

re:Invent 2024関連の書き起こし記事については、こちらのSpreadsheet に情報をまとめています。合わせてご確認ください!

本編

Powertools for AWS Lambdaの紹介と本セッションの概要

Thumbnail 0

みなさん、こんにちは。OPN402へようこそ。私はAWSのSenior SA Engineerで、Powertools for AWS Lambdaチームに所属しているAndrea Amorosiです。私は製品の開発・保守を担当しており、Powertoolsの全ランタイムの方向性を定める役割も担っています。アジェンダの説明に入る前に、一つ質問させてください:Powertools for AWSについて聞いたことがある方は何人いらっしゃいますか?素晴らしいですね。この後のセッションは、きっと皆さんにとって興味深いものになると思います。

このセッションでは、AWS Lambdaを使用しているお客様がよく直面する一般的な課題や問題について説明し、Powertools for AWSを使ってそれらの課題をどのように解決できるか、また、どのような機能が活用できるかをご紹介します。すべての機能が皆さんのワークロードに関連するわけではないかもしれません。それで構いません。これは様々な機能の集まりですので、皆さんにとって最も有用だと思われるものを取り入れていただければと思います。セッションの最後に少し時間を取りますので、製品について質問がありましたら、お答えさせていただきます。

Structured Loggingの重要性とPowertoolsの活用

Thumbnail 90

Thumbnail 120

こちらが本日のアジェンダです。まず、Powertools for AWS Lambdaについて、先ほど手を挙げなかった方々のため、あるいはすでにご存知の方々の復習のために、簡単な紹介をさせていただきます。その後、Structured Logging、APIのEvent Handling、Idempotency、Batch Processingについてお話しします。そして最後に、製品のロードマップへのリンクを共有し、Q&Aの時間を設けます。 ほとんどのワークロードにおけるソフトウェア開発ライフサイクルは、このようになっています:まず、ユースケースとワークロードの計画・設計フェーズから始まります。次に構築フェーズ - これは私たちが最も好きな部分で、ソリューションの構築とアーキテクチャ設計を行います。その後、ワークロードを本番環境にリリースし、運用フェーズに入ります。

Thumbnail 150

Well-Architectedのサーバーレスレンズなどを使用してワークロードの評価を行ったことがある方は、これが理想的には本番環境に移行する前に行うものだとご存知でしょう。これは、ワークロードがwell-architectedで、高可用性があり、耐障害性があるかなどを、チームと共に様々な質問を通じて確認する時間です。ただし、実際には常に本番環境前に実施できるわけではありません。現実には、スケジュールがタイトで本番環境に移行した後に実施しなければならないこともあります。つまり、本質的には構築が完了した後に行うということです。

Thumbnail 190

Thumbnail 210

もし、これらのベストプラクティスを構築フェーズの段階からワークロードに組み込む方法があれば、後からコードを見直したり、リファクタリングしたり、ワークロードに機能を追加したりする必要がなくなるのではないでしょうか?これらのベストプラクティスを採用できる方法があれば素晴らしいと思いませんか? そこで登場するのがPowertools for AWS Lambdaです。Powertoolsは2020年に誕生し、Core Utilitiesと呼ばれるものから始まりました。これらはPowertools for AWSの全言語で利用可能なユーティリティで、Observabilityを中心に構成されています。具体的には、JSON Structured Logs、Business Metrics、Distributed Tracingについてお話ししています。これらはすべて、サーバーレスワークロードからデータを出力できるユーティリティで、本質的にはワークロードに関する任意の質問を自問自答できるようにするものです。

Thumbnail 260

しかし、私たちはさらに多くのことができると気づき、Powertoolsに新機能を追加し、より多くの機能を実装し始めました。 部分的な失敗レポートを含むバッチ処理、設定やシークレット、パラメータの管理などを実装しました。べき等性や入力バリデーションなども導入しました。すべてはPythonから始まり、Powertoolsは最初にPythonでリリースされました。これが現在、最も機能が充実したバージョンのPowertoolsです。その後、Java、TypeScript、.NETなど、他の言語のサポートも追加していきました。

Thumbnail 290

現在、Powertoolsは週に2,000億回以上のLambda関数で使用されており、機能の40%がコミュニティからの貢献によるものです。PowertoolsはAWSが完全にメンテナンスしているオープンソースプロジェクトです。

このプロジェクトはGitHubで開発されています。コードはMITライセンスで公開されており、コードの閲覧、イシューの作成が可能で、私や私のチームがそれらのイシューに回答し、皆さんのPRをレビューしています。それでは、より具体的なユースケースと構造化ログについて説明していきましょう。

Event HandlerとAPI開発におけるPowertoolsの利点

Thumbnail 330

Thumbnail 340

Lambdaを使用している場合、おそらく最もシンプルな形式でログを出力していることでしょう。 Lambdaでログを出力するということは、標準出力や標準エラーに何かを出力し、AWS Lambdaがその情報をCloudWatchに送信するということです。これが最もシンプルな形式です。ログを他のサードパーティの監視プロバイダーや独自のインデックスプラットフォームに送信することもできますが、今日は、それらのログをどのように作成するかに焦点を当てていきます。Lambdaで、あるいは一般的なコードでログを出力する方法には4つあります。

Thumbnail 380

Thumbnail 420

最もシンプルな方法は、 Pythonならloggingライブラリ、Node.jsならconsole.logを使用することです。ログレベルを設定し、文字列を出力します。これがCloudWatchに送られると、リクエストID、ログレベル、そしてメッセージやログペイロードが表示されます。これはデバッグや手動での実行時には便利です。 しかし、本番環境で実行する場合、解析や理解が難しい大量のメタデータが含まれているため、これらのログを活用するのは困難です。

Thumbnail 430

Thumbnail 440

Thumbnail 450

Thumbnail 480

ワークロードが進化し、ロギングのスキルが向上するにつれて、使用している言語に応じてオブジェクトや辞書を作成することを検討するかもしれません。 デフォルトの標準出力ログを使用している場合、ログレベルやリクエストID、タイムスタンプなども含まれます。 Canonical Logsを使用することを選択するチームもあり、これは基本的に追加のキーを持つ異なる構造を作成することを可能にします。ただし、最終的にはCloudWatchを使用している場合、ほとんどの場合Structured Loggingを採用することになるでしょう。Structured Loggingでは、JSONを作成して標準出力に出力することができ、 CloudWatch Insightsを通じてそれらのログをインデックス化し、フィールドの検出可能性を利用してクエリやフィルタリングを行うことができます。

Thumbnail 520

スライドのコードサンプルに注目していた方は、機能を追加するにつれてコードが複雑になっていくことに気付いたと思います。これは、AWSで私たちが「差別化された重労働(differentiated heavy lifting)」と呼んでいるものです。これは本質的に、コードベースに多くのボイラープレートコードを追加していることを表す格好の言葉です。ここで、Powertoolsの出番です。 Powertools Loggerは、私たちのツールキットの中で最も便利なライブラリ機能の1つで、私たちが「適切なデフォルト」と呼ぶものを使用する標準的なロガーを作成し、簡単に拡張できるようにします。最もシンプルな形式では、特定のワークロードに基づいてログをフィルタリングするために使用できるサービスやhigh cardinalityキーを設定でき、ログレベルなどの他の機能もサポートしています。

Thumbnail 560

Thumbnail 590

これがPowertoolsを使用した場合に見られるログです。文字列やタイムスタンプ、リクエストIDなどはもはや個別には存在せず、すべてがJSONオブジェクトの一部となっており、CloudWatchや他のサードパーティソリューションでクエリを実行するために使用できます。例えば、特定のサービスとワークロードについて、エラーレベルを持つすべてのログを表示するように指定でき、ワークロードに関する洞察を簡単に得ることができます。 Powertoolsでは、コンテキスト情報の追加など、他のことも行うことができます。これは本質的に、関数の特定のリクエストに関する追加情報を加えることです。ここではPython特有の例としてデコレータを示していますが、Powertoolsの他の言語でも同様の実装が存在します。

Thumbnail 630

基本的に、これは関数のエントリーポイントとハンドラーをロガーに伝えています。Lambdaに詳しい方なら、2番目のパラメータが関数に関する広範な情報を含むコンテキストであることをご存知でしょう。 例えば、関数にはリクエストIDや関数名、メモリサイズなど、さまざまな他のフィールドが含まれています。相関IDを設定することもできます。例えば、API GatewayからLambda関数へのリクエストがエンドツーエンドでどのように移動するかの可視性が必要な場合、スライドに示されているようにAPI Gateway RESTパスを設定して、リクエストIDなどの特定のフィールドを取得し、ログのすべてのキーに追加することができます。

Thumbnail 660

Thumbnail 700

私たちは、このロガーが「意見を持った(opinionated)」ロガーだと言っています。特定の形式と特定の名前を持つキーを提供しています。しかし、お客様が選択によって、あるいは特定の要件により、独自の形式を好む場合があることも理解しています。このロガーを拡張して独自の形式を導入することは非常に簡単です。これはその例です - ロガーを拡張して、ログの作成に使用する異なる形式を作成できることを示しています。ご覧のように、キーを並べ替えたり、名前を変更したり、削除したりすることができ、 ログは指定した形式を使用することになります。

Thumbnail 710

最近、お客様からよく耳にするようになってきたのが、Wide Logsという考え方です。 LambdaやCloudを使用している場合、大規模になるとワークロードやリクエストごとに大量のログを出力するため、コストが高くなることをご存知でしょう。Wide Logsと呼ばれる手法では、実行ごとに1つ、あるいはInvocationごとに1、2つのログのみを出力し、リクエストの進行に応じて異なるキーやメタデータを追加していきます。リクエストの各段階で個別のログを出力する代わりに、Loggerクラスにキーを追加できます。このLoggerクラスは状態を共有しているため、ログエントリを構築して情報を追加していくことができます。

Thumbnail 780

そして、リクエストの終わり頃に全てを標準出力にダンプすることで、包括的なログを得ることができます。これにより、出力するログの数が減るためコストが抑えられるだけでなく、異なるログを関連付けたり、複数のログエントリを検索したりする必要がなくなるため、ノイズも減ります。全ての情報が1つのログにまとまるため、リクエストIDや関数名があれば、特定のリクエストに関する全ての情報を1つのエントリで確認できます。先ほど申し上げたように、これは多くのお客様が試験的に導入し、より頻繁に使用するようになっている手法で、Powertoolsを使えば非常に簡単に実装できます。

Idempotencyの実装とPowertoolsによる簡素化

Thumbnail 820

Thumbnail 830

では次に、別のトピックであるEvent Handlerに移りましょう。Serverlessアプリケーションを開発する場合、APIを構築することが多いと思います。これは非常に一般的なパターンです。スライドではAmazon API Gatewayをエントリーポイントとして示していますが、ALBやFunction URL、VPC Lattice、あるいはHTTPリクエストを受け付けるあらゆる種類のエントリーポイントでも構いません。そしてその後ろには、コンピューティング層として機能するLambda関数があります。この構成では、ルーティング、リクエストの入出力のデシリアライズ、圧縮、バリデーションなど、考慮すべき点が多くあります。また、チームや大規模な組織で作業している場合は、OpenAPI SpecやSwagger UIを生成したいかもしれません。先ほど話題に上がったMiddlewareやObservabilityについて検討したり、あるいはワークロードをIdempotentにする必要があるかもしれません。

Thumbnail 900

私がよく受ける質問の1つで、おそらく多くのSolution Architectも同じように受けるのが、「ワークロードに必要な関数の数はいくつですか?」というものです。

Thumbnail 920

これまで私たちは、単一目的の関数、つまり1つのことを適切に行う関数の必要性を強調してきました。これは正しく、とても良いアドバイスです。しかし、実際の現場はそれほど単純ではありません。ほとんどのお客様にとって、これは様々なトレードオフがあり、ワークロードによって異なる選択肢がある連続体なのです。

Thumbnail 940

この選択をする際に考慮すべきポイントについてお話しします。単一の関数、私が Lambda モノリスまたは Lambda-lith と呼ぶものがある場合、通常はシンプルな CI/CD パイプラインになります。単一の関数をデプロイするだけだからです。これは一般的にブラウンフィールドアプリケーションに当てはまります。他の場所で実行していたワークロードを Lambda に移行する場合、リフト&シフトのようなアプローチを取るかもしれませんし、あるいはコンテナへのオフランプが可能なことを確認したいワークロードかもしれません。API の異なるルートが同じ関数内にあるため、すべてのルートが同じ実行環境プールに向かうことになり、コールドスタートの可能性は低くなる傾向があります。もちろん、これはリクエストパターンによって変わりますが、一般的にはそうなります。

同時に、ルートが多いということは、おそらくコードも多いということを意味し、より多くの依存関係やモジュールを読み込むことになります。つまり、コールドスタートが発生した場合、若干時間がかかる可能性があります。また、多くの異なるユースケースやリクエストに単一のリソースが使用されるため、スケーリングとクォータについても考慮する必要があります。先ほど少し触れた権限の問題もあります。この関数がデータベースやキューなど、多くの異なるサービスと通信する場合、それらすべてにアクセスする必要があるため、広範な権限モデルが必要になります。しかし、デプロイする関数がシンプルなので、変更を加えた際は1つのものをデプロイするだけですべてが変更されるため、管理は容易です。

Thumbnail 1050

スペクトルの反対側には、マイクロ関数があります。この場合、複雑さを受け入れることになります。変更を加える際には、何をどこにデプロイする必要があるのか、何が変更されるのかを理解する必要があり、多くの動く部分があります。これは理解するのが難しくなる可能性があります。異なるルートには異なる目的があるため、どのルートがヒットするかによってコールドスタートの可能性が高くなることがあります。API の異なるルートすべてが同じように人気があるわけではありません。非常に人気のある「ホット」なパスがある一方で、あまり使用されないパスもあり、それらはより頻繁にコールドスタートが発生する可能性があります。

関数が単一の目的を持つため、より小さく、おそらく使用する依存関係、コード、モジュールも少なくなります。そのため、コールドスタートが発生しても、より素早く立ち上がります。それぞれの関数は独立してスケールするため、理解しやすくなります。クラウドネイティブなワークロードで単一の関数を使用している場合、各ルートについて本当に理解し、それぞれのスケーリングパターンを把握することができます。これは活用できれば非常に興味深い特徴です。また、関数が1つのことだけを行うため、きめ細かな権限設定が可能になりますが、多くのものをデプロイする必要があります。

Thumbnail 1130

Thumbnail 1200

コードを見てみましょう。モノリスパターンを使用している場合、関数はおそらくこのような感じになるでしょう。大きな if 文や、場合によっては switch 文があり、基本的にイベントやペイロードを確認し、イベントがこのパスを持っているか、このヘッダーがあるか、あるいは期待するパラメータがあるかどうかをチェックします。どれにも一致しない場合は、400リクエストや500エラーをスローする必要があり、すべての例外をキャッチして500を返すために try-catch も必要です。これはロケットサイエンスではありません。時間と労力をかければ、このパターンを本当に磨き上げることができます。しかし、ビジネスロジックに集中できるようにライブラリがこれを代行してくれるのに、なぜこのようなコードを維持したいのでしょうか?これは Powertools が多くのコードを削減できる別の例です。

Powertoolsを使えば、すでに実装を運用している多くのお客様の共有知識を継承・活用しながら、さまざまな操作を実行することができます。多くのエッジケースがすでに解決されているため、コードに集中し、特定の条件が満たされた時に実行されるフック、デコレーター、アノテーションを使用することができます。この例では、API GatewayのイベントにマッピングされるクラスであるAPIGatewayRestResolverを紹介しています。これには関数に付加できるデコレーターがあり、異なるペイロードやパスを持つリクエストがあった場合に、コードの特定の部分を実行することができます。

Thumbnail 1260

Thumbnail 1270

カスタマイズして、デフォルトのルートを定義することもできます。 例えば、定義したルートのどれにも一致しない場合に、特定のロジックを実行したい場合などです。 バリデーションも可能です。Pythonをお使いの方なら、バリデーションライブラリのPydanticと互換性があることをご存知かもしれません。他の言語をお使いの方で、例えばZODなどがお好みの場合でも、基本的な考え方は同じです。リクエストが期待するスキーマやモデルを定義すると、ロジックの実行中にバリデーションを行います。バリデーションとパースが成功すると、コードが実行され、関数が受け取るペイロードが期待通りのものであることが保証されます。

Thumbnail 1310

Thumbnail 1340

チームによってはOpenAPIを使いたい場合もあるでしょう。Powertools Event Handlerを使えば、ルートにアノテーションを付け、説明や例を追加し、OpenAPI拡張機能を使用することができます。コードにアノテーションを付けると、API全体のOpenAPIスキーマが生成され、すぐに使用できます。Swagger UIを有効にして、関数やAPIをテストすることもできます。 OpenAPIでお馴染みの機能に加えて、クロスリージョンリクエスト、圧縮、バイナリレスポンス、ミドルウェアなど、さまざまな機能も用意されています。

Thumbnail 1360

Thumbnail 1390

興味深いのは、API Gatewayのモデルを自分で定義して構築している場合でも、APIを別のエントリーポイントの背後に移動する必要が出てくるかもしれないということです。例えば、Elastic Load Balancingを使用したい場合などです。これらのサービスを使用した経験がある方なら、ペイロードが様々な形で微妙に変化することをご存知でしょう。 Powertoolsでは、異なるResolverをインポートするだけで、残りのコードはそのまま使用できます。なぜなら、私たちがバックグラウンドでこれらの微妙な違いを処理し、特定のユースケースに応じて期待されるペイロードをコードに提供するからです。Event Handlerは現在Pythonで利用可能ですが、2025年第1四半期にはPowertools for TypeScriptバージョンでも利用できるようになる予定です。

Thumbnail 1420

Thumbnail 1430

Thumbnail 1460

では、次にIdempotencyについてお話ししましょう。 操作がIdempotentであるとは、同じ入力で何度実行しても、実行回数に関係なく同じ結果が得られる場合を指します。これは例えば、金融取引や注文処理など、関数を実行したいけれども副作用は一度だけにしたい場合に必要となります。 Lambdaでこれを実現するのは通常難しいものです。なぜなら、Lambda関数がタイムアウトした場合や、同時に2つのリクエストを受け取った場合、あるいは一定時間経過後にのみ操作をIdempotentとして扱いたい場合など、さまざまなことを考慮する必要があるからです。

Thumbnail 1480

Thumbnail 1500

Thumbnail 1510

通常のケースでは、異なるリクエストがある場合、操作がべき等でない場合は、この操作を複数回実行することになります。これは一般的にコストがかかり、副作用を引き起こす可能性があります。 購入や注文の場合、ユーザーに二重で請求したくないため、コードをべき等にする必要があります。そのため、基本的にはリクエストを確認し、 AWS Lambdaはステートレスなので、別の永続的なストアに状態を保持する必要があるロジックを実装する必要があります。

リクエストを受け取るたびに、このペイロードを保存している内容と比較し、このリクエストを以前に見たことがあるか、再度処理する必要があるかを確認します。新しいリクエストであれば処理を行い、そうでない場合は、重要な場合には前回のリクエストからのレスポンスを返します。

Thumbnail 1540

それでは、 Lambdaでこれを行う場合について、より具体的に見ていきましょう。これは初めてリクエストを見た場合のハッピーパスです:イベントを確認し、Amazon DynamoDBのようなストレージに状態を設定し、このレコードを進行中としてマークします。これはロックとも呼ばれ、リクエストが進行中とマークされている場合、後から別のリクエストが来ても、進行中であることがわかり、そのリクエストを弾くことができます。その後、コードを実行します - ここでロジックと副作用を実行し - その後、このレスポンスの結果を保存して結果を返します。これが最もシンプルなオプションです。

Thumbnail 1580

では、同じリクエストを再度受け取った場合はどうなるでしょうか?イベントを確認し、永続化層に何が保存されているかをチェックします。数秒前または数分前に見たのと同じペイロードを持つリクエストが存在することがわかります。この場合、副作用が発生するため、コードを実行したくありません。代わりに、Amazon DynamoDBに保存されている結果を取得し、コードを完全にバイパスしてその結果を返します。

Thumbnail 1610

関数がスケーリングされ、同時リクエストがある場合、先ほど説明したことをする代わりに、新しいリクエストがあれば、ペイロードをチェックし、何かが進行中か、すでに処理されているかを確認します。この場合、進行中であれば、単に500エラーを返すか、さらに適切には400エラーを返します。なぜなら、このリクエストは見たことがあるが処理が完了していないので、何が起こるか様子を見る必要があるということを示しているからです。

Thumbnail 1650

コードがクラッシュした場合はどうなるでしょうか? Lambda関数が例外を発生させた場合、ロックが不整合な状態になる可能性があります - 処理が進行中の状態で、Lambdaが例外をスローしたのです。このような場合、IdempotencyユーティリティはあなたのFunction全体をラップし、例外が発生したことを検知して、ロックを解除します。これにより、後続のリクエストを新規のものとして受け付けることができるようになります。

Thumbnail 1680

では、より具体的な仕組みを見ていきましょう。 理想的には、できるだけ侵襲性の低い方法を使用したいところです。Idempotentにしたい関数やコードを、ミドルウェアやデコレータのようなもので包むことで、ロジック自体はそのままに、入力と出力だけを確認することができます。これは理想的には純粋関数として扱えます。

Thumbnail 1710

私たちが提供しているデコレータは、レコードを保存してからコードを実行します。データベースや永続化層に異なるフィールドを保存することで、進行中の状態の有効期限やステータス、レスポンスなどを追跡できます。これはIdempotencyの動作を示す良い例です。ご覧の通り、Lambda handlerの全体をデコレータでIdempotentにしています。この例ではDynamoDBを永続化層としてインスタンス化し、コードを実行して結果を保存しています。

Thumbnail 1780

多くのケースでこれは良いアイデアかもしれませんが、ほとんどの場合そうではありません。その理由は、関数、というよりもペイロードには通常変化するフィールドがあるからです。APIの場合、各リクエストで異なるRequest IDやタイムスタンプを持つことが多いでしょう。 つまり、ユーティリティはJSONが異なることを検知して、これは別のリクエストだと判断するため、関数は実質的にIdempotentにはなりません。したがって、通常はIdempotencyの範囲を絞り込み、本当に重複を避けたい重要なロジックの部分だけをIdempotentにするのが賢明です。例えば、注文を処理する場合、関数全体をIdempotentにするのではなく、実際に注文を処理するロジックだけをIdempotentにすれば良いかもしれません。

Batch ProcessingとPowertoolsの将来展望

このようにすれば、関数自体は毎回実行されますが、私が気にしている副作用、つまり重複を避けたい副作用だけがIdempotentになります。ここでご覧のように、Configurationの概念も導入しています。私たちはJMESPath式をサポートしており、これによってリクエストのサブセットを抽出し、ユーティリティに対して「ペイロードの中身が何であれ、Idempotencyの目的で本当に重要なのはこの1つまたは複数のフィールドだけだ」と伝えることができます。これらのフィールドがIdempotencyキーの生成に使用されます。Idempotencyキーとは、簡単に言えば、ペイロードに対して決定論的なハッシュを作成して保存し、そのハッシュでリクエストを識別するという仕組みのことです。

また、22行目には、LambdaContextをユーティリティに認識させるという別のコンセプトがあります。これは重要なポイントで、Lambda関数の残り時間に関する情報がLambdaContextに含まれているためです。Contextには「remaining time in milliseconds」というメソッドがあり、関数がタイムアウトするまでの残り時間を示します。これが重要な理由は、Persistence Layerがリクエストからの応答を受け取れない場合、それはタイムアウトしたことを意味するからです。後続のリクエストが来た時にこの時間が経過していれば、タイムアウトしたか何らかの問題が発生したと判断でき、そのリクエストは二度と戻ってこないため、もう一度実行する必要があるのです。

Thumbnail 1920

この例をよく見ていた方は、API GatewayのようなAPIを使用している場合、ペイロードがJSON文字列化されている可能性があると気づいたかもしれません。例えば、JMESPathモジュールのカスタム関数である「parse to JSON」を使用すると、パースを行い、辞書型に変換してくれます。つまり、異なるデータ形式をパースできる様々なユーティリティが用意されているということです。Base64エンコーディングやJSONをサポートしており、独自の変換処理を提供することもできます。ですから、ペイロードがエンコードされていても、圧縮されていても、あるいはどのような形式であっても、数行のコードでべき等性を実現できます。ローカルキャッシングも使用できます。例えば、長時間稼働することが分かっているProvisioned Concurrency関数がある場合、同じワーカーにリクエストが来た時に、Persistence Layerと通信せずに前回の結果を返せるようにローカルのインメモリキャッシングを使用したいかもしれません。これはより安価ですが、メモリ内にデータを保持することになるため、異なるトレードオフが発生します。

Thumbnail 2000

先ほどEvent Handlerで示したように、異なる種類のPersistence Layerもサポートしています。先ほどはDynamoDBを例に挙げましたが、いくつかのインポートを変更するだけで、異なるPersistence Layerを使用できることがお分かりいただけると思います。例えば、ここではRedis互換のPersistence Layer(VY Serverlessやその他のRedis互換レイヤー)を示しています。つまり、ほとんどのロジックやコードはそのままで、インポートを変更するだけで、異なるレイヤーの違いや整合性を抽象化し、コードに集中できるようになっています。

Thumbnail 2050

Thumbnail 2060

それでは、プレゼンテーションの最後のトピックに移りましょう。 バッチ処理についてです。Amazon SQSキューやKinesisストリーム、DynamoDBストリームなどでトリガーされる関数の場合、関数は基本的に複数のオブジェクトのリストであるペイロードで呼び出されます。これらのオブジェクトは、順次処理または並列処理する必要がある場合とない場合があります。多くの方が知らず、活用していないのが、私が示したこれらのトリガーが「部分的な失敗」というものをサポートしているという点です。例えば、レコードのバッチを処理していて、そのうちの1つが例外を発生させた場合、Lambdaのデフォルトの動作では、バッチ全体を再処理のためにストリームやキューに戻します。

しかし、5つのレコードがあって1つだけが失敗した場合、他の4つを再処理する必要はありません。部分的な失敗を有効にすると、例外をスローする際に、失敗したアイテムのIDのみを返すようLambdaに指示できます。これにより、他のアイテムは成功として扱われ、ストリームから削除されます。失敗したアイテムは再度投入され、リトライプロセス全体を経て、リトライ回数を使い果たすとデッドレターキューに移動します。つまり、すでに正常に処理されたものを再処理するのではなく、失敗したアイテムのみを再度処理したいというわけです。

Thumbnail 2170

Thumbnail 2200

このプロセスでは、異なるトリガーには異なるペイロードと異なる識別子が必要となるため、コードがより複雑になる可能性があります。理想的には、プロセッサーを指定できる関数のラッパーのようなものが欲しいところです。例えば、 Amazon SQSからのイベントを処理し、それらのレコードそれぞれに対してロジックを実行するということができます。これがそれらのアイテムを処理するビジネスロジックです。他のことを気にする必要はありません - 各オブジェクトに対してこの関数を呼び出すだけです。関数が正常に返された場合、そのオブジェクトは正常に処理されたとみなされます。関数が例外を発生させた場合、そのアイテムは失敗としてマークされ、キューに戻す必要があります。これがBatch Processingが行うことです。

Thumbnail 2240

Thumbnail 2260

前述のように、異なるトリガーに切り替えたい場合、 コードを数行変更するだけです。importを変更するだけで、残りのコードはそのままで、異なるペイロードやフォーマットの処理は私たちが代わりに行います。セッションの締めくくりとして、 ロードマップについて少しお話しさせていただきます。Powertools for AWS Lambdaは活発にメンテナンスされており、AWSのチームが積極的に投資を行っています。これらのQRコードで、主要バージョンのロードマップをご確認いただけます。今後数週間と来年初めには、2025年のロードマップを更新する予定ですのでご期待ください。他のチームからもお聞きになっているかもしれませんが、私たちのロードマップは本当にお客様のニーズによって定義されています。来年、これらの言語のいずれかで見たい機能がありましたら、ぜひご連絡ください。GitHubでIssueを作成するか、Issueを作成したくない場合はメールでご連絡ください。皆様のユースケースについてディスカッションさせていただければと思います。私たちは皆様からのご意見を本当にお待ちしております。ありがとうございました。これでセッションを終了します。ご質問がございましたら、お答えいたしますので、よろしくお願いします。ご質問がなければ、よい夕べをお過ごしください。


※ こちらの記事は Amazon Bedrock を利用することで全て自動で作成しています。
※ 生成AI記事によるインターネット汚染の懸念を踏まえ、本記事ではセッション動画を情報量をほぼ変化させずに文字と画像に変換することで、できるだけオリジナルコンテンツそのものの価値を維持しつつ、多言語でのAccessibilityやGooglabilityを高められればと考えています。

Discussion