every Tech Blog

株式会社エブリーのTech Blogです。

RDS で EBS BurstBalance が枯渇した事例の紹介

この記事は every Tech Blog Advent Calendar 2024(夏) 24 日目の記事です。

はじめに

こんにちは。DELISH KITCHEN 開発部 RHRA グループ所属の池です。

RHRA グループでは主に小売向けプロダクトの開発を行なっています。 本記事では、RDS の EBS BurstBalance が枯渇してパフォーマンスが著しく低下した事例について、その調査過程で得られた知見を共有したいと思います。

背景

事象が発生した環境では、以下の構成の RDS インスタンスを使用していました。

  • MySQL 5.7.44
  • インスタンスタイプ: db.t3.medium
  • ストレージタイプ: 汎用 SSD (gp2)
  • ストレージサイズ: 30GiB

この環境において、BurstBalance が急激に消費され始め、最終的に枯渇してししまい、データベースのパフォーマンスが著しく低下しました。 次の図は BurstBalance が枯渇した際のメトリクスです。

BurstBalance の枯渇

BurstBalance とは

まず BurstBalance について理解を深めておく必要があります。

BurstBalance の仕様

BurstBalance は、Amazon EBS の汎用 SSD (gp2) ボリュームに適用される性能指標です。これは、ベースラインパフォーマンスを超えて一時的に高い IOPS を発揮できるクレジットの残量を表します。 gp2 ボリュームの場合、ボリュームサイズに応じて以下の特性を持ちます。

  • ベースラインパフォーマンス: 3 IOPS/GB、ただし最小 100 IOPS
  • バーストパフォーマンス: 最大 3,000 IOPS
  • 最大クレジット量: 5.4 million I/O クレジット
  • IOPS がベースラインを超える場合にバーストが利用され、最大 3,000 IOPS まで発揮される

BurstBalance が完全に枯渇した場合、IOPS はベースラインの値に制限されます。例えば、30GiB のボリュームの場合、ベースラインは 100 IOPS(3 IOPS/GB × 30GB = 90 IOPS ですが、最小値の 100 IOPS が適用されます)となり、BurstBalance 枯渇時はこの値に制限されます。

これらの仕様により、gp2 ボリュームは短期的な高 I/O 要求に対応できますが、継続的に高い I/O 要求がある場合、バーストバランスが徐々に減少し、最終的にベースラインパフォーマンスに制限されることになります。

消費量の計算式

BurstBalance の消費量は以下の式で概算できます

消費量 = (使用IOPS - ベースラインIOPS) × 秒数

例えば、30GiB のボリュームの場合

ベースラインIOPS = 最小量の100IOPS

1分間300 IOPSを維持した場合の消費量 = (300 - 100) × 60 = 12,000 IOクレジット

より詳細な gp2 ボリュームの仕様と BurstBalance については、AWS の公式ドキュメントを参照してください。

調査方法

問題が発生した際、以下の調査を行いました

  • CloudWatch メトリクスの確認
  • BurstBalance, ReadIOPS, WriteIOPS 等のメトリクスを確認
  • RDS のエラーログの確認
  • スロークエリログの確認
  • Performance Insights の利用
  • 問題のあるクエリの特定と実行計画の分析

調査結果

調査の結果、以下のことが判明しました。

  • BurstBalance は主に WriteIOPS の増加によって発生
  • I/O を大きく消費するクエリが存在

次の図は BurstBalance と WriteIOPS の相関を表すグラフになります。 青色が BurstBalance、オレンジ色が WriteIOPS を示しています。WriteIOPS が増加すると BurstBalane が減少することがわかります。

BurstBalance と WriteIOPS の相関

ここで仮に 1 時間平均の WriteIOPS を 600IOPS/second として、そのペースで消費し続けたと仮定した場合に、枯渇するまでの時間を概算で計算すると以下のように計算できます。

1時間あたりの消費量:
(600 - 100) × 3600 = 1,800,000 IOPS/hour

BurstBalanceを消費し切る時間:
5,400,000 / 1,800,000 = 3時間

また、問題のクエリは複雑な SELECT 文で、実行時に I/O が急上昇していました。 このクエリに対して実行計画を確認したところ、extra に Using temporary, Using filesort という記述があり、内部的なソートの実行と一時テーブルへの書き込みが発生していることがわかりました。

これらの結果から、複雑な SELECT 文の実行により、大量の一時テーブルへの書き込みが発生し、それが BurstBalance の急激な消費につながったと考えられます。

ただ、あるタイミングで突然このクエリが一時テーブルへの書き込みを大量に行うようになった要因については調査中になります。 可能性の一つとして、日々のデータの蓄積によりこのクエリで利用する一時テーブルの容量がオンメモリ内で処理できるメモリ制限を超えてしまい、ディスクに書き込まれるようになった可能性を推察しています。 この部分については引き続き調査中なので、結果がわかったら報告できればと思います。

対策

本事象について、以下の対策が考えられます。

  • 問題のクエリのチューニング
    • インデックスの見直し
    • クエリの書き換え(サブクエリの削除、JOIN の最適化など)
  • ストレージのアップグレード
    • gp2 から gp3 への移行を検討(より予測可能なパフォーマンス特性)
    • ストレージサイズの増加(ベースライン IOPS の向上)
  • 一時テーブルのサイズ制限の調整
    • tmp_table_size と max_heap_table_size パラメータの調整
  • インスタンスタイプの見直し
    • より高性能なインスタンスタイプへの移行
  • 定期的なクエリパフォーマンスの監視と改善
  • システム監視の見直し
    • CloudWatchAlert 等による BurstBalance の監視
    • Pingdom 等による外形監視

まとめ

RDS における BurstBalance の枯渇は、予期せぬパフォーマンス低下を引き起こす可能性がある問題です。今回の事例から、以下の学びを得ることができました

  • 複雑なクエリが I/O に与える影響の大きさ
  • データベース設計とクエリ最適化の継続的な改善の必要性
  • 定期的なパフォーマンスモニタリングの重要性

今回の知見が少しでも皆様の参考になれば幸いです。

AWS Summit Japan 2024に参加しました

この記事は every Tech Blog Advent Calendar 2024(夏) 23日目の記事です。

こんにちは 開発本部データ&AIチームでデータエンジニアを担当している塚田です。

今回は先日行われたAWS Summit Japan 2024に現地参加する時間が取れましたので、 昨日の「Kotlin Fest 2024 に ひよこスポンサー として参加してきました!」に引き続きイベントレポートですが、個人的に印象に残ったことや聴講したセッションについて触れたいと思います。

tech.every.tv

セッション動画やセッション資料も期間限定で公開されているようなので、気になる方はそちらもご確認いただければと思います。

現地で参加したセッション(1日目のみ)

どのセッションも満席で熱量の高さを感じました。 また、セッション内で「生成AI」という単語を聞くことがないと思うほど頻繁に耳にしました。

基調講演 AWSと創る次の時代

1時間30分の大ボリュームだったので詳細は省きますが、

やはりAIについて触れられることが多く「責任あるAI」が何回も出てきており

その言葉からもあるようにAIが当たり前になり生活に溶け込んでいくようになっても

常に意識するべきことであることを再認識しました。

Amazon Q Businessの日本語対応が発表され、使うことができるのを楽しみにしています!

Dive deep on Amazon S3

S3をより深く理解するため3つの軸で詳しく説明がありました。

  • フロントエンドの理解
  • インデックスの理解
  • ストレージ層の理解

その他にもAmazon S3 Express One Zone誤削除に対してにも言及がありましが、ここでは3つの軸についてまとめたいと思います。

フロントエンドの理解

  • ピークのトラフィックは1PB/秒を超える規模となっている
  • 利用者側でもできる緩和策がある
    • マルチアップロード
    • 独自実装せずにCommon Runtime(CRT)を利用しベストプラクティスに則る
  • 新しいマウントポイントがいくつか出てきている
    • S3マウントは新しいマウオンとオプションが出ているけれど、いまだにバッドプラクティス
    • それなのに新しいマウントポイントが出てくる理由としては機械学習ニーズが大きい

インデックスの理解

  • 350兆のオブジェクトを管理しており、1億リクエスト/秒を処理している
  • 利用状況とキャパシティを常に監視し適切にスケールを行っている
  • プレフィックスごとにPUTとGETのリクエスト数の上限が設定されている
    • それを超えると503を返却する
  • キー配列によって負荷が変わる
    • キー名のカーディなりティは左に寄せる
    • キー名に日付を入れる際はできるだけ右側にする

ストレージ層の理解

  • 何百万ものハードドライブを利用して、エキサバイトのデータを保管している
  • データは常に冗長化された環境で保管され保存時、保存後定期的にチェックを行なっている

感想

AWS re:Invent 2023でも同じような内容があったと記憶していますが、日本語で聞くことができ理解がより深まったと感じました。

冗長的にオブジェクト管理をしていたのは理解していましたが、どういう体系で管理されて利用できているかの理解を深めることができました。

S3 Express One Zoneの利用場面がいまいちイメージでできていませんでしたが、機械学習などI/O性能が上がることでメリットを受けられるなど理解が進みました。

データ×生成 AI - 事例から学ぶビジネスインパクト創出の方程式

  • クラウドネイティブが進んでいる企業は生成AI活用も進んでいる傾向がある
  • 活用が進んでいる企業では共通するポイントがある
    • 顧客起点文化
    • 小規模チーム
    • 頻繁な実験
  • 小規模チームでも大規模かつ機微なデータでも迅速に生成AIプロダクトが構築できる
  • Amazon QやKnowledge bases for Amazon Bedrockを活用する
  • 責任あるAIを作るためにも責任共有モデルに基づいた実現が必要になってくる

感想

活用が進んでいる企業に共通するポイントはなるほどと感じる部分が大きく責任あるAIを作っていくためにも上記に記載したもの以外もしっかり考えてアクションしていきたいと思いました。

EXPO

セッション以外にも様々なブースがありましたので、少しだけ触れたいと思います。

Partner Solution Expo

たくさんのパートナーブースがあり、盛り上がりがすごかったと感じました。

また、利用しているサービスのブースもありましたのでオフラインでの質問や相談ができたのがよかったです。

ご対応いただいた皆様ありがとうございます。

認定者ラウンジ

認定者ラウンジを利用させていただきましたが、利用するのにも多数の参加者が並んでおり資格取得者の多さに驚きを覚えました。

保持している資格によってステッカーがいただけたのですが、おまけでクッキーもいただきました。

あとで知りましたが、AWS Certified Data Engineer - Associateを持っているとクッキーももらえるようになっていたようです。

まとめ

たくさんのインプット、知識の更新をすることができ、インプットしたことを業務やプロダクトに還元できるようにしていきたいと強く思える場になりました。

生成AIなど進歩が早い中で頻繁に実験を繰り返し開発する文化をより強くしていきたいと考えています。 1日目最初のセッションが始まる30分前には多数のサインがありました!盛り上がりがすごい!

Kotlin Fest 2024 に ひよこスポンサー として参加してきました!

はじめに

こんにちは、株式会社 エブリー DevEnableグループです。

先日のGo Conference 2024に引き続き、本日、約5年ぶりのオフライン開催となったKotlin Fest 2024にひよこスポンサーとして参加してきました!

Kotlin Fest運営の皆様および参加された皆様、お疲れ様でした!

早速参加レポートをさせていただきます。

www.kotlinfest.dev

5年ぶりのオフライン開催

Kotlin Fest 2024は今回5年ぶりのオフライン開催となりました。会場はベルサール渋谷ファーストの2Fを貸し切って、2つのセッションルームと1つのスポンサーブース兼フリースペースという形の大きな会場での開催でした。

スポンサーブースの紹介

エブリーは8年前からDELISH KITCHENアプリでKotlinを採用しています。Kotlin採用に対する振り返りを当時採用したCTO自らが綴っているのでご覧ください。

tech.every.tv

いつもKotlinコミュニティの恩恵を受けている我々もコミュニティのさらなる盛り上がりに貢献していきたく、スポンサーとして協賛させていただき、ブースも出展しました!

ブース

エブリーでは、弊社が提供するDELISH KITCHENのサービスをイメージしてブースの雰囲気を作っています。今回も多くの方からDELISH KITCHENを使っていますとの声をかけていただき、実際に使っていただいている方の声を聞ける貴重な機会で僕たち開発者もパワーをもらえました。

ノベルティ

今回は以下のようなノベルティを用意させていただきました。

  • クリアファイル
  • 会社とサービスのステッカー
  • DELISH KITCHENグッズ

DELISH KITCHENグッズに関してはXフォローでの抽選プレゼントキャンペーンを行いました。DELISH KITCHENグッズに関してはたくさんの商品があるのですが、その中でも人気のある商品を中心に5つ準備させていただきました。 用意してた全てのグッズがなくなるほど好評で多くの方に参加していただけました!

アンケート

今回、アンケートでは『KotlinのLinter、なにを使ってる?』と題して回答をしてもらいました。また、シールの色でKotlinをAndroid開発で使っているか、サーバーサイドで使っているかがひと目でわかるような工夫もあり、参加者が楽しんでいただけるような内容を考えました。回答いただいた多くの皆様、ありがとうございました!

最終結果はこちら...!

  • Androidエンジニア
    • 👑1位: ktlint
    • 2位: Android Lint
    • 3位: detekt
  • サーバーサイドエンジニア
    • 👑1位: detekt
    • 2位: ktlint
    • 3位: その他(Konsist, CheckStyle)

全体ではktlintが1位となり、枠をはみ出すほど多くの方の回答が集まっていました。一方でサーバーサイドエンジニアの中では、ktlintとdetektを使っている方の割合がほぼ同じ僅差という面白い結果となっています。 ブースに来てくださる方はAndroidエンジニアの方が割合としては多いですが、サーバーサイドでKotlinを活用している方も多くいらっしゃり、両プラットフォーム上でのKotlinの盛り上がりを肌で感じました。

各社スポンサーブースの様子

スポンサーブースは、2つのセッションルーム間で開催され、オープニング前やセッション間での休憩中に多くの人で賑わっていました。こういった光景を見られるのもオフラインならではです。

各社のブースもそれぞれの会社の特色がノベルティや出し物から出ており、スタンプラリーをしながら楽しませてもらいました。

特にエムスリーさんのブースではKotlinのモチーフである『トリ』に関連して、『エンジニアトリ診断』を受けることができ、Kotlinや開発に関するいくつかの質問に答えることでトリタイプが診断されます。僕の結果は...『はやぶさ』タイプでした!

セッションの紹介

今回発表されたセッションの中から気になったものをいくつかまとめさせていただきました。

パフォーマンスと可読性を両立:KotlinのCollection関数をマスター

発表者: Masayuki Sudaさん

fortee.jp

こちらのセッションでは、Kotlin の Collection 関数を有効活用する方法を紹介されていました。 Collection 関数の中には変換を行う Map や Zip、フィルタリングを行う filter や partition、グループ化を行う groupBy、部分取得を行う slice など多種多様な関数が用意されていますが、一つ一つ具体的なソースコードと処理結果を説明してくださいました。 その中で、+ 演算子や - 演算子を扱い要素の追加や削除を行う場合は新しいリストが生成されるためメモリが消費しやすい、windowed を使うと計算量が多くなり、パフォーマンスに影響が出るなど、細かい課題や問題点まで説明してくださっていた点が印象的でした。

また最後に Collection、Sequence、for の3 つでパフォーマンスの最適化の観点で、

Collection … 非常に優秀。即時評価を行い、すべての要素を評価する仕組みで、リストが大きい場合はパフォーマンスに難有り。 Sequence … 遅延評価を行う仕組みで、大規模なコレクションに対して複数の処理を行う場合に向いている。 for … 最速。

といったまとめをしてくださっていました。プログラムを作成するうえではパフォーマンスは重要なため、常に説明してくださった観点は意識しようと改めて感じました。

なお、この記事内では一部の関数のみ抜粋して紹介しましたが、Collection 関数自体はまだまだ多く提供されており、使ったことがないものも多々あるため、これを期に勉強し直したいと強く感じました。

withContextってスレッド切り替え以外にも使えるって知ってた?

発表者: T45Kさん

fortee.jp

こちらのセッションでは、withContext をスレッドの切り替え以外でも使うことができる、という内容を紹介されていました。

Coroutines では API の処理は withContext(Dispatchers.IO)、計算量が多い処理は withContext(Dispatchers.Default)、UI 関連の処理は withContext(Dispatchers.Main) と、スレッドの切り替えのために使用することがあり、私自身このためだけに使用するものと認識していましたが、実際は CoroutineContext を切り替えるものであり、スレッドを切り替える以外の用途で利用されるものと紹介され、しっかりと理解していないまま利用していると反省するきっかけになりました。

なお、コンパイル時に Context の検査が行われる、Coroutines でなくとも使用ができる Context Parameter というものが Kotlin 2.1 で導入予定となっているそうで、これから注目して追っていきたいと思います。

KotlinのLinterまなびなおし2024

発表者 nyafunta9858さん

fortee.jp

こちらのセッションでは、KotlinのLinter導入へのモチベーションや各Linterについての特徴について解説されていました。

アーキテクチャやコードルールをLinterを用いてプロジェクト内で統一するモチベーションの一つとして、メンバーの入れ替わりが挙げられていました。ルールを統一することで、メンバーの入れ替わりに影響されずコードの品質を担保できます。

everyはエンジニアが様々なプロダクトを経験することができる環境ですので、Linterの運用は強力な武器になると最認識しました。 またブースではどのLinterを使用しているかアンケートを取りました。結果としてはktlintが多く、次点でdetectでした。この発表やアンケート結果を弊社プロダクトに活かしていければと思います。

Jetpack Compose: 効果的なComposable関数のAPI設計

発表者: haru067さん

fortee.jp

こちらのセッションでは、主にComposable関数の引数の注意点とテクニックについて紹介されていました。

引数はそのコンポーネントの再利用性や拡張性を考えて書く必要があり、その中で有効なテクニックとして以下が紹介されていました。

stateはコンポーネント内で閉じてもよいなら閉じる。他コンポーネントと状態を共有したい場合は親に委ねる。

引数はパフォーマンスの観点から基本的にフラットに記述する。データクラスを用いると、他画面との兼ね合いで無駄な再描画を引き起こす可能性がある。引数が多くなった場合はクラスへ切り分けることを考慮する。

デフォルト引数は安易に設定しないように気をつけ、値をつど考えることが有益な場合は設定しないようにする。

など開発の際に誰もがどうしようか考える内容に触れられており、大変面白かったです。

特に引数をフラットに書く話は、パフォーマンスに直結するので気をつけようと思います。

最後に

最後になりますが、Kotlin Fest 2024の運営の皆さん、カンファレンスの運営をしていただき本当にありがとうございました!

また、参加者の皆さん、カンファレンスへの参加お疲れ様でした。

弊社も当日多くのエンジニアが参加し、セッションを聞きながらKotlinコミュニティから刺激を受けるいい機会となりました。今後もイベントやこういったスポンサー活動を通じてKotlinコミュニティに貢献していきたいと思います!

今回参加できなかった皆様もぜひ来年は参加してみてください。

エブリーでは、ともに働く仲間を募集しています。

テックブログを読んで少しでもエブリーに興味を持っていただけた方は、ぜひ一度カジュアル面談にお越しください!

corp.every.tv

最後までお読みいただき、ありがとうございました!

8年前にKotlinを採用してたくさん恩恵を受けた話

この記事は every Tech Blog Advent Calendar 2024(夏) 21 日目の記事です。

はじめに

こんにちは、エブリーでCTOをしている今井です。
Kotlin Fest 2024 の開催がいよいよ明日に迫ってきました。
エブリーでは8年前にDELISHKITCHENのアプリを作り始めた時からKotlinを使っており、
今回KotlinFestを通じて、Kotlinコミュニティに貢献できることを嬉しく思っております。

このブログでは、Kotlinを採用した当時の状況や、採用して良かったことに関して振り返ってみたいと思います。

Kotlinの採用

FirstCommitのスクリーンショット
改めてコミットを見たところ、Kotlinを初めて導入したのはなんと2016年のFirstCommitからでした。
当時のバージョンは1.0.3で、その時はまだGoogleの正式なサポートもまだ発表される前1で、
Androidアプリ開発においてまだまだJavaが主流。かつこんなにKotlinが急激に浸透するとは思ってもいませんでした。

また、開発を進めるにあたってもKotlinの導入事例はとても少なく、ちょっとしたことでも調べるのに苦労したことを覚えています。

当時のコードを振り返ると、たくさん拡張関数作って、ActivityやFragmentをシュッと書きたいという
自分なりにKotlinらしく書くことを模索した形跡がありました。

FragmentUtil.kt

fun SupportFragment.inflate(layoutResId: Int, inflater: LayoutInflater?,
                            container: ViewGroup?): View? =
        inflater?.inflate(layoutResId, container, false)

fun Fragment.inflate(layoutResId: Int, inflater: LayoutInflater?,
                     container: ViewGroup?): View? =
        inflater?.inflate(layoutResId, container, false)

MainFragment.kt

class MainFragment : AbstractFragment() {

    companion object {
        @JvmStatic fun newInstance() = MainFragment()
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }

    override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? =
            inflate(R.layout.fragment_main, inflater, container)
}

これ自体は今思うとまだまだ未熟な書き方も多く恥ずかしい部分も多いですが、
Kotlinを採用したことでの恩恵も多く受けたと思います。

Kotlinの恩恵

Null-Safety

一番の恩恵は確実にNull-Safetyにありました。
当時Androidは自分一人での開発だったためレビューもしてもらえない環境かつ、
その時点での自分のAndroid開発経験も半年ほどと未熟だった中で、
リリース当初からCrashFreeRateが99.9%という高いスコアを出せていたのは、
Kotlinを採用していなければ実現できなかったと思います。

シンプルで短く書ける

また、シンタックスがシンプルで短く書けることも初期の開発速度に寄与していたと思います。
上記で紹介した拡張関数やLambdaなどは使えるところは意地でも使うくらい、ハマってました。
Null-Safetyではどうしてもその中身のチェックに記述が増えがちなところあると思いますが、
SmartCastなどにより、書きやすさを維持したままより安全なコードが書けるも良かったです。

書いていて楽しい

Kotlin自体の機能ではないですが、上記の恩恵などから、書いていて楽しかったというのも、
今思うと恩恵として大きかったと思います。まだまだ未知なものを自分の手で開拓していく感じも面白かったです。

まとめ

エブリーではAndroidアプリの開発当初からKotlinを採用し、Kotlinに支えられてきました。
同様に弊社のあらゆるエンジニアは多くの技術やオープンソースに支えられています。
これらを盛り上げる技術コミュニティに支援をすることは、会社にとってもの技術投資でもあると考えています。
今回のKotlinFestをはじめ、今後も技術コミュニティにも積極的に支援していきたいと考えております!

余談: フルKotlinのアプリでストア総合一位はDELISHKITCHENが初?

Kotlinが採用されたGoogleI/O 2017には現地で参加していたのですが、
ちょうどそのとき日本のストアで総合1位になってました。
今日本で1位のアプリはフルKotlinだというのをGooglerに話したところ、
フルKotlinとか聞いたことない / 多分ランキング初めてなんじゃないか?
と言っていただいたので、個人的には勝手に弊社のアプリが初めてだと思ってますw

GoogleI/O 2017の時のストアのスクリーンショット

Android プロジェクトの KSP 化を検討するにあたって

この記事は every Tech Blog Advent Calendar 2024(夏) 20 日目の記事です。

はじめに

こんにちは、DELISH KITCHEN でクライアントエンジニアを担当している岡田です。
今回は KSP 化について執筆させていただきます。

概要

Kotlin でより効率的な開発を行う上で、アノテーション処理は欠かせない要素です。アノテーションを利用することで、定型的なコードを自動生成したり、コンパイル時にコードの検証を行ったりすることができます。

Kotlin のアノテーション処理といえば、従来は kapt が主流です。しかし、近年ではより高速でKotlinの言語機能を活かせる KSP が主流になりつつあります。

この記事では、kapt と KSP について調べ、 Android プロジェクトの KSP 化を検討するにあたって必要な情報を記述します。

kapt について

kapt (Kotlin Annotation Processing Tool) を使用すると、 Java アノテーションプロセッサを使用して Kotlin コードを処理できます。
Room などの Android 開発でよく使用される、多くのライブラリと連携しています。

しかし、 Kotlin ファイルから Java アノテーションプロセッサが読み取れるようにするためには Java スタブを生成する必要があります。
実際に生成された Java スタブ は build/generated/source/kapt/ で確認することができます。
例えば以下は実際に DELISH KITCHEN で Hilt によって生成される、 ArticleListActivity のインスタンスに依存性を注入するためのコードです。

ArticleListActivity_GeneratedInjector.java

@OriginatingElement(
    topLevelClass = ArticleListActivity.class
)
@GeneratedEntryPoint
@InstallIn(ActivityComponent.class)
public interface ArticleListActivity_GeneratedInjector {
  void injectArticleListActivity(ArticleListActivity articleListActivity);
}

.*_GeneratedInjector.java@AndroidEntryPoint@Inject など、依存性を注入する必要がある全箇所に対して生成されます。
この例のような Java スタブが、 Hilt に関してのみでもプロジェクト内で相当な数生成されています。
Java スタブの生成は高コストのオペレーションのため、処理速度が遅く、ビルド速度に大きく影響してしまうのです。

また 2024/6/20 時点で kapt はメンテナンスモードに入っています。
「既に新しい機能が実装されることはない」と明記されており、 KSP の使用が推奨されています。

KSP がサポートされていないライブラリを用いたいなどの理由がない限りは、 kapt ではなく KSP を使用する方が良いでしょう。

KSP について

KSP (Kotlin Symbol Processing) は、 Kotlin コードを直接処理するためのアノテーション処理ツールです。
kapt とは異なり、 Kotlin コンパイラのプラグインとして動作するため、 Java コードへの変換が不要です。
つまり kapt のように Java スタブを生成する必要がなくなり、処理速度が大幅に上昇し、ビルド速度も速くなります。

Googleが公開しているGithubによると、「 kapt と比較すると、 KSP を使用するアノテーションプロセッサは最大 2 倍高速に実行できる」そうです。
また Kotlin の公式ドキュメントでは、コード生成に 7.5 倍ほどのパフォーマンス差があることも確認されています。

For performance evaluation, we implemented a simplified version of Glide in KSP to make it generate code for the Tachiyomi project. While the total Kotlin compilation time of the project is 21.55 seconds on our test device, it took 8.67 seconds for kapt to generate the code, and it took 1.15 seconds for our KSP implementation to generate the code.

仕組みとして、 Kotlin プログラムを Kotlin の文法に沿ってシンボルレベルでモデル化しているそうです。
KSP は Kotlin プログラムの構造を、クラスや関数などの構成要素単位で処理することができます。
Kotlin プログラムの構造をより抽象化された形で捉えて処理することで、アノテーション処理などのタスクを効率的に行います。
ただし、 if 文や for ループなどの制御構文にはアクセスできず、細かい制御フローの解析には向いていません。

kapt から KSP へ移行する方法は、公式ドキュメントを参照ください。
また KSP の最新のリリースはこちらから確認できます。
今後のロードマップには、マルチプラットフォーム対応やパフォーマンス改善も挙げられています。

実際の速度比較

では実際に KSP 化すると、 Kotlin のタスクはどの程度速度が上がるのでしょうか。
今回は簡単に、 Room でデータを Insert するだけの簡単なアプリを作成して比較しました。
測定方法は Android Studio の Build > Clean Project 実行後、 Build > Rebuild Project を実行し、 Build Analyzer にて測定しました。 データはそれぞれ 10 回ずつ測定し、画像は平均に一番近いデータを添付しています。

kapt KSP
1.9s 1.4s

結果として、およそ 0.5s ほど KSP の方が早かったです。 kapt で生成された Java スタブとして、以下のファイルが確認できました。

  • AppDatabase_Impl.java
  • UserDao_Impl.java

これらは Room の Database と Dao 関連のスタブです。たった 2 ファイルの生成ですが、如実な差を確認することができました。
Room や Hilt を導入しているプロジェクトでは、大幅な速度上昇が見込めそうです。

KSP の注意点

kapt と KSP の併用する際は注意が必要です。

Room などの主要なライブラリは KSP に対応していますが、 KSP に対応していないライブラリも存在します。
こちらで現在サポートされている主要ライブラリを確認することが可能です。
例えば 2024/6/20 時点で、 Auto Factory は KSP をサポートしていません。
またプロジェクトのライブラリ更新が滞っており、そちらを先に対応しないと KSP 化できないという場合も想定できます。
プロジェクトでこれらのような KSP に未対応のライブラリを導入している場合は、一部のみを KSP 化し、 kapt と併用する形になるでしょう。

しかし kapt と KSP の併用は、ビルド時間が増加することがあります。
これは同じアノテーションプロセッサを kapt と KSP の両方で実行する場合に処理が重複してしまうためです。
高速なアノテーション処理を期待して KSP を導入したのに、逆に処理が遅くなるという事態に陥ります。

まとめ

この記事では、 Kotlin のアノテーション処理ツールである kapt と KSP について記述しました。

kapt は Java のアノテーションプロセッサを Kotlin で利用できるツールですが、 Javaスタブ の生成が必要なためビルド時間が長くなるという欠点があります。
また、現在メンテナンスモードに入っているため、今後 KSP への移行が推奨されています。

KSP は Kotlin コードを直接処理できるため、 kapt よりも高速にアノテーション処理を行うことができます。
公式ドキュメントによると、 kapt と比較して最大2倍、コード生成では 7.5 倍ほどのパフォーマンス差があるそうです。

KSP 化を進める際は、 KSP に対応していないライブラリが存在することに注意が必要です。
そのようなライブラリを使用している場合は、 kapt と KSP を併用する必要がありますが、ビルド時間が増加する可能性があるため注意が必要です。

おわりに

Kotlin Fest 2024 まで、あと 2 日! https://www.kotlinfest.dev/

株式会社エブリー は、ひよこスポンサー として Kotlin Fest 2024 に参加します。 ぜひ、ブースでお会いしましょう!