-
-
Notifications
You must be signed in to change notification settings - Fork 64
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
402 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
// Build k6 with xk6-cable like this: | ||
// xk6 build v0.38.3 --with github.com/anycable/[email protected] | ||
|
||
import { check, sleep, fail } from "k6"; | ||
import cable from "k6/x/cable"; | ||
import { randomIntBetween } from "https://jslib.k6.io/k6-utils/1.1.0/index.js"; | ||
|
||
const rampingOptions = { | ||
scenarios: { | ||
default: { | ||
executor: "ramping-vus", | ||
startVUs: 100, | ||
stages: [ | ||
{ duration: "10s", target: 300 }, | ||
{ duration: "10s", target: 500 }, | ||
{ duration: "10s", target: 1000 }, | ||
{ duration: "10s", target: 1300 }, | ||
{ duration: "10s", target: 1500 }, | ||
{ duration: "30s", target: 1500 }, | ||
{ duration: "30s", target: 0 }, | ||
], | ||
gracefulStop: "1m", | ||
gracefulRampDown: "1m", | ||
}, | ||
}, | ||
}; | ||
|
||
export const options = __ENV.SKIP_OPTIONS ? {} : rampingOptions; | ||
|
||
import { Trend, Counter } from "k6/metrics"; | ||
let rttTrend = new Trend("rtt", true); | ||
let subTrend = new Trend("suback", true); | ||
let broadcastTrend = new Trend("broadcast_duration", true); | ||
let historyTrend = new Trend("history_duration", true); | ||
let historyRcvd = new Counter("history_rcvd"); | ||
let broadcastsSent = new Counter("broadcasts_sent"); | ||
let broadcastsRcvd = new Counter("broadcasts_rcvd"); | ||
let acksRcvd = new Counter("acks_rcvd"); | ||
|
||
// Load ENV from .env | ||
function loadDotEnv() { | ||
try { | ||
let dotenv = open("./.env"); | ||
dotenv.split(/[\n\r]/m).forEach((line) => { | ||
// Ignore comments | ||
if (line[0] === "#") return; | ||
|
||
let parts = line.split("=", 2); | ||
|
||
__ENV[parts[0]] = parts[1]; | ||
}); | ||
} catch (_err) {} | ||
} | ||
|
||
loadDotEnv(); | ||
|
||
let config = __ENV; | ||
|
||
config.URL = config.URL || "ws://localhost:8080/cable"; | ||
|
||
let url = config.URL; | ||
let channelName = config.CHANNEL_ID || "BenchmarkChannel"; | ||
|
||
let numChannels = parseInt(config.NUM_CHANNELS || "5") || 5; | ||
let channelStreamId = __VU % numChannels; | ||
|
||
let sendersRatio = parseFloat(config.SENDERS_RATIO || "0.3") || 1; | ||
let sendersMod = (1 / sendersRatio) | 0; | ||
let sender = __VU % sendersMod == 0; | ||
|
||
let sendingRate = parseFloat(config.SENDING_RATE || "0.3"); | ||
|
||
let iterations = (config.N || "20") | 0; | ||
|
||
export default function () { | ||
let cableOptions = { | ||
receiveTimeoutMs: 15000, | ||
logLevel: config.DEBUG === "true" ? "debug" : "info", | ||
}; | ||
|
||
// Prevent from creating a lot of connections at once | ||
sleep(randomIntBetween(2, 10) / 5); | ||
|
||
let client = cable.connect(url, cableOptions); | ||
|
||
if ( | ||
!check(client, { | ||
"successful connection": (obj) => obj, | ||
}) | ||
) { | ||
// Cooldown | ||
sleep(randomIntBetween(5, 10) / 5); | ||
fail("connection failed"); | ||
} | ||
|
||
let channel = client.subscribe(channelName, { id: channelStreamId }); | ||
|
||
if ( | ||
!check(channel, { | ||
"successful subscription": (obj) => obj, | ||
}) | ||
) { | ||
// Cooldown | ||
sleep(randomIntBetween(5, 10) / 5); | ||
fail("failed to subscribe"); | ||
} | ||
|
||
subTrend.add(channel.ackDuration()); | ||
|
||
let subscribedAt = Date.now(); | ||
|
||
let result = channel.history({ since: ((Date.now() - 10000) / 1000) | 0 }); | ||
check(result, { | ||
"history received": (obj) => obj, | ||
}); | ||
historyTrend.add(channel.historyDuration()); | ||
|
||
for (let i = 0; ; i++) { | ||
// Sampling | ||
if (sender && randomIntBetween(1, 10) / 10 <= sendingRate) { | ||
let start = Date.now(); | ||
broadcastsSent.add(1); | ||
// Create message via cable instead of a form | ||
channel.perform("broadcast", { | ||
ts: start, | ||
content: `hello from ${__VU} numero ${i + 1}`, | ||
}); | ||
} | ||
|
||
sleep(randomIntBetween(5, 10) / 100); | ||
|
||
let incoming = channel.receiveAll(1); | ||
|
||
for (let message of incoming) { | ||
let received = message.__timestamp__; | ||
|
||
if (message.action == "broadcastResult") { | ||
acksRcvd.add(1); | ||
let ts = message.ts; | ||
rttTrend.add(received - ts); | ||
} | ||
|
||
if (message.action == "broadcast") { | ||
let ts = message.ts; | ||
|
||
// Historical message, shouldn't be taken into account for broadcast duration | ||
if (ts < subscribedAt) { | ||
historyRcvd.add(1); | ||
continue; | ||
} | ||
|
||
broadcastsRcvd.add(1); | ||
broadcastTrend.add(received - ts); | ||
} | ||
} | ||
|
||
sleep(randomIntBetween(5, 10) / 10); | ||
|
||
if (i > iterations) break; | ||
} | ||
|
||
sleep(randomIntBetween(5, 10) / 10); | ||
|
||
client.disconnect(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,215 @@ | ||
# Broker benchmarks | ||
|
||
Scenario: broadcast + history retrieval. | ||
|
||
Key metrics: rtt trend, broadcast trend, history trend. | ||
|
||
No RPC server (gobench-cable binary). | ||
|
||
Multi-node setup uses Traefik as a load balancer and 3 AnyCable-Go nodes. | ||
|
||
## NUM_CHANNELS=5, SENDING_RATE=0.7, SENDERS_RATIO=0.5 | ||
|
||
This configuration helps to see the difference between brokers. Having less channels or broadcast per seconds works fine for all brokers. | ||
|
||
Speaking of the number we have: | ||
|
||
- ~125-150 broadcast/s; | ||
- ~120-150k historical messages received out of ~1kk in total (70-80k/s). | ||
|
||
## Multi-node, no broker, Redis pub/sub | ||
|
||
This setup doesn't use any broker; we can refer to it as baseline setup for multi-node tests. | ||
|
||
```sh | ||
✓ successful connection | ||
✓ successful subscription | ||
✗ history received | ||
↳ 0% — ✓ 0 / ✗ 3267 | ||
|
||
acks_rcvd............: 7198 49.775111/s | ||
broadcast_duration...: avg=4.13ms min=0s med=3ms max=43ms p(90)=7ms p(95)=10ms | ||
broadcasts_rcvd......: 1688013 11672.830561/s | ||
broadcasts_sent......: 7198 49.775111/s | ||
checks...............: 66.66% ✓ 6534 ✗ 3267 | ||
data_received........: 271 MB 1.9 MB/s | ||
data_sent............: 2.9 MB 20 kB/s | ||
history_duration.....: avg=266.91µs min=0s med=0s max=17ms p(90)=0s p(95)=1ms | ||
history_rcvd.........: 19 0.131387/s | ||
iteration_duration...: avg=42.11s min=39.07s med=42.12s max=45.18s p(90)=43.37s p(95)=43.74s | ||
iterations...........: 3267 22.591732/s | ||
rtt..................: avg=2.93ms min=0s med=2ms max=33ms p(90)=6ms p(95)=8ms | ||
suback...............: avg=335.47µs min=0s med=0s max=19ms p(90)=0s p(95)=2ms | ||
vus..................: 6 min=6 max=1500 | ||
vus_max..............: 1500 min=1500 max=1500 | ||
ws_connecting........: avg=4.14ms min=594.08µs med=1.78ms max=113.94ms p(90)=8.48ms p(95)=11.67ms | ||
ws_msgs_received.....: 1759570 12167.65657/s | ||
ws_msgs_sent.........: 13732 94.958575/s | ||
ws_sessions..........: 3267 22.591732/s | ||
``` | ||
|
||
## Multi-node, Redis broker, Redis pub/sub | ||
|
||
```sh | ||
✓ successful connection | ||
✓ successful subscription | ||
✓ history received | ||
|
||
acks_rcvd............: 7183 49.690276/s | ||
broadcast_duration...: avg=4.21ms min=0s med=3ms max=44ms p(90)=8ms p(95)=10ms | ||
broadcasts_rcvd......: 1673008 11573.469106/s | ||
broadcasts_sent......: 7183 49.690276/s | ||
checks...............: 100.00% ✓ 9798 ✗ 0 | ||
data_received........: 406 MB 2.8 MB/s | ||
data_sent............: 2.9 MB 20 kB/s | ||
history_duration.....: avg=1.71ms min=0s med=1ms max=26ms p(90)=3ms p(95)=5ms | ||
history_rcvd.........: 269789 1866.33576/s | ||
iteration_duration...: avg=42.07s min=38.46s med=42.07s max=45.3s p(90)=43.3s p(95)=43.63s | ||
iterations...........: 3266 22.593407/s | ||
rtt..................: avg=3.15ms min=0s med=3ms max=27ms p(90)=6ms p(95)=8ms | ||
suback...............: avg=132.57µs min=0s med=0s max=12ms p(90)=0s p(95)=1ms | ||
vus..................: 2 min=2 max=1500 | ||
vus_max..............: 1500 min=1500 max=1500 | ||
ws_connecting........: avg=3.01ms min=584.7µs med=1.61ms max=30.99ms p(90)=6.93ms p(95)=9.54ms | ||
ws_msgs_received.....: 2014672 13937.018921/s | ||
ws_msgs_sent.........: 13715 94.877089/s | ||
ws_sessions..........: 3266 22.593407/s | ||
``` | ||
|
||
## Multi-node, embedded NATS broker and pub/sub | ||
|
||
```sh | ||
✓ successful connection | ||
✓ successful subscription | ||
✓ history received | ||
|
||
acks_rcvd............: 7206 49.568904/s | ||
broadcast_duration...: avg=4.78ms min=0s med=4ms max=221ms p(90)=9ms p(95)=11ms | ||
broadcasts_rcvd......: 1692092 11639.626116/s | ||
broadcasts_sent......: 7206 49.568904/s | ||
checks...............: 100.00% ✓ 9792 ✗ 0 | ||
data_received........: 407 MB 2.8 MB/s | ||
data_sent............: 2.9 MB 20 kB/s | ||
history_duration.....: avg=22.44ms min=0s med=3ms max=237ms p(90)=67.7ms p(95)=92ms | ||
history_rcvd.........: 266712 1834.668541/s | ||
iteration_duration...: avg=42.13s min=38.9s med=42.12s max=45.35s p(90)=43.42s p(95)=43.77s | ||
iterations...........: 3264 22.452526/s | ||
rtt..................: avg=1.92ms min=0s med=1ms max=63ms p(90)=5ms p(95)=7ms | ||
suback...............: avg=181.37µs min=0s med=0s max=16ms p(90)=0s p(95)=1ms | ||
vus..................: 2 min=2 max=1500 | ||
vus_max..............: 1500 min=1500 max=1500 | ||
ws_connecting........: avg=3.3ms min=473.95µs med=1.67ms max=44.94ms p(90)=7.85ms p(95)=10.64ms | ||
ws_msgs_received.....: 2030319 13966.23473/s | ||
ws_msgs_sent.........: 13734 94.473956/s | ||
ws_sessions..........: 3264 22.452526/s | ||
``` | ||
|
||
## Single node, memory broker | ||
|
||
```sh | ||
✓ successful connection | ||
✓ successful subscription | ||
✓ history received | ||
|
||
acks_rcvd............: 7157 49.461266/s | ||
broadcast_duration...: avg=1.77ms min=0s med=1ms max=16ms p(90)=3ms p(95)=4ms | ||
broadcasts_rcvd......: 1671812 11553.715077/s | ||
broadcasts_sent......: 7157 49.461266/s | ||
checks...............: 100.00% ✓ 9786 ✗ 0 | ||
data_received........: 404 MB 2.8 MB/s | ||
data_sent............: 2.9 MB 20 kB/s | ||
history_duration.....: avg=931.94µs min=0s med=1ms max=21ms p(90)=2ms p(95)=3ms | ||
history_rcvd.........: 267642 1849.645421/s | ||
iteration_duration...: avg=42.13s min=39.04s med=42.14s max=45.28s p(90)=43.34s p(95)=43.68s | ||
iterations...........: 3262 22.543335/s | ||
rtt..................: avg=585.72µs min=0s med=0s max=11ms p(90)=2ms p(95)=3ms | ||
suback...............: avg=77.25µs min=0s med=0s max=8ms p(90)=0s p(95)=0s | ||
vus..................: 6 min=6 max=1500 | ||
vus_max..............: 1500 min=1500 max=1500 | ||
ws_connecting........: avg=948.18µs min=167µs med=521.35µs max=11.82ms p(90)=2.06ms p(95)=3.08ms | ||
ws_msgs_received.....: 2010091 13891.525299/s | ||
ws_msgs_sent.........: 13681 94.547937/s | ||
ws_sessions..........: 3262 22.543335/s | ||
``` | ||
|
||
## Single node, Redis broker | ||
|
||
```sh | ||
✓ successful connection | ||
✓ successful subscription | ||
✓ history received | ||
|
||
acks_rcvd............: 7181 49.690377/s | ||
broadcast_duration...: avg=2.32ms min=0s med=2ms max=32ms p(90)=4ms p(95)=5ms | ||
broadcasts_rcvd......: 1671940 11569.325909/s | ||
broadcasts_sent......: 7181 49.690377/s | ||
checks...............: 100.00% ✓ 9804 ✗ 0 | ||
data_received........: 404 MB 2.8 MB/s | ||
data_sent............: 2.9 MB 20 kB/s | ||
history_duration.....: avg=1.55ms min=0s med=1ms max=18ms p(90)=3ms p(95)=4ms | ||
history_rcvd.........: 269239 1863.053542/s | ||
iteration_duration...: avg=42.11s min=38.69s med=42.11s max=45.73s p(90)=43.33s p(95)=43.69s | ||
iterations...........: 3268 22.613585/s | ||
rtt..................: avg=863.66µs min=0s med=1ms max=31ms p(90)=2ms p(95)=3ms | ||
suback...............: avg=110.77µs min=0s med=0s max=19ms p(90)=0s p(95)=1ms | ||
vus..................: 5 min=5 max=1500 | ||
vus_max..............: 1500 min=1500 max=1500 | ||
ws_connecting........: avg=876.93µs min=115.54µs med=524.25µs max=12.09ms p(90)=2.02ms p(95)=2.81ms | ||
ws_msgs_received.....: 2012477 13925.740336/s | ||
ws_msgs_sent.........: 13717 94.917547/s | ||
ws_sessions..........: 3268 22.613585/s | ||
``` | ||
|
||
## Single node, embedded NATS broker | ||
|
||
```sh | ||
✓ successful connection | ||
✓ successful subscription | ||
✓ history received | ||
|
||
acks_rcvd............: 7205 49.640288/s | ||
broadcast_duration...: avg=2.16ms min=0s med=2ms max=195ms p(90)=4ms p(95)=5ms | ||
broadcasts_rcvd......: 1676679 11551.815128/s | ||
broadcasts_sent......: 7205 49.640288/s | ||
checks...............: 100.00% ✓ 9798 ✗ 0 | ||
data_received........: 404 MB 2.8 MB/s | ||
data_sent............: 2.9 MB 20 kB/s | ||
history_duration.....: avg=20.16ms min=0s med=2ms max=213ms p(90)=60ms p(95)=86ms | ||
history_rcvd.........: 268459 1849.601944/s | ||
iteration_duration...: avg=42.1s min=39.17s med=42.09s max=45.55s p(90)=43.41s p(95)=43.78s | ||
iterations...........: 3266 22.50176/s | ||
rtt..................: avg=934.21µs min=0s med=1ms max=64ms p(90)=2ms p(95)=3ms | ||
suback...............: avg=96.44µs min=0s med=0s max=20ms p(90)=0s p(95)=0s | ||
vus..................: 1 min=1 max=1500 | ||
vus_max..............: 1500 min=1500 max=1500 | ||
ws_connecting........: avg=914.67µs min=177.33µs med=512.52µs max=31.01ms p(90)=2.13ms p(95)=3ms | ||
ws_msgs_received.....: 2016894 13895.794378/s | ||
ws_msgs_sent.........: 13737 94.643807/s | ||
ws_sessions..........: 3266 22.50176/s | ||
``` | ||
|
||
## Notes | ||
|
||
Commands used to run server with different configurations: | ||
|
||
```sh | ||
# Single node, no broker | ||
PORT=8080 ANYCABLE_DEBUG=0 ANYCABLE_LOG_LEVEL=info make run-gobench | ||
|
||
# Single node, memory | ||
ANYCABLE_BROKER=memory PORT=8080 ANYCABLE_DEBUG=0 ANYCABLE_LOG_LEVEL=info make run-gobench | ||
|
||
# Single node, Redis | ||
ANYCABLE_BROKER=redis PORT=8080 ANYCABLE_DEBUG=0 ANYCABLE_LOG_LEVEL=info make run-gobench | ||
|
||
# Multi-node | ||
PORT=8081 ANYCABLE_BROKER=redis ANYCABLE_PUBSUB=redis ANYCABLE_DEBUG=0 ANYCABLE_LOG_LEVEL=info make run-gobench | ||
PORT=8082 ANYCABLE_BROKER=redis ANYCABLE_PUBSUB=redis ANYCABLE_DEBUG=0 ANYCABLE_LOG_LEVEL=info make run-gobench | ||
PORT=8083 ANYCABLE_BROKER=redis ANYCABLE_PUBSUB=redis ANYCABLE_DEBUG=0 ANYCABLE_LOG_LEVEL=info make run-gobench | ||
(cd etc/traefik && traefik --configFile=traefik.yml) | ||
|
||
# Multi-node w/ eNATS | ||
ANYCABLE_EMBED_NATS=1 ANYCABLE_ENATS_CLUSTER=nats://localhost:4342 ANYCABLE_ENATS_ADDR=nats://localhost:4242 ANYCABLE_ENATS_CLUSTER_ROUTES=nats://localhost:4342 PORT=8081 ANYCABLE_PRESETS=broker ANYCABLE_BROADCAST_ADAPTER=nats ANYCABLE_DEBUG=0 ANYCABLE_LOG_LEVEL=info ANYCABLE_ENATS_DEBUG=true make run-gobench | ||
ANYCABLE_EMBED_NATS=1 ANYCABLE_ENATS_CLUSTER=nats://localhost:4343 ANYCABLE_ENATS_CLUSTER_ROUTES=nats://localhost:4342 ANYCABLE_ENATS_ADDR=nats://localhost:4243 PORT=8082 ANYCABLE_PRESETS=broker ANYCABLE_BROADCAST_ADAPTER=nats ANYCABLE_DEBUG=0 ANYCABLE_LOG_LEVEL=info ANYCABLE_ENATS_DEBUG=true make run-gobench | ||
ANYCABLE_EMBED_NATS=1 ANYCABLE_ENATS_CLUSTER=nats://localhost:4344 ANYCABLE_ENATS_CLUSTER_ROUTES=nats://localhost:4342 ANYCABLE_ENATS_ADDR=nats://localhost:4244 PORT=8083 ANYCABLE_PRESETS=broker ANYCABLE_BROADCAST_ADAPTER=nats ANYCABLE_DEBUG=0 ANYCABLE_LOG_LEVEL=info ANYCABLE_ENATS_DEBUG=true make run-gobench | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# rules.toml | ||
|
||
[http.routers.websocket] | ||
rule = "Host(`localhost`) && Path(`/cable`)" | ||
service = "websocket" | ||
|
||
[http.services.websocket.loadBalancer] | ||
[[http.services.websocket.loadBalancer.servers]] | ||
url = "http://localhost:8081" | ||
[[http.services.websocket.loadBalancer.servers]] | ||
url = "http://localhost:8082" | ||
[[http.services.websocket.loadBalancer.servers]] | ||
url = "http://localhost:8083" |
Oops, something went wrong.