All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.enodeframework.mysql.handler.MySQLAddDomainEventsHandler.kt Maven / Gradle / Ivy

package org.enodeframework.mysql.handler

import com.google.common.base.Strings
import com.google.common.collect.Lists
import io.vertx.core.AsyncResult
import io.vertx.core.Handler
import io.vertx.mysqlclient.MySQLException
import io.vertx.sqlclient.Row
import io.vertx.sqlclient.RowSet
import org.enodeframework.common.exception.EventStoreException
import org.enodeframework.common.exception.IORuntimeException
import org.enodeframework.configurations.EventStoreConfiguration
import org.enodeframework.eventing.AggregateEventAppendResult
import org.enodeframework.eventing.EventAppendStatus
import org.slf4j.LoggerFactory
import java.util.concurrent.CompletableFuture
import java.util.regex.Pattern

class MySQLAddDomainEventsHandler(private val configuration: EventStoreConfiguration) :
    Handler>> {

    private val code: String = "23000"

    companion object {
        private val logger = LoggerFactory.getLogger(MySQLAddDomainEventsHandler::class.java)
        private val PATTERN_MYSQL = Pattern.compile("^Duplicate entry '.*-(.*)' for key")
    }

    var future = CompletableFuture()

    private fun getDuplicatedId(message: String): String {
        val matcher = PATTERN_MYSQL.matcher(message)
        if (!matcher.find()) {
            return ""
        }
        return if (matcher.groupCount() == 0) {
            ""
        } else matcher.group(1)
    }

    override fun handle(ar: AsyncResult>) {
        if (ar.succeeded()) {
            val appendResult = AggregateEventAppendResult()
            appendResult.eventAppendStatus = EventAppendStatus.Success
            future.complete(appendResult)
            return
        }
        val throwable = ar.cause()
        if (throwable is MySQLException) {
            if (code == throwable.sqlState && throwable.message?.contains(configuration.eventVersionUkName) == true) {
                val appendResult = AggregateEventAppendResult()
                appendResult.eventAppendStatus = EventAppendStatus.DuplicateEvent
                future.complete(appendResult)
                return
            }
            if (code == throwable.sqlState && throwable.message?.contains(configuration.eventCommandIdUkName) == true) {
                // 不同的数据库在冲突时的错误信息不同,可以通过解析错误信息的方式将冲突的commandId找出来,这里要求id不能命中正则的规则(不包含-字符)
                val appendResult = AggregateEventAppendResult()
                appendResult.eventAppendStatus = EventAppendStatus.DuplicateCommand
                val commandId = this.getDuplicatedId(throwable.message ?: "")
                if (!Strings.isNullOrEmpty(commandId)) {
                    appendResult.duplicateCommandIds = Lists.newArrayList(commandId)
                }
                // 如果没有从异常信息获取到commandId(很低概率获取不到),需要从db查询出来
                // 但是Vert.x的线程模型决定了不能再次使用EventLoop线程执行阻塞的查询操作
                future.complete(appendResult)
                return
            }
            logger.error("Batch append event has sql exception.", throwable)
            future.completeExceptionally(IORuntimeException(throwable))
            return
        }
        logger.error("Batch append event has unknown exception.", throwable)
        future.completeExceptionally(EventStoreException(throwable))
        return
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy