mirror of
https://github.com/vector-im/riotX-android
synced 2025-10-06 00:02:48 +02:00
Compare commits
4 Commits
v1.6.40
...
feature/am
Author | SHA1 | Date | |
---|---|---|---|
|
42d8bbb7b3 | ||
|
98762ef042 | ||
|
b6c0151e97 | ||
|
6e6a2fff31 |
1
changelog.d/7602.sdk
Normal file
1
changelog.d/7602.sdk
Normal file
@@ -0,0 +1 @@
|
||||
Add sentry tracking around send message task.
|
@@ -25,14 +25,17 @@ import kotlin.contracts.contract
|
||||
/**
|
||||
* Executes the given [block] while measuring the transaction.
|
||||
*
|
||||
* @param T type of the output from [block]
|
||||
* @param block Action/Task to be executed within this span.
|
||||
*
|
||||
* @return Output of the [block()]
|
||||
*/
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
inline fun List<MetricPlugin>.measureMetric(block: () -> Unit) {
|
||||
inline fun <T> List<MetricPlugin>.measureTransaction(block: () -> T): T {
|
||||
contract {
|
||||
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
|
||||
}
|
||||
try {
|
||||
return try {
|
||||
this.forEach { plugin -> plugin.startTransaction() } // Start the transaction.
|
||||
block()
|
||||
} catch (throwable: Throwable) {
|
||||
@@ -46,16 +49,19 @@ inline fun List<MetricPlugin>.measureMetric(block: () -> Unit) {
|
||||
/**
|
||||
* Executes the given [block] while measuring a span.
|
||||
*
|
||||
* @param T type of the output from [block]
|
||||
* @param operation Name of the new span.
|
||||
* @param description Description of the new span.
|
||||
* @param block Action/Task to be executed within this span.
|
||||
*
|
||||
* @return Output of the [block]
|
||||
*/
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
inline fun List<SpannableMetricPlugin>.measureSpan(operation: String, description: String, block: () -> Unit) {
|
||||
inline fun <T> List<SpannableMetricPlugin>.measureSpan(operation: String, description: String, block: () -> T): T {
|
||||
contract {
|
||||
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
|
||||
}
|
||||
try {
|
||||
return try {
|
||||
this.forEach { plugin -> plugin.startSpan(operation, description) } // Start the transaction.
|
||||
block()
|
||||
} catch (throwable: Throwable) {
|
||||
|
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* 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 org.matrix.android.sdk.api.metrics
|
||||
|
||||
import org.matrix.android.sdk.api.logger.LoggerTag
|
||||
import timber.log.Timber
|
||||
|
||||
private val loggerTag = LoggerTag("SendServiceMetricPlugin", LoggerTag.CRYPTO)
|
||||
|
||||
/**
|
||||
* An spannable metric plugin for tracking send message, events or command task.
|
||||
*/
|
||||
interface SendServiceMetricPlugin : SpannableMetricPlugin {
|
||||
|
||||
override fun logTransaction(message: String?) {
|
||||
Timber.tag(loggerTag.value).v("## sendService() : $message")
|
||||
}
|
||||
}
|
@@ -22,7 +22,7 @@ import org.matrix.android.sdk.api.MatrixConfiguration
|
||||
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
|
||||
import org.matrix.android.sdk.api.MatrixPatterns
|
||||
import org.matrix.android.sdk.api.auth.data.Credentials
|
||||
import org.matrix.android.sdk.api.extensions.measureMetric
|
||||
import org.matrix.android.sdk.api.extensions.measureTransaction
|
||||
import org.matrix.android.sdk.api.metrics.DownloadDeviceKeysMetricsPlugin
|
||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel
|
||||
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
|
||||
@@ -355,7 +355,7 @@ internal class DeviceListManager @Inject constructor(
|
||||
val relevantPlugins = metricPlugins.filterIsInstance<DownloadDeviceKeysMetricsPlugin>()
|
||||
|
||||
val response: KeysQueryResponse
|
||||
relevantPlugins.measureMetric {
|
||||
relevantPlugins.measureTransaction {
|
||||
response = try {
|
||||
downloadKeysForUsersTask.execute(params)
|
||||
} catch (throwable: Throwable) {
|
||||
|
@@ -15,6 +15,9 @@
|
||||
*/
|
||||
package org.matrix.android.sdk.internal.crypto.tasks
|
||||
|
||||
import org.matrix.android.sdk.api.MatrixConfiguration
|
||||
import org.matrix.android.sdk.api.extensions.measureSpan
|
||||
import org.matrix.android.sdk.api.metrics.SendServiceMetricPlugin
|
||||
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
||||
import org.matrix.android.sdk.internal.network.executeRequest
|
||||
import org.matrix.android.sdk.internal.session.room.RoomAPI
|
||||
@@ -32,18 +35,23 @@ internal interface RedactEventTask : Task<RedactEventTask.Params, String> {
|
||||
|
||||
internal class DefaultRedactEventTask @Inject constructor(
|
||||
private val roomAPI: RoomAPI,
|
||||
private val globalErrorReceiver: GlobalErrorReceiver
|
||||
private val globalErrorReceiver: GlobalErrorReceiver,
|
||||
matrixConfiguration: MatrixConfiguration
|
||||
) : RedactEventTask {
|
||||
|
||||
private val relevantPlugins = matrixConfiguration.metricPlugins.filterIsInstance<SendServiceMetricPlugin>()
|
||||
|
||||
override suspend fun execute(params: RedactEventTask.Params): String {
|
||||
val response = executeRequest(globalErrorReceiver) {
|
||||
roomAPI.redactEvent(
|
||||
txId = params.txID,
|
||||
roomId = params.roomId,
|
||||
eventId = params.eventId,
|
||||
reason = if (params.reason == null) emptyMap() else mapOf("reason" to params.reason)
|
||||
)
|
||||
relevantPlugins.measureSpan("send_service.execute_redact_event_task", "Execute redact event task") {
|
||||
val response = executeRequest(globalErrorReceiver) {
|
||||
roomAPI.redactEvent(
|
||||
txId = params.txID,
|
||||
roomId = params.roomId,
|
||||
eventId = params.eventId,
|
||||
reason = if (params.reason == null) emptyMap() else mapOf("reason" to params.reason)
|
||||
)
|
||||
}
|
||||
return response.eventId
|
||||
}
|
||||
return response.eventId
|
||||
}
|
||||
}
|
||||
|
@@ -15,6 +15,9 @@
|
||||
*/
|
||||
package org.matrix.android.sdk.internal.crypto.tasks
|
||||
|
||||
import org.matrix.android.sdk.api.MatrixConfiguration
|
||||
import org.matrix.android.sdk.api.extensions.measureSpan
|
||||
import org.matrix.android.sdk.api.metrics.SendServiceMetricPlugin
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.room.model.localecho.RoomLocalEcho
|
||||
import org.matrix.android.sdk.api.session.room.send.SendState
|
||||
@@ -41,47 +44,58 @@ internal class DefaultSendEventTask @Inject constructor(
|
||||
private val loadRoomMembersTask: LoadRoomMembersTask,
|
||||
private val createRoomFromLocalRoomTask: CreateRoomFromLocalRoomTask,
|
||||
private val roomAPI: RoomAPI,
|
||||
private val globalErrorReceiver: GlobalErrorReceiver
|
||||
private val globalErrorReceiver: GlobalErrorReceiver,
|
||||
matrixConfiguration: MatrixConfiguration
|
||||
) : SendEventTask {
|
||||
|
||||
private val relevantPlugins = matrixConfiguration.metricPlugins.filterIsInstance<SendServiceMetricPlugin>()
|
||||
|
||||
override suspend fun execute(params: SendEventTask.Params): String {
|
||||
try {
|
||||
if (params.event.isLocalRoomEvent) {
|
||||
return createRoomAndSendEvent(params)
|
||||
}
|
||||
|
||||
// Make sure to load all members in the room before sending the event.
|
||||
params.event.roomId
|
||||
?.takeIf { params.encrypt }
|
||||
?.let { roomId ->
|
||||
try {
|
||||
loadRoomMembersTask.execute(LoadRoomMembersTask.Params(roomId))
|
||||
} catch (failure: Throwable) {
|
||||
// send any way?
|
||||
// the result is that some users won't probably be able to decrypt :/
|
||||
Timber.w(failure, "SendEvent: failed to load members in room ${params.event.roomId}")
|
||||
}
|
||||
relevantPlugins.measureSpan("send_service.execute_send_event_task", "Execute send event task") {
|
||||
try {
|
||||
if (params.event.isLocalRoomEvent) {
|
||||
return relevantPlugins.measureSpan("send_service.create_room_send_event", "Create room and send event") {
|
||||
createRoomAndSendEvent(params)
|
||||
}
|
||||
}
|
||||
|
||||
val event = handleEncryption(params)
|
||||
val localId = event.eventId!!
|
||||
localEchoRepository.updateSendState(localId, params.event.roomId, SendState.SENDING)
|
||||
val response = executeRequest(globalErrorReceiver) {
|
||||
roomAPI.send(
|
||||
localId,
|
||||
roomId = event.roomId ?: "",
|
||||
content = event.content,
|
||||
eventType = event.type ?: ""
|
||||
)
|
||||
}
|
||||
localEchoRepository.updateSendState(localId, params.event.roomId, SendState.SENT)
|
||||
return response.eventId.also {
|
||||
Timber.d("Event: $it just sent in ${params.event.roomId}")
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
// Make sure to load all members in the room before sending the event.
|
||||
relevantPlugins.measureSpan("send_service.load_all_members", "Load all members of room") {
|
||||
params.event.roomId
|
||||
?.takeIf { params.encrypt }
|
||||
?.let { roomId ->
|
||||
try {
|
||||
loadRoomMembersTask.execute(LoadRoomMembersTask.Params(roomId))
|
||||
} catch (failure: Throwable) {
|
||||
// send any way?
|
||||
// the result is that some users won't probably be able to decrypt :/
|
||||
Timber.w(failure, "SendEvent: failed to load members in room ${params.event.roomId}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val event = handleEncryption(params)
|
||||
val localId = event.eventId!!
|
||||
localEchoRepository.updateSendState(localId, params.event.roomId, SendState.SENDING)
|
||||
relevantPlugins.measureSpan("send_service.room_send_event", "Send event in room") {
|
||||
val response = executeRequest(globalErrorReceiver) {
|
||||
roomAPI.send(
|
||||
localId,
|
||||
roomId = event.roomId ?: "",
|
||||
content = event.content,
|
||||
eventType = event.type ?: ""
|
||||
)
|
||||
}
|
||||
localEchoRepository.updateSendState(localId, params.event.roomId, SendState.SENT)
|
||||
return response.eventId.also {
|
||||
Timber.d("Event: $it just sent in ${params.event.roomId}")
|
||||
}
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
// localEchoRepository.updateSendState(params.event.eventId!!, SendState.UNDELIVERED)
|
||||
Timber.w(e, "Unable to send the Event")
|
||||
throw e
|
||||
Timber.w(e, "Unable to send the Event")
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,16 +107,18 @@ internal class DefaultSendEventTask @Inject constructor(
|
||||
|
||||
@Throws
|
||||
private suspend fun handleEncryption(params: SendEventTask.Params): Event {
|
||||
if (params.encrypt && !params.event.isEncrypted()) {
|
||||
return encryptEventTask.execute(
|
||||
EncryptEventTask.Params(
|
||||
params.event.roomId ?: "",
|
||||
params.event,
|
||||
listOf("m.relates_to")
|
||||
)
|
||||
)
|
||||
relevantPlugins.measureSpan("send_service.encrypt_event", "Encrypt event") {
|
||||
if (params.encrypt && !params.event.isEncrypted()) {
|
||||
return encryptEventTask.execute(
|
||||
EncryptEventTask.Params(
|
||||
params.event.roomId ?: "",
|
||||
params.event,
|
||||
listOf("m.relates_to")
|
||||
)
|
||||
)
|
||||
}
|
||||
return params.event
|
||||
}
|
||||
return params.event
|
||||
}
|
||||
|
||||
private val Event.isLocalRoomEvent
|
||||
|
@@ -25,7 +25,11 @@ import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.launch
|
||||
import org.matrix.android.sdk.api.MatrixConfiguration
|
||||
import org.matrix.android.sdk.api.MatrixUrls.isMxcUrl
|
||||
import org.matrix.android.sdk.api.extensions.measureSpan
|
||||
import org.matrix.android.sdk.api.extensions.measureTransaction
|
||||
import org.matrix.android.sdk.api.metrics.SendServiceMetricPlugin
|
||||
import org.matrix.android.sdk.api.session.content.ContentAttachmentData
|
||||
import org.matrix.android.sdk.api.session.events.model.Content
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
@@ -73,9 +77,12 @@ internal class DefaultSendService @AssistedInject constructor(
|
||||
private val taskExecutor: TaskExecutor,
|
||||
private val localEchoRepository: LocalEchoRepository,
|
||||
private val eventSenderProcessor: EventSenderProcessor,
|
||||
private val cancelSendTracker: CancelSendTracker
|
||||
private val cancelSendTracker: CancelSendTracker,
|
||||
matrixConfiguration: MatrixConfiguration,
|
||||
) : SendService {
|
||||
|
||||
private val relevantPlugins = matrixConfiguration.metricPlugins.filterIsInstance<SendServiceMetricPlugin>()
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(roomId: String): DefaultSendService
|
||||
@@ -84,21 +91,45 @@ internal class DefaultSendService @AssistedInject constructor(
|
||||
private val workerFutureListenerExecutor = Executors.newSingleThreadExecutor()
|
||||
|
||||
override fun sendEvent(eventType: String, content: JsonDict?): Cancelable {
|
||||
return localEchoEventFactory.createEvent(roomId, eventType, content)
|
||||
.also { createLocalEcho(it) }
|
||||
.let { sendEvent(it) }
|
||||
return relevantPlugins.measureTransaction {
|
||||
relevantPlugins.measureSpan("send_service.create_event", "Create local event") {
|
||||
localEchoEventFactory.createEvent(roomId, eventType, content)
|
||||
}.also {
|
||||
relevantPlugins.measureSpan("send_service.create_local_echo", "Create local echo") {
|
||||
createLocalEcho(it)
|
||||
}
|
||||
}.let {
|
||||
relevantPlugins.measureSpan("send_service.send_event", "Send event") { sendEvent(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun sendTextMessage(text: CharSequence, msgType: String, autoMarkdown: Boolean, additionalContent: Content?): Cancelable {
|
||||
return localEchoEventFactory.createTextEvent(roomId, msgType, text, autoMarkdown, additionalContent)
|
||||
.also { createLocalEcho(it) }
|
||||
.let { sendEvent(it) }
|
||||
return relevantPlugins.measureTransaction {
|
||||
relevantPlugins.measureSpan("send_service.create_text_event", "Create local text event") {
|
||||
localEchoEventFactory.createTextEvent(roomId, msgType, text, autoMarkdown, additionalContent)
|
||||
}.also {
|
||||
relevantPlugins.measureSpan("send_service.create_local_echo", "Create local echo") {
|
||||
createLocalEcho(it)
|
||||
}
|
||||
}.let {
|
||||
relevantPlugins.measureSpan("send_service.send_event", "Send event") { sendEvent(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun sendFormattedTextMessage(text: String, formattedText: String, msgType: String, additionalContent: Content?): Cancelable {
|
||||
return localEchoEventFactory.createFormattedTextEvent(roomId, TextContent(text, formattedText), msgType, additionalContent)
|
||||
.also { createLocalEcho(it) }
|
||||
.let { sendEvent(it) }
|
||||
return relevantPlugins.measureTransaction {
|
||||
relevantPlugins.measureSpan("send_service.create_formatted_text_event", "Create local formatted text event") {
|
||||
localEchoEventFactory.createFormattedTextEvent(roomId, TextContent(text, formattedText), msgType, additionalContent)
|
||||
}.also {
|
||||
relevantPlugins.measureSpan("send_service.create_local_echo", "Create local echo") {
|
||||
createLocalEcho(it)
|
||||
}
|
||||
}.let {
|
||||
relevantPlugins.measureSpan("send_service.send_event", "Send event") { sendEvent(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun sendQuotedTextMessage(
|
||||
@@ -109,35 +140,67 @@ internal class DefaultSendService @AssistedInject constructor(
|
||||
rootThreadEventId: String?,
|
||||
additionalContent: Content?,
|
||||
): Cancelable {
|
||||
return localEchoEventFactory.createQuotedTextEvent(
|
||||
roomId = roomId,
|
||||
quotedEvent = quotedEvent,
|
||||
text = text,
|
||||
formattedText = formattedText,
|
||||
autoMarkdown = autoMarkdown,
|
||||
rootThreadEventId = rootThreadEventId,
|
||||
additionalContent = additionalContent,
|
||||
)
|
||||
.also { createLocalEcho(it) }
|
||||
.let { sendEvent(it) }
|
||||
return relevantPlugins.measureTransaction {
|
||||
relevantPlugins.measureSpan("send_service.create_quoted_text_event", "Create local quoted text event") {
|
||||
localEchoEventFactory.createQuotedTextEvent(
|
||||
roomId = roomId,
|
||||
quotedEvent = quotedEvent,
|
||||
text = text,
|
||||
formattedText = formattedText,
|
||||
autoMarkdown = autoMarkdown,
|
||||
rootThreadEventId = rootThreadEventId,
|
||||
additionalContent = additionalContent,
|
||||
)
|
||||
}.also {
|
||||
relevantPlugins.measureSpan("send_service.create_local_echo", "Create local echo") {
|
||||
createLocalEcho(it)
|
||||
}
|
||||
}.let {
|
||||
relevantPlugins.measureSpan("send_service.send_event", "Send event") { sendEvent(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun sendPoll(pollType: PollType, question: String, options: List<String>, additionalContent: Content?): Cancelable {
|
||||
return localEchoEventFactory.createPollEvent(roomId, pollType, question, options, additionalContent)
|
||||
.also { createLocalEcho(it) }
|
||||
.let { sendEvent(it) }
|
||||
return relevantPlugins.measureTransaction {
|
||||
relevantPlugins.measureSpan("send_service.create_poll", "Create local poll event") {
|
||||
localEchoEventFactory.createPollEvent(roomId, pollType, question, options, additionalContent)
|
||||
}.also {
|
||||
relevantPlugins.measureSpan("send_service.create_local_echo", "Create local echo") {
|
||||
createLocalEcho(it)
|
||||
}
|
||||
}.let {
|
||||
relevantPlugins.measureSpan("send_service.send_event", "Send event") { sendEvent(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun voteToPoll(pollEventId: String, answerId: String, additionalContent: Content?): Cancelable {
|
||||
return localEchoEventFactory.createPollReplyEvent(roomId, pollEventId, answerId, additionalContent)
|
||||
.also { createLocalEcho(it) }
|
||||
.let { sendEvent(it) }
|
||||
return relevantPlugins.measureTransaction {
|
||||
relevantPlugins.measureSpan("send_service.reply_to_poll", "Create local reply poll event") {
|
||||
localEchoEventFactory.createPollReplyEvent(roomId, pollEventId, answerId, additionalContent)
|
||||
}.also {
|
||||
relevantPlugins.measureSpan("send_service.create_local_echo", "Create local echo") {
|
||||
createLocalEcho(it)
|
||||
}
|
||||
}.let {
|
||||
relevantPlugins.measureSpan("send_service.send_event", "Send event") { sendEvent(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun endPoll(pollEventId: String, additionalContent: Content?): Cancelable {
|
||||
return localEchoEventFactory.createEndPollEvent(roomId, pollEventId, additionalContent)
|
||||
.also { createLocalEcho(it) }
|
||||
.let { sendEvent(it) }
|
||||
return relevantPlugins.measureTransaction {
|
||||
relevantPlugins.measureSpan("send_service.end_poll", "Create local end poll event") {
|
||||
localEchoEventFactory.createEndPollEvent(roomId, pollEventId, additionalContent)
|
||||
}.also {
|
||||
relevantPlugins.measureSpan("send_service.create_local_echo", "Create local echo") {
|
||||
createLocalEcho(it)
|
||||
}
|
||||
}.let {
|
||||
relevantPlugins.measureSpan("send_service.send_event", "Send event") { sendEvent(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun redactEvent(event: Event, reason: String?, additionalContent: Content?): Cancelable {
|
||||
@@ -148,11 +211,13 @@ internal class DefaultSendService @AssistedInject constructor(
|
||||
}
|
||||
|
||||
override fun resendTextMessage(localEcho: TimelineEvent): Cancelable {
|
||||
if (localEcho.root.isTextMessage() && localEcho.root.sendState.hasFailed()) {
|
||||
localEchoRepository.updateSendState(localEcho.eventId, roomId, SendState.UNSENT)
|
||||
return sendEvent(localEcho.root)
|
||||
relevantPlugins.measureTransaction {
|
||||
if (localEcho.root.isTextMessage() && localEcho.root.sendState.hasFailed()) {
|
||||
localEchoRepository.updateSendState(localEcho.eventId, roomId, SendState.UNSENT)
|
||||
return relevantPlugins.measureSpan("send_service.resend_text_event", "Resend text message") { sendEvent(localEcho.root) }
|
||||
}
|
||||
return NoOpCancellable
|
||||
}
|
||||
return NoOpCancellable
|
||||
}
|
||||
|
||||
override fun resendMediaMessage(localEcho: TimelineEvent): Cancelable {
|
||||
@@ -163,8 +228,10 @@ internal class DefaultSendService @AssistedInject constructor(
|
||||
val url = messageContent.getFileUrl() ?: return NoOpCancellable
|
||||
if (url.isMxcUrl()) {
|
||||
// We need to resend only the message as the attachment is ok
|
||||
localEchoRepository.updateSendState(localEcho.eventId, roomId, SendState.UNSENT)
|
||||
return sendEvent(localEcho.root)
|
||||
return relevantPlugins.measureTransaction {
|
||||
localEchoRepository.updateSendState(localEcho.eventId, roomId, SendState.UNSENT)
|
||||
relevantPlugins.measureSpan("send_service.resend_media_message", "Resend media message") { sendEvent(localEcho.root) }
|
||||
}
|
||||
}
|
||||
|
||||
// we need to resend the media
|
||||
|
@@ -21,10 +21,13 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.matrix.android.sdk.api.MatrixConfiguration
|
||||
import org.matrix.android.sdk.api.auth.data.SessionParams
|
||||
import org.matrix.android.sdk.api.extensions.measureTransaction
|
||||
import org.matrix.android.sdk.api.failure.Failure
|
||||
import org.matrix.android.sdk.api.failure.getRetryDelay
|
||||
import org.matrix.android.sdk.api.failure.isLimitExceededError
|
||||
import org.matrix.android.sdk.api.metrics.SendServiceMetricPlugin
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.util.Cancelable
|
||||
@@ -58,11 +61,14 @@ internal class EventSenderProcessorCoroutine @Inject constructor(
|
||||
private val sessionParams: SessionParams,
|
||||
private val queuedTaskFactory: QueuedTaskFactory,
|
||||
private val taskExecutor: TaskExecutor,
|
||||
private val memento: QueueMemento
|
||||
private val memento: QueueMemento,
|
||||
matrixConfiguration: MatrixConfiguration,
|
||||
) : EventSenderProcessor {
|
||||
|
||||
private val waitForNetworkSequencer = SemaphoreCoroutineSequencer()
|
||||
|
||||
private val relevantPlugins = matrixConfiguration.metricPlugins.filterIsInstance<SendServiceMetricPlugin>()
|
||||
|
||||
/**
|
||||
* sequencers use QueuedTask.queueIdentifier as key.
|
||||
*/
|
||||
@@ -137,33 +143,35 @@ internal class EventSenderProcessorCoroutine @Inject constructor(
|
||||
}
|
||||
|
||||
private suspend fun executeTask(task: QueuedTask) {
|
||||
try {
|
||||
if (task.isCancelled()) {
|
||||
Timber.v("## $task has been cancelled, try next task")
|
||||
return
|
||||
}
|
||||
task.waitForNetwork()
|
||||
task.execute()
|
||||
} catch (exception: Throwable) {
|
||||
when {
|
||||
exception is IOException || exception is Failure.NetworkConnection -> {
|
||||
canReachServer.set(false)
|
||||
task.markAsFailedOrRetry(exception, 0)
|
||||
}
|
||||
(exception.isLimitExceededError()) -> {
|
||||
task.markAsFailedOrRetry(exception, exception.getRetryDelay(3_000))
|
||||
}
|
||||
exception is CancellationException -> {
|
||||
relevantPlugins.measureTransaction {
|
||||
try {
|
||||
if (task.isCancelled()) {
|
||||
Timber.v("## $task has been cancelled, try next task")
|
||||
return
|
||||
}
|
||||
else -> {
|
||||
Timber.v("## un-retryable error for $task, try next task")
|
||||
// this task is in error, check next one?
|
||||
task.onTaskFailed()
|
||||
task.waitForNetwork()
|
||||
task.execute()
|
||||
} catch (exception: Throwable) {
|
||||
when {
|
||||
exception is IOException || exception is Failure.NetworkConnection -> {
|
||||
canReachServer.set(false)
|
||||
task.markAsFailedOrRetry(exception, 0)
|
||||
}
|
||||
(exception.isLimitExceededError()) -> {
|
||||
task.markAsFailedOrRetry(exception, exception.getRetryDelay(3_000))
|
||||
}
|
||||
exception is CancellationException -> {
|
||||
Timber.v("## $task has been cancelled, try next task")
|
||||
}
|
||||
else -> {
|
||||
Timber.v("## un-retryable error for $task, try next task")
|
||||
// this task is in error, check next one?
|
||||
task.onTaskFailed()
|
||||
}
|
||||
}
|
||||
}
|
||||
markAsFinished(task)
|
||||
}
|
||||
markAsFinished(task)
|
||||
}
|
||||
|
||||
private suspend fun QueuedTask.markAsFailedOrRetry(failure: Throwable, retryDelay: Long) {
|
||||
|
@@ -19,8 +19,8 @@ package org.matrix.android.sdk.internal.session.sync
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import io.realm.Realm
|
||||
import org.matrix.android.sdk.api.MatrixConfiguration
|
||||
import org.matrix.android.sdk.api.extensions.measureMetric
|
||||
import org.matrix.android.sdk.api.extensions.measureSpan
|
||||
import org.matrix.android.sdk.api.extensions.measureTransaction
|
||||
import org.matrix.android.sdk.api.metrics.SyncDurationMetricPlugin
|
||||
import org.matrix.android.sdk.api.session.pushrules.PushRuleService
|
||||
import org.matrix.android.sdk.api.session.pushrules.RuleScope
|
||||
@@ -71,7 +71,7 @@ internal class SyncResponseHandler @Inject constructor(
|
||||
val isInitialSync = fromToken == null
|
||||
Timber.v("Start handling sync, is InitialSync: $isInitialSync")
|
||||
|
||||
relevantPlugins.measureMetric {
|
||||
relevantPlugins.measureTransaction {
|
||||
startCryptoService(isInitialSync)
|
||||
|
||||
// Handle the to device events before the room ones
|
||||
|
@@ -17,6 +17,7 @@
|
||||
package im.vector.app.features.analytics.metrics
|
||||
|
||||
import im.vector.app.features.analytics.metrics.sentry.SentryDownloadDeviceKeysMetrics
|
||||
import im.vector.app.features.analytics.metrics.sentry.SentrySendServiceMetrics
|
||||
import im.vector.app.features.analytics.metrics.sentry.SentrySyncDurationMetrics
|
||||
import org.matrix.android.sdk.api.metrics.MetricPlugin
|
||||
import javax.inject.Inject
|
||||
@@ -29,9 +30,10 @@ import javax.inject.Singleton
|
||||
data class VectorPlugins @Inject constructor(
|
||||
val sentryDownloadDeviceKeysMetrics: SentryDownloadDeviceKeysMetrics,
|
||||
val sentrySyncDurationMetrics: SentrySyncDurationMetrics,
|
||||
val sentrySendServiceMetrics: SentrySendServiceMetrics
|
||||
) {
|
||||
/**
|
||||
* Returns [List] of all [MetricPlugin] hold by this class.
|
||||
*/
|
||||
fun plugins(): List<MetricPlugin> = listOf(sentryDownloadDeviceKeysMetrics, sentrySyncDurationMetrics)
|
||||
fun plugins(): List<MetricPlugin> = listOf(sentryDownloadDeviceKeysMetrics, sentrySyncDurationMetrics, sentrySendServiceMetrics)
|
||||
}
|
||||
|
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) 2022 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.app.features.analytics.metrics.sentry
|
||||
|
||||
import io.sentry.ISpan
|
||||
import io.sentry.ITransaction
|
||||
import io.sentry.Sentry
|
||||
import io.sentry.SpanStatus
|
||||
import org.matrix.android.sdk.api.metrics.SendServiceMetricPlugin
|
||||
import java.util.EmptyStackException
|
||||
import java.util.Stack
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Sentry based implementation of SyncDurationMetricPlugin.
|
||||
*/
|
||||
class SentrySendServiceMetrics @Inject constructor() : SendServiceMetricPlugin {
|
||||
private var transaction: ITransaction? = null
|
||||
|
||||
// Stacks to keep spans in LIFO order.
|
||||
private var spans: Stack<ISpan> = Stack()
|
||||
|
||||
/**
|
||||
* Starts the span for a sub-task.
|
||||
*
|
||||
* @param operation Name of the new span.
|
||||
* @param description Description of the new span.
|
||||
*
|
||||
* @throws IllegalStateException if this is called without starting a transaction ie. `measureSpan` must be called within `measureMetric`.
|
||||
*/
|
||||
override fun startSpan(operation: String, description: String) {
|
||||
if (Sentry.isEnabled()) {
|
||||
val span = Sentry.getSpan() ?: transaction
|
||||
span?.let {
|
||||
val innerSpan = it.startChild(operation, description)
|
||||
spans.push(innerSpan)
|
||||
logTransaction("Sentry span started: operation=[$operation], description=[$description]")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun finishSpan() {
|
||||
try {
|
||||
spans.pop()
|
||||
} catch (e: EmptyStackException) {
|
||||
null
|
||||
}?.finish()
|
||||
logTransaction("Sentry span finished")
|
||||
}
|
||||
|
||||
override fun startTransaction() {
|
||||
if (Sentry.isEnabled()) {
|
||||
transaction = Sentry.startTransaction("SendService", "action.handle", true)
|
||||
logTransaction("Sentry transaction started")
|
||||
}
|
||||
}
|
||||
|
||||
override fun finishTransaction() {
|
||||
transaction?.finish()
|
||||
logTransaction("Sentry transaction finished")
|
||||
}
|
||||
|
||||
override fun onError(throwable: Throwable) {
|
||||
try {
|
||||
spans.peek()
|
||||
} catch (e: EmptyStackException) {
|
||||
null
|
||||
}?.apply {
|
||||
this.throwable = throwable
|
||||
this.status = SpanStatus.INTERNAL_ERROR
|
||||
} ?: transaction?.apply {
|
||||
this.throwable = throwable
|
||||
this.status = SpanStatus.INTERNAL_ERROR
|
||||
}
|
||||
logTransaction("Sentry transaction encountered error ${throwable.message}")
|
||||
}
|
||||
}
|
@@ -44,10 +44,12 @@ class SentrySyncDurationMetrics @Inject constructor() : SyncDurationMetricPlugin
|
||||
*/
|
||||
override fun startSpan(operation: String, description: String) {
|
||||
if (Sentry.isEnabled()) {
|
||||
val span = Sentry.getSpan() ?: throw IllegalStateException("measureSpan block must be called within measureMetric")
|
||||
val innerSpan = span.startChild(operation, description)
|
||||
spans.push(innerSpan)
|
||||
logTransaction("Sentry span started: operation=[$operation], description=[$description]")
|
||||
val span = Sentry.getSpan()
|
||||
span?.let {
|
||||
val innerSpan = it.startChild(operation, description)
|
||||
spans.push(innerSpan)
|
||||
logTransaction("Sentry span started: operation=[$operation], description=[$description]")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user