Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,13 @@ abstract class AppDatabase : RoomDatabase() {
@Volatile private var instance: AppDatabase? = null

fun get(context: Context): AppDatabase = instance ?: synchronized(this) {
instance ?: Room.databaseBuilder(context, AppDatabase::class.java, "stellar.db")
val deviceContext = context.applicationContext.createDeviceProtectedStorageContext()
runCatching { deviceContext.moveDatabaseFrom(context.applicationContext, DATABASE_NAME) }
instance ?: Room.databaseBuilder(deviceContext, AppDatabase::class.java, DATABASE_NAME)
.fallbackToDestructiveMigration()
.build().also { instance = it }
Comment on lines +19 to 22
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The moveDatabaseFrom migration call is placed outside the inner instance ?: check but inside the synchronized block. In the double-checked locking pattern, a second thread that was waiting at the synchronized boundary (with instance == null at the outer check) will re-evaluate instance in the inner check and skip building the DB — but it will still execute the moveDatabaseFrom call unnecessarily.

While this is safe in practice because runCatching absorbs the resulting error (the source file no longer exists after the first successful migration), it is cleaner and more efficient to place the migration inside the inner instance ?: check alongside the Room builder call, so that it is only ever attempted once during DB initialization.

Suggested change
runCatching { deviceContext.moveDatabaseFrom(context.applicationContext, DATABASE_NAME) }
instance ?: Room.databaseBuilder(deviceContext, AppDatabase::class.java, DATABASE_NAME)
.fallbackToDestructiveMigration()
.build().also { instance = it }
instance ?: run {
runCatching { deviceContext.moveDatabaseFrom(context.applicationContext, DATABASE_NAME) }
Room.databaseBuilder(deviceContext, AppDatabase::class.java, DATABASE_NAME)
.fallbackToDestructiveMigration()
.build().also { instance = it }
}

Copilot uses AI. Check for mistakes.
}

private const val DATABASE_NAME = "stellar.db"
}
}
6 changes: 5 additions & 1 deletion server/src/main/kotlin/roro/stellar/server/ConfigManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,11 @@ class ConfigManager {
fun loadFromManagerWithStatus(): Pair<StellarConfig, Boolean> {
return try {
val reply = callProvider("loadConfig", null)
val json = reply?.getString("configJson")
if (reply == null) {
LOGGER.w("从 manager 加载配置失败: 返回 null,使用默认值并跳过初始化写入")
return Pair(StellarConfig(), false)
}
val json = reply.getString("configJson")
if (json != null) {
val config = GSON_IN.fromJson(json, StellarConfig::class.java) ?: StellarConfig()
Pair(config, true)
Expand Down