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
Roomの監視可能なクエリのカスタマイズとレガシーコードへの適用
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
shiita0903
March 11, 2025
Technology
340
2
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Roomの監視可能なクエリのカスタマイズとレガシーコードへの適用
Qiita Bashで発表したスライドです。
https://increments.connpass.com/event/344818/
shiita0903
March 11, 2025
More Decks by shiita0903
See All by shiita0903
ネストしたdata classの面倒な更新にさようなら!Lensを作って理解するArrowのOpticsの世界
shiita0903
2
1.4k
Other Decks in Technology
See All in Technology
螺旋型キャリアの生存戦略 / kinoko-conf2026
rakus_dev
1
980
「軸足」は 固定しなくていい - 熱量と強みで描く、しなやかなキャリアの形
kakehashi
PRO
1
270
AI 不只幫你寫 Code: 當專案從 300 暴增到 1500, 我們如何撐住 DevOps
appleboy
0
230
PostgreSQL 19 新機能概要 OSC Hokkaido 2026
nori_shinoda
0
240
AI時代のコスト管理を考えよう〜明日から使える実践AWSノウハウ~
yoshimi0227
0
860
脱SaaS!FDEを支えるプロビジョニングと分離設計
knih
0
300
Microsoft のサポートとフィードバック総まとめ
murachiakira
PRO
0
110
[チョークトーク資料]AWS DevOps Agent を使いこなす / AWS Dev Ops Agent Chalk Talk AWS Summit Japan 2026
kinunori
4
770
「ビジネスがわかるエンジニア」とは何か?
ryooob
0
300
[AWS Summit Japan 2026]迷っているあなたへ_小さな一歩が、やがて自分を助けてくれる
sh_fk2
2
410
AWS Security Agent といっしょに脅威モデリングをやってみよう
amarelo_n24
1
210
自分が詳しくない領域でAIを使う #プロヒス2026
konifar
20
7.5k
Featured
See All Featured
jQuery: Nuts, Bolts and Bling
dougneiner
66
8.5k
The Mindset for Success: Future Career Progression
greggifford
PRO
0
370
Automating Front-end Workflow
addyosmani
1370
210k
VelocityConf: Rendering Performance Case Studies
addyosmani
333
25k
A designer walks into a library…
pauljervisheath
211
24k
The Illustrated Children's Guide to Kubernetes
chrisshort
51
52k
Unsuck your backbone
ammeep
672
58k
SEO in 2025: How to Prepare for the Future of Search
ipullrank
3
3.5k
Joys of Absence: A Defence of Solitary Play
codingconduct
1
400
Prompt Engineering for Job Search
mfonobong
0
350
SERP Conf. Vienna - Web Accessibility: Optimizing for Inclusivity and SEO
sarafernandez
2
1.5k
From π to Pie charts
rasagy
0
220
Transcript
Roomの監視可能なクエリのカスタマイ ズとレガシーコードへの適用
Room とは SQLiteを扱うためのAndroidXのライブラリ。 特徴 クエリのコンパイル時の検証 コード生成によるボイラープレートの削減 1: https://developer.android.com/training/data-storage/room 2
Roomとは エンティティ(テーブル)の定義 @Entity(tableName = "user") data class User( @PrimaryKey val
id: Int, @ColumnInfo(name = "name") val name: String, @ColumnInfo(name = "is_premium") val isPremium: Boolean, @ColumnInfo(name = "extra_data") val extraData: String ) 3
Roomとは DAO(Data Access Object)の定義 @Dao interface UserDao { @Insert suspend
fun insert(user: User) @Query("SELECT name FROM user WHERE is_premium") suspend fun selectPremiumUserNames(): List<String> } 4
Roomとは DBの定義 @Database(entities = [User::class], version = 1) abstract class
UserDatabase : RoomDatabase() { abstract fun getUserDao(): UserDao } 5
値の変更をFlowで監視 戻り値の型を Flow でラップするだけでOK @Dao interface UserDao { @Query("SELECT name
FROM user WHERE is_premium") fun selectPremiumUserNamesFlow(): Flow<List<String>> } 6
値の変更をFlowで監視 @Test fun flowSample() = runTest { dao.selectPremiumUserNamesFlow().test { assertEquals(emptyList(),
awaitItem()) dao.insert(User(id = 1, name = "name1", isPremium = true, extraData = "")) assertEquals(listOf("name1"), awaitItem()) dao.insert(User(id = 2, name = "name2", isPremium = false, extraData = "")) assertEquals(listOf("name1"), awaitItem()) dao.insert(User(id = 3, name = "name3", isPremium = true, extraData = "")) assertEquals(listOf("name1", "name3"), awaitItem()) } } https://github.com/cashapp/turbine 7
Flowを返す際の注意事項 テーブルの行が更新されるたびにクエリが実行され、値が変わったか どうかにかかわらず Flow に流れる。 dao.insert(User(id = 1, name =
"name1", isPremium = true, extraData = "")) assertEquals(listOf("name1"), awaitItem()) dao.insert(User(id = 2, name = "name2", isPremium = false, extraData = "")) assertEquals(listOf("name1"), awaitItem()) 2: https://developer.android.com/training/data-storage/room/async-queries#observable 8
Flowを返す際の注意事項 distinctUntilChanged() で、同じ値が連続で流れることを防止。 @Test fun flowDistinctSample() = runTest { dao.selectPremiumUserNamesFlow().distinctUntilChanged().test
{ assertEquals(emptyList(), awaitItem()) dao.insert(User(id = 1, name = "name1", isPremium = true, extraData = "")) assertEquals(listOf("name1"), awaitItem()) dao.insert(User(id = 2, name = "name2", isPremium = false, extraData = "")) // No data is emitted. dao.insert(User(id = 3, name = "name3", isPremium = true, extraData = "")) assertEquals(listOf("name1", "name3"), awaitItem()) } } 9
レガシーコードで起こった問題
レガシーコードで起こった問題1 distinctUntilChanged() では無意味なクエリの実行自体を防ぐことは できない。 短時間での頻繁な更新と時間のかかるクエリの組み合わせが、DBを性 能を低下させる可能性がある。 private fun getExtraDataList(): List<Pair<Int,
String>> = List(size = 1000) { it to "extraData$it" } private suspend fun issue() { getExtraDataList().forEach { (userId, extraData) -> dao.updateExtraData(userId, extraData) } } 11
レガシーコードで起こった問題2 @Transaction を利用したクエリは Flow を返すことができない。 @Transaction suspend fun queries(): List<String>
{ // Flow can't be used for the return value. doSomething() return selectPremiumUserNames() } 12
RoomがDBを監視する仕組み InvalidationTracker がテーブルの更新を検知して通知 SQLiteのTriggerを利用して実装されている publicなので、アプリからも利用可能 13
実装 private fun RoomDatabase.createInvalidationEventFlowWithThrottle( tableName: String, throttleTimeout: Duration = 1.seconds
): Flow<Unit> = callbackFlow { val observer = object : InvalidationTracker.Observer(tableName) { override fun onInvalidated(tables: Set<String>) { trySendBlocking(Unit) } } invalidationTracker.addObserver(observer) send(Unit) // Initial signal to perform first query. awaitClose { invalidationTracker.removeObserver(observer) } } .flowOn(Dispatchers.IO) 14
実装 RxJavaの throttleLatest に対応する関数を作成。 3: https://reactivex.io/RxJava/3.x/javadoc/io/reactivex/rxjava3/core/Observable.html#throttleLatest- long-java.util.concurrent.TimeUnit-io.reactivex.rxjava3.core.Scheduler-boolean- 15
実装 private fun RoomDatabase.createInvalidationEventFlowWithThrottle( ... ): Flow<Unit> = callbackFlow {
... } .flowOn(Dispatchers.IO) .throttleLatest(throttleTimeout.inWholeMilliseconds) private fun <T> Flow<T>.throttleLatest(timeoutMillis: Long): Flow<T> = this .conflate() .transform { emit(it) delay(timeoutMillis) } 16
実装 @Dao abstract class UserDao(private val database: UserDatabase) { @Transaction
open suspend fun queries(): List<String> { doSomething() return selectPremiumUserNames() } fun selectPremiumUserNamesFlow(): Flow<List<String>> = database .createInvalidationEventFlowWithThrottle(tableName = "user") .map { queries() } .distinctUntilChanged() } 17
まとめ Roomで自動生成される Flow を返す関数の問題点 テーブルが更新されるたびに毎回クエリが実行される @Transaction で実装したクエリでは利用できない InvalidationTracker の利用で、監視可能な関数を自作できる 18