feat: migrate nodeAliasCache to room
This commit is contained in:
@@ -10,10 +10,10 @@ import kotlinx.coroutines.flow.StateFlow
|
||||
class LNbitsNodeAliasRepository(
|
||||
private val service: NodeAliasService
|
||||
) : NodeAliasRepository {
|
||||
|
||||
override val aliases: StateFlow<Map<String, String>>
|
||||
get() = service.aliases
|
||||
|
||||
override suspend fun resolve(pubkey: String): String? =
|
||||
service.resolve(pubkey)
|
||||
override suspend fun warmUp() = service.warmUp()
|
||||
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
package com.bitcointxoko.gudariwallet.data
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.util.Log
|
||||
import com.bitcointxoko.gudariwallet.api.*
|
||||
import com.bitcointxoko.gudariwallet.api.LNbitsClient.httpClient
|
||||
import com.bitcointxoko.gudariwallet.security.SecretStore
|
||||
import com.bitcointxoko.gudariwallet.util.LnurlBech32
|
||||
import com.bitcointxoko.gudariwallet.util.LnurlMetadataParser
|
||||
@@ -15,9 +13,6 @@ private const val TAG = "LNbitsWalletRepo"
|
||||
class LNbitsWalletRepository(
|
||||
private val api: LNbitsApi,
|
||||
private val secrets: SecretStore,
|
||||
private val context : Context,
|
||||
private val nodeAliasService: com.bitcointxoko.gudariwallet.service.NodeAliasService =
|
||||
com.bitcointxoko.gudariwallet.service.NodeAliasService(httpClient, context),
|
||||
) : WalletRepository {
|
||||
|
||||
// ── Balance ───────────────────────────────────────────────────────────────
|
||||
@@ -157,7 +152,7 @@ class LNbitsWalletRepository(
|
||||
append(amountMsat)
|
||||
if (!comment.isNullOrBlank()) {
|
||||
append("&comment=")
|
||||
append(android.net.Uri.encode(comment))
|
||||
append(Uri.encode(comment))
|
||||
}
|
||||
}
|
||||
Log.d(TAG, "LNURL [FETCH INVOICE] callback=$callbackUrl")
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.bitcointxoko.gudariwallet.data
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import com.bitcointxoko.gudariwallet.data.db.AppDatabase
|
||||
import com.bitcointxoko.gudariwallet.data.db.NodeAliasCacheEntity
|
||||
import com.bitcointxoko.gudariwallet.util.WalletConstants
|
||||
|
||||
private const val TAG = "NodeAliasCacheRepository"
|
||||
|
||||
class NodeAliasCacheRepository(context: Context) {
|
||||
|
||||
private val dao = AppDatabase.getInstance(context).nodeAliasCacheDao()
|
||||
|
||||
/**
|
||||
* Returns a cached alias for [pubkey] if it exists and is within
|
||||
* [WalletConstants.ALIAS_CACHE_TTL_MS]. Returns null on miss or stale.
|
||||
*/
|
||||
suspend fun get(pubkey: String): String? {
|
||||
val entry = dao.getByPubkey(pubkey) ?: run {
|
||||
Log.d(TAG, "ALIAS [CACHE MISS ] $pubkey")
|
||||
return null
|
||||
}
|
||||
val ageMs = System.currentTimeMillis() - entry.savedAt
|
||||
return if (ageMs < WalletConstants.ALIAS_CACHE_TTL_MS) {
|
||||
Log.d(TAG, "ALIAS [CACHE HIT ] $pubkey → \"${entry.alias}\" (age ${ageMs / 1000}s)")
|
||||
entry.alias
|
||||
} else {
|
||||
Log.d(TAG, "ALIAS [CACHE STALE] $pubkey (age ${ageMs / 1000}s) — will refetch")
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
/** Upsert an alias into the Room cache. */
|
||||
suspend fun put(pubkey: String, alias: String) {
|
||||
dao.insert(
|
||||
NodeAliasCacheEntity(
|
||||
pubkey = pubkey,
|
||||
alias = alias,
|
||||
savedAt = System.currentTimeMillis()
|
||||
)
|
||||
)
|
||||
Log.d(TAG, "ALIAS [PERSIST ] $pubkey → \"$alias\"")
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all non-stale entries from Room as a plain map.
|
||||
* Used to seed the StateFlow on startup — replaces [loadPersistedCache].
|
||||
*/
|
||||
suspend fun loadAll(): Map<String, String> {
|
||||
val now = System.currentTimeMillis()
|
||||
return dao.getAll()
|
||||
.filter { now - it.savedAt < WalletConstants.ALIAS_PERSIST_TTL_MS }
|
||||
.associate { it.pubkey to it.alias }
|
||||
.also { Log.d(TAG, "ALIAS [PERSIST ] Loaded ${it.size} non-stale entries from Room") }
|
||||
}
|
||||
|
||||
/** Remove a single entry. */
|
||||
suspend fun remove(pubkey: String) {
|
||||
dao.deleteByPubkey(pubkey)
|
||||
}
|
||||
|
||||
/** Wipe the entire alias cache — mirrors [clearPersistedCache]. */
|
||||
suspend fun clearAll() {
|
||||
dao.deleteAll()
|
||||
Log.d(TAG, "ALIAS [PERSIST ] Cache cleared")
|
||||
}
|
||||
}
|
||||
@@ -18,4 +18,5 @@ interface NodeAliasRepository {
|
||||
* Results are cached — repeated calls for the same pubkey are cheap.
|
||||
*/
|
||||
suspend fun resolve(pubkey: String): String?
|
||||
suspend fun warmUp()
|
||||
}
|
||||
|
||||
@@ -9,8 +9,12 @@ import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
@Database(
|
||||
entities = [PaymentRecordEntity::class, LnurlCacheEntity::class],
|
||||
version = 2,
|
||||
entities = [
|
||||
PaymentRecordEntity::class,
|
||||
LnurlCacheEntity::class,
|
||||
NodeAliasCacheEntity::class, // ← added
|
||||
],
|
||||
version = 3, // ← bumped
|
||||
exportSchema = false
|
||||
)
|
||||
@TypeConverters(PaymentConverters::class)
|
||||
@@ -18,6 +22,7 @@ abstract class AppDatabase : RoomDatabase() {
|
||||
|
||||
abstract fun paymentDao(): PaymentDao
|
||||
abstract fun lnurlCacheDao(): LnurlCacheDao
|
||||
abstract fun nodeAliasCacheDao(): NodeAliasCacheDao // ← added
|
||||
|
||||
companion object {
|
||||
@Volatile private var INSTANCE: AppDatabase? = null
|
||||
@@ -37,6 +42,21 @@ abstract class AppDatabase : RoomDatabase() {
|
||||
}
|
||||
}
|
||||
|
||||
private val MIGRATION_2_3 = object : Migration(2, 3) { // ← added
|
||||
override fun migrate(db: SupportSQLiteDatabase) {
|
||||
db.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `node_alias_cache` (
|
||||
`pubkey` TEXT NOT NULL,
|
||||
`alias` TEXT NOT NULL,
|
||||
`savedAt` INTEGER NOT NULL,
|
||||
PRIMARY KEY(`pubkey`)
|
||||
)
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun getInstance(context: Context): AppDatabase =
|
||||
INSTANCE ?: synchronized(this) {
|
||||
INSTANCE ?: Room.databaseBuilder(
|
||||
@@ -44,7 +64,7 @@ abstract class AppDatabase : RoomDatabase() {
|
||||
AppDatabase::class.java,
|
||||
"gudari_wallet.db"
|
||||
)
|
||||
.addMigrations(MIGRATION_1_2)
|
||||
.addMigrations(MIGRATION_1_2, MIGRATION_2_3) // ← added
|
||||
.build()
|
||||
.also { INSTANCE = it }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.bitcointxoko.gudariwallet.data.db
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
|
||||
@Dao
|
||||
interface NodeAliasCacheDao {
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insert(entry: NodeAliasCacheEntity)
|
||||
|
||||
@Query("SELECT * FROM node_alias_cache WHERE pubkey = :pubkey LIMIT 1")
|
||||
suspend fun getByPubkey(pubkey: String): NodeAliasCacheEntity?
|
||||
|
||||
@Query("SELECT * FROM node_alias_cache")
|
||||
suspend fun getAll(): List<NodeAliasCacheEntity>
|
||||
|
||||
@Query("DELETE FROM node_alias_cache WHERE pubkey = :pubkey")
|
||||
suspend fun deleteByPubkey(pubkey: String)
|
||||
|
||||
@Query("DELETE FROM node_alias_cache")
|
||||
suspend fun deleteAll()
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.bitcointxoko.gudariwallet.data.db
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(tableName = "node_alias_cache")
|
||||
data class NodeAliasCacheEntity(
|
||||
@PrimaryKey
|
||||
val pubkey: String, // node public key
|
||||
val alias: String, // resolved human-readable alias
|
||||
val savedAt: Long // epoch ms — used for TTL checks
|
||||
)
|
||||
Reference in New Issue
Block a user