RoomMembers/User : get a better and faster handling (still need to fix one small issue)

This commit is contained in:
ganfra 2019-07-12 13:59:37 +02:00
parent 10e4d0190f
commit 9182f2ce4e
8 changed files with 24 additions and 166 deletions

View File

@ -16,7 +16,6 @@


package im.vector.matrix.android.internal.database.helper package im.vector.matrix.android.internal.database.helper


import androidx.annotation.VisibleForTesting
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.api.session.room.send.SendState import im.vector.matrix.android.api.session.room.send.SendState

View File

@ -16,7 +16,6 @@


package im.vector.matrix.android.internal.database.helper package im.vector.matrix.android.internal.database.helper


import com.squareup.moshi.JsonReader
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.room.send.SendState import im.vector.matrix.android.api.session.room.send.SendState
import im.vector.matrix.android.internal.database.mapper.toEntity import im.vector.matrix.android.internal.database.mapper.toEntity
@ -25,10 +24,7 @@ import im.vector.matrix.android.internal.database.model.RoomEntity
import im.vector.matrix.android.internal.database.model.TimelineEventEntity import im.vector.matrix.android.internal.database.model.TimelineEventEntity
import im.vector.matrix.android.internal.database.query.fastContains import im.vector.matrix.android.internal.database.query.fastContains
import im.vector.matrix.android.internal.extensions.assertIsManaged import im.vector.matrix.android.internal.extensions.assertIsManaged
import im.vector.matrix.android.internal.network.parsing.GetRoomMembersResponseHandler
import im.vector.matrix.android.internal.session.room.membership.RoomMembers import im.vector.matrix.android.internal.session.room.membership.RoomMembers
import okhttp3.ResponseBody
import okio.Okio


internal fun RoomEntity.deleteOnCascade(chunkEntity: ChunkEntity) { internal fun RoomEntity.deleteOnCascade(chunkEntity: ChunkEntity) {
chunks.remove(chunkEntity) chunks.remove(chunkEntity)
@ -57,26 +53,6 @@ internal fun RoomEntity.addStateEvent(stateEvent: Event,
untimelinedStateEvents.add(entity) untimelinedStateEvents.add(entity)
} }
} }

internal fun RoomEntity.addStateEvents(stateEvents: List<Event>,
stateIndex: Int = Int.MIN_VALUE,
filterDuplicates: Boolean = false,
isUnlinked: Boolean = false) {
stateEvents.forEach { event ->
addStateEvent(event, stateIndex, filterDuplicates, isUnlinked)
}
}

internal fun RoomEntity.addStateEvents(response: ResponseBody,
stateIndex: Int = Int.MIN_VALUE,
isUnlinked: Boolean = false) {
val manualParser = GetRoomMembersResponseHandler()
val bufferedSource = Okio.buffer(Okio.source(response.byteStream()))
val inputReader = JsonReader.of(bufferedSource)
manualParser.handle(inputReader, this, stateIndex, isUnlinked)
}


internal fun RoomEntity.addSendingEvent(event: Event) { internal fun RoomEntity.addSendingEvent(event: Event) {
assertIsManaged() assertIsManaged()
val senderId = event.senderId ?: return val senderId = event.senderId ?: return

View File

@ -20,8 +20,6 @@ import io.realm.RealmObject
import io.realm.RealmResults import io.realm.RealmResults
import io.realm.annotations.Index import io.realm.annotations.Index
import io.realm.annotations.LinkingObjects import io.realm.annotations.LinkingObjects
import io.realm.annotations.PrimaryKey
import java.util.*




internal open class TimelineEventEntity(var localId: Long = 0, internal open class TimelineEventEntity(var localId: Long = 0,

View File

@ -1,59 +0,0 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package im.vector.matrix.android.internal.network.parsing

import com.squareup.moshi.JsonReader
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.room.send.SendState
import im.vector.matrix.android.internal.database.model.EventEntity
import im.vector.matrix.android.internal.database.model.RoomEntity

internal class GetRoomMembersResponseHandler {

companion object {
private val NAMES = JsonReader.Options.of("event_id", "content", "prev_content", "origin_server_ts", "sender", "state_key")
}

internal fun handle(reader: JsonReader, roomEntity: RoomEntity, stateIndex: Int = Int.MIN_VALUE, isUnlinked: Boolean = false) {
reader.readObject {
reader.nextName()
val eventEntity = EventEntity().apply {
this.roomId = roomEntity.roomId
this.stateIndex = stateIndex
this.isUnlinked = isUnlinked
this.sendState = SendState.SYNCED
this.type = EventType.STATE_ROOM_MEMBER
}
reader.readArray {
reader.readObject {
when
(reader.selectName(NAMES)) {
0 -> eventEntity.eventId = reader.nextString()
1 -> eventEntity.content = reader.readJsonValue()?.toString()
2 -> eventEntity.prevContent = reader.readJsonValue()?.toString()
3 -> eventEntity.originServerTs = reader.nextLong()
4 -> eventEntity.sender = reader.nextString()
5 -> eventEntity.stateKey = reader.nextString()
else -> reader.skipNameAndValue()
}
}
roomEntity.untimelinedStateEvents.add(eventEntity)
}
}
}

}

View File

@ -1,40 +0,0 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package im.vector.matrix.android.internal.network.parsing

import com.squareup.moshi.JsonReader

fun JsonReader.skipNameAndValue() {
skipName()
skipValue()
}

inline fun JsonReader.readObject(body: () -> Unit) {
beginObject()
while (hasNext()) {
body()
}
endObject()
}

inline fun JsonReader.readArray(body: () -> Unit) {
beginArray()
while (hasNext()) {
body()
}
endArray()
}

View File

@ -17,15 +17,12 @@
package im.vector.matrix.android.internal.session.room.membership package im.vector.matrix.android.internal.session.room.membership


import arrow.core.Try import arrow.core.Try
import com.squareup.moshi.JsonReader
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
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.Membership
import im.vector.matrix.android.api.session.room.model.RoomMember
import im.vector.matrix.android.internal.database.helper.addStateEvent import im.vector.matrix.android.internal.database.helper.addStateEvent
import im.vector.matrix.android.internal.database.helper.addStateEvents
import im.vector.matrix.android.internal.database.helper.updateSenderData import im.vector.matrix.android.internal.database.helper.updateSenderData
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.model.UserEntity
import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.session.room.RoomAPI import im.vector.matrix.android.internal.session.room.RoomAPI
@ -37,6 +34,7 @@ import im.vector.matrix.android.internal.util.tryTransactionSync
import io.realm.Realm import io.realm.Realm
import io.realm.kotlin.createObject import io.realm.kotlin.createObject
import okhttp3.ResponseBody import okhttp3.ResponseBody
import okio.Okio
import javax.inject.Inject import javax.inject.Inject


internal interface LoadRoomMembersTask : Task<LoadRoomMembersTask.Params, Boolean> { internal interface LoadRoomMembersTask : Task<LoadRoomMembersTask.Params, Boolean> {
@ -73,14 +71,13 @@ internal class DefaultLoadRoomMembersTask @Inject constructor(private val roomAP
val roomEntity = RoomEntity.where(realm, roomId).findFirst() val roomEntity = RoomEntity.where(realm, roomId).findFirst()
?: realm.createObject(roomId) ?: realm.createObject(roomId)


val userEntities = ArrayList<UserEntity>(response.roomMemberEvents.size)
for (roomMemberEvent in response.roomMemberEvents) { for (roomMemberEvent in response.roomMemberEvents) {
roomEntity.addStateEvent(roomMemberEvent) roomEntity.addStateEvent(roomMemberEvent)
UserEntityFactory.create(roomMemberEvent)?.also { UserEntityFactory.create(roomMemberEvent)?.also {
userEntities.add(it) realm.insertOrUpdate(it)
} }
} }
realm.insertOrUpdate(userEntities)
roomEntity.chunks.flatMap { it.timelineEvents }.forEach { roomEntity.chunks.flatMap { it.timelineEvents }.forEach {
it.updateSenderData() it.updateSenderData()
} }

View File

@ -18,16 +18,9 @@ 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.addStateEvent
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.model.UserEntity
import im.vector.matrix.android.internal.database.query.create import im.vector.matrix.android.internal.database.query.create
import im.vector.matrix.android.internal.database.query.find import im.vector.matrix.android.internal.database.query.find
import im.vector.matrix.android.internal.database.query.findAllIncludingEvents import im.vector.matrix.android.internal.database.query.findAllIncludingEvents
@ -156,11 +149,12 @@ internal class TokenChunkEventPersistor @Inject constructor(private val monarchy
currentChunk.isLastBackward = true currentChunk.isLastBackward = true
} else { } else {
Timber.v("Add ${receivedChunk.events.size} events in chunk(${currentChunk.nextToken} | ${currentChunk.prevToken}") Timber.v("Add ${receivedChunk.events.size} events in chunk(${currentChunk.nextToken} | ${currentChunk.prevToken}")
val userEntities = ArrayList<UserEntity>(receivedChunk.events.size + receivedChunk.stateEvents.size) val eventIds = ArrayList<String>(receivedChunk.events.size)
for (event in receivedChunk.events) { for (event in receivedChunk.events) {
currentChunk.addAll(roomId, receivedChunk.events, direction, isUnlinked = currentChunk.isUnlinked()) event.eventId?.also { eventIds.add(it) }
currentChunk.add(roomId, event, direction, isUnlinked = currentChunk.isUnlinked())
UserEntityFactory.create(event)?.also { UserEntityFactory.create(event)?.also {
userEntities.add(it) realm.insertOrUpdate(it)
} }
} }
// Then we merge chunks if needed // Then we merge chunks if needed
@ -181,10 +175,10 @@ internal class TokenChunkEventPersistor @Inject constructor(private val monarchy
for (stateEvent in receivedChunk.stateEvents) { for (stateEvent in receivedChunk.stateEvents) {
roomEntity.addStateEvent(stateEvent, isUnlinked = currentChunk.isUnlinked()) roomEntity.addStateEvent(stateEvent, isUnlinked = currentChunk.isUnlinked())
UserEntityFactory.create(stateEvent)?.also { UserEntityFactory.create(stateEvent)?.also {
userEntities.add(it) realm.insertOrUpdate(it)
} }
} }
realm.insertOrUpdate(userEntities) currentChunk.updateSenderDataFor(eventIds)
} }
} }
.map { .map {

View File

@ -22,15 +22,9 @@ 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.api.session.events.model.toModel 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.Membership
import im.vector.matrix.android.api.session.room.model.RoomMember
import im.vector.matrix.android.api.session.room.model.tag.RoomTagContent import im.vector.matrix.android.api.session.room.model.tag.RoomTagContent
import im.vector.matrix.android.internal.crypto.CryptoManager import im.vector.matrix.android.internal.crypto.CryptoManager
import im.vector.matrix.android.internal.database.helper.add import im.vector.matrix.android.internal.database.helper.*
import im.vector.matrix.android.internal.database.helper.addAll
import im.vector.matrix.android.internal.database.helper.addOrUpdate
import im.vector.matrix.android.internal.database.helper.addStateEvent
import im.vector.matrix.android.internal.database.helper.addStateEvents
import im.vector.matrix.android.internal.database.helper.lastStateIndex
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.model.UserEntity import im.vector.matrix.android.internal.database.model.UserEntity
@ -138,17 +132,15 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch


// State event // State event
if (roomSync.state != null && roomSync.state.events.isNotEmpty()) { if (roomSync.state != null && roomSync.state.events.isNotEmpty()) {
val userEntities = ArrayList<UserEntity>(roomSync.state.events.size)
val untimelinedStateIndex = if (isInitialSync) Int.MIN_VALUE else stateIndexOffset val untimelinedStateIndex = if (isInitialSync) Int.MIN_VALUE else stateIndexOffset
roomSync.state.events.forEach { event -> roomSync.state.events.forEach { event ->
roomEntity.addStateEvent(event, filterDuplicates = true, stateIndex = untimelinedStateIndex) roomEntity.addStateEvent(event, filterDuplicates = true, stateIndex = untimelinedStateIndex)
// Give info to crypto module // Give info to crypto module
cryptoManager.onStateEvent(roomId, event) cryptoManager.onStateEvent(roomId, event)
UserEntityFactory.create(event)?.also { UserEntityFactory.create(event)?.also {
userEntities.add(it) realm.insertOrUpdate(it)
} }
} }
realm.insertOrUpdate(userEntities)
} }


if (roomSync.timeline != null && roomSync.timeline.events.isNotEmpty()) { if (roomSync.timeline != null && roomSync.timeline.events.isNotEmpty()) {
@ -219,8 +211,9 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
lastChunk?.isLastForward = false lastChunk?.isLastForward = false
chunkEntity.isLastForward = true chunkEntity.isLastForward = true


val userEntities = ArrayList<UserEntity>(eventList.size) val eventIds = ArrayList<String>(eventList.size)
for (event in eventList) { for (event in eventList) {
event.eventId?.also { eventIds.add(it) }
chunkEntity.add(roomEntity.roomId, event, PaginationDirection.FORWARDS, stateIndexOffset) chunkEntity.add(roomEntity.roomId, event, PaginationDirection.FORWARDS, stateIndexOffset)
// Give info to crypto module // Give info to crypto module
cryptoManager.onLiveEvent(roomEntity.roomId, event) cryptoManager.onLiveEvent(roomEntity.roomId, event)
@ -235,10 +228,10 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
} }
} }
UserEntityFactory.create(event)?.also { UserEntityFactory.create(event)?.also {
userEntities.add(it) realm.insertOrUpdate(it)
} }
} }
realm.insertOrUpdate(userEntities) chunkEntity.updateSenderDataFor(eventIds)
return chunkEntity return chunkEntity
} }