mirror of
https://github.com/Suwayomi/Tachidesk.git
synced 2025-12-11 23:32:05 +01:00
Fix/initial scheduling of global update (#1416)
* Fix setting initial global update delay In case no update has run yet, and the "last automated update" defaulted to 0, the calculation always resulted in a multiple of the interval. This resulted for e.g., interval 24, to always be scheduled at 00:00. For e.g., interval 6, it was always one of the following times: 00:00, 06:00, 12:00, 18:00; depending on the current system time. * Delete the existing "last automated update time" So that the update will be triggered based on the time the server got started again after the update. * Extract migrations into separate functions * Cleanup migration execution logic
This commit is contained in:
@@ -8,6 +8,8 @@ import suwayomi.tachidesk.manga.model.dataclass.MangaDataClass
|
|||||||
interface IUpdater {
|
interface IUpdater {
|
||||||
fun getLastUpdateTimestamp(): Long
|
fun getLastUpdateTimestamp(): Long
|
||||||
|
|
||||||
|
fun deleteLastAutomatedUpdateTimestamp()
|
||||||
|
|
||||||
fun addCategoriesToUpdateQueue(
|
fun addCategoriesToUpdateQueue(
|
||||||
categories: List<CategoryDataClass>,
|
categories: List<CategoryDataClass>,
|
||||||
clear: Boolean?,
|
clear: Boolean?,
|
||||||
|
|||||||
@@ -116,10 +116,24 @@ class Updater : IUpdater {
|
|||||||
|
|
||||||
override fun getLastUpdateTimestamp(): Long = preferences.getLong(lastUpdateKey, 0)
|
override fun getLastUpdateTimestamp(): Long = preferences.getLong(lastUpdateKey, 0)
|
||||||
|
|
||||||
|
fun saveLastUpdateTimestamp() {
|
||||||
|
preferences.edit().putLong(lastUpdateKey, System.currentTimeMillis()).apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getLastAutomatedUpdateTimestamp(): Long = preferences.getLong(lastAutomatedUpdateKey, 0)
|
||||||
|
|
||||||
|
fun saveLastAutomatedUpdateTimestamp() {
|
||||||
|
preferences.edit().putLong(lastAutomatedUpdateKey, System.currentTimeMillis()).apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deleteLastAutomatedUpdateTimestamp() {
|
||||||
|
preferences.edit().remove(lastAutomatedUpdateKey).apply()
|
||||||
|
}
|
||||||
|
|
||||||
private fun autoUpdateTask() {
|
private fun autoUpdateTask() {
|
||||||
try {
|
try {
|
||||||
val lastAutomatedUpdate = preferences.getLong(lastAutomatedUpdateKey, 0)
|
val lastAutomatedUpdate = getLastAutomatedUpdateTimestamp()
|
||||||
preferences.edit().putLong(lastAutomatedUpdateKey, System.currentTimeMillis()).apply()
|
saveLastAutomatedUpdateTimestamp()
|
||||||
|
|
||||||
if (getStatus().isRunning) {
|
if (getStatus().isRunning) {
|
||||||
logger.debug { "Global update is already in progress" }
|
logger.debug { "Global update is already in progress" }
|
||||||
@@ -150,12 +164,23 @@ class Updater : IUpdater {
|
|||||||
serverConfig.globalUpdateInterval.value.hours
|
serverConfig.globalUpdateInterval.value.hours
|
||||||
.coerceAtLeast(6.hours)
|
.coerceAtLeast(6.hours)
|
||||||
.inWholeMilliseconds
|
.inWholeMilliseconds
|
||||||
val lastAutomatedUpdate = preferences.getLong(lastAutomatedUpdateKey, 0)
|
val lastAutomatedUpdate = getLastAutomatedUpdateTimestamp()
|
||||||
val timeToNextExecution = (updateInterval - (System.currentTimeMillis() - lastAutomatedUpdate)).mod(updateInterval)
|
val isInitialScheduling = lastAutomatedUpdate == 0L
|
||||||
|
|
||||||
|
val timeToNextExecution =
|
||||||
|
if (!isInitialScheduling) {
|
||||||
|
(updateInterval - (System.currentTimeMillis() - lastAutomatedUpdate)).mod(updateInterval)
|
||||||
|
} else {
|
||||||
|
updateInterval
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isInitialScheduling) {
|
||||||
|
saveLastAutomatedUpdateTimestamp()
|
||||||
|
}
|
||||||
|
|
||||||
val wasPreviousUpdateTriggered =
|
val wasPreviousUpdateTriggered =
|
||||||
System.currentTimeMillis() - (
|
System.currentTimeMillis() - (
|
||||||
if (lastAutomatedUpdate > 0) lastAutomatedUpdate else System.currentTimeMillis()
|
if (!isInitialScheduling) lastAutomatedUpdate else System.currentTimeMillis()
|
||||||
) < updateInterval
|
) < updateInterval
|
||||||
if (!wasPreviousUpdateTriggered) {
|
if (!wasPreviousUpdateTriggered) {
|
||||||
GlobalScope.launch {
|
GlobalScope.launch {
|
||||||
@@ -316,7 +341,7 @@ class Updater : IUpdater {
|
|||||||
clear: Boolean?,
|
clear: Boolean?,
|
||||||
forceAll: Boolean,
|
forceAll: Boolean,
|
||||||
) {
|
) {
|
||||||
preferences.edit().putLong(lastUpdateKey, System.currentTimeMillis()).apply()
|
saveLastUpdateTimestamp()
|
||||||
|
|
||||||
if (clear == true) {
|
if (clear == true) {
|
||||||
reset()
|
reset()
|
||||||
|
|||||||
@@ -3,10 +3,13 @@ package suwayomi.tachidesk.server
|
|||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||||
|
import suwayomi.tachidesk.manga.impl.update.IUpdater
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.prefs.Preferences
|
import java.util.prefs.Preferences
|
||||||
|
import kotlin.collections.isNotEmpty
|
||||||
|
import kotlin.collections.orEmpty
|
||||||
|
|
||||||
private fun migratePreferences(
|
private fun migratePreferences(
|
||||||
parent: String?,
|
parent: String?,
|
||||||
@@ -43,9 +46,50 @@ private fun migratePreferences(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const val MIGRATION_VERSION = 1
|
private fun migratePreferencesToNewXmlFileBasedStorage() {
|
||||||
|
// Migrate from old preferences api
|
||||||
|
val prefRootNode = "suwayomi/tachidesk"
|
||||||
|
val isMigrationRequired = Preferences.userRoot().nodeExists(prefRootNode)
|
||||||
|
if (isMigrationRequired) {
|
||||||
|
val preferences = Preferences.userRoot().node(prefRootNode)
|
||||||
|
migratePreferences(null, preferences)
|
||||||
|
preferences.removeNode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun migrateMangaDownloadDir(applicationDirs: ApplicationDirs) {
|
||||||
|
val oldMangaDownloadDir = File(applicationDirs.downloadsRoot)
|
||||||
|
val newMangaDownloadDir = File(applicationDirs.mangaDownloadsRoot)
|
||||||
|
val downloadDirs = oldMangaDownloadDir.listFiles().orEmpty()
|
||||||
|
|
||||||
|
val moveDownloadsToNewFolder = !newMangaDownloadDir.exists() && downloadDirs.isNotEmpty()
|
||||||
|
if (moveDownloadsToNewFolder) {
|
||||||
|
newMangaDownloadDir.mkdirs()
|
||||||
|
|
||||||
|
for (downloadDir in downloadDirs) {
|
||||||
|
if (downloadDir == File(applicationDirs.thumbnailDownloadsRoot)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadDir.renameTo(File(newMangaDownloadDir, downloadDir.name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val MIGRATIONS =
|
||||||
|
listOf<Pair<String, (ApplicationDirs) -> Unit>>(
|
||||||
|
"InitialMigration" to { applicationDirs ->
|
||||||
|
migrateMangaDownloadDir(applicationDirs)
|
||||||
|
migratePreferencesToNewXmlFileBasedStorage()
|
||||||
|
},
|
||||||
|
"FixGlobalUpdateScheduling" to {
|
||||||
|
Injekt.get<IUpdater>().deleteLastAutomatedUpdateTimestamp()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
fun runMigrations(applicationDirs: ApplicationDirs) {
|
fun runMigrations(applicationDirs: ApplicationDirs) {
|
||||||
|
val logger = KotlinLogging.logger("Migration")
|
||||||
|
|
||||||
val migrationPreferences =
|
val migrationPreferences =
|
||||||
Injekt
|
Injekt
|
||||||
.get<Application>()
|
.get<Application>()
|
||||||
@@ -54,36 +98,22 @@ fun runMigrations(applicationDirs: ApplicationDirs) {
|
|||||||
Context.MODE_PRIVATE,
|
Context.MODE_PRIVATE,
|
||||||
)
|
)
|
||||||
val version = migrationPreferences.getInt("version", 0)
|
val version = migrationPreferences.getInt("version", 0)
|
||||||
val logger = KotlinLogging.logger("Migration")
|
|
||||||
logger.info { "Running migrations, previous version $version, target version $MIGRATION_VERSION" }
|
|
||||||
|
|
||||||
if (version < 1) {
|
logger.info { "Running migrations, previous version $version, target version ${MIGRATIONS.size}" }
|
||||||
logger.info { "Running migration for version: 1" }
|
|
||||||
val oldMangaDownloadDir = File(applicationDirs.downloadsRoot)
|
|
||||||
val newMangaDownloadDir = File(applicationDirs.mangaDownloadsRoot)
|
|
||||||
val downloadDirs = oldMangaDownloadDir.listFiles().orEmpty()
|
|
||||||
|
|
||||||
val moveDownloadsToNewFolder = !newMangaDownloadDir.exists() && downloadDirs.isNotEmpty()
|
MIGRATIONS.forEachIndexed { index, (migrationName, migrationFunction) ->
|
||||||
if (moveDownloadsToNewFolder) {
|
val migrationVersion = index + 1
|
||||||
newMangaDownloadDir.mkdirs()
|
|
||||||
|
|
||||||
for (downloadDir in downloadDirs) {
|
val isMigrationRequired = version < migrationVersion
|
||||||
if (downloadDir == File(applicationDirs.thumbnailDownloadsRoot)) {
|
if (!isMigrationRequired) {
|
||||||
continue
|
logger.info { "Skipping migration version $migrationVersion: $migrationName" }
|
||||||
}
|
return@forEachIndexed
|
||||||
|
|
||||||
downloadDir.renameTo(File(newMangaDownloadDir, downloadDir.name))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Migrate from old preferences api
|
logger.info { "Running migration version $migrationVersion: $migrationName" }
|
||||||
val prefRootNode = "suwayomi/tachidesk"
|
|
||||||
val isMigrationRequired = Preferences.userRoot().nodeExists(prefRootNode)
|
migrationFunction(applicationDirs)
|
||||||
if (isMigrationRequired) {
|
|
||||||
val preferences = Preferences.userRoot().node(prefRootNode)
|
migrationPreferences.edit().putInt("version", migrationVersion).apply()
|
||||||
migratePreferences(null, preferences)
|
|
||||||
preferences.removeNode()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
migrationPreferences.edit().putInt("version", MIGRATION_VERSION).apply()
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user