heartbeat function

This commit is contained in:
Brage Skjønborg 2025-10-14 01:25:06 +02:00
parent b7552dbc67
commit b7c9e2827a
2 changed files with 43 additions and 0 deletions

View File

@ -3,11 +3,16 @@ package no.iktdev.eventi.tasks
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import no.iktdev.eventi.models.Event import no.iktdev.eventi.models.Event
import no.iktdev.eventi.models.Task import no.iktdev.eventi.models.Task
import java.util.UUID import java.util.UUID
import kotlin.coroutines.cancellation.CancellationException import kotlin.coroutines.cancellation.CancellationException
import kotlin.time.Duration
import kotlin.time.Duration.Companion.minutes
/** /**
* Abstract base class for handling tasks with asynchronous processing and reporting. * Abstract base class for handling tasks with asynchronous processing and reporting.
@ -39,6 +44,16 @@ abstract class TaskListener(val taskType: TaskType = TaskType.CPU_INTENSIVE): Ta
} }
} }
private var heartbeatRunner: Job? = null
suspend fun withHeartbeatRunner(interval: Duration = 5.minutes, block: () -> Unit): Job {
return CoroutineScope(currentCoroutineContext()).launch {
while (isActive) {
block()
delay(interval)
}
}.also { heartbeatRunner = it }
}
override fun accept(task: Task, reporter: TaskReporter): Boolean { override fun accept(task: Task, reporter: TaskReporter): Boolean {
if (isBusy || !supports(task)) return false if (isBusy || !supports(task)) return false
this.reporter = reporter this.reporter = reporter
@ -55,6 +70,8 @@ abstract class TaskListener(val taskType: TaskType = TaskType.CPU_INTENSIVE): Ta
} catch (e: Exception) { } catch (e: Exception) {
onError(task, e) onError(task, e)
} finally { } finally {
heartbeatRunner?.cancel()
heartbeatRunner = null
currentJob = null currentJob = null
currentTask = null currentTask = null
this@TaskListener.reporter = null this@TaskListener.reporter = null

View File

@ -2,9 +2,14 @@ package no.iktdev.eventi.tasks
import io.mockk.mockk import io.mockk.mockk
import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.test.setMain
import no.iktdev.eventi.InMemoryTaskStore import no.iktdev.eventi.InMemoryTaskStore
import no.iktdev.eventi.TestBase import no.iktdev.eventi.TestBase
import no.iktdev.eventi.events.EventListener import no.iktdev.eventi.events.EventListener
@ -20,6 +25,10 @@ import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import java.time.Duration import java.time.Duration
import java.util.UUID import java.util.UUID
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.nanoseconds
import kotlin.time.Duration.Companion.seconds
class AbstractTaskPollerTest : TestBase() { class AbstractTaskPollerTest : TestBase() {
@ -60,6 +69,8 @@ class AbstractTaskPollerTest : TestBase() {
open class EchoListener : TaskListener(TaskType.MIXED) { open class EchoListener : TaskListener(TaskType.MIXED) {
var result: Event? = null var result: Event? = null
fun getJob() = currentJob
override fun getWorkerId() = this.javaClass.simpleName override fun getWorkerId() = this.javaClass.simpleName
override fun supports(task: Task): Boolean { override fun supports(task: Task): Boolean {
@ -67,6 +78,9 @@ class AbstractTaskPollerTest : TestBase() {
} }
override suspend fun onTask(task: Task): Event { override suspend fun onTask(task: Task): Event {
withHeartbeatRunner(1.seconds) {
println("Heartbeat")
}
if (task is EchoTask) { if (task is EchoTask) {
return EchoEvent(task.data + " Potetmos").producedFrom(task) return EchoEvent(task.data + " Potetmos").producedFrom(task)
} }
@ -79,6 +93,15 @@ class AbstractTaskPollerTest : TestBase() {
reporter?.publishEvent(result!!) reporter?.publishEvent(result!!)
} }
override fun onError(task: Task, exception: Exception) {
exception.printStackTrace()
super.onError(task, exception)
}
override fun onCancelled() {
super.onCancelled()
}
} }
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
@ -118,6 +141,9 @@ class AbstractTaskPollerTest : TestBase() {
taskStore.persist(task) taskStore.persist(task)
poller.pollOnce() poller.pollOnce()
listener.getJob()?.join()
advanceTimeBy(1.minutes)
advanceUntilIdle() advanceUntilIdle()
assertEquals(initialBackoff, poller.backoff) assertEquals(initialBackoff, poller.backoff)