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

io.wavebeans.execution.LocalBush.kt Maven / Gradle / Ivy

package io.wavebeans.execution

import io.wavebeans.execution.config.ExecutionConfig
import io.wavebeans.execution.medium.PodCallResult
import io.wavebeans.execution.pod.Pod
import io.wavebeans.execution.pod.PodKey
import io.wavebeans.execution.pod.TickPod
import mu.KotlinLogging
import java.util.concurrent.CompletableFuture
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.Future
import kotlin.math.abs
import kotlin.random.Random

class LocalBush(
        override val bushKey: BushKey,
        val podDiscovery: PodDiscovery = PodDiscovery.default
) : Bush {

    companion object {
        private val log = KotlinLogging.logger { }
    }

    private val workingPool = ExecutionConfig.executionThreadPool()

    @Volatile
    private var isDraining = false

    private val pods = ConcurrentHashMap()

    private val tickFinished = ConcurrentHashMap>()

    init {
        podDiscovery.registerBush(bushKey, this)
    }

    inner class Tick(val pod: TickPod) : Runnable {

        override fun run() {
            try {
                if (!isDraining && pod.tick()) {
                    workingPool.submit(Tick(pod))
                } else {
                    log.debug { "Tick pod $pod has finished as it is over [isDraining=$isDraining]" }
                    tickFinished[pod]!!.complete(ExecutionResult.success())
                }
            } catch (e: Throwable) {
                if (e is OutOfMemoryError) throw e // most likely no resources to handle. Just fail
                log.debug(e) { "Tick pod $pod has finished due to error" }
                tickFinished[pod]!!.complete(ExecutionResult.error(e))
            }
        }

    }

    override fun start() {
        pods.values.filterIsInstance()
                .map { pod ->
                    tickFinished[pod] = CompletableFuture()
                    pod.start()
                    pod
                }.map { pod ->
                    workingPool.submit(Tick(pod))
                }
    }

    fun addPod(pod: Pod) {
        pods[pod.podKey] = pod
        podDiscovery.registerPod(bushKey, pod.podKey)
    }

    fun pods(): List = pods.values.toList()

    fun tickPodsFutures(): List> {
        return tickFinished.values.toList()
    }


    override fun close() {
        isDraining = true
        pods.values.forEach {
            it.close()
            podDiscovery.unregisterPod(bushKey, it.podKey)
        }
        podDiscovery.unregisterBush(bushKey)
    }

    override fun call(podKey: PodKey, request: String): Future {
        val pod = pods[podKey]
        check(pod != null) { "Pod $podKey is not found on Bush $bushKey" }
        val call = Call.parseRequest(request)
        val res = CompletableFuture()
        val id = abs(Random.nextInt()).toString(16)
        val start = System.currentTimeMillis()
        val r = try {
            log.trace { "[$id][$this] Calling pod=$podKey, request=$request" }
            val retVal = pod.call(call)
            log.trace { "[$id][$this] Call to pod=$podKey, request=$request took ${System.currentTimeMillis() - start}ms" }
            retVal
        } catch (e: Throwable) {
            if (e is OutOfMemoryError) throw e // most likely no resources to handle. Just fail
            log.info(e) {
                "[$id][$this] Call to pod=$podKey, request=$request " +
                        "took ${System.currentTimeMillis() - start}ms and ended up with error"
            }
            PodCallResult.error(call, e)
        }
        res.complete(r)
        return res
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy