Skip to content

Commit

Permalink
Add k6 example script
Browse files Browse the repository at this point in the history
(cherry picked from commit d423833)
  • Loading branch information
palkan committed Sep 1, 2021
1 parent 079665f commit 5cf579b
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 4 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,5 @@
/yarn-error.log
yarn-debug.log*
.yarn-integrity

.k6/k6
2 changes: 2 additions & 0 deletions config/environments/development.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,6 @@
# Use an evented file watcher to asynchronously detect changes in source code,
# routes, locales, etc. This feature depends on the listen gem.
config.file_watcher = ActiveSupport::EventedFileUpdateChecker

config.log_level = :warn if ENV["ANYCABLE_DEBUG"] == "0"
end
2 changes: 1 addition & 1 deletion config/puma.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
# Workers do not work on JRuby or Windows (both of which do not support
# processes).
#
# workers ENV.fetch("WEB_CONCURRENCY") { 2 }
workers ENV.fetch("WEB_CONCURRENCY", 2)

# Use the `preload_app!` method when specifying a `workers` number.
# This directive tells Puma to first boot the application and load code
Expand Down
5 changes: 2 additions & 3 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ x-backend: &backend
CHROME_URL: http://chrome:3333
BOOTSNAP_CACHE_DIR: /usr/local/bundle/_bootsnap
WEBPACKER_DEV_SERVER_HOST: webpacker
WEB_CONCURRENCY: 1
WEB_CONCURRENCY: ${WEB_CONCURRENCY:-1}
HISTFILE: /app/log/.bash_history
PSQL_HISTFILE: /app/log/.psql_history
EDITOR: vi
Expand Down Expand Up @@ -93,7 +93,7 @@ services:
ANYCABLE_HOST: "0.0.0.0"
ANYCABLE_REDIS_URL: redis://redis:6379/0
ANYCABLE_RPC_HOST: anycable:50051
ANYCABLE_DEBUG: 1
ANYCABLE_DEBUG: ${ANYCABLE_DEBUG:-1}
ANYCABLE_METRICS_HOST: "0.0.0.0"
ANYCABLE_METRICS_PORT: 5100
ANYCABLE_METRICS_HTTP: /metrics
Expand All @@ -120,7 +120,6 @@ services:
<<: *backend_environment
ANYCABLE_REDIS_URL: redis://redis:6379/0
ANYCABLE_RPC_HOST: 0.0.0.0:50051
ANYCABLE_DEBUG: 1
ports:
- '50051'
depends_on:
Expand Down
25 changes: 25 additions & 0 deletions etc/k6/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Benchmarking with k6

This folder contains benchmark/stress test scenarios to be used with [k6][].

We rely on the [xk6-cable][] extension, so you must first build a custom k6 executable (following the instructions described in the xk6-cable repo) and keep it in the `.k6/` folder.

## Scenarios

All scenarios support the following env vars:

- `CABLE_URL`: WebSocket connection url (defaults to `ws://localhost:8080/cable`).
- `WORKSPACE`: Workspace ID.

### `chat.js`

Connect to a chat room, send N (env `NUM` or 5) messages and expect to receive own messages back:

```sh
./k6 run --vus 20 --duration 10s chat.js
```

This scenario uses ramping VUs executor, you can control the max number of VUs and the total duration via the `MAX` and `TIME` env vars (default to 20 and 120 respectively).

[k6]: https://k6.io
[xk6-cable]: https://github.com/anycable/xk6-cable
84 changes: 84 additions & 0 deletions etc/k6/chat.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
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";

import { Trend } from "k6/metrics";

let rttTrend = new Trend("rtt", true);

let userId = `100${__VU}`;
let userName = `Kay${userId}`;

const URL = __ENV.CABLE_URL || "ws://localhost:8080/cable";
const WORKSPACE = __ENV.WORKSPACE || "demo";

// The number of messages each VU sends during an iteration
const MESSAGES_NUM = parseInt(__ENV.NUM || "5");
// Max VUs during the peak
const MAX = parseInt(__ENV.MAX || "20");
// Total test duration
const TIME = parseInt(__ENV.TIME || "120");

export let options = {
thresholds: {
checks: ["rate>0.9"],
},
scenarios: {
chat: {
executor: "ramping-vus",
startVUs: (MAX / 10 || 1) | 0,
stages: [
{ duration: `${TIME / 3}s`, target: (MAX / 4) | 0 },
{ duration: `${(7 * TIME) / 12}s`, target: MAX },
{ duration: `${TIME / 12}s`, target: 0 },
],
},
},
};

export default function () {
let client = cable.connect(URL, {
cookies: `uid=${userName}/${userId}`,
receiveTimeoutMS: 30000,
});

if (
!check(client, {
"successful connection": (obj) => obj,
})
) {
fail("connection failed");
}

let channel = client.subscribe("ChatChannel", { id: WORKSPACE });

if (
!check(channel, {
"successful subscription": (obj) => obj,
})
) {
fail("failed to subscribe");
}

for (let i = 0; i < MESSAGES_NUM; i++) {
let startMessage = Date.now();
channel.perform("speak", { message: `hello from ${userName}` });

let message = channel.receive({ author_id: userId });

if (
!check(message, {
"received its own message": (obj) => obj,
})
) {
fail("expected message hasn't been received");
}

let endMessage = Date.now();
rttTrend.add(endMessage - startMessage);

sleep(randomIntBetween(5, 10) / 10);
}

client.disconnect();
}

0 comments on commit 5cf579b

Please sign in to comment.