diff --git a/backend/api/src/main/kotlin/io/tolgee/api/v2/controllers/AutoTranslationController.kt b/backend/api/src/main/kotlin/io/tolgee/api/v2/controllers/AutoTranslationController.kt index e0561d6dd7..0170697702 100644 --- a/backend/api/src/main/kotlin/io/tolgee/api/v2/controllers/AutoTranslationController.kt +++ b/backend/api/src/main/kotlin/io/tolgee/api/v2/controllers/AutoTranslationController.kt @@ -71,7 +71,7 @@ When no languages provided, it translates only untranslated languages.""" throw BadRequestException(Message.CANNOT_TRANSLATE_BASE_LANGUAGE) } - autoTranslationService.autoTranslateSync( + autoTranslationService.autoTranslateSyncWithRetry( key = key, forcedLanguageTags = languages?.toList(), useTranslationMemory = useTranslationMemory ?: false, diff --git a/backend/data/src/main/kotlin/io/tolgee/batch/state/BatchJobStateProvider.kt b/backend/data/src/main/kotlin/io/tolgee/batch/state/BatchJobStateProvider.kt index 4ce2107f7a..1f3b5fec8f 100644 --- a/backend/data/src/main/kotlin/io/tolgee/batch/state/BatchJobStateProvider.kt +++ b/backend/data/src/main/kotlin/io/tolgee/batch/state/BatchJobStateProvider.kt @@ -125,6 +125,9 @@ class BatchJobStateProvider( } fun getCachedJobIds(): MutableSet { - return getStatesMap().keys + val keys = getStatesMap().keys + // redisson defers the access to the key set, so it was throwing NoSuchElementException when iterating over keys + // so let's rather copy the set + return keys.toMutableSet() } } diff --git a/backend/data/src/main/kotlin/io/tolgee/component/SentryBeforeSendCallback.kt b/backend/data/src/main/kotlin/io/tolgee/component/SentryBeforeSendCallback.kt new file mode 100644 index 0000000000..914445d883 --- /dev/null +++ b/backend/data/src/main/kotlin/io/tolgee/component/SentryBeforeSendCallback.kt @@ -0,0 +1,34 @@ +package io.tolgee.component + +import io.sentry.Hint +import io.sentry.SentryEvent +import io.sentry.SentryOptions +import org.springframework.stereotype.Component + +@Component +class SentryBeforeSendCallback : SentryOptions.BeforeSendCallback { + override fun execute(event: SentryEvent, hint: Hint): SentryEvent? { + if (event.containsMessage("Failed to send message to MessageChannel")) { + return null + } + + if (event.containsExceptionOfType("FailedDontRequeueException")) { + return null + } + + if (event.containsExceptionOfType("ClientAbortException")) { + return null + } + + if (event.containsMessage("Cannot render error page for request [/websocket")) return null + + return event + } + + private fun SentryEvent.containsMessage(string: String): Boolean { + return message?.formatted?.contains(string) == true + } + + private fun SentryEvent.containsExceptionOfType(type: String) = + exceptions?.any { it.type?.contains(type) == true } == true +} diff --git a/backend/data/src/main/kotlin/io/tolgee/exceptions/ExceptionWithMessage.kt b/backend/data/src/main/kotlin/io/tolgee/exceptions/ExceptionWithMessage.kt index 0e8b8914ce..e841739f03 100644 --- a/backend/data/src/main/kotlin/io/tolgee/exceptions/ExceptionWithMessage.kt +++ b/backend/data/src/main/kotlin/io/tolgee/exceptions/ExceptionWithMessage.kt @@ -22,7 +22,7 @@ abstract class ExceptionWithMessage( this.tolgeeMessage = message } - constructor(message: Message) : this(null, null) { + constructor(message: Message) : this(message.code, null) { this.tolgeeMessage = message } } diff --git a/backend/data/src/main/kotlin/io/tolgee/service/translation/AutoTranslationService.kt b/backend/data/src/main/kotlin/io/tolgee/service/translation/AutoTranslationService.kt index 20c53b181c..5fa2fae753 100644 --- a/backend/data/src/main/kotlin/io/tolgee/service/translation/AutoTranslationService.kt +++ b/backend/data/src/main/kotlin/io/tolgee/service/translation/AutoTranslationService.kt @@ -16,10 +16,13 @@ import io.tolgee.repository.AutoTranslationConfigRepository import io.tolgee.security.authentication.AuthenticationFacade import io.tolgee.service.LanguageService import io.tolgee.service.machineTranslation.MtService +import io.tolgee.util.executeInNewTransaction +import io.tolgee.util.tryUntilItDoesntBreakConstraint import jakarta.persistence.EntityManager import org.slf4j.Logger import org.slf4j.LoggerFactory import org.springframework.stereotype.Service +import org.springframework.transaction.PlatformTransactionManager @Service class AutoTranslationService( @@ -30,7 +33,8 @@ class AutoTranslationService( private val languageService: LanguageService, private val batchJobService: BatchJobService, private val authenticationFacade: AuthenticationFacade, - private val entityManager: EntityManager + private val entityManager: EntityManager, + private val transactionManager: PlatformTransactionManager ) { val logger: Logger = LoggerFactory.getLogger(this::class.java) @@ -127,6 +131,20 @@ class AutoTranslationService( return null } + fun autoTranslateSyncWithRetry( + key: Key, + forcedLanguageTags: List? = null, + useTranslationMemory: Boolean? = null, + useMachineTranslation: Boolean? = null, + isBatch: Boolean, + ) { + tryUntilItDoesntBreakConstraint(maxRepeats = 10) { + executeInNewTransaction(transactionManager) { + autoTranslateSync(key, forcedLanguageTags, useTranslationMemory, useMachineTranslation, isBatch) + } + } + } + fun autoTranslateSync( key: Key, forcedLanguageTags: List? = null, diff --git a/backend/data/src/main/kotlin/io/tolgee/util/tryUntilItDoesntBreakContraint.kt b/backend/data/src/main/kotlin/io/tolgee/util/tryUntilItDoesntBreakContraint.kt index 69a8bacc9a..8730fa0950 100644 --- a/backend/data/src/main/kotlin/io/tolgee/util/tryUntilItDoesntBreakContraint.kt +++ b/backend/data/src/main/kotlin/io/tolgee/util/tryUntilItDoesntBreakContraint.kt @@ -4,10 +4,10 @@ import jakarta.persistence.PersistenceException import org.springframework.dao.CannotAcquireLockException import org.springframework.dao.DataIntegrityViolationException -inline fun tryUntilItDoesntBreakConstraint(fn: () -> T): T { +inline fun tryUntilItDoesntBreakConstraint(maxRepeats: Int = 100, fn: () -> T): T { var exception: Exception? = null var repeats = 0 - for (it in 1..100) { + for (it in 1..maxRepeats) { try { return fn() } catch (e: Exception) { @@ -16,6 +16,7 @@ inline fun tryUntilItDoesntBreakConstraint(fn: () -> T): T { repeats++ exception = e } + else -> throw e } }