From 1ca9952de81feeae1333872a7741119d6d8e7814 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Mon, 8 Jul 2024 01:54:37 +0200 Subject: [PATCH] perf: Cache latest announcements for constant access time --- .../repository/AnnouncementRepository.kt | 60 +++++++++++-------- .../services/AnnouncementService.kt | 10 ++-- 2 files changed, 42 insertions(+), 28 deletions(-) diff --git a/src/main/kotlin/app/revanced/api/configuration/repository/AnnouncementRepository.kt b/src/main/kotlin/app/revanced/api/configuration/repository/AnnouncementRepository.kt index 95c4daf..c095669 100644 --- a/src/main/kotlin/app/revanced/api/configuration/repository/AnnouncementRepository.kt +++ b/src/main/kotlin/app/revanced/api/configuration/repository/AnnouncementRepository.kt @@ -1,7 +1,6 @@ package app.revanced.api.configuration.repository import app.revanced.api.configuration.schema.APIAnnouncement -import app.revanced.api.configuration.schema.APIResponseAnnouncementId import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.awaitAll import kotlinx.coroutines.runBlocking @@ -10,7 +9,6 @@ import org.jetbrains.exposed.dao.IntEntity import org.jetbrains.exposed.dao.IntEntityClass import org.jetbrains.exposed.dao.id.EntityID import org.jetbrains.exposed.dao.id.IntIdTable -import org.jetbrains.exposed.dao.load import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.kotlin.datetime.CurrentDateTime import org.jetbrains.exposed.sql.kotlin.datetime.datetime @@ -18,10 +16,26 @@ import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransacti import org.jetbrains.exposed.sql.transactions.experimental.suspendedTransactionAsync internal class AnnouncementRepository { + // This is better than doing a maxByOrNull { it.id }. + private var latestAnnouncement: Announcement? = null + private val latestAnnouncementByChannel = mutableMapOf() + + private fun updateLatestAnnouncement(new: Announcement) { + if (latestAnnouncement?.id?.value == new.id.value) { + latestAnnouncement = new + latestAnnouncementByChannel[new.channel ?: return] = new + } + } + init { runBlocking { transaction { SchemaUtils.create(Announcements, Attachments) + + // Initialize the latest announcement. + latestAnnouncement = Announcement.all().onEach { + latestAnnouncementByChannel[it.channel ?: return@onEach] = it + }.maxByOrNull { it.id } ?: return@transaction } } } @@ -38,29 +52,27 @@ internal class AnnouncementRepository { val announcement = Announcement.findById(id) ?: return@transaction announcement.delete() - } - // TODO: These are inefficient, but I'm not sure how to make them more efficient. + // In case the latest announcement was deleted, query the new latest announcement again. + if (latestAnnouncement?.id?.value == id) { + latestAnnouncement = Announcement.all().maxByOrNull { it.id } - suspend fun latest() = transaction { - Announcement.all().maxByOrNull { it.id }?.load(Announcement::attachments) - } - - suspend fun latest(channel: String) = transaction { - Announcement.find { Announcements.channel eq channel }.maxByOrNull { it.id }?.load(Announcement::attachments) - } - - suspend fun latestId() = transaction { - Announcement.all().maxByOrNull { it.id }?.id?.value?.let { - APIResponseAnnouncementId(it) + // If no latest announcement was found, remove it from the channel map. + if (latestAnnouncement == null) { + latestAnnouncementByChannel.remove(announcement.channel) + } else { + latestAnnouncementByChannel[latestAnnouncement!!.channel ?: return@transaction] = latestAnnouncement!! + } } } - suspend fun latestId(channel: String) = transaction { - Announcement.find { Announcements.channel eq channel }.maxByOrNull { it.id }?.id?.value?.let { - APIResponseAnnouncementId(it) - } - } + fun latest() = latestAnnouncement + + fun latest(channel: String) = latestAnnouncementByChannel[channel] + + fun latestId() = latest()?.id?.value + + fun latestId(channel: String) = latest(channel)?.id?.value suspend fun archive( id: Int, @@ -68,13 +80,13 @@ internal class AnnouncementRepository { ) = transaction { Announcement.findByIdAndUpdate(id) { it.archivedAt = archivedAt ?: java.time.LocalDateTime.now().toKotlinLocalDateTime() - } + }?.also(::updateLatestAnnouncement) } suspend fun unarchive(id: Int) = transaction { Announcement.findByIdAndUpdate(id) { it.archivedAt = null - } + }?.also(::updateLatestAnnouncement) } suspend fun new(new: APIAnnouncement) = transaction { @@ -94,7 +106,7 @@ internal class AnnouncementRepository { } } }.awaitAll() - } + }.also(::updateLatestAnnouncement) } suspend fun update(id: Int, new: APIAnnouncement) = transaction { @@ -120,7 +132,7 @@ internal class AnnouncementRepository { } } }.awaitAll() - } + }?.also(::updateLatestAnnouncement) } private suspend fun transaction(statement: suspend Transaction.() -> T) = diff --git a/src/main/kotlin/app/revanced/api/configuration/services/AnnouncementService.kt b/src/main/kotlin/app/revanced/api/configuration/services/AnnouncementService.kt index b0142e5..ae4cf7e 100644 --- a/src/main/kotlin/app/revanced/api/configuration/services/AnnouncementService.kt +++ b/src/main/kotlin/app/revanced/api/configuration/services/AnnouncementService.kt @@ -9,11 +9,11 @@ import kotlinx.datetime.LocalDateTime internal class AnnouncementService( private val announcementRepository: AnnouncementRepository, ) { - suspend fun latestId(channel: String): APIResponseAnnouncementId? = announcementRepository.latestId(channel) - suspend fun latestId(): APIResponseAnnouncementId? = announcementRepository.latestId() + fun latestId(channel: String): APIResponseAnnouncementId? = announcementRepository.latestId(channel)?.toApi() + fun latestId(): APIResponseAnnouncementId? = announcementRepository.latestId()?.toApi() - suspend fun latest(channel: String) = announcementRepository.latest(channel)?.toApi() - suspend fun latest() = announcementRepository.latest()?.toApi() + fun latest(channel: String) = announcementRepository.latest(channel)?.toApi() + fun latest() = announcementRepository.latest()?.toApi() suspend fun all(channel: String) = announcementRepository.all(channel).map { it.toApi() } suspend fun all() = announcementRepository.all().map { it.toApi() } @@ -45,4 +45,6 @@ internal class AnnouncementService( archivedAt, level, ) + + private fun Int.toApi() = APIResponseAnnouncementId(this) }