🛤️

Firebase Performance Monitoring を用いた GraphQL の計測

2024/12/08に公開

ポート株式会社 サービス開発部 Advent Calendar 2024 の8日目です。

初めに

ポート株式会社でAndroid開発をしている@shxun6934 です。

今年、弊社のAndroidアプリのパフォーマンス計測の一環として、Firebase Performance Monitoring を用いたGraphQL の計測を行う対応を行いました。

Firebase Performance Monitoring をどのように用いてGraphQL の計測を行うのかの知見をまとめていこうと思います。

(パフォーマンス計測をしたのは初めてなので、そもそもの認識が違う部分があると思いますが、暖かくみてもらえると 🙏)

どうして計測するのか

自分が参加しているチームでは、週に1度、Datadog (モニタリングツール)を使用して、スロークエリ(実行時間が一定時間より遅いクエリのこと)をWeb開発者と共に確認する会を設けています。

ある日、アプリからのリクエストで起きているスロークエリがありました。
(↓ 当時のデータではないです)

弊社の環境では、クエリが実行された際に、サーバー側のパフォーマンス状況(どのくらい時間がかかっているのか、具体的にどのようなクエリが実行されたのか など)がわかるようになっています。
が、アプリ側(== フロント側)のパフォーマンス状況は、環境を整えていないため、わかりませんでした。

そのため、サーバー側のパフォーマンス状況は把握できているが、アプリ側のパフォーマンス状況は把握できていない状態になっていました。

この状態では、サーバー側に対して修正をすればいいのかアプリ側に対して修正をすればいいのかの判断が難しく、修正対応を行うタイミングが遅くなってしまいます。

この状況を解決するために、まずは、アプリ側のパフォーマンス状況を常に把握しておく必要があると考えました。

こういった経緯から、アプリのパフォーマンス計測を行うことにしました。

前提

計測の話に映る前に、弊社のAndroidアプリの技術スタックについて説明します。主にバックエンドと関連のある箇所では下記を採用しています。

  • Web とのAPI として、GraphQL を採用している
  • Firebase のいくつかのサービスを利用している
    • その中の1つにPerformance Monitoring がある

GraphQL とは

https://graphql.org/

Facebookが開発したAPIのためのクエリ言語で、アプリ側から取得・更新するデータを自由に記述できます。

去年のアドベントカレンダーで書いた記事にもGraphQLのことを載せているので、そちらも見てください。

https://zenn.dev/shxun6934/articles/15bb841649c705#graphql

GraphQL では、データを取得するためにQueryを、データを更新・削除するためにMutationを使用します。

Firebase Performance Monitoring とは

https://firebase.google.com/docs/perf-mon

アプリの起動時間やUIのレンダリング状況、ネットワークリクエストにかかっている時間などを計測でき、Firebase Console で確認することができます。

例:アプリの起動時間

例えば、「アプリが遅い」というフィードバックをユーザーからもらったとします。

「遅い」と言っても、データを取得するためのAPIの通信速度が遅かったり、UIの描画が遅かったり、特定の画面でのみ遅かったりと、考えられる原因が多くあると思います。

このようなアプリが遅い原因が何かを調べ修正するための指標として、Firebase Performance Monitoring は使用することができます。

導入方法は、公式を見てください。

どう計測する??

GraphQL は、エンドポイントは1つという特徴があります。
そして、Firebase Performance Monitoring のネットワークリクエストの計測は、エンドポイントに対して計測を行います

これでは、どのクエリをリクエストしても1つのエンドポイントの計測として扱われるため、クエリごとの詳細な計測ができません。

GraphQLでクエリごとの計測を行うためには、カスタムコードトレースを作成する必要があります。

カスタムコードトレースでは、アプリの特定のタスクを完了するまでにかかった時間を計測することができます。

今回は、GraphQL のクエリを実行してから成功 or 失敗するまでの時間を計測しました。

対応内容

「クエリを実行してから成功 or 失敗する」箇所にカスタムコードトレースを追加します。

弊社では、以下のような、GraphQL 用のクライアントクラスにQueryMutationをそれぞれ実行するメソッドを用意しています。
この処理の中に、カスタムコードトレースを追加する処理を入れます。

class GraphQLClient @Inject constructor(
    private val client: ApolloClient,
    private val firebasePerformance: FirebasePerformance
) {
    // GraphQL のQuery を実行するメソッド
    suspend fun <T : Query.Data> query(query: Query<T>): ApolloResponse<T> =
        execute(client.query(query))

    // GraphQL のMutation を実行するメソッド
    suspend fun <T : Mutation.Data> mutation(mutation: Mutation<T>): ApolloResponse<T> =
        execute(client.mutation(mutation))

    private suspend fun <T : Operation.Data> execute(call: ApolloCall<T>): ApolloResponse<T>? {
        // 実行予定のGraphQL のクエリ名でカスタムコードトレースを作成
        val apiTrace = firebasePerformance.newTrace(call.operation.name())

        // カスタムコードトレースでの計測開始
        apiTrace.start()

        return runCatching {
            call.execute()
        }.onSuccess { response ->
            if (response.errors.isNullOrEmpty()) {
                // 通信に成功・エラーがない場合は、成功ログを送り、カスタムコードトレースでの計測を終了する
                apiTrace.incrementMetric("success", 1)
                apiTrace.stop()
            } else {
                // 通信に成功したがエラーがある場合は、失敗ログを送り、カスタムコードトレースでの計測を終了する
                apiTrace.incrementMetric("failure", 1)
                apiTrace.stop()

                /* エラーハンドリング */
            }
        }.onFailure {
            // 通信に失敗した場合は、失敗ログを送り、カスタムコードトレースでの計測を終了する
            apiTrace.incrementMetric("failure", 1)
            apiTrace.stop()

            /* エラーハンドリング */
        }.getOrNull()
    }
}

この実装を行うだけで、計測ができるようになります。

確認

実際にデータが送られているかを確認するには、Firebase Console で確認することができます。

ただし、Firebase Console で見れるようになるには、少しだけタイムラグがあります。(5分くらい)

「すぐに見たい!」という方は、AndroidManifest.xmlに以下のコードを追加すると、Logcat でFirebase Performance Monitoring に実際に送っているデータを見ることができます。

このオプションは、あくまでデバッグ用なので、デバッグ環境のAndroidManifestに書くことをお勧めします 。

AndroidManifest.xml
<manifest>
    <application>
        <!-- Logcat でFirebase Performance に送っているデータを見れるようにする -->
        <meta-data
            android:name="firebase_performance_logcat_enabled"
            android:value="true" />
    </application>
</manifest>

Logcat にて、tag:FirebasePerformanceで絞り込みを行うと実際に送られているデータを見ることができます。

実際の計測

上記の対応をリリースすれば、あとはFirebase にデータが集まるのを待ちます。

Firebase Performance Monitoring で集めたデータは、Firebase Console でデータを見ることができます。

一覧

カスタムコードトレース一覧では、以下の画像のように、設定したクエリ名で登録されています。

この画面で見れる情報は以下のデータです。

  • 計測したサンプル数
  • クエリの平均実行時間
  • パフォーマンスの比較

そのほかの機能として、クエリ名による検索やアラート設定も行えます。

詳細

各クエリの詳細なデータを見ることができます。

計測したサンプル数によっては、アプリのバージョンやOSのバージョンごとの絞り込みも可能です。

セッション

セッションごとのデータも見ることができます。

実際のクエリに加えて、他の要因(レンダリング・ネットワーク)もわかります。

対応を終えて

アプリ側が利用しているGraphQL の現在のパフォーマンスをすぐ見れるようになりました。
これによって、Web開発者に対する共有も迅速に詳しくできるようになりました。

まだ、当初のスロークエリへの改善は完全にはできていませんが、計測を開始したことで、今後同様のスロークエリなどの問題が起きた際に改善の足掛かりにすることができるようになったと思います。

とあるスロークエリがきっかけで計測を開始することになりましたが、思っていた以上に多くのデータをFirebase で計測することができるので、とても対応のし甲斐があり、面白かったです。

最後に

計測 is 大事

9日目も僕なので、明日もみてください!

ポート株式会社 エンジニアブログ

Discussion