Slide 1

Slide 1 text

Firestore → Spanner 移⾏ を成功させた段階的移⾏プロセス Shingo Ichijima (@shinichiji)

Slide 2

Slide 2 text

⾃⼰紹介 ● 名前 ○ 市島 慎吾 (Shingo Ichijima) ● 会社 ○ 富⼠ゼロックス (2016/04 ~ 2018/08) ○ バイセルテクノロジーズ (2018/09 ~ 2019/12) ○ ラクスル (2020/01 ~ 2023/01) ○ カウシェ (2023/02 ~ ) ● 役割 ○ Engineering Manager (Backend) ● アカウント ○ X: @shinichiji ○ GitHub: @a-thug

Slide 3

Slide 3 text

会社紹介

Slide 4

Slide 4 text

プロダクト 野菜がもらえる お買い物アプリ

Slide 5

Slide 5 text

今⽇お話しすること ● 🔥 なぜFirestore→Spanner移行が必要だったのか ● 🪜 どうやって移行したか ● 🌟 移行にあたって幸運だった点 ● ⚠ トラブル及び解決策 ● 🎉 結果とまとめ 具体の実装例などの詳細はZennの記事で! https://zenn.dev/kauche/articles/1e733da3748ee1

Slide 6

Slide 6 text

🔥 なぜ移⾏が必要になったのか

Slide 7

Slide 7 text

🔥 なぜFirestore→Spanner移⾏が必要になったのか 主な課題 ● 🏢 事業フェーズの変化 ○ データモデルが安定してきたため、スキーマレスの必要性が低下 ○ NoSQLよりもRDBへの経験があるバックエンドエンジニアの⽅が多くなった ● 🐌 Firestore固有の制限 ○ クエリの制約 ○ 複雑な検索の困難さ ● 💰 コスト問題 ○ 読み取り/書き込み従量課⾦で費⽤が急増 ○ 2024年4⽉段階ではFirestoreにCUD(Committed Use Discount)は無かった

Slide 8

Slide 8 text

なぜ移⾏を決断したのか ● ⭕ Firestoreは初期選択として適切だった ○ サービス開始した2020年当初、Spannerはnode単位でインスタンスを⽴てる必要があり⾼額 だった ○ 成功するかもサービス⽅向性が定まるかも全く⾒えていない状況下で、当時は運⽤コストも抑 えたかった ● 📊 事業成⻑でコスト問題が深刻化 ○ 事業ピボットを経て、よりユーザのアクセスが増えるビジネスモデルへと変化した結果、コスト 問題の顕在化がかなり早くなった ○ 「重要だけど緊急ではない」が「重要かつ緊急」となった ● 🎯 コストや各種KPIの伸び⽅を考えると、今ここで着⼿するしかないとなった ○ キャッシュするなど、⼀時的な対応をしていたが焼け⽯に⽔だった ○ 根本的な解決が必要なタイミングとなった

Slide 9

Slide 9 text

🪜 どうやって移⾏したか

Slide 10

Slide 10 text

どうやって移⾏するか 移行にあたっての制約 ● メンテナンスに入れることなく安全に移行したい ● 今かかっているコストは早く下げたい ● 移行に関する開発作業をやっている間であっても、機能開発は止めたくない 選択肢の⽐較 ● 全コレクション⼀気に移⾏ ○ 移⾏⾃体は短期間で終わるが影響範囲が巨⼤でリスク⼤ ○ リリースにあたっての予⾏演習やSpannerのウォームアップが必要 ● ⭐ コレクション単位で段階的移⾏ ○ ちょっとずつトラフィックを流していくのでリスクを分散できる ○ Firestore, SpannerのTransactionが被ったときの扱いが難しい

Slide 11

Slide 11 text

コレクション単位での段階的移⾏を選んだ理由 段階的移⾏のメリット ● 🛡 影響範囲の限定 : 問題が起きても移行対象のコレクションのみ ● 📈 段階的な効果実感 : コスト削減を徐々に確認 ● 📊 優先順位付けが可能 : 移行効果の高いもの、移行難易度が低いものから着手 ● 🔄 ノウハウの蓄積 : 小さく移行を繰り返すことで、不確実性を減らす 優先順位の考え⽅ 1. 読み書きが低頻度 (例: 社内の管理画面からしかアクセスされない) 2. TransactionがSpannerのみで完結できて、移行がしやすい 3. 移行するとコストが大きく下がる 4. ユーザや注文データなど、サービスの根幹に近い

Slide 12

Slide 12 text

4段階の移⾏プロセス 1. Double Write(両DB書き込み) ● FirestoreとSpanner両方に書き込み開始 ● 先にFirestoreに書き込み、エラーがなければSpannerにも書き込む 2. データ移行 ● 既存データをスクリプトでFirestore→Spannerへ移行 3. Read切り替え ● 読み取りをSpannerに切り替え ○ FirestoreのKey VisualizerでReadがなくなっていることを確認する ● 書き込みはFirestore, Spanner両方継続 ● 2 → 4は一足飛びで可能といえば可能だが、瞬間的に古いCloud Runインスタンスへのトラフィッ クが残っている場合を考慮し、両方に書き込んでいた 4. Write切り替え ● 書き込みをSpannerのみに変更 ● これにてコレクションの移行を完了とする

Slide 13

Slide 13 text

🌟 移行にあたって幸運だった点

Slide 14

Slide 14 text

🔥 当時(2024年5⽉)のカウシェの技術スタック及びチーム状況 ● マイクロサービスとしては3サービス ○ 最初に作られたECを担当するサービスがFirestoreを使っていた ○ 残り2サービスは後発で、Spannerを使っていた ● バックエンドはGoで書かれており、Cloud Runでホスティング ● バックエンドエンジニアは⾃分含めて4⼈ ● 移⾏作業専任として 0.5⼈/⽇ ○ メインの移行作業は柴田芳樹さん(@yoshiki_shibata)
 ■ Go言語 100Tips
 ■ Web API設計実践入門 
 ○ 改修箇所とオーバーラップしたり、コストの兼ね合いで早く移⾏したいコレクションは別 のメンバーで移⾏したりもあった

Slide 15

Slide 15 text

移⾏するにあたって幸運だったこと (1) ● 切り戻しがしやすい環境だった ○ Cloud Runでホスティングしていたため、Read切り替えを⾏ってエラーが出ていた場合 は⼀瞬で前のrevisionに切り戻すことができた ● 移⾏作業がRepositoryレイヤーだけで済むようなコードベースのアーキテクチャ になっていた ○ カウシェのコードベースではクリーンアーキテクチャを採⽤していた ○ SpannerリポジトリがFirestoreリポジトリを内包し、メソッド単位で段階的に移行できる仕組みを採 用することができた ● APIレベルでのE2Eテストがしっかり書かれていた ○ 振る舞いが変わっておらず、移⾏によるデグレがないことを確認しやすかった ○ テスト⽤データは正規のAPI経由で作ることが原則で、DB実装に依存する箇所はほぼ無 かったためテストがpassしてるなら問題ないよね、という判断がしやすかった

Slide 16

Slide 16 text

移⾏するにあたって幸運だったこと (2) ● ⼤半の処理が冪等に作られていた ○ FirestoreとSpannerではclient libraryがtransactionのAbortや競合を検知すると自動でリトライす る ○ 冪等かどうかを検証するE2Eテストが存在していた ● あまりFirestoreに依存していなかった ○ コレクションの階層は深くて3階層だったので、NoSQLから乗り換えるにあたって頭を悩 ますべきことが少なかった ○ 使われ⽅としてもNoSQLをフルに活⽤しているというよりも、RDBライクな使われ⽅をし ていた ● Coding Agentの発達 ○ 移⾏後半になると簡単なコレクションの移⾏作業は型化されて、Coding Agentでもでき るようになった

Slide 17

Slide 17 text

⚠ トラブル及び解決策

Slide 18

Slide 18 text

⚠ それでも移⾏中に直⾯したトラブル/課題と解決策 ● ⚖ 1つのAPIの処理の中で、トランザクションを2回に分けてコミットしている箇 所があった ○ 両DBのロールバックが難しい ○ 特別な制御フローを実装
 ■ 1回目のFirestoreトランザクションコミット時はSpannerへのコミットをスキップ
 ■ 2回目のFirestoreトランザクションが成功してからSpannerに書き込みを行う ● 🔍 Write切り替え後にデータの⽋損が発覚 ○ ⾃分がCoding Agentでガンガン移⾏させていたコレクションで発覚 ■ 全て完全に冪等にできていたわけではなかった... ○ ビジネスロジック側でも冪等性を保つため、処理に使用するいくつかの値をハッシュ化してIDとし て使うことで一意性を担保
 ○ BQに残っているデータをかき集めて突合し、データ復旧


Slide 19

Slide 19 text

🎉 結果とまとめ

Slide 20

Slide 20 text

結果:1年間かけたDB移⾏で達成できたこと ● 💰 DB費⽤93%削減 ○ Firestoreと⽐較して⼤幅なコスト削減 ○ 稼働は0.5⼈/⽇なので、実質半年分の⼯数で出来たとも⾔える ● ⚡ パフォーマンス‧開発効率向上 ○ 複雑なクエリの実⾏時間短縮 ○ SQLの表現⼒による実装の簡素化 ● 📊 分析クエリの効率化 ○ 外部データセットが使えるのでBigQueryとの連携による効率化 ○ Extensionsで使われていたCloud Run Functionsも不要になり、そのコストも浮いた

Slide 21

Slide 21 text

コレクションの段階的移⾏の費⽤インパクト 請求先変わって、旧請求先に来ていた分を合算

Slide 22

Slide 22 text

重要な学び ⼟台が⼤事! ● 今回は移⾏にあたって、幸運な要素が多かった ○ 切り戻しやすさ ○ クリーンアーキテクチャによるDBの⼊れ替えやすさ ○ E2Eテストというガードレール ○ Firestoreへの依存度⼩ ● これらの要素が⽋けていると、困難だった ○ これらは移⾏を⾒据えたからやっていたわけではなく、インフラの初期選択の良さや開発効率 や品質を上げる取り組みとして根付いていた⽂化だった ○ 逆に⾔えば脱Firestoreするならこれらをやっておくと良い、ということ ■ 何かを変える変えないに関わらず、⼟台をちゃんと作っておくのは本当に⼤事

Slide 23

Slide 23 text

まとめ 今⽇覚えて欲しいこと ● 🔄 「重要だけど緊急ではない」は、いきなり「重要かつ緊急」になる ○ 課題の認識が⼤事 ○ 重要な問題については、緊急でなくとも⾝構えておく ● 🛡 不確実性を無くしていくプロセスでやっていこう ○ やってみなくちゃ分からないというのは「それはそう」なので、少しずつやって分かる部 分を増やしていく ● 🎯 ⼟台が最も⼤事 ○ アーキテクチャ設計‧テスト‧依存の少なさが成功の鍵 皆さんの移行プロジェクトの参考になれば幸いです!

Slide 24

Slide 24 text

No content

Slide 25

Slide 25 text

ご静聴ありがとうございました!