mirror of
https://github.com/gotson/komga.git
synced 2025-12-20 15:34:17 +01:00
perf: optimize database transactions to avoid locking
This commit is contained in:
parent
b9546e8605
commit
39dcf5969e
12 changed files with 39 additions and 51 deletions
|
|
@ -15,7 +15,7 @@ import org.gotson.komga.domain.persistence.BookRepository
|
|||
import org.gotson.komga.domain.persistence.LibraryRepository
|
||||
import org.gotson.komga.domain.persistence.MediaRepository
|
||||
import org.springframework.stereotype.Service
|
||||
import org.springframework.transaction.annotation.Transactional
|
||||
import org.springframework.transaction.support.TransactionTemplate
|
||||
import java.io.FileNotFoundException
|
||||
import java.nio.file.FileAlreadyExistsException
|
||||
import java.util.zip.Deflater
|
||||
|
|
@ -38,6 +38,7 @@ class BookConverter(
|
|||
private val bookRepository: BookRepository,
|
||||
private val mediaRepository: MediaRepository,
|
||||
private val libraryRepository: LibraryRepository,
|
||||
private val transactionTemplate: TransactionTemplate,
|
||||
) {
|
||||
|
||||
private val convertibleTypes = listOf("application/x-rar-compressed; version=4")
|
||||
|
|
@ -55,7 +56,6 @@ class BookConverter(
|
|||
fun getConvertibleBookIds(library: Library): Collection<String> =
|
||||
bookRepository.findAllIdsByLibraryIdAndMediaTypes(library.id, convertibleTypes)
|
||||
|
||||
@Transactional
|
||||
fun convertToCbz(book: Book) {
|
||||
if (!libraryRepository.findById(book.libraryId).convertToCbz)
|
||||
return logger.info { "Book conversion is disabled for the library, it may have changed since the task was submitted, skipping" }
|
||||
|
|
@ -127,8 +127,10 @@ class BookConverter(
|
|||
if (book.path.deleteIfExists())
|
||||
logger.info { "Deleted converted file: ${book.path}" }
|
||||
|
||||
bookRepository.update(convertedBook)
|
||||
mediaRepository.update(convertedMedia)
|
||||
transactionTemplate.executeWithoutResult {
|
||||
bookRepository.update(convertedBook)
|
||||
mediaRepository.update(convertedMedia)
|
||||
}
|
||||
}
|
||||
|
||||
fun getMismatchedExtensionBookIds(library: Library): Collection<String> =
|
||||
|
|
@ -136,7 +138,6 @@ class BookConverter(
|
|||
bookRepository.findAllIdsByLibraryIdAndMismatchedExtension(library.id, mediaType, extension)
|
||||
}
|
||||
|
||||
@Transactional
|
||||
fun repairExtension(book: Book) {
|
||||
if (!libraryRepository.findById(book.libraryId).repairExtensions)
|
||||
return logger.info { "Repair extensions is disabled for the library, it may have changed since the task was submitted, skipping" }
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import org.gotson.komga.infrastructure.image.ImageConverter
|
|||
import org.gotson.komga.infrastructure.image.ImageType
|
||||
import org.springframework.stereotype.Service
|
||||
import org.springframework.transaction.annotation.Transactional
|
||||
import org.springframework.transaction.support.TransactionTemplate
|
||||
import java.io.File
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Paths
|
||||
|
|
@ -39,28 +40,29 @@ class BookLifecycle(
|
|||
private val bookAnalyzer: BookAnalyzer,
|
||||
private val imageConverter: ImageConverter,
|
||||
private val eventPublisher: EventPublisher,
|
||||
private val transactionTemplate: TransactionTemplate,
|
||||
) {
|
||||
|
||||
@Transactional
|
||||
fun analyzeAndPersist(book: Book): Boolean {
|
||||
logger.info { "Analyze and persist book: $book" }
|
||||
val media = bookAnalyzer.analyze(book)
|
||||
|
||||
// if the number of pages has changed, delete all read progress for that book
|
||||
mediaRepository.findById(book.id).let { previous ->
|
||||
if (previous.status == Media.Status.OUTDATED && previous.pages.size != media.pages.size) {
|
||||
readProgressRepository.deleteByBookId(book.id)
|
||||
transactionTemplate.executeWithoutResult {
|
||||
// if the number of pages has changed, delete all read progress for that book
|
||||
mediaRepository.findById(book.id).let { previous ->
|
||||
if (previous.status == Media.Status.OUTDATED && previous.pages.size != media.pages.size) {
|
||||
readProgressRepository.deleteByBookId(book.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mediaRepository.update(media)
|
||||
mediaRepository.update(media)
|
||||
}
|
||||
|
||||
eventPublisher.publishEvent(DomainEvent.BookUpdated(book))
|
||||
|
||||
return media.status == Media.Status.READY
|
||||
}
|
||||
|
||||
@Transactional
|
||||
fun generateThumbnailAndPersist(book: Book) {
|
||||
logger.info { "Generate thumbnail and persist for book: $book" }
|
||||
try {
|
||||
|
|
@ -70,7 +72,6 @@ class BookLifecycle(
|
|||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
fun addThumbnailForBook(thumbnail: ThumbnailBook) {
|
||||
when (thumbnail.type) {
|
||||
ThumbnailBook.Type.GENERATED -> {
|
||||
|
|
@ -97,7 +98,6 @@ class BookLifecycle(
|
|||
thumbnailsHouseKeeping(thumbnail.bookId)
|
||||
}
|
||||
|
||||
@Transactional
|
||||
fun getThumbnail(bookId: String): ThumbnailBook? {
|
||||
val selected = thumbnailBookRepository.findSelectedByBookIdOrNull(bookId)
|
||||
|
||||
|
|
@ -200,6 +200,7 @@ class BookLifecycle(
|
|||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
fun deleteOne(book: Book) {
|
||||
logger.info { "Delete book id: ${book.id}" }
|
||||
|
||||
|
|
@ -215,6 +216,7 @@ class BookLifecycle(
|
|||
eventPublisher.publishEvent(DomainEvent.BookDeleted(book))
|
||||
}
|
||||
|
||||
@Transactional
|
||||
fun deleteMany(books: Collection<Book>) {
|
||||
val bookIds = books.map { it.id }
|
||||
logger.info { "Delete book ids: $bookIds" }
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import org.gotson.komga.domain.persistence.SidecarRepository
|
|||
import org.gotson.komga.infrastructure.configuration.KomgaProperties
|
||||
import org.gotson.komga.infrastructure.language.notEquals
|
||||
import org.springframework.stereotype.Service
|
||||
import org.springframework.transaction.annotation.Transactional
|
||||
import org.springframework.transaction.support.TransactionTemplate
|
||||
import java.nio.file.Paths
|
||||
import kotlin.time.measureTime
|
||||
|
||||
|
|
@ -33,9 +33,9 @@ class LibraryContentLifecycle(
|
|||
private val sidecarRepository: SidecarRepository,
|
||||
private val komgaProperties: KomgaProperties,
|
||||
private val taskReceiver: TaskReceiver,
|
||||
private val transactionTemplate: TransactionTemplate,
|
||||
) {
|
||||
|
||||
@Transactional
|
||||
fun scanRootFolder(library: Library) {
|
||||
logger.info { "Updating library: $library" }
|
||||
measureTime {
|
||||
|
|
@ -94,10 +94,12 @@ class LibraryContentLifecycle(
|
|||
fileLastModified = newBook.fileLastModified,
|
||||
fileSize = newBook.fileSize
|
||||
)
|
||||
mediaRepository.findById(existingBook.id).let {
|
||||
mediaRepository.update(it.copy(status = Media.Status.OUTDATED))
|
||||
transactionTemplate.executeWithoutResult {
|
||||
mediaRepository.findById(existingBook.id).let {
|
||||
mediaRepository.update(it.copy(status = Media.Status.OUTDATED))
|
||||
}
|
||||
bookRepository.update(updatedBook)
|
||||
}
|
||||
bookRepository.update(updatedBook)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ import org.gotson.komga.infrastructure.metadata.barcode.IsbnBarcodeProvider
|
|||
import org.gotson.komga.infrastructure.metadata.comicrack.ComicInfoProvider
|
||||
import org.gotson.komga.infrastructure.metadata.epub.EpubMetadataProvider
|
||||
import org.springframework.stereotype.Service
|
||||
import org.springframework.transaction.annotation.Transactional
|
||||
|
||||
private val logger = KotlinLogging.logger {}
|
||||
|
||||
|
|
@ -48,7 +47,6 @@ class MetadataLifecycle(
|
|||
private val eventPublisher: EventPublisher,
|
||||
) {
|
||||
|
||||
@Transactional
|
||||
fun refreshMetadata(book: Book, capabilities: List<BookMetadataPatchCapability>) {
|
||||
logger.info { "Refresh metadata for book: $book with capabilities: $capabilities" }
|
||||
val media = mediaRepository.findById(book.id)
|
||||
|
|
@ -143,7 +141,6 @@ class MetadataLifecycle(
|
|||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
fun refreshMetadata(series: Series) {
|
||||
logger.info { "Refresh metadata for series: $series" }
|
||||
|
||||
|
|
@ -233,7 +230,6 @@ class MetadataLifecycle(
|
|||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
fun aggregateMetadata(series: Series) {
|
||||
logger.info { "Aggregate book metadata for series: $series" }
|
||||
|
||||
|
|
|
|||
|
|
@ -173,7 +173,6 @@ class SeriesLifecycle(
|
|||
eventPublisher.publishEvent(DomainEvent.ReadProgressSeriesDeleted(seriesId, user.id))
|
||||
}
|
||||
|
||||
@Transactional
|
||||
fun getThumbnail(seriesId: String): ThumbnailSeries? {
|
||||
val selected = thumbnailsSeriesRepository.findSelectedBySeriesIdOrNull(seriesId)
|
||||
|
||||
|
|
@ -196,7 +195,6 @@ class SeriesLifecycle(
|
|||
return null
|
||||
}
|
||||
|
||||
@Transactional
|
||||
fun addThumbnailForSeries(thumbnail: ThumbnailSeries) {
|
||||
// delete existing thumbnail with the same url
|
||||
thumbnailsSeriesRepository.findAllBySeriesId(thumbnail.seriesId)
|
||||
|
|
|
|||
|
|
@ -238,17 +238,14 @@ class BookDao(
|
|||
.execute()
|
||||
}
|
||||
|
||||
@Transactional
|
||||
override fun delete(bookId: String) {
|
||||
dsl.deleteFrom(b).where(b.ID.eq(bookId)).execute()
|
||||
}
|
||||
|
||||
@Transactional
|
||||
override fun delete(bookIds: Collection<String>) {
|
||||
dsl.deleteFrom(b).where(b.ID.`in`(bookIds)).execute()
|
||||
}
|
||||
|
||||
@Transactional
|
||||
override fun deleteAll() {
|
||||
dsl.deleteFrom(b).execute()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -176,14 +176,12 @@ class ReadListDao(
|
|||
insertBooks(readList)
|
||||
}
|
||||
|
||||
@Transactional
|
||||
override fun removeBookFromAll(bookId: String) {
|
||||
dsl.deleteFrom(rlb)
|
||||
.where(rlb.BOOK_ID.eq(bookId))
|
||||
.execute()
|
||||
}
|
||||
|
||||
@Transactional
|
||||
override fun removeBooksFromAll(bookIds: Collection<String>) {
|
||||
dsl.deleteFrom(rlb)
|
||||
.where(rlb.BOOK_ID.`in`(bookIds))
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import org.jooq.Condition
|
|||
import org.jooq.DSLContext
|
||||
import org.jooq.impl.DSL
|
||||
import org.springframework.stereotype.Component
|
||||
import org.springframework.transaction.annotation.Transactional
|
||||
import java.net.URL
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneId
|
||||
|
|
@ -84,7 +83,6 @@ class SeriesDao(
|
|||
.map { it.toDomain() }
|
||||
}
|
||||
|
||||
@Transactional
|
||||
override fun insert(series: Series) {
|
||||
dsl.insertInto(s)
|
||||
.set(s.ID, series.id)
|
||||
|
|
@ -95,7 +93,6 @@ class SeriesDao(
|
|||
.execute()
|
||||
}
|
||||
|
||||
@Transactional
|
||||
override fun update(series: Series) {
|
||||
dsl.update(s)
|
||||
.set(s.NAME, series.name)
|
||||
|
|
@ -108,17 +105,14 @@ class SeriesDao(
|
|||
.execute()
|
||||
}
|
||||
|
||||
@Transactional
|
||||
override fun delete(seriesId: String) {
|
||||
dsl.deleteFrom(s).where(s.ID.eq(seriesId)).execute()
|
||||
}
|
||||
|
||||
@Transactional
|
||||
override fun deleteAll() {
|
||||
dsl.deleteFrom(s).execute()
|
||||
}
|
||||
|
||||
@Transactional
|
||||
override fun delete(seriesIds: Collection<String>) {
|
||||
dsl.deleteFrom(s).where(s.ID.`in`(seriesIds)).execute()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import org.gotson.komga.jooq.Tables
|
|||
import org.gotson.komga.jooq.tables.records.SidecarRecord
|
||||
import org.jooq.DSLContext
|
||||
import org.springframework.stereotype.Component
|
||||
import org.springframework.transaction.annotation.Transactional
|
||||
import java.net.URL
|
||||
|
||||
@Component
|
||||
|
|
@ -20,7 +19,6 @@ class SidecarDao(
|
|||
override fun findAll(): Collection<SidecarStored> =
|
||||
dsl.selectFrom(sc).fetch().map { it.toDomain() }
|
||||
|
||||
@Transactional
|
||||
override fun save(libraryId: String, sidecar: Sidecar) {
|
||||
dsl.insertInto(sc)
|
||||
.values(
|
||||
|
|
@ -36,7 +34,6 @@ class SidecarDao(
|
|||
.execute()
|
||||
}
|
||||
|
||||
@Transactional
|
||||
override fun deleteByLibraryIdAndUrls(libraryId: String, urls: Collection<URL>) {
|
||||
dsl.deleteFrom(sc)
|
||||
.where(sc.LIBRARY_ID.eq(libraryId))
|
||||
|
|
@ -44,7 +41,6 @@ class SidecarDao(
|
|||
.execute()
|
||||
}
|
||||
|
||||
@Transactional
|
||||
override fun deleteByLibraryId(libraryId: String) {
|
||||
dsl.deleteFrom(sc)
|
||||
.where(sc.LIBRARY_ID.eq(libraryId))
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ class ThumbnailBookDao(
|
|||
.map { it.toDomain() }
|
||||
.firstOrNull()
|
||||
|
||||
@Transactional
|
||||
override fun insert(thumbnail: ThumbnailBook) {
|
||||
dsl.insertInto(tb)
|
||||
.set(tb.ID, thumbnail.id)
|
||||
|
|
@ -49,7 +48,6 @@ class ThumbnailBookDao(
|
|||
.execute()
|
||||
}
|
||||
|
||||
@Transactional
|
||||
override fun update(thumbnail: ThumbnailBook) {
|
||||
dsl.update(tb)
|
||||
.set(tb.BOOK_ID, thumbnail.bookId)
|
||||
|
|
@ -76,22 +74,18 @@ class ThumbnailBookDao(
|
|||
.execute()
|
||||
}
|
||||
|
||||
@Transactional
|
||||
override fun delete(thumbnailBookId: String) {
|
||||
dsl.deleteFrom(tb).where(tb.ID.eq(thumbnailBookId)).execute()
|
||||
}
|
||||
|
||||
@Transactional
|
||||
override fun deleteByBookId(bookId: String) {
|
||||
dsl.deleteFrom(tb).where(tb.BOOK_ID.eq(bookId)).execute()
|
||||
}
|
||||
|
||||
@Transactional
|
||||
override fun deleteByBookIds(bookIds: Collection<String>) {
|
||||
dsl.deleteFrom(tb).where(tb.BOOK_ID.`in`(bookIds)).execute()
|
||||
}
|
||||
|
||||
@Transactional
|
||||
override fun deleteByBookIdAndType(bookId: String, type: ThumbnailBook.Type) {
|
||||
dsl.deleteFrom(tb)
|
||||
.where(tb.BOOK_ID.eq(bookId))
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ class ThumbnailSeriesDao(
|
|||
.map { it.toDomain() }
|
||||
.firstOrNull()
|
||||
|
||||
@Transactional
|
||||
override fun insert(thumbnail: ThumbnailSeries) {
|
||||
dsl.insertInto(ts)
|
||||
.set(ts.ID, thumbnail.id)
|
||||
|
|
@ -55,17 +54,14 @@ class ThumbnailSeriesDao(
|
|||
.execute()
|
||||
}
|
||||
|
||||
@Transactional
|
||||
override fun delete(thumbnailSeriesId: String) {
|
||||
dsl.deleteFrom(ts).where(ts.ID.eq(thumbnailSeriesId)).execute()
|
||||
}
|
||||
|
||||
@Transactional
|
||||
override fun deleteBySeriesId(seriesId: String) {
|
||||
dsl.deleteFrom(ts).where(ts.SERIES_ID.eq(seriesId)).execute()
|
||||
}
|
||||
|
||||
@Transactional
|
||||
override fun deleteBySeriesIds(seriesIds: Collection<String>) {
|
||||
dsl.deleteFrom(ts).where(ts.SERIES_ID.`in`(seriesIds)).execute()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
package org.gotson.komga.infrastructure.transaction
|
||||
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.transaction.PlatformTransactionManager
|
||||
import org.springframework.transaction.support.TransactionTemplate
|
||||
|
||||
@Configuration
|
||||
class TransactionConfiguration {
|
||||
|
||||
@Bean
|
||||
fun transactionTemplate(transactionManager: PlatformTransactionManager) =
|
||||
TransactionTemplate(transactionManager)
|
||||
}
|
||||
Loading…
Reference in a new issue