Add roomId to EventEntity as it makes queries a lot faster and fixes performance issues.

This commit is contained in:
ganfra 2019-01-03 17:10:02 +01:00 committed by ganfra
parent 51000c4711
commit e9737dff75
11 changed files with 52 additions and 69 deletions

View File

@ -10,6 +10,10 @@ fun Fragment.replaceFragment(fragment: Fragment, frameId: Int) {
fragmentManager?.inTransaction { replace(frameId, fragment) } fragmentManager?.inTransaction { replace(frameId, fragment) }
} }
fun Fragment.addFragmentToBackstack(fragment: Fragment, frameId: Int, tag: String? = null) {
fragmentManager?.inTransaction { replace(frameId, fragment).addToBackStack(tag) }
}
fun Fragment.addChildFragment(fragment: Fragment, frameId: Int) { fun Fragment.addChildFragment(fragment: Fragment, frameId: Int) {
childFragmentManager.inTransaction { add(frameId, fragment) } childFragmentManager.inTransaction { add(frameId, fragment) }
} }
@ -18,10 +22,6 @@ fun Fragment.replaceChildFragment(fragment: Fragment, frameId: Int) {
childFragmentManager.inTransaction { replace(frameId, fragment) } childFragmentManager.inTransaction { replace(frameId, fragment) }
} }
fun Fragment.addFragmentToBackstack(fragment: Fragment, frameId: Int, tag: String? = null) {
fragmentManager?.inTransaction { replace(frameId, fragment).addToBackStack(tag) }
}
fun Fragment.addChildFragmentToBackstack(fragment: Fragment, frameId: Int, tag: String? = null) { fun Fragment.addChildFragmentToBackstack(fragment: Fragment, frameId: Int, tag: String? = null) {
childFragmentManager.inTransaction { replace(frameId, fragment).addToBackStack(tag) } childFragmentManager.inTransaction { replace(frameId, fragment).addToBackStack(tag) }
} }

View File

@ -5,7 +5,7 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import im.vector.riotredesign.R import im.vector.riotredesign.R
import im.vector.riotredesign.core.extensions.replaceFragment import im.vector.riotredesign.core.extensions.replaceChildFragment
import im.vector.riotredesign.core.platform.RiotFragment import im.vector.riotredesign.core.platform.RiotFragment
import im.vector.riotredesign.features.home.group.GroupListFragment import im.vector.riotredesign.features.home.group.GroupListFragment
import im.vector.riotredesign.features.home.room.list.RoomListFragment import im.vector.riotredesign.features.home.room.list.RoomListFragment
@ -27,9 +27,9 @@ class HomeDrawerFragment : RiotFragment() {
super.onActivityCreated(savedInstanceState) super.onActivityCreated(savedInstanceState)
if (savedInstanceState == null) { if (savedInstanceState == null) {
val groupListFragment = GroupListFragment.newInstance() val groupListFragment = GroupListFragment.newInstance()
replaceFragment(groupListFragment, R.id.groupListFragmentContainer) replaceChildFragment(groupListFragment, R.id.groupListFragmentContainer)
val roomListFragment = RoomListFragment.newInstance() val roomListFragment = RoomListFragment.newInstance()
replaceFragment(roomListFragment, R.id.roomListFragmentContainer) replaceChildFragment(roomListFragment, R.id.roomListFragmentContainer)
} }
} }

View File

@ -5,7 +5,6 @@ import im.vector.matrix.android.api.session.events.model.EnrichedEvent
import im.vector.matrix.android.api.session.room.Room import im.vector.matrix.android.api.session.room.Room
import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.session.room.model.RoomSummary
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.schedulers.Schedulers
class RxRoom(private val room: Room) { class RxRoom(private val room: Room) {
@ -15,7 +14,6 @@ class RxRoom(private val room: Room) {
fun timeline(eventId: String? = null): Observable<PagedList<EnrichedEvent>> { fun timeline(eventId: String? = null): Observable<PagedList<EnrichedEvent>> {
return room.timeline(eventId).asObservable() return room.timeline(eventId).asObservable()
.subscribeOn(Schedulers.io())
} }
} }

View File

@ -2,7 +2,8 @@ package im.vector.matrix.android.internal.database.helper
import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.internal.database.mapper.asEntity import im.vector.matrix.android.internal.database.mapper.toEntity
import im.vector.matrix.android.internal.database.mapper.updateWith
import im.vector.matrix.android.internal.database.model.ChunkEntity import im.vector.matrix.android.internal.database.model.ChunkEntity
import im.vector.matrix.android.internal.database.model.EventEntity import im.vector.matrix.android.internal.database.model.EventEntity
import im.vector.matrix.android.internal.database.model.EventEntityFields import im.vector.matrix.android.internal.database.model.EventEntityFields
@ -44,13 +45,14 @@ internal fun ChunkEntity.merge(chunkToMerge: ChunkEntity,
} }
} }
internal fun ChunkEntity.addAll(events: List<Event>, internal fun ChunkEntity.addAll(roomId: String,
events: List<Event>,
direction: PaginationDirection, direction: PaginationDirection,
stateIndexOffset: Int = 0, stateIndexOffset: Int = 0,
isUnlinked: Boolean = false) { isUnlinked: Boolean = false) {
events.forEach { event -> events.forEach { event ->
add(event, direction, stateIndexOffset, isUnlinked) add(roomId, event, direction, stateIndexOffset, isUnlinked)
} }
} }
@ -58,11 +60,13 @@ internal fun ChunkEntity.updateDisplayIndexes() {
events.forEachIndexed { index, eventEntity -> eventEntity.displayIndex = index } events.forEachIndexed { index, eventEntity -> eventEntity.displayIndex = index }
} }
internal fun ChunkEntity.add(event: Event, internal fun ChunkEntity.add(roomId: String,
event: Event,
direction: PaginationDirection, direction: PaginationDirection,
stateIndexOffset: Int = 0, stateIndexOffset: Int = 0,
isUnlinked: Boolean = false) { isUnlinked: Boolean = false) {
add(event.asEntity(), direction, stateIndexOffset, isUnlinked)
add(event.toEntity(roomId), direction, stateIndexOffset, isUnlinked)
} }
internal fun ChunkEntity.add(eventEntity: EventEntity, internal fun ChunkEntity.add(eventEntity: EventEntity,
@ -76,7 +80,6 @@ internal fun ChunkEntity.add(eventEntity: EventEntity,
if (eventEntity.eventId.isEmpty() || events.fastContains(eventEntity.eventId)) { if (eventEntity.eventId.isEmpty() || events.fastContains(eventEntity.eventId)) {
return return
} }
var currentStateIndex = lastStateIndex(direction, defaultValue = stateIndexOffset) var currentStateIndex = lastStateIndex(direction, defaultValue = stateIndexOffset)
if (direction == PaginationDirection.FORWARDS && EventType.isStateEvent(eventEntity.type)) { if (direction == PaginationDirection.FORWARDS && EventType.isStateEvent(eventEntity.type)) {
currentStateIndex += 1 currentStateIndex += 1
@ -86,16 +89,14 @@ internal fun ChunkEntity.add(eventEntity: EventEntity,
currentStateIndex -= 1 currentStateIndex -= 1
} }
} }
eventEntity.updateWith(currentStateIndex, isUnlinked)
eventEntity.stateIndex = currentStateIndex
eventEntity.isUnlinked = isUnlinked
val position = if (direction == PaginationDirection.FORWARDS) 0 else this.events.size val position = if (direction == PaginationDirection.FORWARDS) 0 else this.events.size
events.add(position, eventEntity) events.add(position, eventEntity)
} }
internal fun ChunkEntity.lastStateIndex(direction: PaginationDirection, defaultValue: Int = 0): Int { internal fun ChunkEntity.lastStateIndex(direction: PaginationDirection, defaultValue: Int = 0): Int {
return when (direction) { return when (direction) {
PaginationDirection.FORWARDS -> events.where().sort(EventEntityFields.STATE_INDEX, Sort.DESCENDING).findFirst()?.stateIndex PaginationDirection.FORWARDS -> events.where().sort(EventEntityFields.STATE_INDEX, Sort.DESCENDING).findFirst()?.stateIndex
PaginationDirection.BACKWARDS -> events.where().sort(EventEntityFields.STATE_INDEX, Sort.ASCENDING).findFirst()?.stateIndex PaginationDirection.BACKWARDS -> events.where().sort(EventEntityFields.STATE_INDEX, Sort.ASCENDING).findFirst()?.stateIndex
} ?: defaultValue } ?: defaultValue
} }

View File

@ -1,7 +1,8 @@
package im.vector.matrix.android.internal.database.helper package im.vector.matrix.android.internal.database.helper
import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.internal.database.mapper.asEntity import im.vector.matrix.android.internal.database.mapper.toEntity
import im.vector.matrix.android.internal.database.mapper.updateWith
import im.vector.matrix.android.internal.database.model.ChunkEntity import im.vector.matrix.android.internal.database.model.ChunkEntity
import im.vector.matrix.android.internal.database.model.RoomEntity import im.vector.matrix.android.internal.database.model.RoomEntity
@ -28,9 +29,8 @@ internal fun RoomEntity.addStateEvents(stateEvents: List<Event>,
if (event.eventId == null) { if (event.eventId == null) {
return@forEach return@forEach
} }
val eventEntity = event.asEntity() val eventEntity = event.toEntity(roomId)
eventEntity.stateIndex = stateIndex eventEntity.updateWith(stateIndex, isUnlinked)
eventEntity.isUnlinked = isUnlinked
untimelinedStateEvents.add(eventEntity) untimelinedStateEvents.add(eventEntity)
} }
} }

View File

@ -8,9 +8,10 @@ import im.vector.matrix.android.internal.database.model.EventEntity
internal object EventMapper { internal object EventMapper {
fun map(event: Event): EventEntity { fun map(event: Event, roomId: String): EventEntity {
val eventEntity = EventEntity() val eventEntity = EventEntity()
eventEntity.eventId = event.eventId ?: "" eventEntity.eventId = event.eventId ?: ""
eventEntity.roomId = event.roomId ?: roomId
eventEntity.content = ContentMapper.map(event.content) eventEntity.content = ContentMapper.map(event.content)
val resolvedPrevContent = event.prevContent ?: event.unsignedData?.prevContent val resolvedPrevContent = event.prevContent ?: event.unsignedData?.prevContent
eventEntity.prevContent = ContentMapper.map(resolvedPrevContent) eventEntity.prevContent = ContentMapper.map(resolvedPrevContent)
@ -32,19 +33,24 @@ internal object EventMapper {
originServerTs = eventEntity.originServerTs, originServerTs = eventEntity.originServerTs,
sender = eventEntity.sender, sender = eventEntity.sender,
stateKey = eventEntity.stateKey, stateKey = eventEntity.stateKey,
roomId = null, roomId = eventEntity.roomId,
unsignedData = UnsignedData(eventEntity.age), unsignedData = UnsignedData(eventEntity.age),
redacts = eventEntity.redacts redacts = eventEntity.redacts
) )
} }
}
internal fun EventEntity.updateWith(stateIndex: Int, isUnlinked: Boolean) {
this.stateIndex = stateIndex
this.isUnlinked = isUnlinked
} }
internal fun EventEntity.asDomain(): Event { internal fun EventEntity.asDomain(): Event {
return EventMapper.map(this) return EventMapper.map(this)
} }
internal fun Event.asEntity(): EventEntity { internal fun Event.toEntity(roomId: String): EventEntity {
return EventMapper.map(this) return EventMapper.map(this, roomId)
} }

View File

@ -8,6 +8,7 @@ import java.util.*
internal open class EventEntity(@PrimaryKey var localId: String = UUID.randomUUID().toString(), internal open class EventEntity(@PrimaryKey var localId: String = UUID.randomUUID().toString(),
var eventId: String = "", var eventId: String = "",
var roomId: String = "",
var type: String = "", var type: String = "",
var content: String? = null, var content: String? = null,
var prevContent: String? = null, var prevContent: String? = null,
@ -27,9 +28,7 @@ internal open class EventEntity(@PrimaryKey var localId: String = UUID.randomUUI
BOTH BOTH
} }
companion object { companion object
const val DEFAULT_STATE_INDEX = Int.MIN_VALUE
}
@LinkingObjects("events") @LinkingObjects("events")
val chunk: RealmResults<ChunkEntity>? = null val chunk: RealmResults<ChunkEntity>? = null

View File

@ -1,10 +1,8 @@
package im.vector.matrix.android.internal.database.query package im.vector.matrix.android.internal.database.query
import im.vector.matrix.android.internal.database.model.ChunkEntityFields
import im.vector.matrix.android.internal.database.model.EventEntity import im.vector.matrix.android.internal.database.model.EventEntity
import im.vector.matrix.android.internal.database.model.EventEntity.LinkFilterMode.* import im.vector.matrix.android.internal.database.model.EventEntity.LinkFilterMode.*
import im.vector.matrix.android.internal.database.model.EventEntityFields import im.vector.matrix.android.internal.database.model.EventEntityFields
import im.vector.matrix.android.internal.database.model.RoomEntityFields
import io.realm.Realm import io.realm.Realm
import io.realm.RealmList import io.realm.RealmList
import io.realm.RealmQuery import io.realm.RealmQuery
@ -22,11 +20,7 @@ internal fun EventEntity.Companion.where(realm: Realm,
linkFilterMode: EventEntity.LinkFilterMode = LINKED_ONLY): RealmQuery<EventEntity> { linkFilterMode: EventEntity.LinkFilterMode = LINKED_ONLY): RealmQuery<EventEntity> {
val query = realm.where<EventEntity>() val query = realm.where<EventEntity>()
if (roomId != null) { if (roomId != null) {
query.beginGroup() query.equalTo(EventEntityFields.ROOM_ID, roomId)
.equalTo("${EventEntityFields.CHUNK}.${ChunkEntityFields.ROOM}.${RoomEntityFields.ROOM_ID}", roomId)
.or()
.equalTo("${EventEntityFields.ROOM}.${RoomEntityFields.ROOM_ID}", roomId)
.endGroup()
} }
if (type != null) { if (type != null) {
query.equalTo(EventEntityFields.TYPE, type) query.equalTo(EventEntityFields.TYPE, type)
@ -69,7 +63,6 @@ internal fun RealmList<EventEntity>.find(eventId: String): EventEntity? {
return this.where().equalTo(EventEntityFields.EVENT_ID, eventId).findFirst() return this.where().equalTo(EventEntityFields.EVENT_ID, eventId).findFirst()
} }
internal fun RealmList<EventEntity>. internal fun RealmList<EventEntity>.fastContains(eventId: String): Boolean {
fastContains(eventId: String): Boolean {
return this.find(eventId) != null return this.find(eventId) != null
} }

View File

@ -1,11 +1,6 @@
package im.vector.matrix.android.internal.session.room.send package im.vector.matrix.android.internal.session.room.send
import androidx.work.BackoffPolicy import androidx.work.*
import androidx.work.Constraints
import androidx.work.ExistingWorkPolicy
import androidx.work.NetworkType
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.Event
@ -37,8 +32,8 @@ internal class DefaultSendService(private val roomId: String,
monarchy.tryTransactionAsync { realm -> monarchy.tryTransactionAsync { realm ->
val chunkEntity = ChunkEntity.findLastLiveChunkFromRoom(realm, roomId) val chunkEntity = ChunkEntity.findLastLiveChunkFromRoom(realm, roomId)
?: return@tryTransactionAsync ?: return@tryTransactionAsync
chunkEntity.add(event, PaginationDirection.FORWARDS) chunkEntity.add(roomId, event, PaginationDirection.FORWARDS)
chunkEntity.updateDisplayIndexes() chunkEntity.updateDisplayIndexes()
} }

View File

@ -2,12 +2,7 @@ package im.vector.matrix.android.internal.session.room.timeline
import arrow.core.Try import arrow.core.Try
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.internal.database.helper.addAll import im.vector.matrix.android.internal.database.helper.*
import im.vector.matrix.android.internal.database.helper.addOrUpdate
import im.vector.matrix.android.internal.database.helper.addStateEvents
import im.vector.matrix.android.internal.database.helper.deleteOnCascade
import im.vector.matrix.android.internal.database.helper.isUnlinked
import im.vector.matrix.android.internal.database.helper.merge
import im.vector.matrix.android.internal.database.model.ChunkEntity import im.vector.matrix.android.internal.database.model.ChunkEntity
import im.vector.matrix.android.internal.database.model.RoomEntity import im.vector.matrix.android.internal.database.model.RoomEntity
import im.vector.matrix.android.internal.database.query.create import im.vector.matrix.android.internal.database.query.create
@ -26,7 +21,7 @@ internal class TokenChunkEventPersistor(private val monarchy: Monarchy) {
return monarchy return monarchy
.tryTransactionSync { realm -> .tryTransactionSync { realm ->
val roomEntity = RoomEntity.where(realm, roomId).findFirst() val roomEntity = RoomEntity.where(realm, roomId).findFirst()
?: throw IllegalStateException("You shouldn't use this method without a room") ?: throw IllegalStateException("You shouldn't use this method without a room")
val nextToken: String? val nextToken: String?
val prevToken: String? val prevToken: String?
@ -46,13 +41,13 @@ internal class TokenChunkEventPersistor(private val monarchy: Monarchy) {
var currentChunk = if (direction == PaginationDirection.FORWARDS) { var currentChunk = if (direction == PaginationDirection.FORWARDS) {
prevChunk?.apply { this.nextToken = nextToken } prevChunk?.apply { this.nextToken = nextToken }
?: ChunkEntity.create(realm, prevToken, nextToken) ?: ChunkEntity.create(realm, prevToken, nextToken)
} else { } else {
nextChunk?.apply { this.prevToken = prevToken } nextChunk?.apply { this.prevToken = prevToken }
?: ChunkEntity.create(realm, prevToken, nextToken) ?: ChunkEntity.create(realm, prevToken, nextToken)
} }
currentChunk.addAll(receivedChunk.events, direction, isUnlinked = currentChunk.isUnlinked()) currentChunk.addAll(roomId, receivedChunk.events, direction, isUnlinked = currentChunk.isUnlinked())
// Then we merge chunks if needed // Then we merge chunks if needed
if (currentChunk != prevChunk && prevChunk != null) { if (currentChunk != prevChunk && prevChunk != null) {

View File

@ -15,11 +15,7 @@ import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
import im.vector.matrix.android.internal.database.query.findLastLiveChunkFromRoom import im.vector.matrix.android.internal.database.query.findLastLiveChunkFromRoom
import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection
import im.vector.matrix.android.internal.session.sync.model.InvitedRoomSync import im.vector.matrix.android.internal.session.sync.model.*
import im.vector.matrix.android.internal.session.sync.model.RoomSync
import im.vector.matrix.android.internal.session.sync.model.RoomSyncEphemeral
import im.vector.matrix.android.internal.session.sync.model.RoomSyncSummary
import im.vector.matrix.android.internal.session.sync.model.RoomsSyncResponse
import io.realm.Realm import io.realm.Realm
import io.realm.kotlin.createObject import io.realm.kotlin.createObject
@ -45,9 +41,9 @@ internal class RoomSyncHandler(private val monarchy: Monarchy,
private fun handleRoomSync(realm: Realm, handlingStrategy: HandlingStrategy) { private fun handleRoomSync(realm: Realm, handlingStrategy: HandlingStrategy) {
val rooms = when (handlingStrategy) { val rooms = when (handlingStrategy) {
is HandlingStrategy.JOINED -> handlingStrategy.data.map { handleJoinedRoom(realm, it.key, it.value) } is HandlingStrategy.JOINED -> handlingStrategy.data.map { handleJoinedRoom(realm, it.key, it.value) }
is HandlingStrategy.INVITED -> handlingStrategy.data.map { handleInvitedRoom(realm, it.key, it.value) } is HandlingStrategy.INVITED -> handlingStrategy.data.map { handleInvitedRoom(realm, it.key, it.value) }
is HandlingStrategy.LEFT -> handlingStrategy.data.map { handleLeftRoom(it.key, it.value) } is HandlingStrategy.LEFT -> handlingStrategy.data.map { handleLeftRoom(it.key, it.value) }
} }
realm.insertOrUpdate(rooms) realm.insertOrUpdate(rooms)
} }
@ -57,7 +53,7 @@ internal class RoomSyncHandler(private val monarchy: Monarchy,
roomSync: RoomSync): RoomEntity { roomSync: RoomSync): RoomEntity {
val roomEntity = RoomEntity.where(realm, roomId).findFirst() val roomEntity = RoomEntity.where(realm, roomId).findFirst()
?: realm.createObject(roomId) ?: realm.createObject(roomId)
if (roomEntity.membership == MyMembership.INVITED) { if (roomEntity.membership == MyMembership.INVITED) {
roomEntity.chunks.deleteAllFromRealm() roomEntity.chunks.deleteAllFromRealm()
@ -138,7 +134,7 @@ internal class RoomSyncHandler(private val monarchy: Monarchy,
lastChunk?.isLast = false lastChunk?.isLast = false
chunkEntity.isLast = true chunkEntity.isLast = true
chunkEntity.addAll(eventList, PaginationDirection.FORWARDS, stateIndexOffset) chunkEntity.addAll(roomId, eventList, PaginationDirection.FORWARDS, stateIndexOffset)
return chunkEntity return chunkEntity
} }
@ -147,7 +143,7 @@ internal class RoomSyncHandler(private val monarchy: Monarchy,
roomSummary: RoomSyncSummary) { roomSummary: RoomSyncSummary) {
val roomSummaryEntity = RoomSummaryEntity.where(realm, roomId).findFirst() val roomSummaryEntity = RoomSummaryEntity.where(realm, roomId).findFirst()
?: RoomSummaryEntity(roomId) ?: RoomSummaryEntity(roomId)
if (roomSummary.heroes.isNotEmpty()) { if (roomSummary.heroes.isNotEmpty()) {
roomSummaryEntity.heroes.clear() roomSummaryEntity.heroes.clear()