Perf: try to optimize room summary updates

This commit is contained in:
ganfra 2019-11-08 13:48:35 +01:00
parent 54f93db632
commit 17f3614288
6 changed files with 57 additions and 30 deletions

View File

@ -20,14 +20,20 @@ import io.realm.RealmConfiguration
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.isActive
import kotlinx.coroutines.withContext
import timber.log.Timber
import java.lang.RuntimeException
suspend fun awaitTransaction(config: RealmConfiguration, transaction: suspend (realm: Realm) -> Unit) = withContext(Dispatchers.IO) {
Realm.getInstance(config).use { bgRealm ->
bgRealm.beginTransaction()
try {
val start = System.currentTimeMillis()
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) {
@ -35,4 +41,5 @@ suspend fun awaitTransaction(config: RealmConfiguration, transaction: suspend (r
}
}
}
}

View File

@ -21,6 +21,7 @@ import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.room.model.RoomAvatarContent
import im.vector.matrix.android.api.session.room.model.RoomMember
import im.vector.matrix.android.internal.database.mapper.ContentMapper
import im.vector.matrix.android.internal.database.mapper.asDomain
import im.vector.matrix.android.internal.database.model.EventEntity
import im.vector.matrix.android.internal.database.model.EventEntityFields
@ -41,8 +42,8 @@ internal class RoomAvatarResolver @Inject constructor(private val monarchy: Mona
fun resolve(roomId: String): String? {
var res: String? = null
monarchy.doWithRealm { realm ->
val roomName = EventEntity.where(realm, roomId, EventType.STATE_ROOM_AVATAR).prev()?.asDomain()
res = roomName?.content.toModel<RoomAvatarContent>()?.avatarUrl
val roomName = EventEntity.where(realm, roomId, EventType.STATE_ROOM_AVATAR).prev()
res = ContentMapper.map(roomName?.content).toModel<RoomAvatarContent>()?.avatarUrl
if (!res.isNullOrEmpty()) {
return@doWithRealm
}
@ -60,6 +61,6 @@ internal class RoomAvatarResolver @Inject constructor(private val monarchy: Mona
}
private fun EventEntity?.toRoomMember(): RoomMember? {
return this?.asDomain()?.content?.toModel<RoomMember>()
return ContentMapper.map(this?.content).toModel<RoomMember>()
}
}

View File

@ -21,6 +21,7 @@ import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.room.model.Membership
import im.vector.matrix.android.api.session.room.model.RoomTopicContent
import im.vector.matrix.android.internal.database.mapper.ContentMapper
import im.vector.matrix.android.internal.database.mapper.asDomain
import im.vector.matrix.android.internal.database.model.EventEntity
import im.vector.matrix.android.internal.database.model.EventEntityFields
@ -65,8 +66,10 @@ internal class RoomSummaryUpdater @Inject constructor(@UserId private val userId
roomId: String,
membership: Membership? = null,
roomSummary: RoomSyncSummary? = null,
unreadNotifications: RoomSyncUnreadNotifications? = null) {
val roomSummaryEntity = RoomSummaryEntity.where(realm, roomId).findFirst() ?: realm.createObject(roomId)
unreadNotifications: RoomSyncUnreadNotifications? = null,
updateMembers: Boolean = false) {
val roomSummaryEntity = RoomSummaryEntity.where(realm, roomId).findFirst()
?: realm.createObject(roomId)
if (roomSummary != null) {
if (roomSummary.heroes.isNotEmpty()) {
@ -88,24 +91,30 @@ internal class RoomSummaryUpdater @Inject constructor(@UserId private val userId
}
val latestPreviewableEvent = TimelineEventEntity.latestEvent(realm, roomId, includesSending = true, filterTypes = PREVIEWABLE_TYPES)
val lastTopicEvent = EventEntity.where(realm, roomId, EventType.STATE_ROOM_TOPIC).prev()?.asDomain()
val lastTopicEvent = EventEntity.where(realm, roomId, EventType.STATE_ROOM_TOPIC).prev()
roomSummaryEntity.hasUnreadMessages = roomSummaryEntity.notificationCount > 0
// avoid this call if we are sure there are unread events
|| !isEventRead(monarchy, userId, roomId, latestPreviewableEvent?.eventId)
// avoid this call if we are sure there are unread events
|| !isEventRead(monarchy, userId, roomId, latestPreviewableEvent?.eventId)
val otherRoomMembers = RoomMembers(realm, roomId)
.queryRoomMembersEvent()
.notEqualTo(EventEntityFields.STATE_KEY, userId)
.findAll()
.asSequence()
.map { it.stateKey }
roomSummaryEntity.displayName = roomDisplayNameResolver.resolve(roomId).toString()
roomSummaryEntity.avatarUrl = roomAvatarResolver.resolve(roomId)
roomSummaryEntity.topic = lastTopicEvent?.content.toModel<RoomTopicContent>()?.topic
roomSummaryEntity.topic = ContentMapper.map(lastTopicEvent?.content).toModel<RoomTopicContent>()?.topic
roomSummaryEntity.latestPreviewableEvent = latestPreviewableEvent
roomSummaryEntity.otherMemberIds.clear()
roomSummaryEntity.otherMemberIds.addAll(otherRoomMembers)
if (updateMembers) {
val otherRoomMembers = RoomMembers(realm, roomId)
.queryRoomMembersEvent()
.notEqualTo(EventEntityFields.STATE_KEY, userId)
.findAll()
.asSequence()
.map { it.stateKey }
roomSummaryEntity.otherMemberIds.clear()
roomSummaryEntity.otherMemberIds.addAll(otherRoomMembers)
}
}
}

View File

@ -74,7 +74,7 @@ internal class DefaultLoadRoomMembersTask @Inject constructor(private val roomAP
it.updateSenderData()
}
roomEntity.areAllMembersLoaded = true
roomSummaryUpdater.update(realm, roomId)
roomSummaryUpdater.update(realm, roomId, updateMembers = true)
}
}

View File

@ -22,6 +22,7 @@ import im.vector.matrix.android.R
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.room.model.*
import im.vector.matrix.android.internal.database.mapper.ContentMapper
import im.vector.matrix.android.internal.database.mapper.asDomain
import im.vector.matrix.android.internal.database.model.EventEntity
import im.vector.matrix.android.internal.database.model.EventEntityFields
@ -56,20 +57,20 @@ internal class RoomDisplayNameResolver @Inject constructor(private val context:
var name: CharSequence? = null
monarchy.doWithRealm { realm ->
val roomEntity = RoomEntity.where(realm, roomId = roomId).findFirst()
val roomName = EventEntity.where(realm, roomId, EventType.STATE_ROOM_NAME).prev()?.asDomain()
name = roomName?.content.toModel<RoomNameContent>()?.name
val roomName = EventEntity.where(realm, roomId, EventType.STATE_ROOM_NAME).prev()
name = ContentMapper.map(roomName?.content).toModel<RoomNameContent>()?.name
if (!name.isNullOrEmpty()) {
return@doWithRealm
}
val canonicalAlias = EventEntity.where(realm, roomId, EventType.STATE_CANONICAL_ALIAS).prev()?.asDomain()
name = canonicalAlias?.content.toModel<RoomCanonicalAliasContent>()?.canonicalAlias
val canonicalAlias = EventEntity.where(realm, roomId, EventType.STATE_CANONICAL_ALIAS).prev()
name = ContentMapper.map(canonicalAlias?.content).toModel<RoomCanonicalAliasContent>()?.canonicalAlias
if (!name.isNullOrEmpty()) {
return@doWithRealm
}
val aliases = EventEntity.where(realm, roomId, EventType.STATE_ROOM_ALIASES).prev()?.asDomain()
name = aliases?.content.toModel<RoomAliasesContent>()?.aliases?.firstOrNull()
val aliases = EventEntity.where(realm, roomId, EventType.STATE_ROOM_ALIASES).prev()
name = ContentMapper.map(aliases?.content).toModel<RoomAliasesContent>()?.aliases?.firstOrNull()
if (!name.isNullOrEmpty()) {
return@doWithRealm
}
@ -132,6 +133,6 @@ internal class RoomDisplayNameResolver @Inject constructor(private val context:
}
private fun EventEntity?.toRoomMember(): RoomMember? {
return this?.asDomain()?.content?.toModel<RoomMember>()
return ContentMapper.map(this?.content).toModel<RoomMember>()
}
}

View File

@ -67,6 +67,7 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
}
suspend fun handle(roomsSyncResponse: RoomsSyncResponse, isInitialSync: Boolean, reporter: DefaultInitialSyncProgressService? = null) {
Timber.v("Execute transaction from $this")
monarchy.awaitTransaction { realm ->
handleRoomSync(realm, HandlingStrategy.JOINED(roomsSyncResponse.join), isInitialSync, reporter)
handleRoomSync(realm, HandlingStrategy.INVITED(roomsSyncResponse.invite), isInitialSync, reporter)
@ -133,6 +134,7 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
roomEntity.membership = Membership.JOIN
// State event
if (roomSync.state != null && roomSync.state.events.isNotEmpty()) {
val minStateIndex = roomEntity.untimelinedStateEvents.where().min(EventEntityFields.STATE_INDEX)?.toInt()
?: Int.MIN_VALUE
@ -146,7 +148,6 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
}
}
}
if (roomSync.timeline != null && roomSync.timeline.events.isNotEmpty()) {
val chunkEntity = handleTimelineEvents(
realm,
@ -157,14 +158,19 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
)
roomEntity.addOrUpdate(chunkEntity)
}
roomSummaryUpdater.update(realm, roomId, Membership.JOIN, roomSync.summary, roomSync.unreadNotifications)
val hasRoomMember = roomSync.state?.events?.firstOrNull {
it.type == EventType.STATE_ROOM_MEMBER
} != null || roomSync.timeline?.events?.firstOrNull {
it.type == EventType.STATE_ROOM_MEMBER
} != null
roomSummaryUpdater.update(realm, roomId, Membership.JOIN, roomSync.summary, roomSync.unreadNotifications, updateMembers = hasRoomMember)
return roomEntity
}
private fun handleInvitedRoom(realm: Realm,
roomId: String,
roomSync:
InvitedRoomSync): RoomEntity {
roomSync: InvitedRoomSync): RoomEntity {
Timber.v("Handle invited sync for room $roomId")
val roomEntity = RoomEntity.where(realm, roomId).findFirst() ?: realm.createObject(roomId)
roomEntity.membership = Membership.INVITE
@ -172,7 +178,10 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
val chunkEntity = handleTimelineEvents(realm, roomEntity, roomSync.inviteState.events)
roomEntity.addOrUpdate(chunkEntity)
}
roomSummaryUpdater.update(realm, roomId, Membership.INVITE)
val hasRoomMember = roomSync.inviteState?.events?.firstOrNull {
it.type == EventType.STATE_ROOM_MEMBER
} != null
roomSummaryUpdater.update(realm, roomId, Membership.INVITE, updateMembers = hasRoomMember)
return roomEntity
}