OpenWork Engineer Blog

OpenWork を運営するエンジニアによるテックブログです。

AWS SDK for PHP v2 → v3 へ移行したときの苦戦メモ

f:id:openwork_engineer:20191112213057j:plain
先日富山で撮影した、苦戦している椅子

インフラチームの小川です。普段は Web サービスにおけるクラウドインフラの構築/運用をしています。()
いまホットなのは構想だけが存在しているデータレイクの構成案を具現化するプロジェクトで、特に Web アプリケーション側から DWH の情報をいかに使いやすく取得できるかを、日々考えています。
前職から変わらずインフラエンジニアですが、裁量の大きさが前職の比ではなく、さすがベンチャーといえます。自由に動ける分責任も大きいため、冷や汗をかくことも多いですが、このような環境が成長を後押ししてくれている実感があります。

さて、そんな弊社では昨年 Symfony 3.4 へのアップデートを行いました。
同時に、 PHP ライブラリである AWS SDK for PHP も重い腰を上げ v2 から v3 へアップデートしたのですが、その際に色々とハマってしまいました。
ハマった中には、AWS 公式のアップグレードガイドに言及のないケースもちらほらとありました。あのとき味わったつらみをこの記事で供養してやりたいと思います。

前提

  • PHP 7.2.8
  • Symfony 3.4.17
  • AWS SDK for PHP 2.8.29 -> 3.69.7

基本: AWS 公式のアップグレードガイド

https://docs.aws.amazon.com/ja_jp/sdk-for-php/v3/developer-guide/getting-started_migration.html

基本的にはここに書かれている通りにソースを修正していけばよいはずです。
記載されている主な変更点を抜き出してみました。

  • プロジェクトの依存関係が更新
  • インスタンス化に regionversion が必須化
  • インスタンス化はファクトリメソッドではなく new になった
  • クライアント設定オプションが変更
    • Aws\Sdk オブジェクトが Aws\Common\Aws の置き換えとして導入
  • 一部 API の結果に ラップ要素 が追加
  • AWS の Enum クラスが削除
  • きめ細やかな例外クラスが削除。ルート例外クラスをキャッチしなければいけなくなった
  • enableFacades() の廃止
  • 結果の返却がイテレーターからページネーターに変更
  • 多くの 高レベルの抽象化が変更 (日本語でおk)

以上のことだけを意識すれば良い...わけではありませんでした。

ここに苦戦

AwsException で必須のコンストラクタ引数が増えて苦戦

エラーメッセージ

ArgumentCountError: Too few arguments to function Aws\Exception\AwsException::__construct(), 1 passed in /var/app/current/.../hoge.php on line 147 and at least 2 expected

解説

Aws 系の Exception をインスタンス化するとき、これまでは以下のように引数は最低 1 つだけあれば大丈夫でした。

$sesException = new SesException($errorMessage);

しかし、v3 からは新たに Aws\CommandInterface が必須の引数となりました。

construct ( string $message, Aws\CommandInterface $command, array $context = [], Exception $previous = null )
https://docs.aws.amazon.com/aws-sdk-php/v3/api/class-Aws.Exception.AwsException.html#
_construct

このときは SesException のスタブオブジェクトを作成したかったため、適当な Aws\Command オブジェクトを渡すことでエラーを回避しました。

// テストコードなので引数は適当
$sesException = new SesException($errorMessage, new Aws\Command('test'));

SesException::setExceptionCode() が廃止されて苦戦

エラーメッセージ

Error: Call to undefined method Aws\Ses\Exception\SesException::setExceptionCode()

解説

文字通り、SesException::setExceptionCode() が v3 で消滅しました。またもや、親クラスの AwsException の仕様変更のようです。
setter だけでなく、getter 系メソッドも以下のように名前を変えたので要注意です。

v2 v3
getExceptionCode() getAwsErrorCode()
getExceptionType() getAwsErrorType()
getRequestId() getAwsRequestId()

SES の ResponseMetadata から RequestId が取得できなくなって苦戦

エラーメッセージ

SES の sendEmail() を行ったあと、v2 では以下のようにしてリクエスト ID を取得していました。

$response = $sesClient->sendEmail($parameters);
$requestId = $response->get('ResponseMetadata')['RequestId'],

しかし、v3 では $requestId の値が空っぽになってしまいました。 AWS のサポートに質問したところ、

AWS SDK PHP v2 の sendEmail では Guzzle\Service\Resource\Model オブジェクトを返します。
V3 では sendEmail は AWS\Result オブジェクトを返します
キー名が ResponseMetadata ではなく、@metadata に変更されております

と返事をいただきました。すなわち、

$response = $sesClient->sendEmail($parameters);
$requestId = $response->get('@metadata')['headers']['x-amzn-requestid']

と書く必要があります。こうすることで、v3 でも RequestId を得ることができるようになりました。
なお、上記は正常系を想定しているので、sendEmail() に失敗したときは SesExceptiongetAwsRequestId() を使ってください。

deleteObjects() に Delete Parameter が必須となって苦戦

エラーメッセージ

InvalidArgumentException: Found 1 error while validating the input provided for the DeleteObjects operation:
[Delete] is missing and is a required parameter

解説

S3 で複数のオブジェクトを削除する際に使う deleteObjects() に、 Delete パラメータが必須となりました。
これまでは、削除したいオブジェクトの配列を Objects の値として渡せばよかったのですが、それをさらに Delete で wrap する必要があります。

$s3->deleteObjects([
  'Bucket' => 'hoge-bucket',
  'Objects' => [
    [ 'Key' => 'path/to/hoge' ],
    [ 'Key' => 'path/to/fuga' ],
  ],
])
$s3->deleteObjects([
  'Bucket' => 'hoge-bucket',
  'Delete' => [
    'Objects' => [
      [ 'Key' => 'path/to/hoge' ],
      [ 'Key' => 'path/to/fuga' ],
    ],
  ],
])

さいごに

細かな仕様変更がちらほらとありました。 弊社の場合、上記をふまえて影響調査をしたところ、思っていた以上に修正箇所が発生してしまい、結構な時間をロスしてしまいました。
PHP SDK をバージョンアップする際は、期間に余裕をもって的確に行うことが大切です。