Upgrade to Pro — share decks privately, control downloads, hide ads and more …

運用6年目・500万人が使うアプリのDBをSQLiteからFirestoreに移行した話(iOSDC 2021)

Ryo Iida
September 18, 2021

運用6年目・500万人が使うアプリのDBをSQLiteからFirestoreに移行した話(iOSDC 2021)

Ryo Iida

September 18, 2021
Tweet

More Decks by Ryo Iida

Other Decks in Technology

Transcript

  1. 2021.9.17
    Ryo Iida (@aviciida)

    View Slide

  2. はじめに - 自己紹介
    飯田 諒(@aviciida)
    ● mikanのiOSエンジニア
    ○ 🍊 mikan歴 3年(since 2018/9)
    ○ 🍎 iOSエンジニア歴 2年
    (since 2019/5)
    ● PM/分析もやってます!
    ● ユーザー目線でプロダクト作るの好き
    ● Twitter活発です!@aviciida
    2

    View Slide

  3. 3
    mikanについて
    Mission
    本質的なテクノロジー活用で
    あらゆる人の英語学習によりそい
    人生の可能性を広げる
    「英語を扱えるようになりたい、得意になりたい」人に効果的な手法を提供し、
    語学が人生のボトルネックでは無くなる未来を作るのが私たちmikanです。

    View Slide

  4. アプリダウンロード NO.1
    コアバリューが支持され国内最大級に成長
    4

    View Slide

  5. ● iOSエンジニア
    ● Androidエンジニア
    ● サーバーサイド・インフラ エンジニア
    ● デザイナー
    ( 時期を見て、週1回程度の出社を想定)
    社員 (まずは副業からでもOK)
    ※基本リモート勤務
    ご連絡はこちらから
    (Twitter DMでも!󰢏)
    https://mikan.link/carrers
    mikanは絶賛採用中です!
    5

    View Slide

  6. 今日話すこと
    mikanのiOSアプリのデータベースを
    組み込み型のSQLiteからCloud Firestoreに
    9ヶ月ほどかけて移行した時の話をします
    6

    View Slide

  7. 大前提: レガシーについて
    本セッションは「レガシーを置き換えていく話」ですが、mikan
    では「レガシー」を前向きに捉えています。
    まだ知識がない中で、スピード感持って事業を進めていくための
    当時は最善の手段であり、そのおかげで今の500万DLがありま
    す。(mikanではレガシーではなく「ビンテージ」と読んでます)
    セッション内でもできるだけ配慮した表現を使いますが、大前提
    として先人の方々には尊敬と感謝の念でいっぱいであることを共
    有しておきます🙏(特に創業者の宇佐美さんと高岡さん)
    7

    View Slide

  8. アジェンダ
    ① なぜSQLiteからFirestoreに移行したのか?
    ・なぜ組み込み型SQLiteが採用されていた?
    ・当時のツラミ
    ・Firestoreを採用した理由
    ・移行によって変わること
    ② 実際の移行の話
    ・全体観
    ・ダブルライト
    ・マイグレーション
    ・クライアント側のreadロジック移行
    ③振り返って
    ・うまくいったこと / やればよかったこと
    ・実際Firestore使ってみてどう?
    ・今回の学び
    8

    View Slide

  9. アジェンダ
    ① なぜSQLiteからFirestoreに移行したのか?
    ・なぜ組み込み型SQLiteが採用されていた?
    ・当時のツラミ
    ・Firestoreを採用した理由
    ・移行によって変わること
    ② 実際の移行の話
    ・全体観
    ・ダブルライト
    ・マイグレーション
    ・クライアント側のreadロジック移行
    ③振り返って
    ・うまくいったこと / やればよかったこと
    ・実際Firestore使ってみてどう?
    ・今回の学び
    9

    View Slide

  10. 組み込み型SQLite?
    10
    ● DBを移行する前は、ユーザーの学習データは各端末のSQLite
    データベースに保存されており、サーバーと同期はしない形式
    ● バックアップのために、1日の最初の起動時に、DBファイルを
    AWS S3の各ユーザーのバケットにアップロード

    View Slide

  11. なぜか?
    (創業者の宇佐美さん(@usatie)に聞きました!)
    ● 当時はまだ経験不足で、サーバーにDBを置く選択肢がなかった
    ● クライアントサイドで完結する作りにしたかった
    ● (結果的に、オフラインでも使える実装に)
    なぜ組み込み型SQLiteが採用されていた?
    11

    View Slide

  12. データの同期・移行が不可能、もしくはかなり大変
    ● 異OS間では不可能
    ○ AndroidはRealmを使っている && スキーマも違うため
    ● 同OS間では”一応”可能(だがめちゃ大変)
    ○ まず問い合わせいただく→CSチームがS3に入る→旧端末のバケッ
    トから新端末のバケットにDBファイルをドラッグ&ドロップする
    →ユーザーさんがアプリ内でデータの復元をする
    その他
    ● 生SQLを書く時に、エラーがランタイムにしかわからない
    ● マスターがないため、あらゆるバージョンを想定する必要あり
    当時のツラミ - 組み込み型SQLite
    12

    View Slide

  13. データ構造の一部が負債化
    ● 従来は「Book(本)」の中に「Chapter(章)」
    ● Chapterごとに単語を学んでいく方式。
    (1教材 = 1 Book)
    当時のツラミ - その他、旧DBについて
    13
    Book
    Chapter

    View Slide

  14. データ構造の一部が負債化
    ● ある日、本としては1冊だけど、複数のBookを持
    つ教材が登場
    ○ 1教材 = 複数 Books があり得る状態に
    ● 単体Bookと複数Booksに対応しなければならず、
    コードにif文が大量発生し、負債に
    14
    before
    1教材
    = 1book
    after
    1教材
    = 複数books
    当時のツラミ - その他、旧DBについて
    book table

    View Slide

  15. mikanの求めていた要件 / 当時の状況にマッチしていた
    ● オフライン対応
    ● データの移行・同期
    ● サーバーサイドが薄く、クライアントサイドが厚いチーム体制
    ○ 上記要件を、サーバーレスに実現できるのが魅力的
    当時の候補
    ● API作る: サーバーサイドのリソースの関係で厳しい...!
    ● Realm: サーバーサイドで扱える言語がmikan(Go)とマッチしない
    Firestoreを採用した理由
    15

    View Slide

  16. ① 組み込み型→ネットワーク型へ
    ● 同じIDでログインすれば、簡単にデータ移行可能に
    ● 基本はオフライン対応可能だけど、最初は通信必要に
    ② RDB→KVSへ
    ● カラムの追加が簡単に
    ● 集計が弱いため、サマリーデータを持つ必要が出てくる
    ○ 各単語の記憶度を元にした、各教材の進捗度
    ○ モデルのそのまま移行というわけにはいかなくなる!
    Firestoreへの移行によって変わること整理
    16
    サマリーデータを持っている
    ドキュメント

    View Slide

  17. アジェンダ
    ① なぜSQLiteからFirestoreに移行したのか?
    ・なぜ組み込み型SQLiteが採用されていた?
    ・当時のツラミ
    ・Firestoreを採用した理由
    ・移行によって変わること
    ② 実際の移行の話
    ・全体観
    ・ダブルライト
    ・マイグレーション
    ・クライアント側のreadロジック移行
    ③振り返って
    ・うまくいったこと / やればよかったこと
    ・実際Firestore使ってみてどう?
    ・今回の学び
    17

    View Slide

  18. 移行の全体観
    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

    View Slide

  19. Phase① ダブルライト
    ● 基本はクライアントサイドの仕事
    ● データの書き込みを、SQLiteだけでなく、Firestoreにも
    ● ★ ダブルライト開始日をユーザーごとに保持しておく
    ○ マイグレーションの際、ダブルライト以降のデータ
    は対象にしないようにするため
    ○ ユーザーのアップデートに依存してしまい、開始タ
    イミングがバラバラなため
    19
    app
    write
    read
    Firestore
    write
    ローカル
    SQLite

    View Slide

  20. Phase① ダブルライト
    20
    SQLiteへの書き込み
    Firestoreへの書き込み
    Firestoreへの書き込み

    View Slide

  21. Phase② マイグレーション
    ● ダブルライト前のデータをFirestoreに移す作業
    ● サーバーサイドの仕事
    ● 非同期で行う
    ○ ユーザーによっては大変な量になるので
    (多ければ数年分)
    21
    Firestore
    ゴリっと
    移行
    GCS DBファイルを
    upload
    app

    View Slide

  22. 1. client: マイグレーション対象かどうかを判断
    2. client: 自分のSQLiteファイルをGoogle Cloud Storageにアップロード
    3. client: 自分のステータスを「マイグレーション待ち」に変更
    4. server: ステータスが「待ち」のユーザーに対してマイグレーションを実行
    22
    DBファイルをuploadするコード
    Phase② マイグレーション

    View Slide

  23. Phase③ readロジック移行
    ● クライアント側の最後の大仕事
    ● 読み込みロジックをSQLiteからFirestoreに移行
    ○ これ以降はFirestoreで書き込み/読み込みが成立し、
    SQLiteの依存からは脱却
    ● 大変なところ
    ○ 同期処理→非同期処理への書き換え
    ○ RDB→KVSという変更によって、データの持ち方も変更
    ■ 合わせてコアの学習機能のロジックも少し変更
    ○ DBの書き込み・読み取りロジックが元々クライアント
    サイドに寄っていたため、対象ファイルが膨大... 23
    app
    Firestore
    write
    read
    👋
    ローカル
    SQLite

    View Slide

  24. 24
    Phase③ readロジック移行
    同期処理で、呼び出すだけ
    非同期で単語取得
    処理順を変更
    before after

    View Slide

  25. 25
    Phase③ readロジック移行

    View Slide

  26. アジェンダ
    ① なぜSQLiteからFirestoreに移行したのか?
    ・なぜ組み込み型SQLiteが採用されていた?
    ・当時のツラミ
    ・Firestoreを採用した理由
    ・移行によって変わること
    ② 実際の移行の話
    ・全体観
    ・ダブルライト
    ・マイグレーション
    ・クライアント側のreadロジック移行
    ③振り返って
    ・うまくいったこと / やればよかったこと
    ・実際Firestore使ってみてどう?
    ・今回の学び
    26

    View Slide

  27. 自前で集客して、パブリックベータを実施できた。
    うまくいったこと / やってよかったこと 1/3
    27
    ● Why: iOSの段階的リリースは、実際は段階
    的ではない問題
    ○ 少数に実際に使ってほしい
    ● 集客は[email protected]やpush通知
    ● 社内で潰せなかった不具合がたくさん潰せた
    ○ 色んなユーザーのデータで触ってもらえ
    たおかげ
    ● ただ、600人以上はなかなか集まらず...

    View Slide

  28. うまくいったこと / やってよかったこと 2/3
    問い合わせてくれたユーザーさんの多くに個別対応
    ● コンソールで自ら返信対応
    ○ 不具合の再現手順などを詳しくヒアリングして不具合修正
    ● +α 素敵なフィードバックもいただけて、やる気が出る💪
    28

    View Slide

  29. うまくいったこと / やってよかったこと 3/3
    最小単位のログを別のところで貯めておく
    ● mikanでいうと「1単語ごとの学習ログ」
    ○ 2021/9/17 18:50 に、appleという単語の問題を解いて、不正解
    ○ みたいな
    ● もし不具合などで「学習履歴」や「学習進捗」のデータが消えても、
    そのログがあればサマライズして、復元できる
    29

    View Slide

  30. 難しかったところ・後悔 1/4
    ダブルライトの不具合を見つけるのが困難
    ● アプリ内で「Firestoreへの書き込みがうまくいっているか」を確認で
    きない
    ○ readをFirestoreから行うように書き換えるまでは、SQLiteの
    データを参照しているため
    ○ Firestoreへの書き込みがうまく行っていなくても検知できない
    ○ リリース前のQAは、Firestoreのコンソールを目視で確認
    30

    View Slide

  31. 難しかったところ・後悔 1/4
    ダブルライトの不具合を見つけるのが困難
    ダブルライト期間中に起きた、本当にあった怖い話...
    1. 新教材をFirestoreに入稿
    2. 入稿の際に、field名のスペルミス
    3. クライアントからFSにも学習データを書き込みたいが、クライアント
    で指定しているキー名と異なるので、Decode Error
    4. ダブルライト失敗しているが、SQLiteには書き込みできているので、
    気づかない
    31

    View Slide

  32. 難しかったところ・後悔 2/4
    強制アップデートとセキュリティルールのどちらも、後になって導入
    セキュリティルールを後から導入したけど、強制アップデートの仕組みがな
    いため、めちゃくちゃ苦労しました...!
    以下のサイクルを4周くらいしました。
    1. しっかりしたセキュリティルールを適用
    2. むかーしのバージョンで、ある違法な値が投げられることが判明
    3. セキュリティを2に合わせて、少し緩める(アップデートさせられないので)
    4. 1に戻る
    32

    View Slide

  33. 難しかったところ・後悔 3/4
    ABテストをできるならやるべきだった
    やるべき理由
    ● 数字が上がったのか、下がったのかの判断を正しくするため
    ○ 継続率は時期要因にも左右されるので、同時期にABしてないと、
    正しく分析できない
    ● パブリックベータの人数確保も簡単だった
    方法
    ● DBを触っているVCを全部複製して、片方をDB刷新版にする
    ○ 入口から分けてあげると「従来の世界」と「DB刷新の世界」を分
    けられる。 33

    View Slide

  34. 一度に多くの問題を解決しない方がいい
    ● 一度に多くの問題を解決しようとすると、開発項目が大きくなる
    ● 開発項目が大きくなると、シンプルに大変で、価値を届けるのが遅く
    なり、検証も遅くなってしまう
    ● 解決したい課題は、個別で対応できるかも
    難しかったところ・後悔 4/4
    34

    View Slide

  35. 一度に多くの問題を解決しない方がいい
    今回解決しようとした課題は大きく分けると以下
    ● OS間のデータ移行ができない
    ○ SQLite ⇔ Realmのconverterを作る
    ● 同OSでデータ同期ができない
    ○ 今のデータ構造・スキーマのままFirestoreに移行する
    ● アプリのデータ構造がしんどい
    ○ SQLite、Realmのままデータ構造だけ変える
    難しかったところ・後悔 4/4
    35

    View Slide

  36. ● 「オフライン対応」と「データ移行」が特に何もせずに実現されてる
    のすごい
    ○ オフラインの永続性はデフォルトでオンになっている*
    ○ データ移行は、Firebase Authでログインすれば完了
    Firestore実際使ってみて - 良いところ 1/2
    * https://firebase.google.com/docs/firestore/manage-data/enable-offline?hl=ja#swift
    36

    View Slide

  37. ● スケールを気にしなくていい
    ● セキュリティルール!バリデーションのコードをDBに持たせられる
    ○ 型だけじゃなく、その型の中でどんな値を許容するかを決められ

    ○ 誰が書き込めるか、読み取れるか、も決められる
    ● クライアントサイドだけで、自由にカラムを追加できる
    Firestore実際使ってみて - 良いところ 2/2
    37

    View Slide

  38. ドキュメントの取得速度
    ● データは綺麗に階層構造にしてしまうとし
    んどくなる
    例: あるbookに属するwordsを全部取りたいと
    きに、chapterでfor文回してwordsを全部取っ
    てこないといけない(これが時間かかる)
    Firestore実際使ってみて - 難しいところ 1/3
    38
    実際のFirestoreの階層構造
    books > chapters > words
    の構造になっている

    View Slide

  39. ● 集計弱いので、サマリーデータを持たないといけなくなる
    ○ サマリーデータと詳細データが食い違う不整合が発生し得る
    Firestore実際使ってみて - 難しいところ 2/3
    39

    View Slide

  40. Firestore実際使ってみて - 難しいところ 3/3
    40
    ● 不整合なくデータを入れていくために必要な学習コストが高い
    ○ security rules, transaction, batch, increment valuesなど
    ● 値段が高い
    ○ 今はFirestoreだけで25万円
    ○ AWSのRDS使っていた時は、EC2やロードバランサー含め15万ほど

    View Slide

  41. なんの課題を解決したいのかを明確化しよう
    ● 「OS間のデータ移行もできるようにしたいし、データ構造変だし、デー
    タの同期もできるようにしたいし...!よし!データ構造変えながら
    SQLiteからFirestoreに移行しちゃおう!」
    ○ ふわっとした課題に対しては、ふわっとした解決策になりがち。
    ● 「我々は、今なんの課題を解決しようとしているのか?」を問い続けよ
    う。
    ○ 課題を常にシャープにしよう
    プロジェクトでの学び・アドバイス 1/4
    41

    View Slide

  42. ダブルライトは要らなかった説
    ● readが旧DBのときに、新DBへのダブルライトのバグを見つけるのは至
    難の業
    ● ダブルライトやらずに、readロジックの移行の📱アップデートのタイミ
    ングでマイグレーションを走らせて、非同期でゴリっとデータを移行さ
    せるだけでよかったかも。
    ○ 今選択している教材だけは同期的にクライアント側で移行してあげ
    れば、起動直後の学習体験は最低限担保できる
    プロジェクトでの学び・アドバイス 2/4
    42

    View Slide

  43. データの構造は最初にしっかり設計しよう!
    ● book > chapter > word の構造じゃなくて
    book > chapter, wordの構造に
    ○ chapterとwordを同じ階層に置く
    ○ 👉 book内の全wordを取得するのがもっと高
    速化してた。
    ● 後から変更しづらいので、最初の設計が大事。
    プロジェクトでの学び・アドバイス 3/4
    43
    こんな感じにしておけば...!

    View Slide

  44. セキュリティルール・強制アップデートは早めに入れておこう
    ● DBの移行関係ないですが、強制アップデートは少なくとも早めに入れて
    おくと便利です...!
    プロジェクトでの学び・アドバイス 4/4
    44

    View Slide

  45. 改めて: mikanは絶賛採用中です!
    ● iOSエンジニア
    ● Androidエンジニア
    ● サーバーサイド・インフラ エンジニア
    ● デザイナー
    ( 時期を見て、週1回程度の出社を想定)
    社員 (まずは副業からでもOK)
    ※基本リモート勤務
    ご連絡はこちらから
    (Twitter DMでも!󰢏)
    https://mikan.link/carrers
    45

    View Slide

  46. ありがとうございました!!
    46

    View Slide