運用6年目・500万人が使うアプリのDBをSQLiteからFirestoreに移行した話(iOSDC 2021)
by
Ryo Iida
×
Copy
Open
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Slide 1
Slide 1 text
2021.9.17 Ryo Iida (@aviciida)
Slide 2
Slide 2 text
はじめに - 自己紹介 飯田 諒(@aviciida) ● mikanのiOSエンジニア ○ 🍊 mikan歴 3年(since 2018/9) ○ 🍎 iOSエンジニア歴 2年 (since 2019/5) ● PM/分析もやってます! ● ユーザー目線でプロダクト作るの好き ● Twitter活発です!@aviciida 2
Slide 3
Slide 3 text
3 mikanについて Mission 本質的なテクノロジー活用で あらゆる人の英語学習によりそい 人生の可能性を広げる 「英語を扱えるようになりたい、得意になりたい」人に効果的な手法を提供し、 語学が人生のボトルネックでは無くなる未来を作るのが私たちmikanです。
Slide 4
Slide 4 text
アプリダウンロード NO.1 コアバリューが支持され国内最大級に成長 4
Slide 5
Slide 5 text
● iOSエンジニア ● Androidエンジニア ● サーバーサイド・インフラ エンジニア ● デザイナー ( 時期を見て、週1回程度の出社を想定) 社員 (まずは副業からでもOK) ※基本リモート勤務 ご連絡はこちらから (Twitter DMでも!) https://mikan.link/carrers mikanは絶賛採用中です! 5
Slide 6
Slide 6 text
今日話すこと mikanのiOSアプリのデータベースを 組み込み型のSQLiteからCloud Firestoreに 9ヶ月ほどかけて移行した時の話をします 6
Slide 7
Slide 7 text
大前提: レガシーについて 本セッションは「レガシーを置き換えていく話」ですが、mikan では「レガシー」を前向きに捉えています。 まだ知識がない中で、スピード感持って事業を進めていくための 当時は最善の手段であり、そのおかげで今の500万DLがありま す。(mikanではレガシーではなく「ビンテージ」と読んでます) セッション内でもできるだけ配慮した表現を使いますが、大前提 として先人の方々には尊敬と感謝の念でいっぱいであることを共 有しておきます🙏(特に創業者の宇佐美さんと高岡さん) 7
Slide 8
Slide 8 text
アジェンダ ① なぜSQLiteからFirestoreに移行したのか? ・なぜ組み込み型SQLiteが採用されていた? ・当時のツラミ ・Firestoreを採用した理由 ・移行によって変わること ② 実際の移行の話 ・全体観 ・ダブルライト ・マイグレーション ・クライアント側のreadロジック移行 ③振り返って ・うまくいったこと / やればよかったこと ・実際Firestore使ってみてどう? ・今回の学び 8
Slide 9
Slide 9 text
アジェンダ ① なぜSQLiteからFirestoreに移行したのか? ・なぜ組み込み型SQLiteが採用されていた? ・当時のツラミ ・Firestoreを採用した理由 ・移行によって変わること ② 実際の移行の話 ・全体観 ・ダブルライト ・マイグレーション ・クライアント側のreadロジック移行 ③振り返って ・うまくいったこと / やればよかったこと ・実際Firestore使ってみてどう? ・今回の学び 9
Slide 10
Slide 10 text
組み込み型SQLite? 10 ● DBを移行する前は、ユーザーの学習データは各端末のSQLite データベースに保存されており、サーバーと同期はしない形式 ● バックアップのために、1日の最初の起動時に、DBファイルを AWS S3の各ユーザーのバケットにアップロード
Slide 11
Slide 11 text
なぜか? (創業者の宇佐美さん(@usatie)に聞きました!) ● 当時はまだ経験不足で、サーバーにDBを置く選択肢がなかった ● クライアントサイドで完結する作りにしたかった ● (結果的に、オフラインでも使える実装に) なぜ組み込み型SQLiteが採用されていた? 11
Slide 12
Slide 12 text
データの同期・移行が不可能、もしくはかなり大変 ● 異OS間では不可能 ○ AndroidはRealmを使っている && スキーマも違うため ● 同OS間では”一応”可能(だがめちゃ大変) ○ まず問い合わせいただく→CSチームがS3に入る→旧端末のバケッ トから新端末のバケットにDBファイルをドラッグ&ドロップする →ユーザーさんがアプリ内でデータの復元をする その他 ● 生SQLを書く時に、エラーがランタイムにしかわからない ● マスターがないため、あらゆるバージョンを想定する必要あり 当時のツラミ - 組み込み型SQLite 12
Slide 13
Slide 13 text
データ構造の一部が負債化 ● 従来は「Book(本)」の中に「Chapter(章)」 ● Chapterごとに単語を学んでいく方式。 (1教材 = 1 Book) 当時のツラミ - その他、旧DBについて 13 Book Chapter
Slide 14
Slide 14 text
データ構造の一部が負債化 ● ある日、本としては1冊だけど、複数のBookを持 つ教材が登場 ○ 1教材 = 複数 Books があり得る状態に ● 単体Bookと複数Booksに対応しなければならず、 コードにif文が大量発生し、負債に 14 before 1教材 = 1book after 1教材 = 複数books 当時のツラミ - その他、旧DBについて book table
Slide 15
Slide 15 text
mikanの求めていた要件 / 当時の状況にマッチしていた ● オフライン対応 ● データの移行・同期 ● サーバーサイドが薄く、クライアントサイドが厚いチーム体制 ○ 上記要件を、サーバーレスに実現できるのが魅力的 当時の候補 ● API作る: サーバーサイドのリソースの関係で厳しい...! ● Realm: サーバーサイドで扱える言語がmikan(Go)とマッチしない Firestoreを採用した理由 15
Slide 16
Slide 16 text
① 組み込み型→ネットワーク型へ ● 同じIDでログインすれば、簡単にデータ移行可能に ● 基本はオフライン対応可能だけど、最初は通信必要に ② RDB→KVSへ ● カラムの追加が簡単に ● 集計が弱いため、サマリーデータを持つ必要が出てくる ○ 各単語の記憶度を元にした、各教材の進捗度 ○ モデルのそのまま移行というわけにはいかなくなる! Firestoreへの移行によって変わること整理 16 サマリーデータを持っている ドキュメント
Slide 17
Slide 17 text
アジェンダ ① なぜSQLiteからFirestoreに移行したのか? ・なぜ組み込み型SQLiteが採用されていた? ・当時のツラミ ・Firestoreを採用した理由 ・移行によって変わること ② 実際の移行の話 ・全体観 ・ダブルライト ・マイグレーション ・クライアント側のreadロジック移行 ③振り返って ・うまくいったこと / やればよかったこと ・実際Firestore使ってみてどう? ・今回の学び 17
Slide 18
Slide 18 text
移行の全体観 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
Slide 19
Slide 19 text
Phase① ダブルライト ● 基本はクライアントサイドの仕事 ● データの書き込みを、SQLiteだけでなく、Firestoreにも ● ★ ダブルライト開始日をユーザーごとに保持しておく ○ マイグレーションの際、ダブルライト以降のデータ は対象にしないようにするため ○ ユーザーのアップデートに依存してしまい、開始タ イミングがバラバラなため 19 app write read Firestore write ローカル SQLite
Slide 20
Slide 20 text
Phase① ダブルライト 20 SQLiteへの書き込み Firestoreへの書き込み Firestoreへの書き込み
Slide 21
Slide 21 text
Phase② マイグレーション ● ダブルライト前のデータをFirestoreに移す作業 ● サーバーサイドの仕事 ● 非同期で行う ○ ユーザーによっては大変な量になるので (多ければ数年分) 21 Firestore ゴリっと 移行 GCS DBファイルを upload app
Slide 22
Slide 22 text
1. client: マイグレーション対象かどうかを判断 2. client: 自分のSQLiteファイルをGoogle Cloud Storageにアップロード 3. client: 自分のステータスを「マイグレーション待ち」に変更 4. server: ステータスが「待ち」のユーザーに対してマイグレーションを実行 22 DBファイルをuploadするコード Phase② マイグレーション
Slide 23
Slide 23 text
Phase③ readロジック移行 ● クライアント側の最後の大仕事 ● 読み込みロジックをSQLiteからFirestoreに移行 ○ これ以降はFirestoreで書き込み/読み込みが成立し、 SQLiteの依存からは脱却 ● 大変なところ ○ 同期処理→非同期処理への書き換え ○ RDB→KVSという変更によって、データの持ち方も変更 ■ 合わせてコアの学習機能のロジックも少し変更 ○ DBの書き込み・読み取りロジックが元々クライアント サイドに寄っていたため、対象ファイルが膨大... 23 app Firestore write read 👋 ローカル SQLite
Slide 24
Slide 24 text
24 Phase③ readロジック移行 同期処理で、呼び出すだけ 非同期で単語取得 処理順を変更 before after
Slide 25
Slide 25 text
25 Phase③ readロジック移行
Slide 26
Slide 26 text
アジェンダ ① なぜSQLiteからFirestoreに移行したのか? ・なぜ組み込み型SQLiteが採用されていた? ・当時のツラミ ・Firestoreを採用した理由 ・移行によって変わること ② 実際の移行の話 ・全体観 ・ダブルライト ・マイグレーション ・クライアント側のreadロジック移行 ③振り返って ・うまくいったこと / やればよかったこと ・実際Firestore使ってみてどう? ・今回の学び 26
Slide 27
Slide 27 text
自前で集客して、パブリックベータを実施できた。 うまくいったこと / やってよかったこと 1/3 27 ● Why: iOSの段階的リリースは、実際は段階 的ではない問題 ○ 少数に実際に使ってほしい ● 集客はLine@やpush通知 ● 社内で潰せなかった不具合がたくさん潰せた ○ 色んなユーザーのデータで触ってもらえ たおかげ ● ただ、600人以上はなかなか集まらず...
Slide 28
Slide 28 text
うまくいったこと / やってよかったこと 2/3 問い合わせてくれたユーザーさんの多くに個別対応 ● コンソールで自ら返信対応 ○ 不具合の再現手順などを詳しくヒアリングして不具合修正 ● +α 素敵なフィードバックもいただけて、やる気が出る💪 28
Slide 29
Slide 29 text
うまくいったこと / やってよかったこと 3/3 最小単位のログを別のところで貯めておく ● mikanでいうと「1単語ごとの学習ログ」 ○ 2021/9/17 18:50 に、appleという単語の問題を解いて、不正解 ○ みたいな ● もし不具合などで「学習履歴」や「学習進捗」のデータが消えても、 そのログがあればサマライズして、復元できる 29
Slide 30
Slide 30 text
難しかったところ・後悔 1/4 ダブルライトの不具合を見つけるのが困難 ● アプリ内で「Firestoreへの書き込みがうまくいっているか」を確認で きない ○ readをFirestoreから行うように書き換えるまでは、SQLiteの データを参照しているため ○ Firestoreへの書き込みがうまく行っていなくても検知できない ○ リリース前のQAは、Firestoreのコンソールを目視で確認 30
Slide 31
Slide 31 text
難しかったところ・後悔 1/4 ダブルライトの不具合を見つけるのが困難 ダブルライト期間中に起きた、本当にあった怖い話... 1. 新教材をFirestoreに入稿 2. 入稿の際に、field名のスペルミス 3. クライアントからFSにも学習データを書き込みたいが、クライアント で指定しているキー名と異なるので、Decode Error 4. ダブルライト失敗しているが、SQLiteには書き込みできているので、 気づかない 31
Slide 32
Slide 32 text
難しかったところ・後悔 2/4 強制アップデートとセキュリティルールのどちらも、後になって導入 セキュリティルールを後から導入したけど、強制アップデートの仕組みがな いため、めちゃくちゃ苦労しました...! 以下のサイクルを4周くらいしました。 1. しっかりしたセキュリティルールを適用 2. むかーしのバージョンで、ある違法な値が投げられることが判明 3. セキュリティを2に合わせて、少し緩める(アップデートさせられないので) 4. 1に戻る 32
Slide 33
Slide 33 text
難しかったところ・後悔 3/4 ABテストをできるならやるべきだった やるべき理由 ● 数字が上がったのか、下がったのかの判断を正しくするため ○ 継続率は時期要因にも左右されるので、同時期にABしてないと、 正しく分析できない ● パブリックベータの人数確保も簡単だった 方法 ● DBを触っているVCを全部複製して、片方をDB刷新版にする ○ 入口から分けてあげると「従来の世界」と「DB刷新の世界」を分 けられる。 33
Slide 34
Slide 34 text
一度に多くの問題を解決しない方がいい ● 一度に多くの問題を解決しようとすると、開発項目が大きくなる ● 開発項目が大きくなると、シンプルに大変で、価値を届けるのが遅く なり、検証も遅くなってしまう ● 解決したい課題は、個別で対応できるかも 難しかったところ・後悔 4/4 34
Slide 35
Slide 35 text
一度に多くの問題を解決しない方がいい 今回解決しようとした課題は大きく分けると以下 ● OS間のデータ移行ができない ○ SQLite ⇔ Realmのconverterを作る ● 同OSでデータ同期ができない ○ 今のデータ構造・スキーマのままFirestoreに移行する ● アプリのデータ構造がしんどい ○ SQLite、Realmのままデータ構造だけ変える 難しかったところ・後悔 4/4 35
Slide 36
Slide 36 text
● 「オフライン対応」と「データ移行」が特に何もせずに実現されてる のすごい ○ オフラインの永続性はデフォルトでオンになっている* ○ データ移行は、Firebase Authでログインすれば完了 Firestore実際使ってみて - 良いところ 1/2 * https://firebase.google.com/docs/firestore/manage-data/enable-offline?hl=ja#swift 36
Slide 37
Slide 37 text
● スケールを気にしなくていい ● セキュリティルール!バリデーションのコードをDBに持たせられる ○ 型だけじゃなく、その型の中でどんな値を許容するかを決められ る ○ 誰が書き込めるか、読み取れるか、も決められる ● クライアントサイドだけで、自由にカラムを追加できる Firestore実際使ってみて - 良いところ 2/2 37
Slide 38
Slide 38 text
ドキュメントの取得速度 ● データは綺麗に階層構造にしてしまうとし んどくなる 例: あるbookに属するwordsを全部取りたいと きに、chapterでfor文回してwordsを全部取っ てこないといけない(これが時間かかる) Firestore実際使ってみて - 難しいところ 1/3 38 実際のFirestoreの階層構造 books > chapters > words の構造になっている
Slide 39
Slide 39 text
● 集計弱いので、サマリーデータを持たないといけなくなる ○ サマリーデータと詳細データが食い違う不整合が発生し得る Firestore実際使ってみて - 難しいところ 2/3 39
Slide 40
Slide 40 text
Firestore実際使ってみて - 難しいところ 3/3 40 ● 不整合なくデータを入れていくために必要な学習コストが高い ○ security rules, transaction, batch, increment valuesなど ● 値段が高い ○ 今はFirestoreだけで25万円 ○ AWSのRDS使っていた時は、EC2やロードバランサー含め15万ほど
Slide 41
Slide 41 text
なんの課題を解決したいのかを明確化しよう ● 「OS間のデータ移行もできるようにしたいし、データ構造変だし、デー タの同期もできるようにしたいし...!よし!データ構造変えながら SQLiteからFirestoreに移行しちゃおう!」 ○ ふわっとした課題に対しては、ふわっとした解決策になりがち。 ● 「我々は、今なんの課題を解決しようとしているのか?」を問い続けよ う。 ○ 課題を常にシャープにしよう プロジェクトでの学び・アドバイス 1/4 41
Slide 42
Slide 42 text
ダブルライトは要らなかった説 ● readが旧DBのときに、新DBへのダブルライトのバグを見つけるのは至 難の業 ● ダブルライトやらずに、readロジックの移行の📱アップデートのタイミ ングでマイグレーションを走らせて、非同期でゴリっとデータを移行さ せるだけでよかったかも。 ○ 今選択している教材だけは同期的にクライアント側で移行してあげ れば、起動直後の学習体験は最低限担保できる プロジェクトでの学び・アドバイス 2/4 42
Slide 43
Slide 43 text
データの構造は最初にしっかり設計しよう! ● book > chapter > word の構造じゃなくて book > chapter, wordの構造に ○ chapterとwordを同じ階層に置く ○ 👉 book内の全wordを取得するのがもっと高 速化してた。 ● 後から変更しづらいので、最初の設計が大事。 プロジェクトでの学び・アドバイス 3/4 43 こんな感じにしておけば...!
Slide 44
Slide 44 text
セキュリティルール・強制アップデートは早めに入れておこう ● DBの移行関係ないですが、強制アップデートは少なくとも早めに入れて おくと便利です...! プロジェクトでの学び・アドバイス 4/4 44
Slide 45
Slide 45 text
改めて: mikanは絶賛採用中です! ● iOSエンジニア ● Androidエンジニア ● サーバーサイド・インフラ エンジニア ● デザイナー ( 時期を見て、週1回程度の出社を想定) 社員 (まずは副業からでもOK) ※基本リモート勤務 ご連絡はこちらから (Twitter DMでも!) https://mikan.link/carrers 45
Slide 46
Slide 46 text
ありがとうございました!! 46