Skip to content

Commit d9fc7fb

Browse files
authored
feat: adding ping pong benchmark (#44)
1 parent 76f114d commit d9fc7fb

File tree

11 files changed

+218
-2
lines changed

11 files changed

+218
-2
lines changed

.github/workflows/docker.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
strategy:
1616
fail-fast: false
1717
matrix:
18-
target: [ diningPhilosophers, count, cigaretteSmokers, chameneos, big, sleepingBarber ]
18+
target: [ diningPhilosophers, count, cigaretteSmokers, chameneos, big, sleepingBarber, pingPong ]
1919
permissions:
2020
packages: write
2121
contents: read
@@ -54,4 +54,4 @@ jobs:
5454
file: ./${{ matrix.target }}.Dockerfile
5555
push: true
5656
tags: ${{ steps.meta.outputs.tags }}
57-
labels: ${{ steps.meta.outputs.labels }}
57+
labels: ${{ steps.meta.outputs.labels }}

pingPong.Dockerfile

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
FROM gradle:9.3.0-jdk25 AS build
2+
3+
COPY --chown=gradle:gradle . /usr/src/cirrina-baselines
4+
5+
WORKDIR /usr/src/cirrina-baselines
6+
7+
RUN gradle :pingPong:distZip
8+
9+
RUN unzip pingPong/build/distributions/pingPong.zip -d /tmp
10+
11+
FROM gcr.io/distroless/java25-debian13 AS runtime
12+
13+
COPY --from=build /tmp/pingPong /opt/pingPong
14+
15+
ENTRYPOINT ["java", "-cp", "/opt/pingPong/lib/*", "ac.at.uibk.dps.dapr.pingPong.PingPongKt"]

pingPong/build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
plugins { id("common-conventions") }
2+
3+
application { mainClass.set("ac.at.uibk.dps.dapr.pingPong.PingPongKt") }
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package ac.at.uibk.dps.dapr.pingPong
2+
3+
import ac.at.uibk.dps.dapr.pingPong.ping.PingActor
4+
import ac.at.uibk.dps.dapr.pingPong.ping.PingActorImpl
5+
import ac.at.uibk.dps.dapr.pingPong.pong.PongActorImpl
6+
import com.codahale.metrics.CsvReporter
7+
import com.codahale.metrics.MetricRegistry
8+
import io.dapr.actors.ActorId
9+
import io.dapr.actors.client.ActorClient
10+
import io.dapr.actors.client.ActorProxyBuilder
11+
import io.dapr.actors.runtime.ActorRuntime
12+
import io.micrometer.core.instrument.Clock
13+
import io.micrometer.core.instrument.binder.jvm.JvmGcMetrics
14+
import io.micrometer.core.instrument.binder.jvm.JvmMemoryMetrics
15+
import io.micrometer.core.instrument.binder.system.ProcessorMetrics
16+
import io.micrometer.core.instrument.dropwizard.DropwizardConfig
17+
import io.micrometer.core.instrument.dropwizard.DropwizardMeterRegistry
18+
import io.micrometer.core.instrument.util.HierarchicalNameMapper
19+
import java.nio.file.Paths
20+
import java.util.concurrent.TimeUnit
21+
import org.springframework.boot.ApplicationArguments
22+
import org.springframework.boot.ApplicationRunner
23+
import org.springframework.boot.autoconfigure.SpringBootApplication
24+
import org.springframework.boot.runApplication
25+
import org.springframework.stereotype.Component
26+
27+
@SpringBootApplication
28+
class PingPong {
29+
companion object {
30+
val metricsDirectory = System.getenv("METRICS_DIRECTORY") ?: "metrics"
31+
32+
fun provideMetricRegistry(): MetricRegistry =
33+
MetricRegistry().apply {
34+
val path = Paths.get(metricsDirectory).toAbsolutePath()
35+
36+
CsvReporter.forRegistry(this).build(path.toFile()).start(1L, TimeUnit.SECONDS)
37+
38+
object :
39+
DropwizardMeterRegistry(
40+
object : DropwizardConfig {
41+
override fun get(key: String): String? = null
42+
43+
override fun prefix(): String = ""
44+
},
45+
this,
46+
HierarchicalNameMapper.DEFAULT,
47+
Clock.SYSTEM,
48+
) {
49+
override fun nullGaugeValue(): Double = Double.NaN
50+
}
51+
.apply {
52+
ProcessorMetrics().bindTo(this)
53+
JvmMemoryMetrics().bindTo(this)
54+
JvmGcMetrics().bindTo(this)
55+
}
56+
}
57+
}
58+
}
59+
60+
fun main(args: Array<String>) {
61+
val role = System.getenv("ROLE")
62+
if (role == "ping") ActorRuntime.getInstance().registerActor(PingActorImpl::class.java)
63+
if (role == "pong") ActorRuntime.getInstance().registerActor(PongActorImpl::class.java)
64+
runApplication<PingPong>(*args)
65+
}
66+
67+
@Component
68+
class AutoStarter : ApplicationRunner {
69+
70+
override fun run(args: ApplicationArguments?) {
71+
val role = System.getenv("ROLE") ?: "ping"
72+
if (role != "ping") return
73+
val proxy = ActorProxyBuilder(PingActor::class.java, ActorClient()).build(ActorId("ping-1"))
74+
proxy.ping(-1L)
75+
}
76+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package ac.at.uibk.dps.dapr.pingPong.ping
2+
3+
import io.dapr.actors.ActorType
4+
5+
@ActorType(name = "PingActor")
6+
interface PingActor {
7+
fun ping(time: Long)
8+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package ac.at.uibk.dps.dapr.pingPong.ping
2+
3+
import ac.at.uibk.dps.dapr.pingPong.PingPong
4+
import io.dapr.actors.ActorId
5+
import io.dapr.actors.runtime.AbstractActor
6+
import io.dapr.actors.runtime.ActorRuntimeContext
7+
import io.dapr.client.DaprClient
8+
import io.dapr.client.DaprClientBuilder
9+
import java.util.concurrent.TimeUnit
10+
import kotlin.time.Clock
11+
12+
class PingActorImpl(runtimeContext: ActorRuntimeContext<PingActorImpl>, actorId: ActorId) :
13+
AbstractActor(runtimeContext, actorId), PingActor {
14+
val client: DaprClient = DaprClientBuilder().build()
15+
16+
var metricRegistry = PingPong.provideMetricRegistry()
17+
18+
override fun ping(time: Long) {
19+
val now = Clock.System.now()
20+
val nowNanos = (now.epochSeconds * 1_000_000_000L) + now.nanosecondsOfSecond
21+
val deltaNanos = (nowNanos - time).coerceAtLeast(0L)
22+
23+
if (time >= 0) metricRegistry.timer("event.latency").update((deltaNanos), TimeUnit.NANOSECONDS)
24+
25+
client.publishEvent("pubsub", "ping", nowNanos).subscribe()
26+
27+
metricRegistry.counter("ping.count").inc()
28+
}
29+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package ac.at.uibk.dps.dapr.pingPong.ping
2+
3+
import io.dapr.Topic
4+
import io.dapr.actors.ActorId
5+
import io.dapr.actors.client.ActorClient
6+
import io.dapr.actors.client.ActorProxyBuilder
7+
import io.dapr.client.domain.CloudEvent
8+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
9+
import org.springframework.web.bind.annotation.PostMapping
10+
import org.springframework.web.bind.annotation.RequestBody
11+
import org.springframework.web.bind.annotation.RestController
12+
13+
@RestController
14+
@ConditionalOnProperty("app.role", havingValue = "ping")
15+
class PingSubscriber {
16+
private val pingProxy =
17+
ActorProxyBuilder(PingActor::class.java, ActorClient()).build(ActorId("ping-1"))
18+
19+
@Topic(name = "pong", pubsubName = "pubsub")
20+
@PostMapping("/pong")
21+
fun pongSubscriber(@RequestBody event: CloudEvent<Long>) {
22+
pingProxy.ping(event.data)
23+
}
24+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package ac.at.uibk.dps.dapr.pingPong.pong
2+
3+
import io.dapr.actors.ActorType
4+
5+
@ActorType(name = "PongActor")
6+
interface PongActor {
7+
fun pong(time: Long)
8+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package ac.at.uibk.dps.dapr.pingPong.pong
2+
3+
import ac.at.uibk.dps.dapr.pingPong.PingPong
4+
import io.dapr.actors.ActorId
5+
import io.dapr.actors.runtime.AbstractActor
6+
import io.dapr.actors.runtime.ActorRuntimeContext
7+
import io.dapr.client.DaprClient
8+
import io.dapr.client.DaprClientBuilder
9+
import java.util.concurrent.TimeUnit
10+
import kotlin.time.Clock
11+
12+
class PongActorImpl(runtimeContext: ActorRuntimeContext<PongActorImpl>, actorId: ActorId) :
13+
AbstractActor(runtimeContext, actorId), PongActor {
14+
val client: DaprClient = DaprClientBuilder().build()
15+
16+
var metricRegistry = PingPong.provideMetricRegistry()
17+
18+
override fun pong(time: Long) {
19+
val now = Clock.System.now()
20+
val nowNanos = (now.epochSeconds * 1_000_000_000L) + now.nanosecondsOfSecond
21+
val deltaNanos = (nowNanos - time).coerceAtLeast(0L)
22+
23+
metricRegistry.timer("event.latency").update((deltaNanos), TimeUnit.NANOSECONDS)
24+
25+
client.publishEvent("pubsub", "pong", nowNanos).subscribe()
26+
}
27+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package ac.at.uibk.dps.dapr.pingPong.pong
2+
3+
import io.dapr.Topic
4+
import io.dapr.actors.ActorId
5+
import io.dapr.actors.client.ActorClient
6+
import io.dapr.actors.client.ActorProxyBuilder
7+
import io.dapr.client.domain.CloudEvent
8+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
9+
import org.springframework.web.bind.annotation.PostMapping
10+
import org.springframework.web.bind.annotation.RequestBody
11+
import org.springframework.web.bind.annotation.RestController
12+
13+
@RestController
14+
@ConditionalOnProperty("app.role", havingValue = "pong")
15+
class PongSubscriber {
16+
private val pongProxy =
17+
ActorProxyBuilder(PongActor::class.java, ActorClient()).build(ActorId("pong-1"))
18+
19+
@Topic(name = "ping", pubsubName = "pubsub")
20+
@PostMapping("/ping")
21+
fun pingSubscriber(@RequestBody event: CloudEvent<Long>) {
22+
pongProxy.pong(event.data)
23+
}
24+
}

0 commit comments

Comments
 (0)