fix: resolve bugs across connection, PKI, admin, packet flow, and stability subsystems (#5011)

This commit is contained in:
James Rich 2026-04-09 08:20:06 -05:00 committed by GitHub
parent cd9f1c0600
commit 60cc2f4237
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 413 additions and 45 deletions

View file

@ -23,6 +23,7 @@ import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.core.longPreferencesKey
import co.touchlab.kermit.Logger
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.SupervisorJob
@ -135,10 +136,12 @@ open class DatabaseManager(
// Also mark the previous DB as used "just now" so LRU has an accurate, recent timestamp
previousDbName?.let { markLastUsed(it) }
// Now safe to close the previous DB — collectors have switched to the new instance.
if (previousDbName != null && previousDbName != dbName) {
closeCachedDatabase(previousDbName)
}
// Do NOT close the previous DB synchronously here. Even though _currentDb has been
// updated, in-flight `withDb` calls may still hold a reference to the old database
// (captured before the emission). Closing the connection pool while those queries are
// executing causes "Connection pool is closed" crashes. Instead, let LRU eviction
// (enforceCacheLimit) handle cleanup — it only runs on databases that are not the
// active target and have not been used recently.
// Defer LRU eviction so switch is not blocked by filesystem work
managerScope.launch(dispatchers.io) { enforceCacheLimit(activeDbName = dbName) }
@ -167,11 +170,26 @@ open class DatabaseManager(
private val limitedIo = dispatchers.io.limitedParallelism(4)
/** Execute [block] with the current DB instance. */
@Suppress("TooGenericExceptionCaught")
override suspend fun <T> withDb(block: suspend (MeshtasticDatabase) -> T): T? = withContext(limitedIo) {
val db = _currentDb.value ?: return@withContext null
val active = buildDbName(_currentAddress.value)
markLastUsed(active)
block(db)
try {
block(db)
} catch (e: CancellationException) {
throw e // Preserve structured concurrency cancellation propagation.
} catch (e: Exception) {
// If the connection pool was closed between capturing `db` and executing the query
// (e.g., during a database switch), retry once with the current DB instance.
if (e.message?.contains("Connection pool is closed") == true) {
Logger.w { "withDb: connection pool closed, retrying with current DB" }
val retryDb = _currentDb.value ?: return@withContext null
block(retryDb)
} else {
throw e
}
}
}
/** Returns true if a database exists for the given device address. */

View file

@ -289,6 +289,7 @@ interface NodeInfoDao {
@Upsert suspend fun doUpsert(node: NodeEntity)
@Transaction
suspend fun upsert(node: NodeEntity) {
val verifiedNode = getVerifiedNodeForUpsert(node)
doUpsert(verifiedNode)