Slide 1

Slide 1 text

©Fusic Co., Ltd. 0 S3操作の落とし⽳から学ぶ Laravel File Storageと例外処理 2024.07.02 Mai Miyazaki @maimyyym Fusic Tech Live Vol.20 〜PHPer CONNECT〜

Slide 2

Slide 2 text

©Fusic Co., Ltd. 1 はじめに ⾃⼰紹介 / 今⽇話すこと 0

Slide 3

Slide 3 text

©Fusic Co., Ltd. 2 宮崎 真⾐ Miyazaki Mai HN: mai (@maimyyym ) ◉ I am - 管理栄養⼠(養成校卒業・資格保持のみ) - 元百貨店スタッフ(Beauty Counselor) - 2023年10⽉ Fusic⼊社 ◉ Skill - PHP / AWS / TypeScript / Python ◉ Comment - Tech Live初登壇です!うれしい! ⾃⼰紹介 はじめに 事業本部 技術創造部⾨ / エンジニア 株式会社Fusic

Slide 4

Slide 4 text

©Fusic Co., Ltd. 3 今⽇話すこと はじめに Laravel on FargateでのS3操作 Laravel Filesystemにおける例外処理 について経験して、学んで、考えてみた話

Slide 5

Slide 5 text

©Fusic Co., Ltd. 4 CONTENTS ⽬次 1. こんな構成・処理 2. Laravelのファイルシステム 3. 困ったこと・その答え 4. 例外処理の話 / どうしたらよかったのか 5. まとめ

Slide 6

Slide 6 text

©Fusic Co., Ltd. 5 こんな構成・こんな処理 環境 / Laravel on FargateでS3操作したい / 実装 1

Slide 7

Slide 7 text

©Fusic Co., Ltd. 6 環境 こんな構成・こんな処理 PHP8.3 Laravel10

Slide 8

Slide 8 text

©Fusic Co., Ltd. 7 Laravel on FargateでS3操作したい こんな構成・こんな処理

Slide 9

Slide 9 text

©Fusic Co., Ltd. 8 LaravelのStorageファサードで実装する こんな構成・こんな処理 try { Storage::disk("s3")->copy( $sourcePath, $distinationPath ); } catch (\Exception $e) { Log::error(''. $e->getMessage()); return redirect()->route(‘user.top’)->with(‘error’, ‘copy失敗'); } return redirect()->route('user.top')->with('success', 'copy成功'); Storageファサードは抽象化された Filesystemによって、ストレージの 種類によらずコピー等の操作を 簡単に実装できる。 Storage::disk(ʻs3ʼ)->copy($from, $to);

Slide 10

Slide 10 text

©Fusic Co., Ltd. 9 Laravelのファイルシステム Flysystem / 使い⽅ / 動かしてみる 2

Slide 11

Slide 11 text

©Fusic Co., Ltd. 10 Flysystem Laravelのファイルシステム Laravelのファイルシステムは、Flysystemというライブラリを使⽤して 実装されている。 【 Flysystem とは?】 PHPのためのファイルストレージライブラリ。 異なるストレージドライバ(ローカル、S3、FTPなど)に対する ⼀貫したインターフェースを提供する。 ストレージシステムの種類を問わず、 コードを変更することなく簡単に切り替えることができる。

Slide 12

Slide 12 text

©Fusic Co., Ltd. 11 使い⽅ Laravelのファイルシステム 1. アダプタをインストールする 2. ファイルシステム設定(config/filesystems.php) 3. 使う: composer require league/flysystem-aws-s3-v3 "^3.0" --with-all-dependencies 's3' => [ 'driver' => 's3’, 'key' => env('AWS_ACCESS_KEY_ID’), 'secret' => env('AWS_SECRET_ACCESS_KEY’), 'region' => env('AWS_DEFAULT_REGION’), 'bucket' => env('AWS_BUCKET’), 'throw’ => false, ] Storage::disk("s3")->xxx();

Slide 13

Slide 13 text

©Fusic Co., Ltd. 12 動かしてみる Laravelのファイルシステム Storage::disk("s3")->copy( $source/hoge.png, $copy/hoge.png ); "POST /xxx.php" 200 logs コピーできてない!

Slide 14

Slide 14 text

©Fusic Co., Ltd. 13 困ったこと・その答え コピー処理ができていない / 考えたこと / 答えは・・・ 3

Slide 15

Slide 15 text

©Fusic Co., Ltd. 14 コピー処理ができていなかった 困ったこと・その答え 【やろうとしたこと】 S3に保存された画像ファイルのコピー操作や その他DB操作等を含む⼀連の処理 《⼊⼒》 ↓ 処理A ↓ 処理B ↓ S3コピー処理 ↓ 《出⼒》

Slide 16

Slide 16 text

©Fusic Co., Ltd. 15 コピー処理ができていなかった 困ったこと・その答え 【やろうとしたこと】 S3に保存された画像ファイルのコピー操作や その他DB操作等を含む⼀連の処理 【結果】 レスポンス:200OK しかし、S3のコピー処理だけができていない ※その他DB操作等の処理は全てできていた 《⼊⼒》 ↓ 処理A ↓ 処理B ↓ S3コピー処理 ↓ 《出⼒》 OK! OK! Failed…😰 200OK $request

Slide 17

Slide 17 text

©Fusic Co., Ltd. 16 コピー処理ができていなかった 困ったこと・その答え 【やろうとしたこと】 S3に保存された画像ファイルのコピー操作や その他DB操作等を含む⼀連の処理 【結果】 レスポンス:200OK しかし、S3のコピー処理だけができていない ※その他DB操作等の処理は全てできていた 《⼊⼒》 ↓ 処理A ↓ 処理B ↓ S3コピー処理 ↓ 《出⼒》 OK! OK! Failed…😰 200OK $request コピーしたオブジェクトの 取得処理ができず、気づいた

Slide 18

Slide 18 text

©Fusic Co., Ltd. 17 考えたこと 困ったこと・その答え CPU使⽤率が⾼くなる… move() もできない! get() → put() だと うまくいく Filesystemの裏側の 処理に違いが? ロジックに問題が?

Slide 19

Slide 19 text

©Fusic Co., Ltd. 18 考えたこと 困ったこと・その答え CPU使⽤率が⾼くなる… move() もできない! get() → put() だと うまくいく Filesystemの裏側の 処理に違いが? ロジックに問題が? でも・・・ 裏側が複雑な実装だとは思えない! 【 copy() 】と【 get() → put() 】で ⼤きな差が出るとは思えない…

Slide 20

Slide 20 text

©Fusic Co., Ltd. 19 考えたこと 困ったこと・その答え CPU使⽤率が⾼くなる… move() もできない! get() → put() だと うまくいく Filesystemの裏側の 処理に違いが? ロジックに問題が? では、原因は何…?

Slide 21

Slide 21 text

©Fusic Co., Ltd. 20 答えは・・・ 困ったこと・その答え ポリシー不⾜でした。

Slide 22

Slide 22 text

©Fusic Co., Ltd. 21 { "Version": "2012-10-17", "Statement": [ { "Action": [ "s3:ListBucket", "s3:PutObject", "s3:GetObject", "s3:PutObjectAcl", "s3:GetObjectAcl", ], "Effect": "Allow", "Resource": "*" }, ] } 【正しくは…】 答えは・・・ 困ったこと・その答え ポリシー不⾜でした。 タスクロール { "Version": "2012-10-17", "Statement": [ { "Action": [ "s3:ListBucket", "s3:PutObject", "s3:GetObject", ], "Effect": "Allow", "Resource": "*" }, ] } 【現在】

Slide 23

Slide 23 text

©Fusic Co., Ltd. 22 答えは・・・ 困ったこと・その答え ドキュメントがあった。 またコンソール内のオブジェクトのコピー、カット、貼り付けを⾏う ためには、s3:PutObjectAcl および s3:GetObjectAcl アクションが必要 となります。 https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/example-policies-s3.html#iam-policy-ex3

Slide 24

Slide 24 text

©Fusic Co., Ltd. 23 S3のアクセスコントロールリスト(ACL) 困ったこと・その答え PutObjectAcl, GetObjectAclとは? 【Amazon S3のアクセスコントロールリスト(ACL)とは】 バケットとオブジェクトへのアクセスを管理するもの。 各バケットとオブジェクトにはサブリソースとしてACLがアタッチされる。

Slide 25

Slide 25 text

©Fusic Co., Ltd. 24 S3のアクセスコントロールリスト(ACL) 困ったこと・その答え PutObjectAcl, GetObjectAclとは? 【Amazon S3のアクセスコントロールリスト(ACL)とは】 バケットとオブジェクトへのアクセスを管理するもの。 各バケットとオブジェクトにはサブリソースとしてACLがアタッチされる。

Slide 26

Slide 26 text

©Fusic Co., Ltd. 25 ACL ACL S3のアクセスコントロールリスト(ACL) 困ったこと・その答え PutObjectAcl, GetObjectAclとは? ACL ACL 同じ状態を複製する=コピーという操作には オブジェクトとACLの両⽅を取得・保存する権限が必要 GetObject GetObjectAcl

Slide 27

Slide 27 text

©Fusic Co., Ltd. 26 答え 困ったこと・その答え ポリシー不⾜は分かった。copy()は使えるようになった。 でも・・・ この問題が発⽣した根本的な原因は? なぜ成功を⽰すレスポンスが? 失敗をキャッチできなかったのは何故?

Slide 28

Slide 28 text

©Fusic Co., Ltd. 27 例外処理の話 / どうしたらよかったのか copyメソッドの中⾝を⾒てみる / Flysystem3.xの仕様 /例外 vs false 仕様と向き合い、仕様を定める 4

Slide 29

Slide 29 text

©Fusic Co., Ltd. 28 copyメソッドの中⾝を⾒てみる 例外処理の話 / どうしたらよかったのか FilesystemAdapter.phpのcopyメソッドを⾒てみる public function copy($from, $to) { try { $this->driver->copy($from, $to); } catch (UnableToCopyFile $e) { throw_if($this->throwsExceptions(), $e); return false; } return true; } 私たちがコードとして書くストレージ操作と Flysystemを橋渡しするクラス

Slide 30

Slide 30 text

©Fusic Co., Ltd. 29 copyメソッドの中⾝を⾒てみる 例外処理の話 / どうしたらよかったのか FilesystemAdapter.phpのcopyメソッドを⾒てみる public function copy($from, $to) { try { $this->driver->copy($from, $to); } catch (UnableToCopyFile $e) { throw_if($this->throwsExceptions(), $e); return false; } return true; } 私たちがコードとして書くストレージ操作と Flysystemを橋渡しするクラス 返り値は true / false

Slide 31

Slide 31 text

©Fusic Co., Ltd. 30 copyメソッドの中⾝を⾒てみる 例外処理の話 / どうしたらよかったのか FilesystemAdapter.phpのcopyメソッドを⾒てみる public function copy($from, $to) { try { $this->driver->copy($from, $to); } catch (UnableToCopyFile $e) { throw_if($this->throwsExceptions(), $e); return false; } return true; } 私たちがコードとして書くストレージ操作と Flysystemを橋渡しするクラス 返り値は true / false $this->throwsExceptions() がtrueの時に例外を投げる

Slide 32

Slide 32 text

©Fusic Co., Ltd. 31 copyメソッドの中⾝を⾒てみる 例外処理の話 / どうしたらよかったのか throwsExceptions()を⾒てみる public function copy($from, $to) { try { $this->driver->copy($from, $to); } catch (UnableToCopyFile $e) { throw_if($this->throwsExceptions(), $e); return false; } return true; } protected function throwsExceptions(): bool { return (bool) ($this->config['throw'] ?? false); } config🤔

Slide 33

Slide 33 text

©Fusic Co., Ltd. 32 copyメソッドの中⾝を⾒てみる 例外処理の話 / どうしたらよかったのか つまり、copyメソッドはどのように例外を投げるのか public function copy($from, $to) { try { $this->driver->copy($from, $to); } catch (UnableToCopyFile $e) { throw_if($this->throwsExceptions(), $e); return false; } return true; } protected function throwsExceptions(): bool { return (bool) ($this->config['throw'] ?? false); } config[ʻthrowʼ] = 例外を投げる設定 がtrueであれば例外を投げ、 そうでなければfalseを返す

Slide 34

Slide 34 text

©Fusic Co., Ltd. 33 copyメソッドの中⾝を⾒てみる 例外処理の話 / どうしたらよかったのか 設定ファイルを⾒てみる 's3' => [ 'driver' => 's3’, 'key' => env('AWS_ACCESS_KEY_ID’), 'secret' => env('AWS_SECRET_ACCESS_KEY’), 'region' => env('AWS_DEFAULT_REGION’), 'bucket' => env('AWS_BUCKET’), 'throw’ => false, ] 【config/filesystems.php】 デフォルト設定 =falseのまま

Slide 35

Slide 35 text

©Fusic Co., Ltd. 34 copyメソッドの中⾝を⾒てみる 例外処理の話 / どうしたらよかったのか 呼び出し元を⾒てみる try { Storage::disk("s3")->copy( $sourcePath, $distinationPath ); } catch (\Exception $e) { Log::error(''. $e->getMessage()); return redirect()->route(‘user.top’)->with('error', 'copy失敗'); } return redirect()->route('user.top')->with('success', 'copy成功'); この処理結果を出⼒すると ”false ”だった。 例外をキャッチしていない

Slide 36

Slide 36 text

©Fusic Co., Ltd. 35 copyメソッドの中⾝を⾒てみる 例外処理の話 / どうしたらよかったのか 例外を投げる設定にする 's3' => [ 'driver' => 's3’, 'key' => env('AWS_ACCESS_KEY_ID’), 'secret' => env('AWS_SECRET_ACCESS_KEY’), 'region' => env('AWS_DEFAULT_REGION’), 'bucket' => env('AWS_BUCKET’), 'throw’ => true, ] 【config/filesystems.php】

Slide 37

Slide 37 text

©Fusic Co., Ltd. 36 copyメソッドの中⾝を⾒てみる 例外処理の話 / どうしたらよかったのか 再度、呼び出してみる try { Storage::disk("s3")->copy( $sourcePath, $distinationPath ); } catch (\Exception $e) { Log::error(''. $e->getMessage()); return redirect()->route(‘user.top’)->with('error', 'copy失敗'); } return redirect()->route('user.top')->with('success', 'copy成功'); 処理に失敗すると、 例外をキャッチするように!

Slide 38

Slide 38 text

©Fusic Co., Ltd. 37 Flysystem3.xの仕様 例外処理の話 / どうしたらよかったのか Laravel8までは例外を投げていた。 Laravel9で、Flysystemのバージョンが1.xから3.xに。 伴って、ファイル操作の仕様が⼀部変更。 その⼀つが書き込み失敗時に例外を投げなくなったこと。 (代わりに、falseを返すように) https://laravel.com/docs/9.x/upgrade#flysystem-3

Slide 39

Slide 39 text

©Fusic Co., Ltd. 38 例外 vs false 例外処理の話 / どうしたらよかったのか 仕組みは分かった。 例外を投げる設定をしていれば、確かに気づけた。 しかし、デフォルトはtrue / falseを返すだけ。 その思想は「アプリケーションを⽌めないこと」などが考えられる。 そもそもポリシーを知っていればよかった問題! テストフェーズで気づいたから成果物の挙動としては問題ない。 本当にそれでいいの?

Slide 40

Slide 40 text

©Fusic Co., Ltd. 39 例外 vs false 例外処理の話 / どうしたらよかったのか betterはどっち? 例外を投げる その場で失敗が分かるべき 詳細なエラーログを保存・通知したい falseを返す アプリケーションは⽌めない falseの場合の挙動ロジックを⽤意する

Slide 41

Slide 41 text

©Fusic Co., Ltd. 40 仕様と向き合い、仕様を定める 例外処理の話 / どうしたらよかったのか betterはどっち? 例外を投げる その場で失敗が分かるべき 詳細なエラーログを保存・通知したい falseを返す アプリケーションは⽌めない falseの場合の挙動ロジックを⽤意する ⼀つの正解はない

Slide 42

Slide 42 text

©Fusic Co., Ltd. 41 仕様と向き合い、仕様を定める 例外処理の話 / どうしたらよかったのか betterはどっち? 例外を投げる その場で失敗が分かるべき 詳細なエラーログを保存・通知したい falseを返す アプリケーションは⽌めない falseの場合の挙動ロジックを⽤意する ⾔語・フレームワークの 仕組みや仕様を知ること どうあるべきかを考える 前提

Slide 43

Slide 43 text

©Fusic Co., Ltd. 42 仕様と向き合い、仕様を定める 例外処理の話 / どうしたらよかったのか フレームワークの仕様と向き合い、 アプリケーションの仕様を定める

Slide 44

Slide 44 text

©Fusic Co., Ltd. 43 まとめ S3操作の落とし⽳から学ぶLaravel File Storageと例外処理 Laravelのストレージ操作はPHPのファイルストレージライブラリ”Flysystem”で抽象化されている Point 01 S3のコピー処理においては、オブジェクトだけではなくACLを取得・保存する権限を付与する必要がある Point 02 Laravelのストレージ操作において、書き込み失敗時のデフォルトは「例外を投げず、falseを返す」 Point 03 フレームワークの仕様と向き合い、アプリケーションの仕様を定めることが⼤切 Point 04

Slide 45

Slide 45 text

©Fusic Co., Ltd. 44 Thank You We are Hiring! https://recruit.fusic.co.jp/ ご清聴いただきありがとうございました