From fcca75ee235edfa442bfb0c3b4298a42b2827311 Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 22 Feb 2022 12:45:10 +0100 Subject: [PATCH 1/4] Realm: remove usage of freeze as it was not necessary (unique thread) --- .../session/room/timeline/TimelineChunk.kt | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineChunk.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineChunk.kt index 8507b63d1f7..25957de1b58 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineChunk.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineChunk.kt @@ -90,8 +90,7 @@ internal class TimelineChunk(private val chunkEntity: ChunkEntity, private val timelineEventsChangeListener = OrderedRealmCollectionChangeListener { results: RealmResults, changeSet: OrderedCollectionChangeSet -> Timber.v("on timeline events chunk update") - val frozenResults = results.freeze() - handleDatabaseChangeSet(frozenResults, changeSet) + handleDatabaseChangeSet(results, changeSet) } private var timelineEventEntities: RealmResults = 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 * 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 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. * */ - private fun handleDatabaseChangeSet(frozenResults: RealmResults, changeSet: OrderedCollectionChangeSet) { + private fun handleDatabaseChangeSet(results: RealmResults, changeSet: OrderedCollectionChangeSet) { val insertions = changeSet.insertionRanges for (range in insertions) { - val newItems = frozenResults + val newItems = results .subList(range.startIndex, range.startIndex + range.length) .map { it.buildAndDecryptIfNeeded() } 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 for (range in modifications) { for (modificationIndex in (range.startIndex until range.startIndex + range.length)) { - val updatedEntity = frozenResults[modificationIndex] ?: continue + val updatedEntity = results[modificationIndex] ?: continue try { builtEvents[modificationIndex] = updatedEntity.buildAndDecryptIfNeeded() } catch (failure: Throwable) { @@ -458,20 +457,20 @@ internal class TimelineChunk(private val chunkEntity: ChunkEntity, if (insertions.isNotEmpty() || modifications.isNotEmpty()) { onBuiltEvents(true) } + } private fun getNextDisplayIndex(direction: Timeline.Direction): Int? { - val frozenTimelineEvents = timelineEventEntities.freeze() - if (frozenTimelineEvents.isEmpty()) { + if (timelineEventEntities.isEmpty()) { return null } return if (builtEvents.isEmpty()) { 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) { - frozenTimelineEvents.first(null)?.displayIndex + timelineEventEntities.first(null)?.displayIndex } else { - frozenTimelineEvents.last(null)?.displayIndex + timelineEventEntities.last(null)?.displayIndex } } else if (direction == Timeline.Direction.FORWARDS) { builtEvents.first().displayIndex + 1 From d27acfa64f7b5d014eb54888b764f0d078dfb4f4 Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 22 Feb 2022 12:45:54 +0100 Subject: [PATCH 2/4] Read receipts: use RoomMember instead of User and avoid creating realm instance each time --- .../sdk/api/session/room/model/ReadReceipt.kt | 4 +--- .../mapper/ReadReceiptsSummaryMapper.kt | 18 ++++++++---------- .../database/mapper/TimelineEventMapper.kt | 2 +- .../detail/timeline/TimelineEventController.kt | 2 +- .../factory/ReadReceiptsItemFactory.kt | 2 +- 5 files changed, 12 insertions(+), 16 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/ReadReceipt.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/ReadReceipt.kt index 67cb9600c82..5639730219e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/ReadReceipt.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/ReadReceipt.kt @@ -16,9 +16,7 @@ package org.matrix.android.sdk.api.session.room.model -import org.matrix.android.sdk.api.session.user.model.User - data class ReadReceipt( - val user: User, + val roomMember: RoomMemberSummary, val originServerTs: Long ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/ReadReceiptsSummaryMapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/ReadReceiptsSummaryMapper.kt index 5413dd3d71c..5aaa49b9e82 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/ReadReceiptsSummaryMapper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/ReadReceiptsSummaryMapper.kt @@ -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.internal.database.RealmSessionProvider 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 javax.inject.Inject @@ -29,14 +29,12 @@ internal class ReadReceiptsSummaryMapper @Inject constructor(private val realmSe if (readReceiptsSummaryEntity == null) { return emptyList() } - return realmSessionProvider.withRealm { realm -> - val readReceipts = readReceiptsSummaryEntity.readReceipts - readReceipts - .mapNotNull { - val user = UserEntity.where(realm, it.userId).findFirst() - ?: return@mapNotNull null - ReadReceipt(user.asDomain(), it.originServerTs.toLong()) - } - } + val readReceipts = readReceiptsSummaryEntity.readReceipts + return readReceipts + .mapNotNull { + val roomMember = RoomMemberSummaryEntity.where(readReceiptsSummaryEntity.realm, roomId = it.roomId, userId = it.userId).findFirst() + ?: return@mapNotNull null + ReadReceipt(roomMember.asDomain(), it.originServerTs.toLong()) + } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/TimelineEventMapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/TimelineEventMapper.kt index f3bea68c26e..55c7f2a8ee6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/TimelineEventMapper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/TimelineEventMapper.kt @@ -48,7 +48,7 @@ internal class TimelineEventMapper @Inject constructor(private val readReceiptsS ), readReceipts = readReceipts ?.distinctBy { - it.user + it.roomMember }?.sortedByDescending { it.originServerTs }.orEmpty() diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt index e3f162dfd4a..43fa9e0c2ec 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt @@ -516,7 +516,7 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec val event = itr.previous() timelineEventsGroups.addOrIgnore(event) val currentReadReceipts = ArrayList(event.readReceipts).filter { - it.user.userId != session.myUserId + it.roomMember.userId != session.myUserId } if (timelineEventVisibilityHelper.shouldShowEvent( timelineEvent = event, diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/ReadReceiptsItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/ReadReceiptsItemFactory.kt index d477a3d40ed..e66dd4b0430 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/ReadReceiptsItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/ReadReceiptsItemFactory.kt @@ -36,7 +36,7 @@ class ReadReceiptsItemFactory @Inject constructor(private val avatarRenderer: Av } val readReceiptsData = readReceipts .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() From 80d19fa49780217e8d0b8c3cd8e496422742cd63 Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 22 Feb 2022 13:00:22 +0100 Subject: [PATCH 3/4] Realm transactions: use Realm.WRITE_EXECUTOR (and use in Create/Join Room tasks) --- .../sdk/internal/database/AsyncTransaction.kt | 42 ++++++++----------- .../session/room/create/CreateRoomTask.kt | 3 +- .../room/membership/joining/JoinRoomTask.kt | 5 +-- 3 files changed, 22 insertions(+), 28 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/AsyncTransaction.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/AsyncTransaction.kt index d5a96f5ba19..ebc9bcce5ad 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/AsyncTransaction.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/AsyncTransaction.kt @@ -19,11 +19,9 @@ import com.zhuinden.monarchy.Monarchy import io.realm.Realm import io.realm.RealmConfiguration import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.isActive import kotlinx.coroutines.launch -import kotlinx.coroutines.sync.Semaphore -import kotlinx.coroutines.sync.withPermit import kotlinx.coroutines.withContext import timber.log.Timber @@ -37,30 +35,26 @@ internal fun CoroutineScope.asyncTransaction(realmConfiguration: RealmConfig } } -private val realmSemaphore = Semaphore(1) - suspend fun awaitTransaction(config: RealmConfiguration, transaction: suspend (realm: Realm) -> T): T { - return realmSemaphore.withPermit { - withContext(Dispatchers.IO) { - Realm.getInstance(config).use { bgRealm -> - bgRealm.beginTransaction() - val result: T - try { - val start = System.currentTimeMillis() - result = transaction(bgRealm) - if (isActive) { - bgRealm.commitTransaction() - val end = System.currentTimeMillis() - val time = end - start - Timber.v("Execute transaction in $time millis") - } - } finally { - if (bgRealm.isInTransaction) { - bgRealm.cancelTransaction() - } + return withContext(Realm.WRITE_EXECUTOR.asCoroutineDispatcher()) { + Realm.getInstance(config).use { bgRealm -> + bgRealm.beginTransaction() + val result: T + try { + val start = System.currentTimeMillis() + result = transaction(bgRealm) + if (isActive) { + bgRealm.commitTransaction() + val end = System.currentTimeMillis() + val time = end - start + Timber.v("Execute transaction in $time millis") + } + } finally { + if (bgRealm.isInTransaction) { + bgRealm.cancelTransaction() } - result } + result } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomTask.kt index ac6e0562b0a..4377bcbd556 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomTask.kt @@ -28,6 +28,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.CreateRoomPreset 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.RoomSummaryEntityFields import org.matrix.android.sdk.internal.database.query.where @@ -105,7 +106,7 @@ internal class DefaultCreateRoomTask @Inject constructor( throw CreateRoomFailure.CreatedWithTimeout(roomId) } - Realm.getInstance(realmConfiguration).executeTransactionAsync { + awaitTransaction(realmConfiguration){ RoomSummaryEntity.where(it, roomId).findFirst()?.lastActivityTime = System.currentTimeMillis() } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/joining/JoinRoomTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/joining/JoinRoomTask.kt index 82fea237dbd..13fc6e37089 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/joining/JoinRoomTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/joining/JoinRoomTask.kt @@ -24,6 +24,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.model.Membership 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.RoomSummaryEntityFields import org.matrix.android.sdk.internal.database.query.where @@ -89,11 +90,9 @@ internal class DefaultJoinRoomTask @Inject constructor( } catch (exception: TimeoutCancellationException) { throw JoinRoomFailure.JoinedWithTimeout } - - Realm.getInstance(realmConfiguration).executeTransactionAsync { + awaitTransaction(realmConfiguration){ RoomSummaryEntity.where(it, roomId).findFirst()?.lastActivityTime = System.currentTimeMillis() } - setReadMarkers(roomId) } From 4cc80162cacf10a4b63b58ed30536de608436195 Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 22 Feb 2022 14:23:45 +0100 Subject: [PATCH 4/4] Clean and add Changelog --- changelog.d/5297.misc | 1 + .../android/sdk/internal/session/room/create/CreateRoomTask.kt | 3 +-- .../internal/session/room/membership/joining/JoinRoomTask.kt | 3 +-- .../sdk/internal/session/room/timeline/TimelineChunk.kt | 1 - 4 files changed, 3 insertions(+), 5 deletions(-) create mode 100644 changelog.d/5297.misc diff --git a/changelog.d/5297.misc b/changelog.d/5297.misc new file mode 100644 index 00000000000..f45490ce3dd --- /dev/null +++ b/changelog.d/5297.misc @@ -0,0 +1 @@ +Improve some internal realm usages. \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomTask.kt index 4377bcbd556..9bd15a02671 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomTask.kt @@ -17,7 +17,6 @@ package org.matrix.android.sdk.internal.session.room.create import com.zhuinden.monarchy.Monarchy -import io.realm.Realm import io.realm.RealmConfiguration import kotlinx.coroutines.TimeoutCancellationException import org.matrix.android.sdk.api.failure.Failure @@ -106,7 +105,7 @@ internal class DefaultCreateRoomTask @Inject constructor( throw CreateRoomFailure.CreatedWithTimeout(roomId) } - awaitTransaction(realmConfiguration){ + awaitTransaction(realmConfiguration) { RoomSummaryEntity.where(it, roomId).findFirst()?.lastActivityTime = System.currentTimeMillis() } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/joining/JoinRoomTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/joining/JoinRoomTask.kt index 13fc6e37089..22a46b6cfc9 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/joining/JoinRoomTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/joining/JoinRoomTask.kt @@ -16,7 +16,6 @@ package org.matrix.android.sdk.internal.session.room.membership.joining -import io.realm.Realm import io.realm.RealmConfiguration import kotlinx.coroutines.TimeoutCancellationException import org.matrix.android.sdk.api.session.events.model.toContent @@ -90,7 +89,7 @@ internal class DefaultJoinRoomTask @Inject constructor( } catch (exception: TimeoutCancellationException) { throw JoinRoomFailure.JoinedWithTimeout } - awaitTransaction(realmConfiguration){ + awaitTransaction(realmConfiguration) { RoomSummaryEntity.where(it, roomId).findFirst()?.lastActivityTime = System.currentTimeMillis() } setReadMarkers(roomId) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineChunk.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineChunk.kt index 25957de1b58..c0dc31fcf8a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineChunk.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineChunk.kt @@ -457,7 +457,6 @@ internal class TimelineChunk(private val chunkEntity: ChunkEntity, if (insertions.isNotEmpty() || modifications.isNotEmpty()) { onBuiltEvents(true) } - } private fun getNextDisplayIndex(direction: Timeline.Direction): Int? {