こんにちは。カヤックボンドの駒田です。
この記事は 面白法人グループ Advent Calendar 2022 の2日目を予定していた記事でした。
今年ももう終わりですね。
さて、何について書こうか悩みましたが…
過去にPHP Laravel x Spanner の組み合わせで苦労したことを書いてみようと思います。
1.Spannerとは
Cloud Spanner は、フルマネージドのミッション クリティカルなリレーショナル データベース サービスです。グローバルなトランザクション整合性、高可用性のための自動の同期レプリケーション、2 つの SQL 言語(Google 標準 SQL(拡張機能を含む ANSI 2011)と PostgreSQL)が含まれています。
Cloud Spanner ドキュメント | Google Cloudより
Google Cloud の Spanner というフルマネジメントRDBのサービスですね。
最近は結構あちこちで採用されている印象です。
2.Spannerの特徴
NewSQLなどと呼ばれているものの一種で
- SQLが実行可能(2022年6月にPostgre SQL正式互換!)
- フルマネージメント
- オートシャーディング
なのが特徴です。
概ねフルマネージメントかつオートシャーディングを目当てに導入される印象ですね。
その分コストが高いのですが、シャーディングが必要になる規模ですと
ユーザーデータの格納先用途等で、候補に挙がってくるサービスじゃないかな、と思います。
ちなみに、昔よりもこまかーいユニットという単位で立てれるようになったので
最小構成で立てる場合はそこまで高くないようです。
1ノードだと、2週間たたずに無料枠クレジットが吹っ飛んでいたのでこれはありがたい!
https://cloud.google.com/blog/ja/products/databases/use-spanner-at-low-cost-with-granular-instance-sizing
3.PHP(Laravel)とSpanner 導入
そんなSpannerのためのクライアントは各種言語が用意されていますがGoで触る人がやはり多いようで
phpで扱おう!というケースはあまり無いんじゃないかな、と思います。
私が扱ったタイミングは少し前になってしまうのですが、
その当時もやはりphpで扱おうという人は同様にまだあまり多くは無く…。
どうしたもんかな、と思っていた中、コロプラさんがLaravelでSpannerを扱うためのOSSを提供しておりました。
有難くこちらを利用させて頂き、比較的スムーズに導入することができました。
その後、MySQLに存在する機能が使えずにMigration(afterが使えなかったりした)や
その他クエリ実行でコケたりと細かい苦労はありつつ、とはいえ比較的スムーズに導入、実装は進みました。
(今はエミュレータがありますし、機能の対応範囲も増え、このあたりの苦労は激減しています。)
で、わーいやったぜ。これでRDBは安心だーと思っていた、のですが…
4.導入して困った事
負荷試験をしたところ、Spannerの接続周りが明らかにボトルネックになってしまっていました。
各種Spanner導入記事にも「sessionの構築にはコストがかかる、session poolはしっかり管理せえよ」
と記載があることが多いですが、見事にそこにハマった形です。
では、いったい何が原因だったのか。
いくつかあるので順番に説明していきます。
4-1. flockによる排他制御
Laravel-Spannerでは、Spannerへの接続にCloudSpannerのセッションをプールしておくSessionPoolのキャッシュ、
そしてGCPのアクセストークンのキャッシュであるAuthCacheを利用しているようで、
デフォルトですと、このキャッシュはファイルキャッシュとしての実装がされていました。
このプロジェクトにおけるデフォルトの排他制御はflockが使われており
これによるロック解除待ちが発生し、1つずつ順番にしかアクセスできないような状態になっていました。
そのためまずこの形式をセマフォへ変更してみたところ
全体的な性能の改善は見られませんでしたが、ロック周りの時間は短くなり、負荷の傾向に変化がみられました。
4-2. ファイルキャッシュを別のキャッシュ形式へ
負荷の傾向が変わった、ということはボトルネックの箇所が変わった、ということで
一旦ロックはこのままで、別のアプローチをとることにしました。
Laravel-SpannerのReadMeには下記の記載があり、そもそもキャッシュを何で持つか、を変更できるようになっていました。
By default, laravel-spanner uses Filesystem Cache Adapter as the caching pool. If you want to use your own caching pool, you can extend ServiceProvider and inject it into the constructor of Colopl\Spanner\Connection.
そのためファイルキャッシュから共有メモリ内へのキャッシュへと変更を行うことで改善を図りました。
これを行ったところある程度の性能改善がみられました…が、
セッションプールの異常な増加、一定の負荷を超えるとメモリ不足になる、といったような事象が発生し始めてしまいました。
4-3. 共有メモリキーの分離
共有メモリの格納はSysVCacheItemPoolを利用して実装していましたが
パラメータにproj
というものがありました。
ftokによる共有メモリキーの指定に使われる値であり、デフォルトだとA
が指定されます。
そのため未指定の場合だと、SpannerのセッションもAuthCacheも常にA
の指定になってしまっていました。
これに加え、AuthCacheについてはロック処理は入っていないことが分かったことで
おそらく
同一の領域が参照された場合に共有メモリに格納されたキャッシュデータが破壊される
→ 破壊された部分が使われないゴミキャッシュとして中途半端に残る
→ 新規で作られたセッションやAuthCacheが書き込まれ、
→ また破壊され…のループでメモリが溢れる。
というような状態になってしまっていたものと思われました。
この対応として、明示的にproj
を分けて共有メモリに書き込むようにすることでボトルネックは解消し
おおむね予想していた性能がでる結果となりました。
もちろんこの後も細かい諸々だったり付随する対応はあったのですが、大きなハマりはこのあたりだったかなと思います。
5. 他、quotaについてなど
これは言語に依存する部分ではないですが、spannerについてその他での注意点といえばquota周りですよね。
https://cloud.google.com/spanner/quotas?hl=ja
何かと困るのはミューテーション制限!
バッチ処理とかでもあっさり引っかかる値なので、十分に注意が必要ですね。
ちなみに22年10月に倍に増えたらしいです、やったね。
値 | 上限 |
---|---|
commit サイズ(インデックスを含む) | 100 MB |
セッションあたりの同時読み取り数 | 100 |
commit あたりのミューテーション(インデックスを含む) | |
データベースあたりのパーティション化 DML の同時実行ステートメント数 | 20,000 |
Cloud Spanner のトランザクションあたりの更新回数が 2 倍に増加 | Google Cloud 公式ブログ ※ミューテーションの推定なども詳しく書いてあるので是非。
というわけでPHP Laravel x spanner の組み合わせの話でした。
この組み合わせ、あまり情報も多く無いので、もし今後同じ組み合わせを試す人が居たときに参考になるかなあと思い記事にしてみました。
いやあ、いいサービスですよね。Spanner。
それでは皆様、良いお年を。