🦭

Lambda15分制限 → 並列化 → 429 → バックオフ → タイムアウト。この雁字搦めをどう解いたか

に公開

AWS Lambdaの寿命は15分。
並列にすれば終わる、そう思った。

並列化した。
429が返ってきた。

バックオフを入れた。
そして、Lambdaはタイムアウトした。


遅ればせながら‥
新年、明けましておめでとうございます。
今年も宜しくお願いいたします٩(๑òωó๑)۶

さて、2026年 個人としては 1発目のテックブログ!

本記事について

本記事では、Google Local Feed Partner(LFP)認定を獲得するために、この雁字搦めな制約の中で構築したローカル在庫連携基盤の技術的な取り組みをまとめています。

特に以下の3点に取り組みました。

  • 数十万件の在庫データを安全に同期するレートリミット・リトライ戦略
  • 突貫開発後の技術的負債返済とモノレポ化
  • AIツールを活用したコード整理・仕様抽出・共通化

今回はレートリミット・リトライ戦略とAIツールの使い分けについて書き、AIを使った分散リポジトリのモノレポ化については後日、別記事にまとめようと思います。
顧客対応や商談なども対応する中で、1人エンジニアとしてどう開発を進めたか、何に詰まったか、どこでAIが効いたかを残した約180日間の記録です。

はじめに

カンリーでは2025年10月にカンリーローカル在庫という新しいサービスの提供を開始しました。
https://local.can-ly.com/

サービスの提供と同時に、株式会社カンリーではGoogleの公式パートナープログラム「Local Feed Partner(LFP)」のTrusted Partner 認定を獲得しています。
https://prtimes.jp/main/html/rd/p/000000428.000037205.html

約180日の記録とはいえ、一応テックブログ枠なので商談や客先訪問などについては端折りつつ、技術的な実装や課題解決にフォーカスして書いていきます(゜д゜)

技術スタック

細々したところは割愛しますが、今回主に使った技術は以下の通りです。

  • バックエンド:Golang
  • インフラ:AWS
    • ローカル環境のみDockerを使用
  • IaC:Terraform
  • 認証・認可:OAuth2.0
  • API:Merchant API(v1beta -> v1)
  • 開発ツール:Cursor, Claude Code, Gemini, etc...

最小工数で機能実現するため現在は、お客様に対してUIは提供しておらずデータ連携バッチのみが動くような形をとっております。

カンリーローカル在庫で達成したいこと

結論、オンラインからお客様の実店舗へ来店を促すことです(゜д゜)

カンリーではビジョンとして「店舗の顧客接点を最適化する」としてきました。
そして2026年からは「ヒトとAIの力で店舗の集客力を上げる」と新たにビジョンを掲げ、より店舗への集客といったところにフォーカスするようになりました。

そんな中、昨今OMOが注目されておりGoogle Business Profileを始めとする店舗DXに強いカンリーとしてもお客様に対して新たな価値貢献としてローカルインベントリーに目をつけオンラインから実店舗来店を促す仕組みを構築したいと考えていました。

初期フェーズとしては5社のGoogleローカル在庫連携を完了させ、公式パートナーとなることを目指してきたのが始まりです。

詳細は冒頭に記載したLPやプレスリリースを御覧ください(´-`)

ITリテラシーが高くなくても使ってもらうために

LFP認定取得を目指す上で、まずは複数社との連携実績を作ることが必要でした。
ローカルインベントリーを達成するためにはお客様から商品データや在庫データをお借りし適切な形式でMerchant Centerへ連携しなければなりません。

データ連携の仕組みはカンリー側の責務としつつ、商品や在庫データについてはお客様にご用意いただきカンリーへ連携していただく必要があったのです。

本来であればAPI連携などが望ましいとは思うのですが、PoCということもあり時間的な制約や開発コストを最小限に抑えること、お客様側にもできる限り負担の少ない形で且つIT担当でなくても簡単なレクチャで商品、在庫データを連携する方法を考えました。

結果として初期フェーズではお客様にGoogle OAuth認証を行っていただいた後、SFTP経由で簡単にファイルをアップロードできる仕組みを構築しました。簡単なアーキテクチャ図を以下に記します。

SFTPにはAWS Transfer Familyを採用していますが、やはりお客様の中には「鍵認証がわからない、パスワードにしてほしい」といった声も複数ありTransfer Familyだけではパスワード認証を実現することが困難だったため、AWS Lambdaで認証部分を自前実装しています。

しかし、それによって海外からの不正アクセスが大量に増え、現在は国内IPに限定しホワイトリストでブロックしている状態ですが、近い将来、認証部分をVPCの中に入れ適切にネットワークを設計し、或いはSFTPからAPIへ完全移行し技術的負債の返済に取り組んでいくつもりです。

Merchant API連携

ここからはサービスの肝となるMerchant APIの実装についてです。
そもそも私はMerchant Centerなんてもの自体を知りませんでしたし社内にもナレッジを持っている人はいませんでした。なので本当にゼロからのスタートでした。

しかし、幸いなことに期間限定でGoogleの技術者をサポートにつけて下さることになり、定例でオンラインMTGを組みLFP獲得に向けて必要なことの洗い出しや、手順などについてもたくさん質問させていただけました。

余談ですが、開発当時はContent APIとMerchant API Beta(v1beta)が利用可能でした。将来的にMerchant APIへの移行が予定されていたため、Merchant API Betaを採用して実装を進めました。Merchant APIで実装されていないものはContent APIを使うなど混在した状態で開発を進め、時にはリファレンスの不備やAPIの不具合を私が見つけ、その場でフィードバックし、即日直してもらうといった貴重な経験もさせていただきました。

レートリミット・リトライ戦略

本記事のタイトルにも書いている雁字搦めをどのように解いたのかを書いていきます。

どのような課題があったのか?

データ連携にはAWS Lambdaを採用しました。
御存知の通り、AWS Lambdaには実行時間に制約があり最大15分までの実行時間となっています。
そのため、お客様からご提供いただく数万、数十万件ある商品、在庫データを素早くMerchant Centerへ連携しなければなりません。

このとき、考えられる方法としてはAWS Lambdaの同時実行数を増やす、goroutineで非同期並行で処理する、またはその両方だと思います。
しかし、ここで問題になってくるのがMerchant APIのレートリミット問題(゜д゜)
商品連携、在庫連携でそれぞれ1分間にコール可能な上限が設定されており、何も対策しないと秒で死にますι(´Д`υ)

リファレンスを見ると複数リクエストをバッチにまとめて送信するといった方法も紹介されているのですが、実はこれはリクエスト数を減らすだけでAPIのコール数はまとめた分もしっかりカウントされるため全く効果なしでした。

そこでアプリケーションコードの方でセマフォを実装し、且つエクスポネンシャルバックオフも導入しました。同時にAPIをコールできる件数を絞り、APIのレスポンスで429が発生した場合に待機時間を伸ばしていくというものです。

商品や在庫件数が少ない場合においてはこの方法で充分解決できたのですが、連携される店舗数×在庫数となると、とんでもないデータ量となっていきます。
そのため、レスポンス429が頻発しエクスポネンシャルバックオフが発動すると待機時間がどんどん伸びていき、結果AWS Lambdaの実行時間制約に引っかかってしまうことになってしまいました。

どのように解決したのか?

結論、レスポンス429が発生した時点でプロセスを切りAWS Lambdaの処理を終了させてしまうという方法です。

上で記したアーキテクチャ図の通り、データ連携用のAWS Lambdaの実行契機はすべてSQSトリガーによるもの。商品や在庫のデータサイズが非常に大きくなるため、連携にはCSV或いはNDJSONを採用することになりますが、初期フェーズにおいてはお客様にも比較的わかりやすいCSVを採用しています。

SQSトリガーで発火したデータ連携用のAWS Lambdaは、CSVファイル内のレコードを数十から数百のバッチサイズにまとめMerchant Centerへ連携します。処理の途中でレスポンス429を検知するとCSVの未処理レコードのみを別CSVとして作成し、元のS3へとアップロードし直しAWS Lambdaを終了します。そうすることで再度、対象データは未処理分のみSQSに追加されることとなり別のAWS Lambdaが処理を開始しリトライを実現しています。

このときに注意すべきポイントとしては以下の2点です。

  • リトライはレスポンス429に限定する
  • Delay Queuesの設定を使う

この方法でレスポンス429以外もリトライしてしまうと、キューメッセージは常に新しいものとなるため、デッドレターキューを設定していても基本的には拾われず、無限ループに陥ってしまいます。
また、Delay Queuesの設定を使うことでレートリミットに引っかかった後も即座に処理が再開されずレートリミットが解除されたタイミングで処理を再開することが出来ます。

アプリケーション内でエクスポネンシャルバックオフを実現するのではなく、アーキテクチャレベルで実現するようにしたことでAWS Lambdaの実行時間制限を回避しつつ、効率良くリトライができるようになりました。

以上が雁字搦めを解いた取り組みとなります。

AIツールの使い分け

ここからは開発を進めていく過程で、いくつかのAIツールを使ってきたので、その使い分けについて私なりの解釈を含めながらどのように使ってきたのかを書きたいと思います。

とは言え、正直なところ明確な根拠やルールを持って使い分けているわけではないです(꒪⌓꒪ )
ただ、試行錯誤していく中で自分に合った使い方を見つけ型化していくのが良きだと思ってます。
私の場合、以下のように使い分けました。

Gemini

プロジェクト立ち上がり当初は、とにかく社外のMTGが多かったです。
いつ、どのお客さんと、なんのMTGをするのか‥
カレンダーをチェックし、メールをチェックし、議事録・アジェンダ(Docs)をチェックする。
毎日のこの作業が結構面倒だったため、Geminiに抽出してもらい要約をしてもらっています。

意外にもChatGPTはGeminiよりも検索性に優れていると言われていてMerchant APIやローカルインベントリーについての調査に使っていました。ただ、未だ一定のハルシネーションはあるため必ず根拠やエビデンスを一緒に提出するよう指示し、最終的な判断は自分の目で確認するようにしています。
(余談ですが、Anthropicの研究チームがAIのディセプションを確認する実験をやったとの記事を見て、怖いなと思いつつSF感満載でちょっとワクワクした^∀^)
Gemini3がリリースされて以降は、正直ChatGPTは全然使わなくなって今はAI on Google Searchをめちゃくちゃ重宝しています。ChatGPTや他のAIはインデックスが遅く情報鮮度に不安がありましたが、AI on Google Searchはかなり鮮度の高い情報を提供してくれ、GenerativeUIも導入されていることから情報収集や、コストなどのシミュレーションなどで活用しています。

Claude Code

長文理解に長けていて、計画的に実装を進めていくことからComposer-1よりも実装時は扱いやすかったように思います。役割に応じたサブエージェントやMCPを用い、スムーズに実装を進めることが出来ました。微妙に痒いところに手が届かないシーンや、指示に対してヌケモレが目立ったりもしましたが、コーディングはClaude Code、レビューはComposer-1というサイクルが一番しっくり来たように思います。

Cursor

プロジェクト初期はコーディングにもCursorを使っていたのですが、使用する時間帯によってめちゃくちゃモッサリしたり、既存コードの理解には長けているがゼロから新規を作るときにはなんとなく使いづらさを感じていたので、リファクタリング時の全体解析や提案、コードレビューにフル活用しました。

Notion MCP

基本的にはClaude Codeから毎日TODOや技術負債、課題管理、全体進捗などを記録するようにしていました。claude -cでセッションが切れても直前までの会話やコンテキストを引き継げたりはするものの、API Error: 400 Input is too long for requested model.が発生すると既知のバグによりClaude Codeを再起動しなければ全く動かなくなるため小まめに進捗は記録するようにしていました。また、コードベースから仕様書を外部ドキュメントに残すことができるため忘れがちな仕様書の更新も、実態と乖離の少ないドキュメントを常にアップデートすることができる点が非常に便利だと思っています。

まとめ

Google LFP獲得という目標に向けて、1人エンジニアとして幅広く対応してきた180日間でした( ꒪⌓꒪)

技術的にはレートリミット対策やモノレポ化など課題も多くありましたが、AIツールを活用しながら試行錯誤を重ね、何とか乗り切ることができました。完璧な使い分けルールがあったわけではなく、各ツールの特性を理解しながら自分に合った形を見つけていった感じです。(とはいえ、まだまだ課題はモリモリヽ(゜▽、゜)ノ)

突貫開発で積み上がった技術的負債とも向き合いながら、今後もより良いサービスを提供できるよう改善を続けていきます(゜д゜)

カンリーテックブログ

Discussion