From 4d1307f14615cb4f506620834e2c5d5a635a8a17 Mon Sep 17 00:00:00 2001 From: schroda <50052685+schroda@users.noreply.github.com> Date: Fri, 20 Oct 2023 00:18:58 +0200 Subject: [PATCH] Fix update subscription returning stale data In case a manga was already loaded via the data loader, the cached data will get used. Due to this, the update status did not return the updated manga data, but instead, stale data --- .../tachidesk/graphql/types/MangaType.kt | 18 ++++++++ .../tachidesk/graphql/types/UpdateType.kt | 45 ++++++++++++++++++- 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/MangaType.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/MangaType.kt index d006f82ee..75858e3dd 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/MangaType.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/MangaType.kt @@ -41,6 +41,24 @@ class MangaType( var lastFetchedAt: Long?, // todo var chaptersLastFetchedAt: Long?, // todo ) : Node { + companion object { + fun clearCacheFor( + mangaId: Int, + dataFetchingEnvironment: DataFetchingEnvironment, + ) { + dataFetchingEnvironment.getDataLoader, MangaNodeList>("MangaDataLoader").clear(listOf(mangaId)) + dataFetchingEnvironment.getDataLoader, MangaNodeList>("MangaForIdsDataLoader").clear(listOf(mangaId)) + dataFetchingEnvironment.getDataLoader("DownloadedChapterCountForMangaDataLoader").clear(mangaId) + dataFetchingEnvironment.getDataLoader("UnreadChapterCountForMangaDataLoader").clear(mangaId) + dataFetchingEnvironment.getDataLoader("LastReadChapterForMangaDataLoader").clear(mangaId) + dataFetchingEnvironment.getDataLoader( + "ChaptersForMangaDataLoader", + ).clear(mangaId) + dataFetchingEnvironment.getDataLoader("MangaMetaDataLoader").clear(mangaId) + dataFetchingEnvironment.getDataLoader("CategoriesForMangaDataLoader").clear(mangaId) + } + } + constructor(row: ResultRow) : this( row[MangaTable.id].value, row[MangaTable.sourceReference], diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/UpdateType.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/UpdateType.kt index e91291d11..dbf691f26 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/UpdateType.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/UpdateType.kt @@ -8,6 +8,8 @@ import suwayomi.tachidesk.manga.impl.update.JobStatus import suwayomi.tachidesk.manga.impl.update.UpdateStatus import java.util.concurrent.CompletableFuture +private val jobStatusToMangaIdsToCacheClearedStatus = mutableMapOf>() + class UpdateStatus( val isRunning: Boolean, val skippedCategories: UpdateStatusCategoryType, @@ -24,8 +26,22 @@ class UpdateStatus( updatingCategories = UpdateStatusCategoryType(status.categoryStatusMap[CategoryUpdateStatus.UPDATING]?.map { it.id }.orEmpty()), pendingJobs = UpdateStatusType(status.mangaStatusMap[JobStatus.PENDING]?.map { it.id }.orEmpty()), runningJobs = UpdateStatusType(status.mangaStatusMap[JobStatus.RUNNING]?.map { it.id }.orEmpty()), - completeJobs = UpdateStatusType(status.mangaStatusMap[JobStatus.COMPLETE]?.map { it.id }.orEmpty()), - failedJobs = UpdateStatusType(status.mangaStatusMap[JobStatus.FAILED]?.map { it.id }.orEmpty()), + completeJobs = + UpdateStatusType( + status.mangaStatusMap[JobStatus.COMPLETE]?.map { + it.id + }.orEmpty(), + JobStatus.COMPLETE, + status.running, + true, + ), + failedJobs = + UpdateStatusType( + status.mangaStatusMap[JobStatus.FAILED]?.map { it.id }.orEmpty(), + JobStatus.FAILED, + status.running, + true, + ), skippedJobs = UpdateStatusType(status.mangaStatusMap[JobStatus.SKIPPED]?.map { it.id }.orEmpty()), ) } @@ -42,8 +58,33 @@ class UpdateStatusCategoryType( class UpdateStatusType( @get:GraphQLIgnore val mangaIds: List, + private val jobStatus: JobStatus? = null, + private val isRunning: Boolean = false, + private val clearCache: Boolean = false, ) { fun mangas(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture { + val resetClearedMangaIds = !isRunning && clearCache && jobStatus != null + if (resetClearedMangaIds) { + jobStatusToMangaIdsToCacheClearedStatus[jobStatus]?.clear() + } + + if (isRunning && clearCache && jobStatus != null) { + val cacheClearedForMangaIds = + jobStatusToMangaIdsToCacheClearedStatus.getOrPut( + jobStatus, + ) { emptyMap().toMutableMap() } + + mangaIds.forEach { + if (cacheClearedForMangaIds[it] == true) { + return@forEach + } + + MangaType.clearCacheFor(it, dataFetchingEnvironment) + + cacheClearedForMangaIds[it] = true + } + } + return dataFetchingEnvironment.getValueFromDataLoader, MangaNodeList>("MangaForIdsDataLoader", mangaIds) } }