Merge pull request #468 from vector-im/feature/fix_realm_issues

Feature/fix realm issues
This commit is contained in:
ganfra 2019-08-07 18:05:06 +02:00 committed by GitHub
commit 7fef063e15
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 119 additions and 107 deletions

View File

@ -16,6 +16,7 @@


package im.vector.matrix.android.api.session.room.send package im.vector.matrix.android.api.session.room.send



enum class SendState { enum class SendState {
UNKNOWN, UNKNOWN,
// the event has not been sent // the event has not been sent
@ -33,16 +34,19 @@ enum class SendState {
// the event failed to be sent because some unknown devices have been found while encrypting it // the event failed to be sent because some unknown devices have been found while encrypting it
FAILED_UNKNOWN_DEVICES; FAILED_UNKNOWN_DEVICES;


fun isSent(): Boolean { internal companion object {
return this == SENT || this == SYNCED val HAS_FAILED_STATES = listOf(UNDELIVERED, FAILED_UNKNOWN_DEVICES)
val IS_SENT_STATES = listOf(SENT, SYNCED)
val IS_SENDING_STATES = listOf(UNSENT, ENCRYPTING, SENDING)
val PENDING_STATES = IS_SENDING_STATES + HAS_FAILED_STATES
} }


fun hasFailed(): Boolean { fun isSent() = IS_SENT_STATES.contains(this)
return this == UNDELIVERED || this == FAILED_UNKNOWN_DEVICES
}


fun isSending(): Boolean { fun hasFailed() = HAS_FAILED_STATES.contains(this)
return this == UNSENT || this == ENCRYPTING || this == SENDING
} fun isSending() = IS_SENDING_STATES.contains(this)


} }



View File

@ -33,6 +33,8 @@ internal open class ChunkEntity(@Index var prevToken: String? = null,
var forwardsStateIndex: Int? = null var forwardsStateIndex: Int? = null
) : RealmObject() { ) : RealmObject() {


fun identifier() = "${prevToken}_${nextToken}"

@LinkingObjects("chunks") @LinkingObjects("chunks")
val room: RealmResults<RoomEntity>? = null val room: RealmResults<RoomEntity>? = null



View File

@ -18,20 +18,20 @@ package im.vector.matrix.android.internal.database.model


import im.vector.matrix.android.api.session.room.model.Membership import im.vector.matrix.android.api.session.room.model.Membership
import io.realm.RealmObject import io.realm.RealmObject
import io.realm.annotations.Ignore
import io.realm.annotations.PrimaryKey import io.realm.annotations.PrimaryKey
import kotlin.properties.Delegates


internal open class GroupEntity(@PrimaryKey var groupId: String = "" internal open class GroupEntity(@PrimaryKey var groupId: String = ""


) : RealmObject() { ) : RealmObject() {


private var membershipStr: String = Membership.NONE.name private var membershipStr: String = Membership.NONE.name

var membership: Membership
@delegate:Ignore get() {
var membership: Membership by Delegates.observable(Membership.valueOf(membershipStr)) { _, _, newValue -> return Membership.valueOf(membershipStr)
membershipStr = newValue.name }
} set(value) {
membershipStr = value.name
}


companion object companion object



View File

@ -19,9 +19,7 @@ package im.vector.matrix.android.internal.database.model
import im.vector.matrix.android.api.session.room.model.Membership import im.vector.matrix.android.api.session.room.model.Membership
import io.realm.RealmList import io.realm.RealmList
import io.realm.RealmObject import io.realm.RealmObject
import io.realm.annotations.Ignore
import io.realm.annotations.PrimaryKey import io.realm.annotations.PrimaryKey
import kotlin.properties.Delegates


internal open class RoomEntity(@PrimaryKey var roomId: String = "", internal open class RoomEntity(@PrimaryKey var roomId: String = "",
var chunks: RealmList<ChunkEntity> = RealmList(), var chunks: RealmList<ChunkEntity> = RealmList(),
@ -31,11 +29,13 @@ internal open class RoomEntity(@PrimaryKey var roomId: String = "",
) : RealmObject() { ) : RealmObject() {


private var membershipStr: String = Membership.NONE.name private var membershipStr: String = Membership.NONE.name

var membership: Membership
@delegate:Ignore get() {
var membership: Membership by Delegates.observable(Membership.valueOf(membershipStr)) { _, _, newValue -> return Membership.valueOf(membershipStr)
membershipStr = newValue.name }
} set(value) {
membershipStr = value.name
}


companion object companion object
} }

View File

@ -20,9 +20,7 @@ import im.vector.matrix.android.api.session.room.model.Membership
import im.vector.matrix.android.api.session.room.model.VersioningState import im.vector.matrix.android.api.session.room.model.VersioningState
import io.realm.RealmList import io.realm.RealmList
import io.realm.RealmObject import io.realm.RealmObject
import io.realm.annotations.Ignore
import io.realm.annotations.PrimaryKey import io.realm.annotations.PrimaryKey
import kotlin.properties.Delegates


internal open class RoomSummaryEntity(@PrimaryKey var roomId: String = "", internal open class RoomSummaryEntity(@PrimaryKey var roomId: String = "",
var displayName: String? = "", var displayName: String? = "",
@ -41,18 +39,22 @@ internal open class RoomSummaryEntity(@PrimaryKey var roomId: String = "",
) : RealmObject() { ) : RealmObject() {


private var membershipStr: String = Membership.NONE.name private var membershipStr: String = Membership.NONE.name
var membership: Membership
get() {
return Membership.valueOf(membershipStr)
}
set(value) {
membershipStr = value.name
}

private var versioningStateStr: String = VersioningState.NONE.name private var versioningStateStr: String = VersioningState.NONE.name

var versioningState: VersioningState

get() {
@delegate:Ignore return VersioningState.valueOf(versioningStateStr)
var membership: Membership by Delegates.observable(Membership.valueOf(membershipStr)) { _, _, newValue -> }
membershipStr = newValue.name set(value) {
} versioningStateStr = value.name

}
@delegate:Ignore
var versioningState: VersioningState by Delegates.observable(VersioningState.valueOf(versioningStateStr)) { _, _, newValue ->
versioningStateStr = newValue.name
}


companion object companion object



View File

@ -16,12 +16,10 @@


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


import im.vector.matrix.android.api.session.room.send.SendState
import im.vector.matrix.android.internal.database.model.* import im.vector.matrix.android.internal.database.model.*
import im.vector.matrix.android.internal.database.model.EventEntity.LinkFilterMode.* import im.vector.matrix.android.internal.database.model.EventEntity.LinkFilterMode.*
import io.realm.Realm import io.realm.*
import io.realm.RealmList
import io.realm.RealmQuery
import io.realm.Sort
import io.realm.kotlin.where import io.realm.kotlin.where


internal fun TimelineEventEntity.Companion.where(realm: Realm, eventId: String): RealmQuery<TimelineEventEntity> { internal fun TimelineEventEntity.Companion.where(realm: Realm, eventId: String): RealmQuery<TimelineEventEntity> {
@ -114,3 +112,15 @@ internal fun RealmList<TimelineEventEntity>.find(eventId: String): TimelineEvent
.equalTo(TimelineEventEntityFields.ROOT.EVENT_ID, eventId) .equalTo(TimelineEventEntityFields.ROOT.EVENT_ID, eventId)
.findFirst() .findFirst()
} }

internal fun TimelineEventEntity.Companion.findAllInRoomWithSendStates(realm: Realm,
roomId: String,
sendStates: List<SendState>)
: RealmResults<TimelineEventEntity> {

val sendStatesStr = sendStates.map { it.name }.toTypedArray()
return realm.where<TimelineEventEntity>()
.equalTo(TimelineEventEntityFields.ROOM_ID, roomId)
.`in`(TimelineEventEntityFields.ROOT.SEND_STATE_STR,sendStatesStr)
.findAll()
}

View File

@ -34,6 +34,7 @@ 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.EventEntity
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.TimelineEventEntity import im.vector.matrix.android.internal.database.model.TimelineEventEntity
import im.vector.matrix.android.internal.database.query.findAllInRoomWithSendStates
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.content.UploadContentWorker import im.vector.matrix.android.internal.session.content.UploadContentWorker
import im.vector.matrix.android.internal.session.room.timeline.TimelineSendEventWorkCommon import im.vector.matrix.android.internal.session.room.timeline.TimelineSendEventWorkCommon
@ -188,48 +189,47 @@ internal class DefaultSendService @Inject constructor(private val context: Conte


override fun resendAllFailedMessages() { override fun resendAllFailedMessages() {
monarchy.writeAsync { realm -> monarchy.writeAsync { realm ->
RoomEntity.where(realm, roomId).findFirst()?.let { room -> TimelineEventEntity
room.sendingTimelineEvents.filter { .findAllInRoomWithSendStates(realm, roomId, SendState.HAS_FAILED_STATES)
it.root?.sendState?.hasFailed() ?: false .sortedBy { it.root?.originServerTs ?: 0 }
}.sortedBy { it.root?.originServerTs ?: 0 }.forEach { timelineEventEntity -> .forEach { timelineEventEntity ->
timelineEventEntity.root?.let { timelineEventEntity.root?.let {
val event = it.asDomain() val event = it.asDomain()
when (event.getClearType()) { when (event.getClearType()) {
EventType.MESSAGE, EventType.MESSAGE,
EventType.REDACTION, EventType.REDACTION,
EventType.REACTION -> { EventType.REACTION -> {
val content = event.getClearContent().toModel<MessageContent>() val content = event.getClearContent().toModel<MessageContent>()
if (content != null) { if (content != null) {
when (content.type) { when (content.type) {
MessageType.MSGTYPE_EMOTE, MessageType.MSGTYPE_EMOTE,
MessageType.MSGTYPE_NOTICE, MessageType.MSGTYPE_NOTICE,
MessageType.MSGTYPE_LOCATION, MessageType.MSGTYPE_LOCATION,
MessageType.MSGTYPE_TEXT -> { MessageType.MSGTYPE_TEXT -> {
it.sendState = SendState.UNSENT it.sendState = SendState.UNSENT
sendEvent(event) sendEvent(event)
} }
MessageType.MSGTYPE_FILE, MessageType.MSGTYPE_FILE,
MessageType.MSGTYPE_VIDEO, MessageType.MSGTYPE_VIDEO,
MessageType.MSGTYPE_IMAGE, MessageType.MSGTYPE_IMAGE,
MessageType.MSGTYPE_AUDIO -> { MessageType.MSGTYPE_AUDIO -> {
//need to resend the attachement //need to resend the attachement
} }
else -> { else -> {
Timber.e("Cannot resend message ${event.type} / ${content.type}") Timber.e("Cannot resend message ${event.type} / ${content.type}")
} }


}
} else {
Timber.e("Unsupported message to resend ${event.type}")
} }
} else { }
else -> {
Timber.e("Unsupported message to resend ${event.type}") Timber.e("Unsupported message to resend ${event.type}")
} }
} }
else -> {
Timber.e("Unsupported message to resend ${event.type}")
}
} }
} }
}
}
} }
} }



View File

@ -19,16 +19,14 @@ package im.vector.matrix.android.internal.session.room.timeline
import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.crypto.CryptoService import im.vector.matrix.android.api.session.crypto.CryptoService
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.timeline.Timeline import im.vector.matrix.android.api.session.room.timeline.Timeline
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.matrix.android.api.util.CancelableBag import im.vector.matrix.android.api.util.CancelableBag
import im.vector.matrix.android.internal.database.mapper.asDomain import im.vector.matrix.android.internal.database.mapper.asDomain
import im.vector.matrix.android.internal.database.model.* import im.vector.matrix.android.internal.database.model.*
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.query.findIncludingEvent import im.vector.matrix.android.internal.database.query.*
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.whereInRoom
import im.vector.matrix.android.internal.task.TaskConstraints import im.vector.matrix.android.internal.task.TaskConstraints
import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.configureWith import im.vector.matrix.android.internal.task.configureWith
@ -208,21 +206,15 @@ internal class DefaultTimeline(
} }


override fun pendingEventCount(): Int { override fun pendingEventCount(): Int {
var count = 0 return Realm.getInstance(realmConfiguration).use {
Realm.getInstance(realmConfiguration).use { RoomEntity.where(it, roomId).findFirst()?.sendingTimelineEvents?.count() ?: 0
count = RoomEntity.where(it, roomId).findFirst()?.sendingTimelineEvents?.count() ?: 0
} }
return count
} }


override fun failedToDeliverEventCount(): Int { override fun failedToDeliverEventCount(): Int {
var count = 0 return Realm.getInstance(realmConfiguration).use {
Realm.getInstance(realmConfiguration).use { TimelineEventEntity.findAllInRoomWithSendStates(it, roomId, SendState.HAS_FAILED_STATES).count()
count = RoomEntity.where(it, roomId).findFirst()?.sendingTimelineEvents?.filter {
it.root?.sendState?.hasFailed() ?: false
}?.count() ?: 0
} }
return count
} }


override fun start() { override fun start() {
@ -278,30 +270,32 @@ internal class DefaultTimeline(
// Private methods ***************************************************************************** // Private methods *****************************************************************************


private fun hasMoreInCache(direction: Timeline.Direction): Boolean { private fun hasMoreInCache(direction: Timeline.Direction): Boolean {
val localRealm = Realm.getInstance(realmConfiguration) return Realm.getInstance(realmConfiguration).use { localRealm ->
val timelineEventEntity = buildEventQuery(localRealm).findFirst(direction) ?: return false val timelineEventEntity = buildEventQuery(localRealm).findFirst(direction)
val hasMoreInCache = if (direction == Timeline.Direction.FORWARDS) { ?: return false
val firstEvent = builtEvents.firstOrNull() ?: return true if (direction == Timeline.Direction.FORWARDS) {
firstEvent.displayIndex < timelineEventEntity.root!!.displayIndex if (findCurrentChunk(localRealm)?.isLastForward == true) {
} else { return false
val lastEvent = builtEvents.lastOrNull() ?: return true }
lastEvent.displayIndex > timelineEventEntity.root!!.displayIndex val firstEvent = builtEvents.firstOrNull() ?: return true
firstEvent.displayIndex < timelineEventEntity.root!!.displayIndex
} else {
val lastEvent = builtEvents.lastOrNull() ?: return true
lastEvent.displayIndex > timelineEventEntity.root!!.displayIndex
}
} }
localRealm.close()
return hasMoreInCache
} }


private fun hasReachedEnd(direction: Timeline.Direction): Boolean { private fun hasReachedEnd(direction: Timeline.Direction): Boolean {
val localRealm = Realm.getInstance(realmConfiguration) return Realm.getInstance(realmConfiguration).use { localRealm ->
val currentChunk = findCurrentChunk(localRealm) ?: return false val currentChunk = findCurrentChunk(localRealm) ?: return false
val hasReachedEnd = if (direction == Timeline.Direction.FORWARDS) { if (direction == Timeline.Direction.FORWARDS) {
currentChunk.isLastForward currentChunk.isLastForward
} else { } else {
val eventEntity = buildEventQuery(localRealm).findFirst(direction) val eventEntity = buildEventQuery(localRealm).findFirst(direction)
currentChunk.isLastBackward || eventEntity?.root?.type == EventType.STATE_ROOM_CREATE currentChunk.isLastBackward || eventEntity?.root?.type == EventType.STATE_ROOM_CREATE
}
} }
localRealm.close()
return hasReachedEnd
} }