Skip to content

Commit 5d105c6

Browse files
authored
feat: adding metrics and Vagrantfile to chameneos example (#24)
1 parent c61e37b commit 5d105c6

11 files changed

Lines changed: 283 additions & 65 deletions

File tree

.github/workflows/docker.yml

Lines changed: 1 addition & 1 deletion
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 ]
18+
target: [ diningPhilosophers, count, cigaretteSmokers, chameneos ]
1919
permissions:
2020
packages: write
2121
contents: read

chameneos.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 :chameneos:distZip
8+
9+
RUN unzip chameneos/build/distributions/chameneos.zip -d /tmp
10+
11+
FROM gcr.io/distroless/java25-debian13 AS runtime
12+
13+
COPY --from=build /tmp/chameneos /opt/chameneos
14+
15+
ENTRYPOINT ["java", "-cp", "/opt/chameneos/lib/*", "ac.at.uibk.dps.dapr.chameneos.ChameneosKt"]

chameneos.Vagrantfile

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
2+
Vagrant.configure("2") do |config|
3+
nodes = {
4+
"redis_pub_sub" => {},
5+
"mall" => {},
6+
"w0" => { "CHAMENEOS_ID" => "0" },
7+
"w1" => { "CHAMENEOS_ID" => "1" },
8+
"w2" => { "CHAMENEOS_ID" => "2" },
9+
"w3" => { "CHAMENEOS_ID" => "3" },
10+
"w4" => { "CHAMENEOS_ID" => "4" },
11+
"w5" => { "CHAMENEOS_ID" => "5" },
12+
"w6" => { "CHAMENEOS_ID" => "6" },
13+
"w7" => { "CHAMENEOS_ID" => "7" },
14+
"w8" => { "CHAMENEOS_ID" => "8" },
15+
"w9" => { "CHAMENEOS_ID" => "9" },
16+
"w10" => { "CHAMENEOS_ID" => "10" },
17+
"w11" => { "CHAMENEOS_ID" => "11" }
18+
}
19+
20+
nodes.each do |name, env_vars|
21+
config.vm.define name do |node|
22+
node.vm.box = "generic/ubuntu2004"
23+
ip_suffix = case name
24+
when "mall" then 10
25+
when "redis_pub_sub" then 9
26+
else 11 + name[1..-1].to_i
27+
end
28+
node.vm.network "private_network", ip: "192.168.56.#{ip_suffix}"
29+
node.vm.synced_folder ".", "/app"
30+
node.vm.provision "shell", inline: <<-SHELL
31+
apt-get update -qq && apt-get install -y docker.io linuxptp
32+
33+
if [ "#{name}" = "redis_pub_sub" ]; then
34+
docker run -d --name redis --network host redis:8.2.4-alpine \
35+
redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru --save ""
36+
exit 0
37+
fi
38+
# Every node gets own state store (default config is fine)
39+
docker rm -f redis || true
40+
docker run -d --name redis --network host redis:8.2.4-alpine
41+
42+
# Every node gets its own placement
43+
docker rm -f placement || true
44+
docker run -d --name placement --network host daprio/placement:1.16.0 ./placement --port 50006
45+
46+
# Sidecar
47+
docker rm -f #{name}-sidecar || true
48+
docker run -d \
49+
--name #{name}-sidecar \
50+
--network host \
51+
-v /app/config/redis/components-vagrant:/components \
52+
daprio/daprd:edge \
53+
./daprd \
54+
--app-id #{name}-sidecar \
55+
--app-port 3000 \
56+
--resources-path /components \
57+
--placement-host-address localhost:50006 \
58+
--metrics-port 9091
59+
60+
sleep 2
61+
62+
# Application
63+
docker rm -f #{name} || true
64+
if [ "#{name}" = "mall" ]; then
65+
docker run -d \
66+
--name #{name} \
67+
--network host \
68+
-e ROLE=mall \
69+
-e DAPR_HTTP_ENDPOINT=http://localhost:3500 \
70+
-e METRICS_DIRECTORY=/app/metrics \
71+
-e DAPR_GRPC_ENDPOINT=http://localhost:50001 \
72+
-v /app/chameneos/runTest/metrics_#{name}:/app/metrics \
73+
collaborativestatemachines/cirrina-baselines-chameneos:unstable
74+
else
75+
docker run -d \
76+
--name #{name} \
77+
--network host \
78+
-e ROLE=chameneos \
79+
-e CHAMENEOS_ID=#{env_vars['CHAMENEOS_ID']} \
80+
-e DAPR_HTTP_ENDPOINT=http://localhost:3500 \
81+
-e METRICS_DIRECTORY=/app/metrics \
82+
-e DAPR_GRPC_ENDPOINT=http://localhost:50001 \
83+
-v /app/chameneos/runTest/metrics_#{name}:/app/metrics \
84+
collaborativestatemachines/cirrina-baselines-chameneos:unstable
85+
fi
86+
87+
nohup bash -c 'while true; do docker stats --no-stream --format "$(date +%s),{{.Name}},{{.CPUPerc}},{{.MemUsage}}" >> /app/chameneos/run/metrics_#{name}/docker_stats.csv; sleep 1; done' &
88+
SHELL
89+
end
90+
end
91+
end

chameneos/src/main/kotlin/ac/at/uibk/dps/dapr/chameneos/Chameneos.kt

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,61 @@ package ac.at.uibk.dps.dapr.chameneos
33
import ac.at.uibk.dps.dapr.chameneos.chameneos.ChameneosActor
44
import ac.at.uibk.dps.dapr.chameneos.chameneos.ChameneosActorImpl
55
import ac.at.uibk.dps.dapr.chameneos.mall.MallActorImpl
6+
import com.codahale.metrics.CsvReporter
7+
import com.codahale.metrics.MetricRegistry
68
import io.dapr.actors.ActorId
79
import io.dapr.actors.client.ActorClient
810
import io.dapr.actors.client.ActorProxyBuilder
911
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.Files
20+
import java.nio.file.Paths
21+
import java.util.concurrent.TimeUnit
1022
import org.springframework.boot.ApplicationArguments
1123
import org.springframework.boot.ApplicationRunner
1224
import org.springframework.boot.autoconfigure.SpringBootApplication
1325
import org.springframework.boot.runApplication
1426
import org.springframework.stereotype.Component
1527

16-
@SpringBootApplication class Chameneos
28+
@SpringBootApplication
29+
class Chameneos {
30+
companion object {
31+
val metricsDirectory = System.getenv("METRICS_DIRECTORY") ?: "metrics"
32+
33+
fun provideMetricRegistry(): MetricRegistry =
34+
MetricRegistry().apply {
35+
val path = Paths.get(metricsDirectory).toAbsolutePath()
36+
Files.createDirectories(path)
37+
38+
CsvReporter.forRegistry(this).build(path.toFile()).start(1L, TimeUnit.SECONDS)
39+
40+
object :
41+
DropwizardMeterRegistry(
42+
object : DropwizardConfig {
43+
override fun get(key: String): String? = null
44+
45+
override fun prefix(): String = ""
46+
},
47+
this,
48+
HierarchicalNameMapper.DEFAULT,
49+
Clock.SYSTEM,
50+
) {
51+
override fun nullGaugeValue(): Double = Double.NaN
52+
}
53+
.apply {
54+
ProcessorMetrics().bindTo(this)
55+
JvmMemoryMetrics().bindTo(this)
56+
JvmGcMetrics().bindTo(this)
57+
}
58+
}
59+
}
60+
}
1761

1862
fun main(args: Array<String>) {
1963
val role = System.getenv("ROLE") ?: "chameneos"

chameneos/src/main/kotlin/ac/at/uibk/dps/dapr/chameneos/chameneos/ChameneosActor.kt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,11 @@ package ac.at.uibk.dps.dapr.chameneos.chameneos
22

33
import io.dapr.actors.ActorType
44

5-
data class MeetRequest(val partner: String = "", val partnerColor: Int = 0)
6-
75
@ActorType(name = "ChameneosActor")
86
interface ChameneosActor {
97
fun request()
108

11-
fun meet(request: MeetRequest)
9+
fun meet(data: Map<String, Any>)
1210

13-
fun change(partnerColor: Int)
11+
fun change(data: Map<String, Any>)
1412
}
Lines changed: 70 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,95 @@
11
package ac.at.uibk.dps.dapr.chameneos.chameneos
22

3+
import ac.at.uibk.dps.dapr.chameneos.Chameneos
34
import io.dapr.actors.ActorId
45
import io.dapr.actors.runtime.AbstractActor
56
import io.dapr.actors.runtime.ActorRuntimeContext
67
import io.dapr.client.DaprClientBuilder
8+
import java.util.concurrent.TimeUnit
79
import kotlin.random.Random
10+
import kotlin.time.Clock
11+
import kotlin.time.measureTime
12+
import kotlin.time.toJavaDuration
813

914
class ChameneosActorImpl(
1015
runtimeContext: ActorRuntimeContext<ChameneosActorImpl>,
1116
val actorId: ActorId,
1217
) : AbstractActor(runtimeContext, actorId), ChameneosActor {
13-
1418
val client = DaprClientBuilder().build()
19+
1520
var color = Random.nextInt(1, 4)
1621

22+
var metricRegistry = Chameneos.provideMetricRegistry()
23+
24+
var eventTimer = metricRegistry.timer("event.latency")
25+
var requestTimer = metricRegistry.timer("request.duration")
26+
var meetTimer = metricRegistry.timer("meet.duration")
27+
var changeTimer = metricRegistry.timer("change.duration")
28+
1729
override fun request() {
18-
client
19-
.publishEvent(
20-
"pubsub",
21-
"request",
22-
mapOf<String, Any>("requestor" to actorId.toString(), "color" to color),
23-
)
24-
.subscribe()
30+
val delta = measureTime {
31+
val now = Clock.System.now()
32+
val epochNanos = (now.epochSeconds * 1_000_000_000L) + now.nanosecondsOfSecond
33+
34+
client
35+
.publishEvent(
36+
"pubsub",
37+
"request",
38+
mapOf<String, Any>(
39+
"requestor" to actorId.toString(),
40+
"color" to color,
41+
"time" to epochNanos,
42+
),
43+
)
44+
.subscribe()
45+
}
46+
requestTimer.update(delta.toJavaDuration())
2547
}
2648

27-
override fun meet(request: MeetRequest) {
28-
color = if (color == request.partnerColor) color else (color xor request.partnerColor)
29-
client
30-
.publishEvent(
31-
"pubsub",
32-
"change",
33-
mapOf<String, Any>("partner" to request.partner, "color" to color),
34-
)
35-
.subscribe()
49+
override fun meet(data: Map<String, Any>) {
50+
val delta = measureTime {
51+
val time = data["time"] as Long
52+
val partnerColor = data["color"] as Int
53+
val partner = data["partner"] as String
54+
55+
var now = Clock.System.now()
56+
var nowNanos = (now.epochSeconds * 1_000_000_000L) + now.nanosecondsOfSecond
57+
58+
val deltaNanos = (nowNanos - time).coerceAtLeast(0L)
3659

60+
eventTimer.update(deltaNanos, TimeUnit.NANOSECONDS)
61+
62+
color = if (color == partnerColor) color else (color xor partnerColor)
63+
64+
now = Clock.System.now()
65+
nowNanos = (now.epochSeconds * 1_000_000_000L) + now.nanosecondsOfSecond
66+
client
67+
.publishEvent(
68+
"pubsub",
69+
"change",
70+
mapOf<String, Any>("partner" to partner, "color" to color, "time" to nowNanos),
71+
)
72+
.subscribe()
73+
}
74+
meetTimer.update(delta.toJavaDuration())
3775
request()
3876
}
3977

40-
override fun change(partnerColor: Int) {
41-
color = partnerColor
78+
override fun change(data: Map<String, Any>) {
79+
val delta = measureTime {
80+
val time = data["time"] as Long
81+
82+
val now = Clock.System.now()
83+
val nowNanos = (now.epochSeconds * 1_000_000_000L) + now.nanosecondsOfSecond
84+
85+
val deltaNanos = (nowNanos - time).coerceAtLeast(0L)
86+
87+
eventTimer.update(deltaNanos, TimeUnit.NANOSECONDS)
88+
89+
color = data["color"] as Int
90+
}
91+
changeTimer.update(delta.toJavaDuration())
92+
4293
request()
4394
}
4495
}

chameneos/src/main/kotlin/ac/at/uibk/dps/dapr/chameneos/chameneos/ChameneosSubscriber.kt

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,26 +21,22 @@ class ChameneosSubscriber {
2121
@Topic(name = "meet", pubsubName = "pubsub")
2222
@PostMapping("/meet")
2323
fun handleMeet(@RequestBody body: Map<String, Any>) {
24-
val data = body["data"] as? Map<*, *> ?: body
24+
val data = body["data"] as? Map<String, Any> ?: body
2525
val initiator = data["initiator"] as? String ?: return
2626

2727
if (initiator == id) {
28-
val partner = data["partner"] as? String ?: return
29-
val color = (data["color"] as? Number)?.toInt() ?: return
30-
31-
chameneosProxy.meet(MeetRequest(partner, color))
28+
chameneosProxy.meet(data)
3229
}
3330
}
3431

3532
@Topic(name = "change", pubsubName = "pubsub")
3633
@PostMapping("/change")
3734
fun handleChange(@RequestBody body: Map<String, Any>) {
38-
val data = body["data"] as? Map<*, *> ?: body
35+
val data = body["data"] as? Map<String, Any> ?: body
3936
val partner = data["partner"] as? String ?: return
4037

4138
if (partner == id) {
42-
val color = (data["color"] as? Number)?.toInt() ?: return
43-
chameneosProxy.change(color)
39+
chameneosProxy.change(data)
4440
}
4541
}
4642
}
Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package ac.at.uibk.dps.dapr.chameneos.mall
22

3-
data class MallRequest(val requester: String = "", val color: Int = 0)
4-
53
@io.dapr.actors.ActorType(name = "MallActor")
64
interface MallActor {
7-
fun requesting(request: MallRequest)
5+
fun requesting(data: Map<String, Any>)
86
}

0 commit comments

Comments
 (0)