Merge pull request #3018 from vector-im/feature/bma/improve_perf

Fix issue with Realm in UpdateTrustWorker
This commit is contained in:
Benoit Marty 2021-03-18 10:27:54 +01:00 committed by GitHub
commit ef6e57066b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 66 additions and 63 deletions

View file

@ -8,6 +8,7 @@ Improvements 🙌:
- -
Bugfix 🐛: Bugfix 🐛:
- Fix regression in UpdateTrustWorker (introduced in 1.1.2)
- Timeline : Fix ripple effect on text item and fix background color of separators. - Timeline : Fix ripple effect on text item and fix background color of separators.
Translations 🗣: Translations 🗣:

View file

@ -39,7 +39,7 @@ internal class GossipingWorkManager @Inject constructor(
.setConstraints(WorkManagerProvider.workConstraints) .setConstraints(WorkManagerProvider.workConstraints)
.startChain(startChain) .startChain(startChain)
.setInputData(data) .setInputData(data)
.setBackoffCriteria(BackoffPolicy.LINEAR, 10_000L, TimeUnit.MILLISECONDS) .setBackoffCriteria(BackoffPolicy.LINEAR, WorkManagerProvider.BACKOFF_DELAY_MILLIS, TimeUnit.MILLISECONDS)
.build() .build()
} }

View file

@ -43,6 +43,7 @@ import org.matrix.android.sdk.internal.task.TaskThread
import org.matrix.android.sdk.internal.task.configureWith import org.matrix.android.sdk.internal.task.configureWith
import org.matrix.android.sdk.internal.util.JsonCanonicalizer import org.matrix.android.sdk.internal.util.JsonCanonicalizer
import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
import org.matrix.android.sdk.internal.util.logLimit
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
import org.matrix.olm.OlmPkSigning import org.matrix.olm.OlmPkSigning
import org.matrix.olm.OlmUtility import org.matrix.olm.OlmUtility
@ -750,7 +751,7 @@ internal class DefaultCrossSigningService @Inject constructor(
} }
override fun onUsersDeviceUpdate(userIds: List<String>) { override fun onUsersDeviceUpdate(userIds: List<String>) {
Timber.d("## CrossSigning - onUsersDeviceUpdate for ${userIds.size} users: $userIds") Timber.d("## CrossSigning - onUsersDeviceUpdate for users: ${userIds.logLimit()}")
val workerParams = UpdateTrustWorker.Params( val workerParams = UpdateTrustWorker.Params(
sessionId = sessionId, sessionId = sessionId,
filename = updateTrustWorkerDataRepository.createParam(userIds) filename = updateTrustWorkerDataRepository.createParam(userIds)
@ -759,7 +760,7 @@ internal class DefaultCrossSigningService @Inject constructor(
val workRequest = workManagerProvider.matrixOneTimeWorkRequestBuilder<UpdateTrustWorker>() val workRequest = workManagerProvider.matrixOneTimeWorkRequestBuilder<UpdateTrustWorker>()
.setInputData(workerData) .setInputData(workerData)
.setBackoffCriteria(BackoffPolicy.LINEAR, 2_000L, TimeUnit.MILLISECONDS) .setBackoffCriteria(BackoffPolicy.LINEAR, WorkManagerProvider.BACKOFF_DELAY_MILLIS, TimeUnit.MILLISECONDS)
.build() .build()
workManagerProvider.workManager workManagerProvider.workManager

View file

@ -97,22 +97,17 @@ internal class UpdateTrustWorker(context: Context,
// Unfortunately we don't have much info on what did exactly changed (is it the cross signing keys of that user, // Unfortunately we don't have much info on what did exactly changed (is it the cross signing keys of that user,
// or a new device?) So we check all again :/ // or a new device?) So we check all again :/
Timber.d("## CrossSigning - Updating trust for users: ${userList.logLimit()}") Timber.d("## CrossSigning - Updating trust for users: ${userList.logLimit()}")
updateTrust(userList)
Realm.getInstance(cryptoRealmConfiguration).use { cryptoRealm ->
Realm.getInstance(sessionRealmConfiguration).use {
updateTrust(userList, cryptoRealm)
}
}
} }
cleanup(params) cleanup(params)
return Result.success() return Result.success()
} }
private suspend fun updateTrust(userListParam: List<String>, private suspend fun updateTrust(userListParam: List<String>) {
cRealm: Realm) {
var userList = userListParam var userList = userListParam
var myCrossSigningInfo: MXCrossSigningInfo? = null var myCrossSigningInfo: MXCrossSigningInfo? = null
// First we check that the users MSK are trusted by mine // First we check that the users MSK are trusted by mine
// After that we check the trust chain for each devices of each users // After that we check the trust chain for each devices of each users
awaitTransaction(cryptoRealmConfiguration) { cryptoRealm -> awaitTransaction(cryptoRealmConfiguration) { cryptoRealm ->
@ -203,38 +198,43 @@ internal class UpdateTrustWorker(context: Context,
// So Cross Signing keys trust is updated, device trust is updated // So Cross Signing keys trust is updated, device trust is updated
// We can now update room shields? in the session DB? // We can now update room shields? in the session DB?
updateTrustStep2(userList, myCrossSigningInfo)
}
private suspend fun updateTrustStep2(userList: List<String>, myCrossSigningInfo: MXCrossSigningInfo?) {
Timber.d("## CrossSigning - Updating shields for impacted rooms...") Timber.d("## CrossSigning - Updating shields for impacted rooms...")
awaitTransaction(sessionRealmConfiguration) { sessionRealm -> awaitTransaction(sessionRealmConfiguration) { sessionRealm ->
sessionRealm.where(RoomMemberSummaryEntity::class.java) Realm.getInstance(cryptoRealmConfiguration).use { cryptoRealm ->
.`in`(RoomMemberSummaryEntityFields.USER_ID, userList.toTypedArray()) sessionRealm.where(RoomMemberSummaryEntity::class.java)
.distinct(RoomMemberSummaryEntityFields.ROOM_ID) .`in`(RoomMemberSummaryEntityFields.USER_ID, userList.toTypedArray())
.findAll() .distinct(RoomMemberSummaryEntityFields.ROOM_ID)
.map { it.roomId } .findAll()
.also { Timber.d("## CrossSigning - ... impacted rooms ${it.logLimit()}") } .map { it.roomId }
.forEach { roomId -> .also { Timber.d("## CrossSigning - ... impacted rooms ${it.logLimit()}") }
RoomSummaryEntity.where(sessionRealm, roomId) .forEach { roomId ->
.equalTo(RoomSummaryEntityFields.IS_ENCRYPTED, true) RoomSummaryEntity.where(sessionRealm, roomId)
.findFirst() .equalTo(RoomSummaryEntityFields.IS_ENCRYPTED, true)
?.let { roomSummary -> .findFirst()
Timber.d("## CrossSigning - Check shield state for room $roomId") ?.let { roomSummary ->
val allActiveRoomMembers = RoomMemberHelper(sessionRealm, roomId).getActiveRoomMemberIds() Timber.d("## CrossSigning - Check shield state for room $roomId")
try { val allActiveRoomMembers = RoomMemberHelper(sessionRealm, roomId).getActiveRoomMemberIds()
val updatedTrust = computeRoomShield( try {
myCrossSigningInfo, val updatedTrust = computeRoomShield(
cRealm, myCrossSigningInfo,
allActiveRoomMembers, cryptoRealm,
roomSummary allActiveRoomMembers,
) roomSummary
if (roomSummary.roomEncryptionTrustLevel != updatedTrust) { )
Timber.d("## CrossSigning - Shield change detected for $roomId -> $updatedTrust") if (roomSummary.roomEncryptionTrustLevel != updatedTrust) {
roomSummary.roomEncryptionTrustLevel = updatedTrust Timber.d("## CrossSigning - Shield change detected for $roomId -> $updatedTrust")
roomSummary.roomEncryptionTrustLevel = updatedTrust
}
} catch (failure: Throwable) {
Timber.e(failure)
} }
} catch (failure: Throwable) {
Timber.e(failure)
} }
} }
} }
} }
} }

View file

@ -45,7 +45,7 @@ internal class CrossSigningKeysMapper @Inject constructor(moshi: Moshi) {
return CryptoCrossSigningKey( return CryptoCrossSigningKey(
userId = userId ?: "", userId = userId ?: "",
keys = mapOf("ed25519:$pubKey" to pubKey), keys = mapOf("ed25519:$pubKey" to pubKey),
usages = keyInfo.usages.map { it }, usages = keyInfo.usages.toList(),
signatures = deserializeSignatures(keyInfo.signatures), signatures = deserializeSignatures(keyInfo.signatures),
trustLevel = keyInfo.trustLevelEntity?.let { trustLevel = keyInfo.trustLevelEntity?.let {
DeviceTrustLevel( DeviceTrustLevel(

View file

@ -183,7 +183,7 @@ internal class VerificationTransportRoomMessage(
val workRequest = workManagerProvider.matrixOneTimeWorkRequestBuilder<SendVerificationMessageWorker>() val workRequest = workManagerProvider.matrixOneTimeWorkRequestBuilder<SendVerificationMessageWorker>()
.setConstraints(WorkManagerProvider.workConstraints) .setConstraints(WorkManagerProvider.workConstraints)
.setInputData(workerParams) .setInputData(workerParams)
.setBackoffCriteria(BackoffPolicy.LINEAR, 2_000L, TimeUnit.MILLISECONDS) .setBackoffCriteria(BackoffPolicy.LINEAR, WorkManagerProvider.BACKOFF_DELAY_MILLIS, TimeUnit.MILLISECONDS)
.build() .build()
workManagerProvider.workManager workManagerProvider.workManager
@ -280,7 +280,7 @@ internal class VerificationTransportRoomMessage(
val workRequest = workManagerProvider.matrixOneTimeWorkRequestBuilder<SendVerificationMessageWorker>() val workRequest = workManagerProvider.matrixOneTimeWorkRequestBuilder<SendVerificationMessageWorker>()
.setConstraints(WorkManagerProvider.workConstraints) .setConstraints(WorkManagerProvider.workConstraints)
.setInputData(workerParams) .setInputData(workerParams)
.setBackoffCriteria(BackoffPolicy.LINEAR, 2_000L, TimeUnit.MILLISECONDS) .setBackoffCriteria(BackoffPolicy.LINEAR, WorkManagerProvider.BACKOFF_DELAY_MILLIS, TimeUnit.MILLISECONDS)
.build() .build()
return workManagerProvider.workManager return workManagerProvider.workManager
.beginUniqueWork(uniqueQueueName(), ExistingWorkPolicy.APPEND_OR_REPLACE, workRequest) .beginUniqueWork(uniqueQueueName(), ExistingWorkPolicy.APPEND_OR_REPLACE, workRequest)

View file

@ -23,6 +23,7 @@ import androidx.work.NetworkType
import androidx.work.OneTimeWorkRequestBuilder import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.PeriodicWorkRequestBuilder import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager import androidx.work.WorkManager
import androidx.work.WorkRequest
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
@ -69,6 +70,7 @@ internal class WorkManagerProvider @Inject constructor(
.setRequiredNetworkType(NetworkType.CONNECTED) .setRequiredNetworkType(NetworkType.CONNECTED)
.build() .build()
const val BACKOFF_DELAY = 10_000L // Use min value, smaller value will be ignored
const val BACKOFF_DELAY_MILLIS = WorkRequest.MIN_BACKOFF_MILLIS
} }
} }

View file

@ -96,7 +96,7 @@ internal class DefaultPushersService @Inject constructor(
val request = workManagerProvider.matrixOneTimeWorkRequestBuilder<AddHttpPusherWorker>() val request = workManagerProvider.matrixOneTimeWorkRequestBuilder<AddHttpPusherWorker>()
.setConstraints(WorkManagerProvider.workConstraints) .setConstraints(WorkManagerProvider.workConstraints)
.setInputData(WorkerParamsFactory.toData(params)) .setInputData(WorkerParamsFactory.toData(params))
.setBackoffCriteria(BackoffPolicy.LINEAR, 10_000L, TimeUnit.MILLISECONDS) .setBackoffCriteria(BackoffPolicy.LINEAR, WorkManagerProvider.BACKOFF_DELAY_MILLIS, TimeUnit.MILLISECONDS)
.build() .build()
workManagerProvider.workManager.enqueue(request) workManagerProvider.workManager.enqueue(request)
return request.id return request.id

View file

@ -318,7 +318,7 @@ internal class DefaultSendService @AssistedInject constructor(
.setConstraints(WorkManagerProvider.workConstraints) .setConstraints(WorkManagerProvider.workConstraints)
.startChain(true) .startChain(true)
.setInputData(uploadWorkData) .setInputData(uploadWorkData)
.setBackoffCriteria(BackoffPolicy.LINEAR, WorkManagerProvider.BACKOFF_DELAY, TimeUnit.MILLISECONDS) .setBackoffCriteria(BackoffPolicy.LINEAR, WorkManagerProvider.BACKOFF_DELAY_MILLIS, TimeUnit.MILLISECONDS)
.build() .build()
} }
@ -332,7 +332,7 @@ internal class DefaultSendService @AssistedInject constructor(
// .setConstraints(WorkManagerProvider.workConstraints) // .setConstraints(WorkManagerProvider.workConstraints)
.startChain(false) .startChain(false)
.setInputData(workData) .setInputData(workData)
.setBackoffCriteria(BackoffPolicy.LINEAR, WorkManagerProvider.BACKOFF_DELAY, TimeUnit.MILLISECONDS) .setBackoffCriteria(BackoffPolicy.LINEAR, WorkManagerProvider.BACKOFF_DELAY_MILLIS, TimeUnit.MILLISECONDS)
.build() .build()
} }
} }

View file

@ -50,7 +50,7 @@ internal class TimelineSendEventWorkCommon @Inject constructor(
.setConstraints(WorkManagerProvider.workConstraints) .setConstraints(WorkManagerProvider.workConstraints)
.startChain(startChain) .startChain(startChain)
.setInputData(data) .setInputData(data)
.setBackoffCriteria(BackoffPolicy.LINEAR, BACKOFF_DELAY, TimeUnit.MILLISECONDS) .setBackoffCriteria(BackoffPolicy.LINEAR, WorkManagerProvider.BACKOFF_DELAY_MILLIS, TimeUnit.MILLISECONDS)
.build() .build()
} }
@ -60,6 +60,5 @@ internal class TimelineSendEventWorkCommon @Inject constructor(
companion object { companion object {
private const val SEND_WORK = "SEND_WORK" private const val SEND_WORK = "SEND_WORK"
private const val BACKOFF_DELAY = 10_000L
} }
} }

View file

@ -65,7 +65,7 @@ internal class FileInitialSyncStatusRepository(directory: File) : InitialSyncSta
val state = cache?.step ?: InitialSyncStatus.STEP_INIT val state = cache?.step ?: InitialSyncStatus.STEP_INIT
return if (state >= InitialSyncStatus.STEP_DOWNLOADED return if (state >= InitialSyncStatus.STEP_DOWNLOADED
&& System.currentTimeMillis() > (cache?.downloadedDate ?: 0) + INIT_SYNC_FILE_LIFETIME) { && System.currentTimeMillis() > (cache?.downloadedDate ?: 0) + INIT_SYNC_FILE_LIFETIME) {
Timber.v("INIT_SYNC downloaded file is outdated, download it again") Timber.d("INIT_SYNC downloaded file is outdated, download it again")
// The downloaded file is outdated // The downloaded file is outdated
setStep(InitialSyncStatus.STEP_INIT) setStep(InitialSyncStatus.STEP_INIT)
InitialSyncStatus.STEP_INIT InitialSyncStatus.STEP_INIT

View file

@ -147,13 +147,13 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
if (numberOfChunks > 1) { if (numberOfChunks > 1) {
reportSubtask(reporter, InitSyncStep.ImportingAccountJoinedRooms, numberOfChunks, 0.6f) { reportSubtask(reporter, InitSyncStep.ImportingAccountJoinedRooms, numberOfChunks, 0.6f) {
val chunkSize = listSize / numberOfChunks val chunkSize = listSize / numberOfChunks
Timber.v("INIT_SYNC $listSize rooms to insert, split into $numberOfChunks sublists of $chunkSize items") Timber.d("INIT_SYNC $listSize rooms to insert, split into $numberOfChunks sublists of $chunkSize items")
// I cannot find a better way to chunk a map, so chunk the keys and then create new maps // I cannot find a better way to chunk a map, so chunk the keys and then create new maps
handlingStrategy.data.keys handlingStrategy.data.keys
.chunked(chunkSize) .chunked(chunkSize)
.forEachIndexed { index, roomIds -> .forEachIndexed { index, roomIds ->
val roomEntities = roomIds val roomEntities = roomIds
.also { Timber.v("INIT_SYNC insert ${roomIds.size} rooms") } .also { Timber.d("INIT_SYNC insert ${roomIds.size} rooms") }
.map { .map {
handleJoinedRoom( handleJoinedRoom(
realm = realm, realm = realm,

View file

@ -101,7 +101,7 @@ internal class DefaultSyncTask @Inject constructor(
val readTimeOut = (params.timeout + TIMEOUT_MARGIN).coerceAtLeast(TimeOutInterceptor.DEFAULT_LONG_TIMEOUT) val readTimeOut = (params.timeout + TIMEOUT_MARGIN).coerceAtLeast(TimeOutInterceptor.DEFAULT_LONG_TIMEOUT)
if (isInitialSync) { if (isInitialSync) {
Timber.v("INIT_SYNC with filter: ${requestParams["filter"]}") Timber.d("INIT_SYNC with filter: ${requestParams["filter"]}")
val initSyncStrategy = initialSyncStrategy val initSyncStrategy = initialSyncStrategy
logDuration("INIT_SYNC strategy: $initSyncStrategy") { logDuration("INIT_SYNC strategy: $initSyncStrategy") {
if (initSyncStrategy is InitialSyncStrategy.Optimized) { if (initSyncStrategy is InitialSyncStrategy.Optimized) {
@ -145,7 +145,7 @@ internal class DefaultSyncTask @Inject constructor(
val workingFile = File(workingDir, "initSync.json") val workingFile = File(workingDir, "initSync.json")
val status = initialSyncStatusRepository.getStep() val status = initialSyncStatusRepository.getStep()
if (workingFile.exists() && status >= InitialSyncStatus.STEP_DOWNLOADED) { if (workingFile.exists() && status >= InitialSyncStatus.STEP_DOWNLOADED) {
Timber.v("INIT_SYNC file is already here") Timber.d("INIT_SYNC file is already here")
reportSubtask(initialSyncProgressService, InitSyncStep.Downloading, 1, 0.3f) { reportSubtask(initialSyncProgressService, InitSyncStep.Downloading, 1, 0.3f) {
// Empty task // Empty task
} }
@ -204,7 +204,7 @@ internal class DefaultSyncTask @Inject constructor(
// Log some stats // Log some stats
val nbOfJoinedRooms = syncResponse.rooms?.join?.size ?: 0 val nbOfJoinedRooms = syncResponse.rooms?.join?.size ?: 0
val nbOfJoinedRoomsInFile = syncResponse.rooms?.join?.values?.count { it.ephemeral is LazyRoomSyncEphemeral.Stored } val nbOfJoinedRoomsInFile = syncResponse.rooms?.join?.values?.count { it.ephemeral is LazyRoomSyncEphemeral.Stored }
Timber.v("INIT_SYNC $nbOfJoinedRooms rooms, $nbOfJoinedRoomsInFile ephemeral stored into files") Timber.d("INIT_SYNC $nbOfJoinedRooms rooms, $nbOfJoinedRoomsInFile ephemeral stored into files")
logDuration("INIT_SYNC Database insertion") { logDuration("INIT_SYNC Database insertion") {
syncResponseHandler.handleResponse(syncResponse, null, initialSyncProgressService) syncResponseHandler.handleResponse(syncResponse, null, initialSyncProgressService)

View file

@ -106,7 +106,7 @@ internal class SyncWorker(context: Context,
val data = WorkerParamsFactory.toData(Params(sessionId, serverTimeout, 0L, false)) val data = WorkerParamsFactory.toData(Params(sessionId, serverTimeout, 0L, false))
val workRequest = workManagerProvider.matrixOneTimeWorkRequestBuilder<SyncWorker>() val workRequest = workManagerProvider.matrixOneTimeWorkRequestBuilder<SyncWorker>()
.setConstraints(WorkManagerProvider.workConstraints) .setConstraints(WorkManagerProvider.workConstraints)
.setBackoffCriteria(BackoffPolicy.LINEAR, 1_000, TimeUnit.MILLISECONDS) .setBackoffCriteria(BackoffPolicy.LINEAR, WorkManagerProvider.BACKOFF_DELAY_MILLIS, TimeUnit.MILLISECONDS)
.setInputData(data) .setInputData(data)
.build() .build()
workManagerProvider.workManager workManagerProvider.workManager
@ -118,7 +118,7 @@ internal class SyncWorker(context: Context,
val workRequest = workManagerProvider.matrixOneTimeWorkRequestBuilder<SyncWorker>() val workRequest = workManagerProvider.matrixOneTimeWorkRequestBuilder<SyncWorker>()
.setConstraints(WorkManagerProvider.workConstraints) .setConstraints(WorkManagerProvider.workConstraints)
.setInputData(data) .setInputData(data)
.setBackoffCriteria(BackoffPolicy.LINEAR, 1_000, TimeUnit.MILLISECONDS) .setBackoffCriteria(BackoffPolicy.LINEAR, WorkManagerProvider.BACKOFF_DELAY_MILLIS, TimeUnit.MILLISECONDS)
.setInitialDelay(delayInSeconds, TimeUnit.SECONDS) .setInitialDelay(delayInSeconds, TimeUnit.SECONDS)
.build() .build()

View file

@ -57,12 +57,12 @@ internal class SplitLazyRoomSyncEphemeralJsonAdapter(
} }
val limit = syncStrategy.minSizeToStoreInFile val limit = syncStrategy.minSizeToStoreInFile
return if (json.length > limit) { return if (json.length > limit) {
Timber.v("INIT_SYNC $path content length: ${json.length} copy to a file") Timber.d("INIT_SYNC $path content length: ${json.length} copy to a file")
// Copy the source to a file // Copy the source to a file
roomSyncEphemeralTemporaryStore.write(roomId, json) roomSyncEphemeralTemporaryStore.write(roomId, json)
LazyRoomSyncEphemeral.Stored LazyRoomSyncEphemeral.Stored
} else { } else {
Timber.v("INIT_SYNC $path content length: ${json.length} parse it now") Timber.d("INIT_SYNC $path content length: ${json.length} parse it now")
val roomSync = delegate.fromJson(json) ?: return null val roomSync = delegate.fromJson(json) ?: return null
LazyRoomSyncEphemeral.Parsed(roomSync) LazyRoomSyncEphemeral.Parsed(roomSync)
} }

View file

@ -33,9 +33,9 @@ internal class InitialSyncResponseParser @Inject constructor(
fun parse(syncStrategy: InitialSyncStrategy.Optimized, workingFile: File): SyncResponse { fun parse(syncStrategy: InitialSyncStrategy.Optimized, workingFile: File): SyncResponse {
val syncResponseLength = workingFile.length().toInt() val syncResponseLength = workingFile.length().toInt()
Timber.v("INIT_SYNC Sync file size is $syncResponseLength bytes") Timber.d("INIT_SYNC Sync file size is $syncResponseLength bytes")
val shouldSplit = syncResponseLength >= syncStrategy.minSizeToSplit val shouldSplit = syncResponseLength >= syncStrategy.minSizeToSplit
Timber.v("INIT_SYNC should split in several files: $shouldSplit") Timber.d("INIT_SYNC should split in several files: $shouldSplit")
return getMoshi(syncStrategy, shouldSplit) return getMoshi(syncStrategy, shouldSplit)
.adapter(SyncResponse::class.java) .adapter(SyncResponse::class.java)
.fromJson(workingFile.source().buffer())!! .fromJson(workingFile.source().buffer())!!

View file

@ -33,13 +33,13 @@ internal fun <T> Collection<T>.logLimit(maxQuantity: Int = 5): String {
internal suspend fun <T> logDuration(message: String, internal suspend fun <T> logDuration(message: String,
block: suspend () -> T): T { block: suspend () -> T): T {
Timber.v("$message -- BEGIN") Timber.d("$message -- BEGIN")
val start = System.currentTimeMillis() val start = System.currentTimeMillis()
val result = logRamUsage(message) { val result = logRamUsage(message) {
block() block()
} }
val duration = System.currentTimeMillis() - start val duration = System.currentTimeMillis() - start
Timber.v("$message -- END duration: $duration ms") Timber.d("$message -- END duration: $duration ms")
return result return result
} }
@ -50,12 +50,12 @@ internal suspend fun <T> logRamUsage(message: String, block: suspend () -> T): T
runtime.gc() runtime.gc()
val freeMemoryInMb = runtime.freeMemory() / 1048576L val freeMemoryInMb = runtime.freeMemory() / 1048576L
val usedMemInMBStart = runtime.totalMemory() / 1048576L - freeMemoryInMb val usedMemInMBStart = runtime.totalMemory() / 1048576L - freeMemoryInMb
Timber.v("$message -- BEGIN (free memory: $freeMemoryInMb MB)") Timber.d("$message -- BEGIN (free memory: $freeMemoryInMb MB)")
val result = block() val result = block()
runtime.gc() runtime.gc()
val usedMemInMBEnd = (runtime.totalMemory() - runtime.freeMemory()) / 1048576L val usedMemInMBEnd = (runtime.totalMemory() - runtime.freeMemory()) / 1048576L
val usedMemInMBDiff = usedMemInMBEnd - usedMemInMBStart val usedMemInMBDiff = usedMemInMBEnd - usedMemInMBStart
Timber.v("$message -- END RAM usage: $usedMemInMBDiff MB") Timber.d("$message -- END RAM usage: $usedMemInMBDiff MB")
result result
} else { } else {
block() block()