Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
運用6年目・500万人が使うアプリのDBをSQLiteからFirestoreに移行した話(iO...
Search
Ryo Iida
September 18, 2021
Technology
2
5.2k
運用6年目・500万人が使うアプリのDBをSQLiteからFirestoreに移行した話(iOSDC 2021)
https://fortee.jp/iosdc-japan-2021/proposal/b1b94452-dd4b-4581-8797-f348919ee362
Ryo Iida
September 18, 2021
Tweet
Share
More Decks by Ryo Iida
See All by Ryo Iida
船橋高校出前授業
aviciida
0
94
Other Decks in Technology
See All in Technology
マルチプロダクト開発の現場でAWS Security Hubを1年以上運用して得た教訓
muziyoshiz
3
2.3k
【re:Invent 2024 アプデ】 Prompt Routing の紹介
champ
0
140
小学3年生夏休みの自由研究「夏休みに Copilot で遊んでみた」
taichinakamura
0
160
.NET 9 のパフォーマンス改善
nenonaninu
0
950
NilAway による静的解析で「10 億ドル」を節約する #kyotogo / Kyoto Go 56th
ytaka23
3
380
祝!Iceberg祭開幕!re:Invent 2024データレイク関連アップデート10分総ざらい
kniino
3
290
UI State設計とテスト方針
rmakiyama
2
600
第3回Snowflake女子会_LT登壇資料(合成データ)_Taro_CCCMK
tarotaro0129
0
190
PHPerのための計算量入門/Complexity101 for PHPer
hanhan1978
5
120
LINEスキマニにおけるフロントエンド開発
lycorptech_jp
PRO
0
330
2024年にチャレンジしたことを振り返るぞ
mitchan
0
140
10個のフィルタをAXI4-Streamでつなげてみた
marsee101
0
170
Featured
See All Featured
Imperfection Machines: The Place of Print at Facebook
scottboms
266
13k
How to Think Like a Performance Engineer
csswizardry
22
1.2k
Producing Creativity
orderedlist
PRO
341
39k
Optimizing for Happiness
mojombo
376
70k
Being A Developer After 40
akosma
87
590k
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
29
2k
Faster Mobile Websites
deanohume
305
30k
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
232
17k
Product Roadmaps are Hard
iamctodd
PRO
49
11k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
656
59k
Scaling GitHub
holman
458
140k
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
280
13k
Transcript
2021.9.17 Ryo Iida (@aviciida)
はじめに - 自己紹介 飯田 諒(@aviciida) • mikanのiOSエンジニア ◦ 🍊 mikan歴
3年(since 2018/9) ◦ 🍎 iOSエンジニア歴 2年 (since 2019/5) • PM/分析もやってます! • ユーザー目線でプロダクト作るの好き • Twitter活発です!@aviciida 2
3 mikanについて Mission 本質的なテクノロジー活用で あらゆる人の英語学習によりそい 人生の可能性を広げる 「英語を扱えるようになりたい、得意になりたい」人に効果的な手法を提供し、 語学が人生のボトルネックでは無くなる未来を作るのが私たちmikanです。
アプリダウンロード NO.1 コアバリューが支持され国内最大級に成長 4
• iOSエンジニア • Androidエンジニア • サーバーサイド・インフラ エンジニア • デザイナー (
時期を見て、週1回程度の出社を想定) 社員 (まずは副業からでもOK) ※基本リモート勤務 ご連絡はこちらから (Twitter DMでも!) https://mikan.link/carrers mikanは絶賛採用中です! 5
今日話すこと mikanのiOSアプリのデータベースを 組み込み型のSQLiteからCloud Firestoreに 9ヶ月ほどかけて移行した時の話をします 6
大前提: レガシーについて 本セッションは「レガシーを置き換えていく話」ですが、mikan では「レガシー」を前向きに捉えています。 まだ知識がない中で、スピード感持って事業を進めていくための 当時は最善の手段であり、そのおかげで今の500万DLがありま す。(mikanではレガシーではなく「ビンテージ」と読んでます) セッション内でもできるだけ配慮した表現を使いますが、大前提 として先人の方々には尊敬と感謝の念でいっぱいであることを共 有しておきます🙏(特に創業者の宇佐美さんと高岡さん)
7
アジェンダ ① なぜSQLiteからFirestoreに移行したのか? ・なぜ組み込み型SQLiteが採用されていた? ・当時のツラミ ・Firestoreを採用した理由 ・移行によって変わること ② 実際の移行の話 ・全体観
・ダブルライト ・マイグレーション ・クライアント側のreadロジック移行 ③振り返って ・うまくいったこと / やればよかったこと ・実際Firestore使ってみてどう? ・今回の学び 8
アジェンダ ① なぜSQLiteからFirestoreに移行したのか? ・なぜ組み込み型SQLiteが採用されていた? ・当時のツラミ ・Firestoreを採用した理由 ・移行によって変わること ② 実際の移行の話 ・全体観
・ダブルライト ・マイグレーション ・クライアント側のreadロジック移行 ③振り返って ・うまくいったこと / やればよかったこと ・実際Firestore使ってみてどう? ・今回の学び 9
組み込み型SQLite? 10 • DBを移行する前は、ユーザーの学習データは各端末のSQLite データベースに保存されており、サーバーと同期はしない形式 • バックアップのために、1日の最初の起動時に、DBファイルを AWS S3の各ユーザーのバケットにアップロード
なぜか? (創業者の宇佐美さん(@usatie)に聞きました!) • 当時はまだ経験不足で、サーバーにDBを置く選択肢がなかった • クライアントサイドで完結する作りにしたかった • (結果的に、オフラインでも使える実装に) なぜ組み込み型SQLiteが採用されていた? 11
データの同期・移行が不可能、もしくはかなり大変 • 異OS間では不可能 ◦ AndroidはRealmを使っている && スキーマも違うため • 同OS間では”一応”可能(だがめちゃ大変) ◦
まず問い合わせいただく→CSチームがS3に入る→旧端末のバケッ トから新端末のバケットにDBファイルをドラッグ&ドロップする →ユーザーさんがアプリ内でデータの復元をする その他 • 生SQLを書く時に、エラーがランタイムにしかわからない • マスターがないため、あらゆるバージョンを想定する必要あり 当時のツラミ - 組み込み型SQLite 12
データ構造の一部が負債化 • 従来は「Book(本)」の中に「Chapter(章)」 • Chapterごとに単語を学んでいく方式。 (1教材 = 1 Book) 当時のツラミ
- その他、旧DBについて 13 Book Chapter
データ構造の一部が負債化 • ある日、本としては1冊だけど、複数のBookを持 つ教材が登場 ◦ 1教材 = 複数 Books があり得る状態に
• 単体Bookと複数Booksに対応しなければならず、 コードにif文が大量発生し、負債に 14 before 1教材 = 1book after 1教材 = 複数books 当時のツラミ - その他、旧DBについて book table
mikanの求めていた要件 / 当時の状況にマッチしていた • オフライン対応 • データの移行・同期 • サーバーサイドが薄く、クライアントサイドが厚いチーム体制 ◦
上記要件を、サーバーレスに実現できるのが魅力的 当時の候補 • API作る: サーバーサイドのリソースの関係で厳しい...! • Realm: サーバーサイドで扱える言語がmikan(Go)とマッチしない Firestoreを採用した理由 15
① 組み込み型→ネットワーク型へ • 同じIDでログインすれば、簡単にデータ移行可能に • 基本はオフライン対応可能だけど、最初は通信必要に ② RDB→KVSへ • カラムの追加が簡単に
• 集計が弱いため、サマリーデータを持つ必要が出てくる ◦ 各単語の記憶度を元にした、各教材の進捗度 ◦ モデルのそのまま移行というわけにはいかなくなる! Firestoreへの移行によって変わること整理 16 サマリーデータを持っている ドキュメント
アジェンダ ① なぜSQLiteからFirestoreに移行したのか? ・なぜ組み込み型SQLiteが採用されていた? ・当時のツラミ ・Firestoreを採用した理由 ・移行によって変わること ② 実際の移行の話 ・全体観
・ダブルライト ・マイグレーション ・クライアント側のreadロジック移行 ③振り返って ・うまくいったこと / やればよかったこと ・実際Firestore使ってみてどう? ・今回の学び 17
移行の全体観 18 ローカル SQLite app Phase⓪ 従来 write read app
Phase① ダブルライト write read Firestore write Phase② マイグレーション Firestore ゴリっと 移行 app Phase③ readロジック移行 Firestore write read 👋 ローカル SQLite GCS ローカル SQLite DBファイルを upload app
Phase① ダブルライト • 基本はクライアントサイドの仕事 • データの書き込みを、SQLiteだけでなく、Firestoreにも • ★ ダブルライト開始日をユーザーごとに保持しておく ◦
マイグレーションの際、ダブルライト以降のデータ は対象にしないようにするため ◦ ユーザーのアップデートに依存してしまい、開始タ イミングがバラバラなため 19 app write read Firestore write ローカル SQLite
Phase① ダブルライト 20 SQLiteへの書き込み Firestoreへの書き込み Firestoreへの書き込み
Phase② マイグレーション • ダブルライト前のデータをFirestoreに移す作業 • サーバーサイドの仕事 • 非同期で行う ◦ ユーザーによっては大変な量になるので
(多ければ数年分) 21 Firestore ゴリっと 移行 GCS DBファイルを upload app
1. client: マイグレーション対象かどうかを判断 2. client: 自分のSQLiteファイルをGoogle Cloud Storageにアップロード 3. client:
自分のステータスを「マイグレーション待ち」に変更 4. server: ステータスが「待ち」のユーザーに対してマイグレーションを実行 22 DBファイルをuploadするコード Phase② マイグレーション
Phase③ readロジック移行 • クライアント側の最後の大仕事 • 読み込みロジックをSQLiteからFirestoreに移行 ◦ これ以降はFirestoreで書き込み/読み込みが成立し、 SQLiteの依存からは脱却 •
大変なところ ◦ 同期処理→非同期処理への書き換え ◦ RDB→KVSという変更によって、データの持ち方も変更 ▪ 合わせてコアの学習機能のロジックも少し変更 ◦ DBの書き込み・読み取りロジックが元々クライアント サイドに寄っていたため、対象ファイルが膨大... 23 app Firestore write read 👋 ローカル SQLite
24 Phase③ readロジック移行 同期処理で、呼び出すだけ 非同期で単語取得 処理順を変更 before after
25 Phase③ readロジック移行
アジェンダ ① なぜSQLiteからFirestoreに移行したのか? ・なぜ組み込み型SQLiteが採用されていた? ・当時のツラミ ・Firestoreを採用した理由 ・移行によって変わること ② 実際の移行の話 ・全体観
・ダブルライト ・マイグレーション ・クライアント側のreadロジック移行 ③振り返って ・うまくいったこと / やればよかったこと ・実際Firestore使ってみてどう? ・今回の学び 26
自前で集客して、パブリックベータを実施できた。 うまくいったこと / やってよかったこと 1/3 27 • Why: iOSの段階的リリースは、実際は段階 的ではない問題
◦ 少数に実際に使ってほしい • 集客はLine@やpush通知 • 社内で潰せなかった不具合がたくさん潰せた ◦ 色んなユーザーのデータで触ってもらえ たおかげ • ただ、600人以上はなかなか集まらず...
うまくいったこと / やってよかったこと 2/3 問い合わせてくれたユーザーさんの多くに個別対応 • コンソールで自ら返信対応 ◦ 不具合の再現手順などを詳しくヒアリングして不具合修正 •
+α 素敵なフィードバックもいただけて、やる気が出る💪 28
うまくいったこと / やってよかったこと 3/3 最小単位のログを別のところで貯めておく • mikanでいうと「1単語ごとの学習ログ」 ◦ 2021/9/17 18:50
に、appleという単語の問題を解いて、不正解 ◦ みたいな • もし不具合などで「学習履歴」や「学習進捗」のデータが消えても、 そのログがあればサマライズして、復元できる 29
難しかったところ・後悔 1/4 ダブルライトの不具合を見つけるのが困難 • アプリ内で「Firestoreへの書き込みがうまくいっているか」を確認で きない ◦ readをFirestoreから行うように書き換えるまでは、SQLiteの データを参照しているため ◦
Firestoreへの書き込みがうまく行っていなくても検知できない ◦ リリース前のQAは、Firestoreのコンソールを目視で確認 30
難しかったところ・後悔 1/4 ダブルライトの不具合を見つけるのが困難 ダブルライト期間中に起きた、本当にあった怖い話... 1. 新教材をFirestoreに入稿 2. 入稿の際に、field名のスペルミス 3. クライアントからFSにも学習データを書き込みたいが、クライアント
で指定しているキー名と異なるので、Decode Error 4. ダブルライト失敗しているが、SQLiteには書き込みできているので、 気づかない 31
難しかったところ・後悔 2/4 強制アップデートとセキュリティルールのどちらも、後になって導入 セキュリティルールを後から導入したけど、強制アップデートの仕組みがな いため、めちゃくちゃ苦労しました...! 以下のサイクルを4周くらいしました。 1. しっかりしたセキュリティルールを適用 2. むかーしのバージョンで、ある違法な値が投げられることが判明
3. セキュリティを2に合わせて、少し緩める(アップデートさせられないので) 4. 1に戻る 32
難しかったところ・後悔 3/4 ABテストをできるならやるべきだった やるべき理由 • 数字が上がったのか、下がったのかの判断を正しくするため ◦ 継続率は時期要因にも左右されるので、同時期にABしてないと、 正しく分析できない •
パブリックベータの人数確保も簡単だった 方法 • DBを触っているVCを全部複製して、片方をDB刷新版にする ◦ 入口から分けてあげると「従来の世界」と「DB刷新の世界」を分 けられる。 33
一度に多くの問題を解決しない方がいい • 一度に多くの問題を解決しようとすると、開発項目が大きくなる • 開発項目が大きくなると、シンプルに大変で、価値を届けるのが遅く なり、検証も遅くなってしまう • 解決したい課題は、個別で対応できるかも 難しかったところ・後悔 4/4
34
一度に多くの問題を解決しない方がいい 今回解決しようとした課題は大きく分けると以下 • OS間のデータ移行ができない ◦ SQLite ⇔ Realmのconverterを作る • 同OSでデータ同期ができない
◦ 今のデータ構造・スキーマのままFirestoreに移行する • アプリのデータ構造がしんどい ◦ SQLite、Realmのままデータ構造だけ変える 難しかったところ・後悔 4/4 35
• 「オフライン対応」と「データ移行」が特に何もせずに実現されてる のすごい ◦ オフラインの永続性はデフォルトでオンになっている* ◦ データ移行は、Firebase Authでログインすれば完了 Firestore実際使ってみて -
良いところ 1/2 * https://firebase.google.com/docs/firestore/manage-data/enable-offline?hl=ja#swift 36
• スケールを気にしなくていい • セキュリティルール!バリデーションのコードをDBに持たせられる ◦ 型だけじゃなく、その型の中でどんな値を許容するかを決められ る ◦ 誰が書き込めるか、読み取れるか、も決められる •
クライアントサイドだけで、自由にカラムを追加できる Firestore実際使ってみて - 良いところ 2/2 37
ドキュメントの取得速度 • データは綺麗に階層構造にしてしまうとし んどくなる 例: あるbookに属するwordsを全部取りたいと きに、chapterでfor文回してwordsを全部取っ てこないといけない(これが時間かかる) Firestore実際使ってみて -
難しいところ 1/3 38 実際のFirestoreの階層構造 books > chapters > words の構造になっている
• 集計弱いので、サマリーデータを持たないといけなくなる ◦ サマリーデータと詳細データが食い違う不整合が発生し得る Firestore実際使ってみて - 難しいところ 2/3 39
Firestore実際使ってみて - 難しいところ 3/3 40 • 不整合なくデータを入れていくために必要な学習コストが高い ◦ security rules,
transaction, batch, increment valuesなど • 値段が高い ◦ 今はFirestoreだけで25万円 ◦ AWSのRDS使っていた時は、EC2やロードバランサー含め15万ほど
なんの課題を解決したいのかを明確化しよう • 「OS間のデータ移行もできるようにしたいし、データ構造変だし、デー タの同期もできるようにしたいし...!よし!データ構造変えながら SQLiteからFirestoreに移行しちゃおう!」 ◦ ふわっとした課題に対しては、ふわっとした解決策になりがち。 • 「我々は、今なんの課題を解決しようとしているのか?」を問い続けよ う。
◦ 課題を常にシャープにしよう プロジェクトでの学び・アドバイス 1/4 41
ダブルライトは要らなかった説 • readが旧DBのときに、新DBへのダブルライトのバグを見つけるのは至 難の業 • ダブルライトやらずに、readロジックの移行の📱アップデートのタイミ ングでマイグレーションを走らせて、非同期でゴリっとデータを移行さ せるだけでよかったかも。 ◦ 今選択している教材だけは同期的にクライアント側で移行してあげ
れば、起動直後の学習体験は最低限担保できる プロジェクトでの学び・アドバイス 2/4 42
データの構造は最初にしっかり設計しよう! • book > chapter > word の構造じゃなくて book >
chapter, wordの構造に ◦ chapterとwordを同じ階層に置く ◦ 👉 book内の全wordを取得するのがもっと高 速化してた。 • 後から変更しづらいので、最初の設計が大事。 プロジェクトでの学び・アドバイス 3/4 43 こんな感じにしておけば...!
セキュリティルール・強制アップデートは早めに入れておこう • DBの移行関係ないですが、強制アップデートは少なくとも早めに入れて おくと便利です...! プロジェクトでの学び・アドバイス 4/4 44
改めて: mikanは絶賛採用中です! • iOSエンジニア • Androidエンジニア • サーバーサイド・インフラ エンジニア •
デザイナー ( 時期を見て、週1回程度の出社を想定) 社員 (まずは副業からでもOK) ※基本リモート勤務 ご連絡はこちらから (Twitter DMでも!) https://mikan.link/carrers 45
ありがとうございました!! 46