Skip to content

Commit ed39e3a

Browse files
Fixed: Play reported android.database.sqlite.SQLiteConstraintException while saving history.
* Fix: Prevent a rare `SQLiteConstraintException` that occurred when the `saveHistory()` method was called at the same time by the migration job (running on the IO thread) and the main application flow. There is very little chance of this race condition, which is why it caused only a single crash. * Wrapped the `saveHistory()` method inside a coroutine `Mutex` to make concurrent database writes thread-safe, ensuring that only one insertion executes at a time while others wait. This prevents data loss during migration and ensures that new history entries are safely persisted in the database.
1 parent 46c97a3 commit ed39e3a

File tree

1 file changed

+25
-19
lines changed

1 file changed

+25
-19
lines changed

core/src/main/java/org/kiwix/kiwixmobile/core/dao/HistoryRoomDao.kt

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,17 @@ import androidx.room.TypeConverter
2626
import androidx.room.Update
2727
import kotlinx.coroutines.flow.Flow
2828
import kotlinx.coroutines.flow.map
29+
import kotlinx.coroutines.sync.Mutex
30+
import kotlinx.coroutines.sync.withLock
2931
import org.kiwix.kiwixmobile.core.dao.entities.HistoryRoomEntity
3032
import org.kiwix.kiwixmobile.core.page.adapter.Page
3133
import org.kiwix.kiwixmobile.core.page.history.adapter.HistoryListItem
3234
import org.kiwix.kiwixmobile.core.reader.ZimReaderSource
3335

3436
@Dao
3537
abstract class HistoryRoomDao : PageDao {
38+
private val saveMutex = Mutex()
39+
3640
@Query("SELECT * FROM HistoryRoomEntity ORDER BY HistoryRoomEntity.timeStamp DESC")
3741
abstract fun historyRoomEntity(): Flow<List<HistoryRoomEntity>>
3842

@@ -65,26 +69,28 @@ abstract class HistoryRoomDao : PageDao {
6569
@Query("SELECT COUNT() FROM HistoryRoomEntity WHERE id = :id")
6670
abstract fun count(id: Int): Int
6771

68-
fun saveHistory(historyItem: HistoryListItem.HistoryItem) {
69-
getHistoryRoomEntity(
70-
historyItem.historyUrl,
71-
historyItem.dateString
72-
)?.let {
73-
it.apply {
74-
// update the existing entity
75-
historyUrl = historyItem.historyUrl
76-
historyTitle = historyItem.title
77-
timeStamp = historyItem.timeStamp
78-
dateString = historyItem.dateString
79-
}
80-
updateHistoryItem(it)
81-
} ?: run {
82-
val historyEntity = HistoryRoomEntity(historyItem)
83-
if (count(historyEntity.id.toInt()) > 0) {
84-
// set the default id so that room will automatically generates the database id.
85-
historyEntity.id = 0
72+
suspend fun saveHistory(historyItem: HistoryListItem.HistoryItem) {
73+
saveMutex.withLock {
74+
getHistoryRoomEntity(
75+
historyItem.historyUrl,
76+
historyItem.dateString
77+
)?.let {
78+
it.apply {
79+
// update the existing entity
80+
historyUrl = historyItem.historyUrl
81+
historyTitle = historyItem.title
82+
timeStamp = historyItem.timeStamp
83+
dateString = historyItem.dateString
84+
}
85+
updateHistoryItem(it)
86+
} ?: run {
87+
val historyEntity = HistoryRoomEntity(historyItem)
88+
if (count(historyEntity.id.toInt()) > 0) {
89+
// set the default id so that room will automatically generates the database id.
90+
historyEntity.id = 0
91+
}
92+
insertHistoryItem(historyEntity)
8693
}
87-
insertHistoryItem(historyEntity)
8894
}
8995
}
9096

0 commit comments

Comments
 (0)