From 2dc6311ed0c2a5660f00f6a0feff07f8f58b4733 Mon Sep 17 00:00:00 2001 From: Oleg Date: Fri, 14 Oct 2022 12:21:45 +0400 Subject: [PATCH] [TH2-4316] Add additional events to the root event with information about the cause of failure --- build.gradle | 4 +- .../th2/check1/rule/AbstractCheckTask.kt | 40 ++++++++++++++++++- .../rule/nomessage/NoMessageCheckTask.kt | 3 ++ .../check1/rule/sequence/SilenceCheckTask.kt | 3 ++ .../check1/rule/check/TestCheckRuleTask.kt | 4 +- 5 files changed, 49 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index 0c45dfc..1f930ff 100644 --- a/build.gradle +++ b/build.gradle @@ -229,13 +229,13 @@ test { } application { - mainClassName 'com.exactpro.th2.check1.Check1Main' + mainClass.set('com.exactpro.th2.check1.Check1Main') } applicationName = 'service' distTar { - archiveName "${applicationName}.tar" + archiveFileName.set("${applicationName}.tar") } dockerPrepare { diff --git a/src/main/kotlin/com/exactpro/th2/check1/rule/AbstractCheckTask.kt b/src/main/kotlin/com/exactpro/th2/check1/rule/AbstractCheckTask.kt index 340d135..e3a300e 100644 --- a/src/main/kotlin/com/exactpro/th2/check1/rule/AbstractCheckTask.kt +++ b/src/main/kotlin/com/exactpro/th2/check1/rule/AbstractCheckTask.kt @@ -70,6 +70,7 @@ import java.util.concurrent.ForkJoinPool import java.util.concurrent.TimeUnit.MILLISECONDS import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicReference +import com.exactpro.th2.common.util.toInstant /** * Implements common logic for check task. @@ -170,7 +171,7 @@ abstract class AbstractCheckTask( super.onError(e) refs.rootEvent.status(FAILED) - .bodyData(EventUtils.createMessageBean(e.message)) + .exception(e, true) end(State.ERROR, "Error ${e.message} received in message stream") } @@ -424,6 +425,9 @@ abstract class AbstractCheckTask( protected open val skipPublication: Boolean = false + protected open val errorEventOnTimeout: Boolean + get() = true + protected fun isCheckpointLastReceivedMessage(): Boolean = bufferContainsStartMessage && !hasMessagesInTimeoutInterval /** @@ -466,6 +470,9 @@ abstract class AbstractCheckTask( private fun completeEventOrReportError(prevState: State): Boolean { return try { if (started) { + if (errorEventOnTimeout && prevState in TIMEOUT_STATES) { + addTimeoutEvent(prevState) + } completeEvent(prevState) doAfterCompleteEvent() false @@ -489,6 +496,36 @@ abstract class AbstractCheckTask( } } + private fun addTimeoutEvent(timeoutType: State) { + refs.rootEvent.addSubEventWithSamePeriod() + .status(FAILED) + .type( + when (timeoutType) { + State.TIMEOUT -> "CheckTimeoutInterrupted" + State.MESSAGE_TIMEOUT -> "CheckMessageTimeoutInterrupted" + else -> error("unexpected timeout state: $timeoutType") + } + ).name("Check task was interrupter because of ${timeoutType.name.lowercase()}") + .bodyData( + EventUtils.createMessageBean( + when (timeoutType) { + State.TIMEOUT -> + "Check task was interrupted because the task execution took longer than ${taskTimeout.timeout} mls. " + + "It might be caused by the lack of the memory or CPU resources. Check the component resources consumption" + State.MESSAGE_TIMEOUT -> + "Check task was interrupted because the timestamp on the last processed message exceeds the message timeout. " + + (checkpointTimeout + ?.toInstant() + ?.let { + "Rule expects messages between $it and ${it.plusMillis(taskTimeout.messageTimeout)} " + + "but processed one outside this range. Check the attached messages." + } ?: "But the message timeout is not specified. Contact the developers.") + else -> error("unexpected timeout state: $timeoutType") + } + ) + ) + } + private fun configureRootEvent() { refs.rootEvent.name(name()).type(type()) setup(refs.rootEvent) @@ -585,6 +622,7 @@ abstract class AbstractCheckTask( private val RESPONSE_EXECUTOR = ForkJoinPool.commonPool() @JvmField val CONVERTER = ProtoToIMessageConverter(VerificationUtil.FACTORY_PROXY, null, null, createParameters().setUseMarkerForNullsInMessage(true)) + private val TIMEOUT_STATES: Set = setOf(State.TIMEOUT, State.MESSAGE_TIMEOUT) val EMPTY_STATUS_CONSUMER: (EventStatus) -> Unit = {} } diff --git a/src/main/kotlin/com/exactpro/th2/check1/rule/nomessage/NoMessageCheckTask.kt b/src/main/kotlin/com/exactpro/th2/check1/rule/nomessage/NoMessageCheckTask.kt index 4bbc32d..d78b64a 100644 --- a/src/main/kotlin/com/exactpro/th2/check1/rule/nomessage/NoMessageCheckTask.kt +++ b/src/main/kotlin/com/exactpro/th2/check1/rule/nomessage/NoMessageCheckTask.kt @@ -107,6 +107,9 @@ class NoMessageCheckTask( override fun type(): String = "noMessageCheck" + override val errorEventOnTimeout: Boolean + get() = false + override fun setup(rootEvent: Event) { rootEvent.bodyData(EventUtils.createMessageBean("No message check rule for messages from ${sessionKey.run { "$sessionAlias ($direction direction)" }}")) } diff --git a/src/main/kotlin/com/exactpro/th2/check1/rule/sequence/SilenceCheckTask.kt b/src/main/kotlin/com/exactpro/th2/check1/rule/sequence/SilenceCheckTask.kt index addf2ab..fe1f35c 100644 --- a/src/main/kotlin/com/exactpro/th2/check1/rule/sequence/SilenceCheckTask.kt +++ b/src/main/kotlin/com/exactpro/th2/check1/rule/sequence/SilenceCheckTask.kt @@ -119,6 +119,9 @@ class SilenceCheckTask( } } + override val errorEventOnTimeout: Boolean + get() = false + override fun name(): String = "AutoSilenceCheck" override fun type(): String = "AutoSilenceCheck" diff --git a/src/test/kotlin/com/exactpro/th2/check1/rule/check/TestCheckRuleTask.kt b/src/test/kotlin/com/exactpro/th2/check1/rule/check/TestCheckRuleTask.kt index a534b32..e8b3d79 100644 --- a/src/test/kotlin/com/exactpro/th2/check1/rule/check/TestCheckRuleTask.kt +++ b/src/test/kotlin/com/exactpro/th2/check1/rule/check/TestCheckRuleTask.kt @@ -538,8 +538,8 @@ internal class TestCheckRuleTask : AbstractCheckTaskTest() { val eventBatches = awaitEventBatchRequest(1000L, 2) val eventList = eventBatches.flatMap(EventBatch::getEventsList) - assertEquals(3, eventList.size) - assertEquals(2, eventList.filter { it.status == FAILED }.size) + assertEquals(4, eventList.size) + assertEquals(3, eventList.filter { it.status == FAILED }.size) } @Test