Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.enodeframework.eventing.impl.DefaultEventCommittingService.kt Maven / Gradle / Ivy
package org.enodeframework.eventing.impl
import kotlinx.coroutines.CoroutineDispatcher
import org.enodeframework.commanding.CommandResult
import org.enodeframework.commanding.CommandStatus
import org.enodeframework.commanding.ProcessingCommand
import org.enodeframework.common.io.IOHelper
import org.enodeframework.common.serializing.SerializeService
import org.enodeframework.domain.AggregateRoot
import org.enodeframework.domain.MemoryCache
import org.enodeframework.eventing.DomainEventStream
import org.enodeframework.eventing.EventAppendResult
import org.enodeframework.eventing.EventCommittingContext
import org.enodeframework.eventing.EventCommittingContextMailBox
import org.enodeframework.eventing.EventCommittingService
import org.enodeframework.eventing.EventStore
import org.enodeframework.messaging.MessagePublisher
import org.slf4j.LoggerFactory
import java.util.concurrent.CompletableFuture
import kotlin.math.abs
/**
* @author [email protected]
*/
class DefaultEventCommittingService(
private val memoryCache: MemoryCache,
private val eventStore: EventStore,
private val serializeService: SerializeService,
private val domainEventPublisher: MessagePublisher,
private val coroutineDispatcher: CoroutineDispatcher,
private val eventMailBoxCount: Int = 4,
private val eventMailBoxPersistenceMaxBatchSize: Int = 1000,
) : EventCommittingService {
private val eventCommittingContextMailBoxList: MutableList = ArrayList()
constructor(
memoryCache: MemoryCache,
eventStore: EventStore,
serializeService: SerializeService,
domainEventPublisher: MessagePublisher,
coroutineDispatcher: CoroutineDispatcher
) : this(memoryCache, eventStore, serializeService, domainEventPublisher, coroutineDispatcher, 4, 1000)
override fun commitDomainEventAsync(eventCommittingContext: EventCommittingContext) {
val eventMailboxIndex = getEventMailBoxIndex(eventCommittingContext.eventStream.aggregateRootId)
val eventMailbox = eventCommittingContextMailBoxList[eventMailboxIndex]
eventMailbox.enqueueMessage(eventCommittingContext)
}
override fun publishDomainEventAsync(
processingCommand: ProcessingCommand, eventStream: DomainEventStream
): CompletableFuture {
// 这里取出event时,没有合并command的信息
eventStream.mergeItems(processingCommand.items)
val eventStreamMessage = DomainEventStream(
processingCommand.message.id,
eventStream.aggregateRootId,
eventStream.version,
eventStream.aggregateRootTypeName,
eventStream.events,
eventStream.items
)
return publishDomainEventAsync(processingCommand, eventStreamMessage, 0)
}
private fun getEventMailBoxIndex(aggregateRootId: String): Int {
var hash = 23
for (c in aggregateRootId.toCharArray()) {
hash = (hash shl 5) - hash + c.code
}
if (hash < 0) {
hash = abs(hash)
}
return hash % eventMailBoxCount
}
private fun batchPersistEventAsync(committingContexts: List, retryTimes: Int) {
if (committingContexts.isEmpty()) {
return
}
IOHelper.tryAsyncActionRecursively("BatchPersistEventAsync", {
eventStore.batchAppendAsync(committingContexts.map { obj: EventCommittingContext -> obj.eventStream })
}, { result: EventAppendResult ->
val eventMailBox = committingContexts.first().mailBox
val appendContextList = ArrayList()
//针对持久化成功的聚合根,正常发布这些聚合根的事件到Q端
if (result.successAggregateRootIdList.size > 0) {
for (aggregateRootId in result.successAggregateRootIdList) {
committingContexts.filter { x: EventCommittingContext -> x.eventStream.aggregateRootId == aggregateRootId }
.forEach { eventCommittingContext ->
val context = EventAppendContext()
context.success = true
context.duplicateCommandIdList = ArrayList()
context.committingContext = eventCommittingContext
appendContextList.add(context)
}
}
if (logger.isDebugEnabled) {
logger.debug(
"Batch persist events success, mailboxNumber: {}, result: {}",
eventMailBox.getNumber(),
serializeService.serialize(result.successAggregateRootIdList)
)
}
}
//针对持久化出现重复的命令ID,在命令MailBox中标记为已重复,在事件MailBox中清除对应聚合根产生的事件,且重新发布这些命令对应的领域事件到Q端
if (result.duplicateCommandAggregateRootIdList.isNotEmpty()) {
for ((key, value) in result.duplicateCommandAggregateRootIdList) {
committingContexts.filter { x: EventCommittingContext -> key == x.eventStream.aggregateRootId }
.firstOrNull { eventCommittingContext: EventCommittingContext ->
val context = EventAppendContext()
context.duplicateCommandIdList = value
context.committingContext = eventCommittingContext
appendContextList.add(context)
}
}
logger.warn(
"Batch persist events has duplicate commandIds, mailboxNumber: {}, result: {}",
eventMailBox.getNumber(),
serializeService.serialize(result.duplicateCommandAggregateRootIdList)
)
}
//针对持久化出现版本冲突的聚合根,则自动处理每个聚合根的冲突
if (result.duplicateEventAggregateRootIdList.size > 0) {
for (aggregateRootId in result.duplicateEventAggregateRootIdList) {
committingContexts.filter { x: EventCommittingContext -> x.eventStream.aggregateRootId == aggregateRootId }
.firstOrNull { eventCommittingContext ->
val context = EventAppendContext()
context.duplicateCommandIdList = ArrayList()
context.committingContext = eventCommittingContext
appendContextList.add(context)
}
}
logger.warn(
"Batch persist events duplicated, mailboxNumber: {}, result: {}",
eventMailBox.getNumber(),
serializeService.serialize(result.duplicateEventAggregateRootIdList)
)
}
processDuplicateAggregateRootRecursively(0, appendContextList, eventMailBox)
//最终将当前的EventMailBox的本次处理标记为处理完成,然后继续可以处理下一批事件
}, {
String.format("[contextListCount:%d]", committingContexts.size)
}, null, retryTimes, true)
}
class EventAppendContext {
lateinit var committingContext: EventCommittingContext
var duplicateCommandIdList: List = ArrayList()
var success: Boolean = false
}
private fun processDuplicateAggregateRootRecursively(
index: Int, contexts: List, eventMailBox: EventCommittingContextMailBox
) {
if (contexts.isEmpty()) {
return
}
if (index == contexts.size) {
eventMailBox.completeRun()
return
}
val context = contexts[index]
val eventCommittingContext = context.committingContext
val duplicateCommandIdList = context.duplicateCommandIdList
if (context.success) {
publishDomainEventAsync(
eventCommittingContext.processingCommand, eventCommittingContext.eventStream
).whenComplete { _, _ ->
processDuplicateAggregateRootRecursively(index + 1, contexts, eventMailBox)
}
return
}
if (eventCommittingContext.eventStream.version == 1) {
handleFirstEventDuplicationAsync(eventCommittingContext, 0).whenComplete { _, _ ->
processDuplicateAggregateRootRecursively(index + 1, contexts, eventMailBox)
}
} else {
resetCommandMailBoxConsumingSequence(
eventCommittingContext, eventCommittingContext.processingCommand.sequence, duplicateCommandIdList
).whenComplete { _, _ ->
processDuplicateAggregateRootRecursively(index + 1, contexts, eventMailBox)
}
}
}
private fun resetCommandMailBoxConsumingSequence(
context: EventCommittingContext, consumingSequence: Long, duplicateCommandIdList: List?
): CompletableFuture {
val commandMailBox = context.processingCommand.mailBox
val eventMailBox = context.mailBox
val aggregateRootId = context.eventStream.aggregateRootId
commandMailBox.pause()
val future = CompletableFuture()
eventMailBox.removeAggregateAllEventCommittingContexts(aggregateRootId)
memoryCache.refreshAggregateFromEventStoreAsync(
context.eventStream.aggregateRootTypeName,
aggregateRootId
).whenComplete { _, _ ->
try {
if (duplicateCommandIdList != null) {
for (commandId in duplicateCommandIdList) {
commandMailBox.addDuplicateCommandId(commandId)
}
}
commandMailBox.resetConsumingSequence(consumingSequence)
} finally {
commandMailBox.resume()
commandMailBox.tryRun()
}
future.complete(true)
}
return future
}
private fun handleFirstEventDuplicationAsync(
context: EventCommittingContext, retryTimes: Int
): CompletableFuture {
val future = CompletableFuture()
IOHelper.tryAsyncActionRecursively("FindFirstEventByVersion", {
eventStore.findAsync(context.eventStream.aggregateRootId, 1)
}, { result: DomainEventStream? ->
if (result != null) {
//判断是否是同一个command,如果是,则再重新做一遍发布事件;
//之所以要这样做,是因为虽然该command产生的事件已经持久化成功,但并不表示事件也已经发布出去了;
//有可能事件持久化成功了,但那时正好机器断电了,则发布事件都没有做;
if (context.processingCommand.message.id == result.commandId) {
resetCommandMailBoxConsumingSequence(
context, context.processingCommand.sequence + 1, null
).whenComplete { _, _ ->
publishDomainEventAsync(context.processingCommand, result).whenComplete { _, _ ->
future.complete(true)
}
}
} else {
//如果不是同一个command,则认为是两个不同的command重复创建ID相同的聚合根,我们需要记录错误日志,然后通知当前command的处理完成;
val errorMessage = String.format(
"Duplicate aggregate creation. current commandId:%s, existing commandId:%s, aggregateRootId:%s, aggregateRootTypeName:%s",
context.processingCommand.message.id,
result.commandId,
result.aggregateRootId,
result.aggregateRootTypeName
)
logger.error(errorMessage)
resetCommandMailBoxConsumingSequence(
context, context.processingCommand.sequence + 1, null
).whenComplete { _, _ ->
val commandResult = CommandResult(
CommandStatus.Failed,
context.processingCommand.message.id,
context.eventStream.aggregateRootId,
"Duplicate aggregate creation.",
String::class.java.name
)
completeCommand(context.processingCommand, commandResult).whenComplete { _, _ ->
future.complete(true)
}
}
}
} else {
val errorMessage = String.format(
"Duplicate aggregate creation, but we cannot find the existing eventstream from eventstore. commandId:%s, aggregateRootId:%s, aggregateRootTypeName:%s",
context.eventStream.commandId,
context.eventStream.aggregateRootId,
context.eventStream.aggregateRootTypeName
)
logger.error(errorMessage)
resetCommandMailBoxConsumingSequence(
context, context.processingCommand.sequence + 1, null
).whenComplete { _, _ ->
val commandResult = CommandResult(
CommandStatus.Failed,
context.processingCommand.message.id,
context.eventStream.aggregateRootId,
"Duplicate aggregate creation, but we cannot find the existing eventstream from eventstore.",
String::class.java.name
)
completeCommand(context.processingCommand, commandResult).whenComplete { _, _ ->
future.complete(
true
)
}
}
}
}, {
String.format("[eventStream:%s]", serializeService.serialize(context.eventStream))
}, null, retryTimes, true)
return future
}
private fun publishDomainEventAsync(
processingCommand: ProcessingCommand, eventStream: DomainEventStream, retryTimes: Int
): CompletableFuture {
val future = CompletableFuture()
IOHelper.tryAsyncActionRecursivelyWithoutResult("PublishDomainEventAsync", {
domainEventPublisher.publishAsync(eventStream)
}, {
if (logger.isDebugEnabled) {
logger.debug("Publish domain events success, {}", serializeService.serialize(eventStream))
}
val commandHandleResult = processingCommand.commandExecuteContext.result
val commandResult = CommandResult(
CommandStatus.Success,
processingCommand.message.id,
eventStream.aggregateRootId,
commandHandleResult,
String::class.java.name
)
completeCommand(processingCommand, commandResult).whenComplete { _, _ ->
future.complete(true)
}
}, {
String.format("[eventStream:%s]", serializeService.serialize(eventStream))
}, null, retryTimes, true)
return future
}
private fun completeCommand(
processingCommand: ProcessingCommand, commandResult: CommandResult
): CompletableFuture {
return processingCommand.mailBox.completeMessage(processingCommand, commandResult)
}
private val logger = LoggerFactory.getLogger(DefaultEventCommittingService::class.java)
init {
for (i in 0 until eventMailBoxCount) {
val mailBox =
EventCommittingContextMailBox(
i,
eventMailBoxPersistenceMaxBatchSize,
coroutineDispatcher
) { x: List ->
batchPersistEventAsync(
x, 0
)
}
eventCommittingContextMailBoxList.add(mailBox)
}
}
}