Merge pull request #5297 from vector-im/feature/fga/fix_bad_realm_usages

Improve usage of realm
This commit is contained in:
Benoit Marty 2022-02-22 15:21:14 +01:00 committed by GitHub
commit 79b511b44d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 44 additions and 57 deletions

1
changelog.d/5297.misc Normal file
View File

@ -0,0 +1 @@
Improve some internal realm usages.

View File

@ -16,9 +16,7 @@
package org.matrix.android.sdk.api.session.room.model package org.matrix.android.sdk.api.session.room.model
import org.matrix.android.sdk.api.session.user.model.User
data class ReadReceipt( data class ReadReceipt(
val user: User, val roomMember: RoomMemberSummary,
val originServerTs: Long val originServerTs: Long
) )

View File

@ -19,11 +19,9 @@ import com.zhuinden.monarchy.Monarchy
import io.realm.Realm import io.realm.Realm
import io.realm.RealmConfiguration import io.realm.RealmConfiguration
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.isActive import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withPermit
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import timber.log.Timber import timber.log.Timber
@ -37,30 +35,26 @@ internal fun <T> CoroutineScope.asyncTransaction(realmConfiguration: RealmConfig
} }
} }
private val realmSemaphore = Semaphore(1)
suspend fun <T> awaitTransaction(config: RealmConfiguration, transaction: suspend (realm: Realm) -> T): T { suspend fun <T> awaitTransaction(config: RealmConfiguration, transaction: suspend (realm: Realm) -> T): T {
return realmSemaphore.withPermit { return withContext(Realm.WRITE_EXECUTOR.asCoroutineDispatcher()) {
withContext(Dispatchers.IO) { Realm.getInstance(config).use { bgRealm ->
Realm.getInstance(config).use { bgRealm -> bgRealm.beginTransaction()
bgRealm.beginTransaction() val result: T
val result: T try {
try { val start = System.currentTimeMillis()
val start = System.currentTimeMillis() result = transaction(bgRealm)
result = transaction(bgRealm) if (isActive) {
if (isActive) { bgRealm.commitTransaction()
bgRealm.commitTransaction() val end = System.currentTimeMillis()
val end = System.currentTimeMillis() val time = end - start
val time = end - start Timber.v("Execute transaction in $time millis")
Timber.v("Execute transaction in $time millis") }
} } finally {
} finally { if (bgRealm.isInTransaction) {
if (bgRealm.isInTransaction) { bgRealm.cancelTransaction()
bgRealm.cancelTransaction()
}
} }
result
} }
result
} }
} }
} }

View File

@ -19,7 +19,7 @@ package org.matrix.android.sdk.internal.database.mapper
import org.matrix.android.sdk.api.session.room.model.ReadReceipt import org.matrix.android.sdk.api.session.room.model.ReadReceipt
import org.matrix.android.sdk.internal.database.RealmSessionProvider import org.matrix.android.sdk.internal.database.RealmSessionProvider
import org.matrix.android.sdk.internal.database.model.ReadReceiptsSummaryEntity import org.matrix.android.sdk.internal.database.model.ReadReceiptsSummaryEntity
import org.matrix.android.sdk.internal.database.model.UserEntity import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntity
import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.database.query.where
import javax.inject.Inject import javax.inject.Inject
@ -29,14 +29,12 @@ internal class ReadReceiptsSummaryMapper @Inject constructor(private val realmSe
if (readReceiptsSummaryEntity == null) { if (readReceiptsSummaryEntity == null) {
return emptyList() return emptyList()
} }
return realmSessionProvider.withRealm { realm -> val readReceipts = readReceiptsSummaryEntity.readReceipts
val readReceipts = readReceiptsSummaryEntity.readReceipts return readReceipts
readReceipts .mapNotNull {
.mapNotNull { val roomMember = RoomMemberSummaryEntity.where(readReceiptsSummaryEntity.realm, roomId = it.roomId, userId = it.userId).findFirst()
val user = UserEntity.where(realm, it.userId).findFirst() ?: return@mapNotNull null
?: return@mapNotNull null ReadReceipt(roomMember.asDomain(), it.originServerTs.toLong())
ReadReceipt(user.asDomain(), it.originServerTs.toLong()) }
}
}
} }
} }

View File

@ -48,7 +48,7 @@ internal class TimelineEventMapper @Inject constructor(private val readReceiptsS
), ),
readReceipts = readReceipts readReceipts = readReceipts
?.distinctBy { ?.distinctBy {
it.user it.roomMember
}?.sortedByDescending { }?.sortedByDescending {
it.originServerTs it.originServerTs
}.orEmpty() }.orEmpty()

View File

@ -17,7 +17,6 @@
package org.matrix.android.sdk.internal.session.room.create package org.matrix.android.sdk.internal.session.room.create
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import io.realm.Realm
import io.realm.RealmConfiguration import io.realm.RealmConfiguration
import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.TimeoutCancellationException
import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.failure.Failure
@ -28,6 +27,7 @@ import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomPreset import org.matrix.android.sdk.api.session.room.model.create.CreateRoomPreset
import org.matrix.android.sdk.internal.database.awaitNotEmptyResult import org.matrix.android.sdk.internal.database.awaitNotEmptyResult
import org.matrix.android.sdk.internal.database.awaitTransaction
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields
import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.database.query.where
@ -105,7 +105,7 @@ internal class DefaultCreateRoomTask @Inject constructor(
throw CreateRoomFailure.CreatedWithTimeout(roomId) throw CreateRoomFailure.CreatedWithTimeout(roomId)
} }
Realm.getInstance(realmConfiguration).executeTransactionAsync { awaitTransaction(realmConfiguration) {
RoomSummaryEntity.where(it, roomId).findFirst()?.lastActivityTime = System.currentTimeMillis() RoomSummaryEntity.where(it, roomId).findFirst()?.lastActivityTime = System.currentTimeMillis()
} }

View File

@ -16,7 +16,6 @@
package org.matrix.android.sdk.internal.session.room.membership.joining package org.matrix.android.sdk.internal.session.room.membership.joining
import io.realm.Realm
import io.realm.RealmConfiguration import io.realm.RealmConfiguration
import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.TimeoutCancellationException
import org.matrix.android.sdk.api.session.events.model.toContent import org.matrix.android.sdk.api.session.events.model.toContent
@ -24,6 +23,7 @@ import org.matrix.android.sdk.api.session.room.failure.JoinRoomFailure
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.internal.database.awaitNotEmptyResult import org.matrix.android.sdk.internal.database.awaitNotEmptyResult
import org.matrix.android.sdk.internal.database.awaitTransaction
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields
import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.database.query.where
@ -89,11 +89,9 @@ internal class DefaultJoinRoomTask @Inject constructor(
} catch (exception: TimeoutCancellationException) { } catch (exception: TimeoutCancellationException) {
throw JoinRoomFailure.JoinedWithTimeout throw JoinRoomFailure.JoinedWithTimeout
} }
awaitTransaction(realmConfiguration) {
Realm.getInstance(realmConfiguration).executeTransactionAsync {
RoomSummaryEntity.where(it, roomId).findFirst()?.lastActivityTime = System.currentTimeMillis() RoomSummaryEntity.where(it, roomId).findFirst()?.lastActivityTime = System.currentTimeMillis()
} }
setReadMarkers(roomId) setReadMarkers(roomId)
} }

View File

@ -90,8 +90,7 @@ internal class TimelineChunk(private val chunkEntity: ChunkEntity,
private val timelineEventsChangeListener = private val timelineEventsChangeListener =
OrderedRealmCollectionChangeListener { results: RealmResults<TimelineEventEntity>, changeSet: OrderedCollectionChangeSet -> OrderedRealmCollectionChangeListener { results: RealmResults<TimelineEventEntity>, changeSet: OrderedCollectionChangeSet ->
Timber.v("on timeline events chunk update") Timber.v("on timeline events chunk update")
val frozenResults = results.freeze() handleDatabaseChangeSet(results, changeSet)
handleDatabaseChangeSet(frozenResults, changeSet)
} }
private var timelineEventEntities: RealmResults<TimelineEventEntity> = chunkEntity.sortedTimelineEvents(timelineSettings.rootThreadEventId) private var timelineEventEntities: RealmResults<TimelineEventEntity> = chunkEntity.sortedTimelineEvents(timelineSettings.rootThreadEventId)
@ -287,7 +286,7 @@ internal class TimelineChunk(private val chunkEntity: ChunkEntity,
* @return the number of events loaded. If we are in a thread timeline it also returns * @return the number of events loaded. If we are in a thread timeline it also returns
* whether or not we reached the end/root message * whether or not we reached the end/root message
*/ */
private suspend fun loadFromStorage(count: Int, direction: Timeline.Direction): LoadedFromStorage { private fun loadFromStorage(count: Int, direction: Timeline.Direction): LoadedFromStorage {
val displayIndex = getNextDisplayIndex(direction) ?: return LoadedFromStorage() val displayIndex = getNextDisplayIndex(direction) ?: return LoadedFromStorage()
val baseQuery = timelineEventEntities.where() val baseQuery = timelineEventEntities.where()
@ -428,10 +427,10 @@ internal class TimelineChunk(private val chunkEntity: ChunkEntity,
* This method is responsible for managing insertions and updates of events on this chunk. * This method is responsible for managing insertions and updates of events on this chunk.
* *
*/ */
private fun handleDatabaseChangeSet(frozenResults: RealmResults<TimelineEventEntity>, changeSet: OrderedCollectionChangeSet) { private fun handleDatabaseChangeSet(results: RealmResults<TimelineEventEntity>, changeSet: OrderedCollectionChangeSet) {
val insertions = changeSet.insertionRanges val insertions = changeSet.insertionRanges
for (range in insertions) { for (range in insertions) {
val newItems = frozenResults val newItems = results
.subList(range.startIndex, range.startIndex + range.length) .subList(range.startIndex, range.startIndex + range.length)
.map { it.buildAndDecryptIfNeeded() } .map { it.buildAndDecryptIfNeeded() }
builtEventsIndexes.entries.filter { it.value >= range.startIndex }.forEach { it.setValue(it.value + range.length) } builtEventsIndexes.entries.filter { it.value >= range.startIndex }.forEach { it.setValue(it.value + range.length) }
@ -447,7 +446,7 @@ internal class TimelineChunk(private val chunkEntity: ChunkEntity,
val modifications = changeSet.changeRanges val modifications = changeSet.changeRanges
for (range in modifications) { for (range in modifications) {
for (modificationIndex in (range.startIndex until range.startIndex + range.length)) { for (modificationIndex in (range.startIndex until range.startIndex + range.length)) {
val updatedEntity = frozenResults[modificationIndex] ?: continue val updatedEntity = results[modificationIndex] ?: continue
try { try {
builtEvents[modificationIndex] = updatedEntity.buildAndDecryptIfNeeded() builtEvents[modificationIndex] = updatedEntity.buildAndDecryptIfNeeded()
} catch (failure: Throwable) { } catch (failure: Throwable) {
@ -461,17 +460,16 @@ internal class TimelineChunk(private val chunkEntity: ChunkEntity,
} }
private fun getNextDisplayIndex(direction: Timeline.Direction): Int? { private fun getNextDisplayIndex(direction: Timeline.Direction): Int? {
val frozenTimelineEvents = timelineEventEntities.freeze() if (timelineEventEntities.isEmpty()) {
if (frozenTimelineEvents.isEmpty()) {
return null return null
} }
return if (builtEvents.isEmpty()) { return if (builtEvents.isEmpty()) {
if (initialEventId != null) { if (initialEventId != null) {
frozenTimelineEvents.where().equalTo(TimelineEventEntityFields.EVENT_ID, initialEventId).findFirst()?.displayIndex timelineEventEntities.where().equalTo(TimelineEventEntityFields.EVENT_ID, initialEventId).findFirst()?.displayIndex
} else if (direction == Timeline.Direction.BACKWARDS) { } else if (direction == Timeline.Direction.BACKWARDS) {
frozenTimelineEvents.first(null)?.displayIndex timelineEventEntities.first(null)?.displayIndex
} else { } else {
frozenTimelineEvents.last(null)?.displayIndex timelineEventEntities.last(null)?.displayIndex
} }
} else if (direction == Timeline.Direction.FORWARDS) { } else if (direction == Timeline.Direction.FORWARDS) {
builtEvents.first().displayIndex + 1 builtEvents.first().displayIndex + 1

View File

@ -516,7 +516,7 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
val event = itr.previous() val event = itr.previous()
timelineEventsGroups.addOrIgnore(event) timelineEventsGroups.addOrIgnore(event)
val currentReadReceipts = ArrayList(event.readReceipts).filter { val currentReadReceipts = ArrayList(event.readReceipts).filter {
it.user.userId != session.myUserId it.roomMember.userId != session.myUserId
} }
if (timelineEventVisibilityHelper.shouldShowEvent( if (timelineEventVisibilityHelper.shouldShowEvent(
timelineEvent = event, timelineEvent = event,

View File

@ -36,7 +36,7 @@ class ReadReceiptsItemFactory @Inject constructor(private val avatarRenderer: Av
} }
val readReceiptsData = readReceipts val readReceiptsData = readReceipts
.map { .map {
ReadReceiptData(it.user.userId, it.user.avatarUrl, it.user.displayName, it.originServerTs) ReadReceiptData(it.roomMember.userId, it.roomMember.avatarUrl, it.roomMember.displayName, it.originServerTs)
} }
.toList() .toList()