Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
strategy:
fail-fast: false
matrix:
target: [ diningPhilosophers, count, cigaretteSmokers, chameneos, big, sleepingBarber ]
target: [ diningPhilosophers, count, cigaretteSmokers, chameneos, big, sleepingBarber, pingPong ]
permissions:
packages: write
contents: read
Expand Down Expand Up @@ -54,4 +54,4 @@ jobs:
file: ./${{ matrix.target }}.Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
labels: ${{ steps.meta.outputs.labels }}
15 changes: 15 additions & 0 deletions pingPong.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
FROM gradle:9.3.0-jdk25 AS build

COPY --chown=gradle:gradle . /usr/src/cirrina-baselines

WORKDIR /usr/src/cirrina-baselines

RUN gradle :pingPong:distZip

RUN unzip pingPong/build/distributions/pingPong.zip -d /tmp

FROM gcr.io/distroless/java25-debian13 AS runtime

COPY --from=build /tmp/pingPong /opt/pingPong

ENTRYPOINT ["java", "-cp", "/opt/pingPong/lib/*", "ac.at.uibk.dps.dapr.pingPong.PingPongKt"]
3 changes: 3 additions & 0 deletions pingPong/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
plugins { id("common-conventions") }

application { mainClass.set("ac.at.uibk.dps.dapr.pingPong.PingPongKt") }
76 changes: 76 additions & 0 deletions pingPong/src/main/kotlin/ac.at.uibk.dps.dapr.pingPong/PingPong.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package ac.at.uibk.dps.dapr.pingPong

import ac.at.uibk.dps.dapr.pingPong.ping.PingActor
import ac.at.uibk.dps.dapr.pingPong.ping.PingActorImpl
import ac.at.uibk.dps.dapr.pingPong.pong.PongActorImpl
import com.codahale.metrics.CsvReporter
import com.codahale.metrics.MetricRegistry
import io.dapr.actors.ActorId
import io.dapr.actors.client.ActorClient
import io.dapr.actors.client.ActorProxyBuilder
import io.dapr.actors.runtime.ActorRuntime
import io.micrometer.core.instrument.Clock
import io.micrometer.core.instrument.binder.jvm.JvmGcMetrics
import io.micrometer.core.instrument.binder.jvm.JvmMemoryMetrics
import io.micrometer.core.instrument.binder.system.ProcessorMetrics
import io.micrometer.core.instrument.dropwizard.DropwizardConfig
import io.micrometer.core.instrument.dropwizard.DropwizardMeterRegistry
import io.micrometer.core.instrument.util.HierarchicalNameMapper
import java.nio.file.Paths
import java.util.concurrent.TimeUnit
import org.springframework.boot.ApplicationArguments
import org.springframework.boot.ApplicationRunner
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.stereotype.Component

@SpringBootApplication
class PingPong {
companion object {
val metricsDirectory = System.getenv("METRICS_DIRECTORY") ?: "metrics"

fun provideMetricRegistry(): MetricRegistry =
MetricRegistry().apply {
val path = Paths.get(metricsDirectory).toAbsolutePath()

CsvReporter.forRegistry(this).build(path.toFile()).start(1L, TimeUnit.SECONDS)

object :
DropwizardMeterRegistry(
object : DropwizardConfig {
override fun get(key: String): String? = null

override fun prefix(): String = ""
},
this,
HierarchicalNameMapper.DEFAULT,
Clock.SYSTEM,
) {
override fun nullGaugeValue(): Double = Double.NaN
}
.apply {
ProcessorMetrics().bindTo(this)
JvmMemoryMetrics().bindTo(this)
JvmGcMetrics().bindTo(this)
}
}
}
}

fun main(args: Array<String>) {
val role = System.getenv("ROLE")
if (role == "ping") ActorRuntime.getInstance().registerActor(PingActorImpl::class.java)
if (role == "pong") ActorRuntime.getInstance().registerActor(PongActorImpl::class.java)
runApplication<PingPong>(*args)
}

@Component
class AutoStarter : ApplicationRunner {

override fun run(args: ApplicationArguments?) {
val role = System.getenv("ROLE") ?: "ping"
if (role != "ping") return
val proxy = ActorProxyBuilder(PingActor::class.java, ActorClient()).build(ActorId("ping-1"))
proxy.ping(-1L)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package ac.at.uibk.dps.dapr.pingPong.ping

import io.dapr.actors.ActorType

@ActorType(name = "PingActor")
interface PingActor {
fun ping(time: Long)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package ac.at.uibk.dps.dapr.pingPong.ping

import ac.at.uibk.dps.dapr.pingPong.PingPong
import io.dapr.actors.ActorId
import io.dapr.actors.runtime.AbstractActor
import io.dapr.actors.runtime.ActorRuntimeContext
import io.dapr.client.DaprClient
import io.dapr.client.DaprClientBuilder
import java.util.concurrent.TimeUnit
import kotlin.time.Clock

class PingActorImpl(runtimeContext: ActorRuntimeContext<PingActorImpl>, actorId: ActorId) :
AbstractActor(runtimeContext, actorId), PingActor {
val client: DaprClient = DaprClientBuilder().build()

var metricRegistry = PingPong.provideMetricRegistry()

override fun ping(time: Long) {
val now = Clock.System.now()
val nowNanos = (now.epochSeconds * 1_000_000_000L) + now.nanosecondsOfSecond
val deltaNanos = (nowNanos - time).coerceAtLeast(0L)

if (time >= 0) metricRegistry.timer("event.latency").update((deltaNanos), TimeUnit.NANOSECONDS)

client.publishEvent("pubsub", "ping", nowNanos).subscribe()

metricRegistry.counter("ping.count").inc()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package ac.at.uibk.dps.dapr.pingPong.ping

import io.dapr.Topic
import io.dapr.actors.ActorId
import io.dapr.actors.client.ActorClient
import io.dapr.actors.client.ActorProxyBuilder
import io.dapr.client.domain.CloudEvent
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RestController

@RestController
@ConditionalOnProperty("app.role", havingValue = "ping")
class PingSubscriber {
private val pingProxy =
ActorProxyBuilder(PingActor::class.java, ActorClient()).build(ActorId("ping-1"))

@Topic(name = "pong", pubsubName = "pubsub")
@PostMapping("/pong")
fun pongSubscriber(@RequestBody event: CloudEvent<Long>) {
pingProxy.ping(event.data)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package ac.at.uibk.dps.dapr.pingPong.pong

import io.dapr.actors.ActorType

@ActorType(name = "PongActor")
interface PongActor {
fun pong(time: Long)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package ac.at.uibk.dps.dapr.pingPong.pong

import ac.at.uibk.dps.dapr.pingPong.PingPong
import io.dapr.actors.ActorId
import io.dapr.actors.runtime.AbstractActor
import io.dapr.actors.runtime.ActorRuntimeContext
import io.dapr.client.DaprClient
import io.dapr.client.DaprClientBuilder
import java.util.concurrent.TimeUnit
import kotlin.time.Clock

class PongActorImpl(runtimeContext: ActorRuntimeContext<PongActorImpl>, actorId: ActorId) :
AbstractActor(runtimeContext, actorId), PongActor {
val client: DaprClient = DaprClientBuilder().build()

var metricRegistry = PingPong.provideMetricRegistry()

override fun pong(time: Long) {
val now = Clock.System.now()
val nowNanos = (now.epochSeconds * 1_000_000_000L) + now.nanosecondsOfSecond
val deltaNanos = (nowNanos - time).coerceAtLeast(0L)

metricRegistry.timer("event.latency").update((deltaNanos), TimeUnit.NANOSECONDS)

client.publishEvent("pubsub", "pong", nowNanos).subscribe()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package ac.at.uibk.dps.dapr.pingPong.pong

import io.dapr.Topic
import io.dapr.actors.ActorId
import io.dapr.actors.client.ActorClient
import io.dapr.actors.client.ActorProxyBuilder
import io.dapr.client.domain.CloudEvent
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RestController

@RestController
@ConditionalOnProperty("app.role", havingValue = "pong")
class PongSubscriber {
private val pongProxy =
ActorProxyBuilder(PongActor::class.java, ActorClient()).build(ActorId("pong-1"))

@Topic(name = "ping", pubsubName = "pubsub")
@PostMapping("/ping")
fun pingSubscriber(@RequestBody event: CloudEvent<Long>) {
pongProxy.pong(event.data)
}
}
2 changes: 2 additions & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ include("count")
include("diningPhilosophers")

include("sleepingBarber")

include("pingPong")
Loading