diff --git a/.github/workflows/release-tag.yaml b/.github/workflows/release-tag.yaml
index 2e47063..a258fdd 100644
--- a/.github/workflows/release-tag.yaml
+++ b/.github/workflows/release-tag.yaml
@@ -62,6 +62,10 @@ jobs:
name: Create GitHub release
runs-on: ubuntu-latest
steps:
+ - id: get-tag
+ run: |
+ tag=$(echo "${{github.ref}}" | sed 's/^refs\/tags\/v//')
+ echo "::set-output name=tag::$tag"
- uses: actions/checkout@v2
- uses: actions/setup-go@v2
with:
@@ -72,7 +76,7 @@ jobs:
make build
working-directory: server
env:
- DSPS_VERSION_ID=${{ steps.get-tag.outputs.tag }}
+ DSPS_VERSION_ID: ${{ steps.get-tag.outputs.tag }}
# https://zenn.dev/seita/articles/d1dba77043be8fd50eeb
- name: Get the version
diff --git a/loadtest/.gitignore b/loadtest/.gitignore
new file mode 100644
index 0000000..40b1010
--- /dev/null
+++ b/loadtest/.gitignore
@@ -0,0 +1,6 @@
+/summary.json
+/test.json
+
+/node_modules
+/report.*.json
+/yarn-error.log
diff --git a/loadtest/.keep b/loadtest/.keep
deleted file mode 100644
index e69de29..0000000
diff --git a/loadtest/experiment-local.sh b/loadtest/experiment-local.sh
new file mode 100755
index 0000000..5c078c0
--- /dev/null
+++ b/loadtest/experiment-local.sh
@@ -0,0 +1,77 @@
+#!/bin/bash -e
+if [ $# -le 1 ]; then
+ echo "Usage: $0 EXPERIMENT_DIR OUTPUT_DIR"
+ echo " EXPERIMENT_DIR: directory of the experiment, must have dsps.config.yaml file"
+ exit 1
+fi
+
+EXPERIMENT_DIR="$(realpath "$1")"
+OUTPUT_DIR="$(realpath "$2")"
+cd "$(dirname "$0")"
+trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT
+
+# Ensure $1 points valid directory
+if [ ! -f "${EXPERIMENT_DIR}/dsps.config.yaml" ]; then
+ echo "DSPS config file not found: ${EXPERIMENT_DIR}/dsps.config.yaml" >&2
+ exit 2
+fi
+
+# Create output directories
+LOG_DIR="${OUTPUT_DIR}/logs"
+RESULT_DIR="${OUTPUT_DIR}/result"
+test -d "${LOG_DIR}" && rm -r "${LOG_DIR}"
+mkdir -p "${LOG_DIR}"
+test -d "${RESULT_DIR}" && rm -r "${RESULT_DIR}"
+mkdir -p "${RESULT_DIR}"
+exec > >(tee -a "${LOG_DIR}/experiment-local.log") 2>&1 # Save all logs after this
+
+# Show some limits
+ulimit -a
+sysctl kern.ipc.somaxconn || true
+
+export REDIS1_PORT=$(($(($RANDOM%1000))+10000))
+export REDIS2_PORT=$(($(($RANDOM%1000))+11000))
+DSPS_PORT=$(($(($RANDOM%1000))+12000))
+
+echo "Starting redis servers... (port: ${REDIS1_PORT}, ${REDIS2_PORT})"
+redis-server --port ${REDIS1_PORT} > "${LOG_DIR}/redis1.log" 2>&1 &
+redis-server --port ${REDIS2_PORT} > "${LOG_DIR}/redis2.log" 2>&1 &
+
+echo "Check redis server readiness..."
+until redis-cli -p "${REDIS1_PORT}" ping
+do
+ echo "Waiting until redis get ready..."
+ sleep 1
+done
+until redis-cli -p "${REDIS2_PORT}" ping
+do
+ echo "Waiting until redis get ready..."
+ sleep 1
+done
+
+echo "Starting DSPS server... (port: ${DSPS_PORT})"
+pushd ../server
+cat "${EXPERIMENT_DIR}/dsps.config.yaml" | envsubst '${REDIS1_PORT} ${REDIS2_PORT}' | \
+ go run main.go --port ${DSPS_PORT} --dump-config - > "${LOG_DIR}/dsps.log" 2>&1 &
+popd
+
+echo "Check DSPS server readiness..."
+until curl -s "http://localhost:${DSPS_PORT}/probe/readiness"
+do
+ echo "Waiting until DSPS server get ready..."
+ sleep 1
+done
+
+echo "Starting loadtest... ($(LC_ALL=C date))"
+BASE_URL="http://localhost:${DSPS_PORT}" \
+ time k6 run \
+ "--summary-export=${RESULT_DIR}/summary.json" \
+ --out "json=${RESULT_DIR}/data.json" \
+ --summary-trend-stats="min,avg,med,max,p(90),p(95),p(99)" \
+ ./loadtest.k6.js \
+ | tee "${LOG_DIR}/k6.log"
+echo "Experiment completed ($(LC_ALL=C date))."
+
+echo "Compressing logs..."
+gzip "${LOG_DIR}"/*.log
+gzip "${RESULT_DIR}/data.json"
diff --git a/loadtest/experiments/20201229-on-macbook/README.md b/loadtest/experiments/20201229-on-macbook/README.md
new file mode 100644
index 0000000..d25873e
--- /dev/null
+++ b/loadtest/experiments/20201229-on-macbook/README.md
@@ -0,0 +1,53 @@
+# Quick load test experiment on single macbook
+
+## Environment
+
+- MacBook Pro (mac OS 10.15.7, mem 16 GB, Core i7-8557U 8 threads)
+ - [k6 (load test tool)](https://k6.io/) v0.29.0
+ - DSPS server (Darwin/amd64 build, source code revision `804907ea773b8cbeba55fee4594be2bf79b68a43`)
+ - 2x [redis:6.0.9](https://hub.docker.com/_/redis) server processes
+
+## Test scenario
+
+- Load test script runs...
+ - `1` publisher per channel
+ - `3` subscribers per channel
+- Publisher publishes `3` messages with `1` second interval
+- Subscriber call message acknowledge API after `0.01` sec, then polling next message after `0.01` sec sleep.
+
+For detail, see [../../loadtest.k6.js](../../loadtest.k6.js).
+
+## Key results
+
+Keep in mind: everything runs on single machine in this experiment (**load testing tool occupies most of CPU so that it should cause latency**).
+
+| # | msg/sec | HTTP req/sec | Msg delay [ms]
(incl. 20ms sleep *1) | Publish API TTFB [ms] | Acknowledge API TTFB [ms] | Total CPU Usage
Sys + User |
+| -------------- | -------- | ------------ | --------------------------------------- | ------------------------ | ------------------------- | ----------------------------- |
+| ` 50` ( `150`) | ` 406.7` | ` 689.1` | `22, 27, 29, 37` | `1.2, 2.9, 3.9, 10.7` | `0.8, 2.2, 3.0, 5.6` | 10% + 15-18% |
+| `100` ( `300`) | ` 811.6` | `1374.8` | `22, 26, 29, 64` | `0.9, 2.8, 4.2, 30.3` | `0.7, 2.0, 2.9, 9.4` | 10% + 20% |
+| `200` ( `600`) | `1608.1` | `2714.1` | `22, 33, 42, 329` | `2.0, 6.8, 10.3, 179.4` | `1.0, 4.1, 7.1, 16.6` | 15% + 35-50% |
+| `300` ( `900`) | `2278.2` | `3838.5` | `23, 41, 62, 1470.3` | `1.6, 7.5, 12.3, 450.7` | `1.1, 5.3, 9.2, 22.9` | 20% + 50-60% |
+| `400` (`1200`) | `2636.6` | `4524.1` | `36, 215, 326, 2616.1` | `7.9, 61.2, 90.3, 830.4` | `5.2, 49.5, 70.5, 115.9` | 30% + 60-70% |
+
+- Values of 4-tuple are `median, 90 percentile, 95 percentile, 99 percentile`.
+- `TTFB` = [Time to first byte](https://en.wikipedia.org/wiki/Time_to_first_byte)
+- `#` column shows total number of `channels (subscribers)`.
+- `msg/sec` column shows average count of received messages per second.
+- `HTTP req/sec` column shows HTTP request rate to DSPS server (note that this experiment runs only 1 server process).
+- `Total CPU Usage` column shows CPU usage of the machine, 100% means all CPU cores running in top gear.
+
+`*1`: Note that subscribers sleep `20` ms (`2 * 0.01` sec) for each API cycles in this scenario. `Msg delay` (duration from messsage JSON creation to received) contains the sleep durations.
+
+## Raw data
+
+See [result](./result) directory for k6 output files.
+
+Look [../../experiment-local.sh](../../experiment-local.sh) and [../../loadtest.k6.js](../../loadtest.k6.js) for experiment detail.
+
+## Steps to reproduction
+
+1. Increase `nofile` soft ulimit and `kern.ipc.somaxconn` sysctl value (macOS default is to small)
+2. Run `CHANNELS=nnn TEST_RAMPUP_SEC=0 TEST_DURATION_SEC=30 ../../experiment-local.sh . output-name`
+ - Replace `CHANNEL=nnn` to desired channel count
+ - Replace `output-name` to test pattern name (directory name)
+ - See [../../loadtest.k6.js](../../loadtest.k6.js) for more available options
diff --git a/loadtest/experiments/20201229-on-macbook/dsps.config.yaml b/loadtest/experiments/20201229-on-macbook/dsps.config.yaml
new file mode 100644
index 0000000..48158b2
--- /dev/null
+++ b/loadtest/experiments/20201229-on-macbook/dsps.config.yaml
@@ -0,0 +1,17 @@
+logging:
+ category:
+ http: WARN
+
+storages:
+ redis1:
+ redis:
+ singleNode: "127.0.0.1:${REDIS1_PORT}"
+ connection:
+ max: 2048
+ min: 2048
+ redis2:
+ redis:
+ singleNode: "127.0.0.1:${REDIS2_PORT}"
+ connection:
+ max: 2048
+ min: 2048
diff --git a/loadtest/experiments/20201229-on-macbook/result/channels-100/logs/dsps.log.gz b/loadtest/experiments/20201229-on-macbook/result/channels-100/logs/dsps.log.gz
new file mode 100644
index 0000000..e088d4a
Binary files /dev/null and b/loadtest/experiments/20201229-on-macbook/result/channels-100/logs/dsps.log.gz differ
diff --git a/loadtest/experiments/20201229-on-macbook/result/channels-100/logs/k6.log.gz b/loadtest/experiments/20201229-on-macbook/result/channels-100/logs/k6.log.gz
new file mode 100644
index 0000000..5a8beb6
Binary files /dev/null and b/loadtest/experiments/20201229-on-macbook/result/channels-100/logs/k6.log.gz differ
diff --git a/loadtest/experiments/20201229-on-macbook/result/channels-100/logs/redis1.log.gz b/loadtest/experiments/20201229-on-macbook/result/channels-100/logs/redis1.log.gz
new file mode 100644
index 0000000..22d65ce
Binary files /dev/null and b/loadtest/experiments/20201229-on-macbook/result/channels-100/logs/redis1.log.gz differ
diff --git a/loadtest/experiments/20201229-on-macbook/result/channels-100/logs/redis2.log.gz b/loadtest/experiments/20201229-on-macbook/result/channels-100/logs/redis2.log.gz
new file mode 100644
index 0000000..6d4c2d2
Binary files /dev/null and b/loadtest/experiments/20201229-on-macbook/result/channels-100/logs/redis2.log.gz differ
diff --git a/loadtest/experiments/20201229-on-macbook/result/channels-100/result/data.json.gz b/loadtest/experiments/20201229-on-macbook/result/channels-100/result/data.json.gz
new file mode 100644
index 0000000..340edd5
Binary files /dev/null and b/loadtest/experiments/20201229-on-macbook/result/channels-100/result/data.json.gz differ
diff --git a/loadtest/experiments/20201229-on-macbook/result/channels-100/result/summary.json b/loadtest/experiments/20201229-on-macbook/result/channels-100/result/summary.json
new file mode 100644
index 0000000..32222ed
--- /dev/null
+++ b/loadtest/experiments/20201229-on-macbook/result/channels-100/result/summary.json
@@ -0,0 +1,224 @@
+{
+ "metrics": {
+ "checks": {
+ "fails": 0,
+ "passes": 63573,
+ "thresholds": {
+ "rate >= 1.0": false
+ },
+ "value": 0
+ },
+ "data_received": {
+ "count": 16532061,
+ "rate": 500656.54146150406
+ },
+ "data_sent": {
+ "count": 9156485,
+ "rate": 277294.77359441994
+ },
+ "dsps_fetched_messages": {
+ "count": 26800,
+ "rate": 811.6105615124641,
+ "thresholds": {
+ "count >= 10800": false
+ }
+ },
+ "dsps_msg_delay_ms": {
+ "avg": 21.427835820895524,
+ "max": 1052,
+ "med": 22,
+ "min": 0,
+ "p(90)": 26,
+ "p(95)": 29
+ },
+ "dsps_ttfb_ms_ack": {
+ "avg": 1.1637135524186788,
+ "max": 714.848,
+ "med": 0.685,
+ "min": 0.195,
+ "p(90)": 1.961800000000001,
+ "p(95)": 2.9386999999999937
+ },
+ "dsps_ttfb_ms_publish": {
+ "avg": 5.262406555555541,
+ "max": 736.772,
+ "med": 0.882,
+ "min": 0.254,
+ "p(90)": 2.8497000000000026,
+ "p(95)": 4.226
+ },
+ "group_duration": {
+ "avg": 382.9728388429216,
+ "max": 3020.788957,
+ "med": 21.874523,
+ "min": 0.000283,
+ "p(90)": 982.9938394,
+ "p(95)": 1002.9519002000001
+ },
+ "http_req_blocked": {
+ "avg": 0.2462839772681683,
+ "max": 52.504,
+ "med": 0.002,
+ "min": 0.001,
+ "p(90)": 0.004,
+ "p(95)": 0.007
+ },
+ "http_req_connecting": {
+ "avg": 0.24100724701528697,
+ "max": 43.27,
+ "med": 0,
+ "min": 0,
+ "p(90)": 0,
+ "p(95)": 0
+ },
+ "http_req_duration": {
+ "avg": 204.68227979205966,
+ "max": 3007.625,
+ "med": 1.085,
+ "min": 0.216,
+ "p(90)": 959.422,
+ "p(95)": 961.253
+ },
+ "http_req_receiving": {
+ "avg": 0.04582640204414061,
+ "max": 31.406,
+ "med": 0.025,
+ "min": 0.008,
+ "p(90)": 0.052,
+ "p(95)": 0.081
+ },
+ "http_req_sending": {
+ "avg": 0.05237973038460094,
+ "max": 19.573,
+ "med": 0.011,
+ "min": 0.005,
+ "p(90)": 0.035,
+ "p(95)": 0.088
+ },
+ "http_req_tls_handshaking": {
+ "avg": 0,
+ "max": 0,
+ "med": 0,
+ "min": 0,
+ "p(90)": 0,
+ "p(95)": 0
+ },
+ "http_req_waiting": {
+ "avg": 204.58407365963194,
+ "max": 3007.581,
+ "med": 1.025,
+ "min": 0.195,
+ "p(90)": 959.3563,
+ "p(95)": 961.1981499999999
+ },
+ "http_reqs": {
+ "count": 45398,
+ "rate": 1374.831950430703
+ },
+ "iteration_duration": {
+ "avg": 599.2223159736039,
+ "max": 3020.823341,
+ "med": 978.705485,
+ "min": 0.229524,
+ "p(90)": 1003.7370645,
+ "p(95)": 1007.05829225
+ },
+ "iterations": {
+ "count": 21175,
+ "rate": 641.2631955233742
+ },
+ "vus": {
+ "max": 400,
+ "min": 6,
+ "value": 6
+ },
+ "vus_max": {
+ "max": 400,
+ "min": 400,
+ "value": 400
+ }
+ },
+ "root_group": {
+ "name": "",
+ "path": "",
+ "id": "d41d8cd98f00b204e9800998ecf8427e",
+ "groups": {
+ "publisher": {
+ "name": "publisher",
+ "path": "::publisher",
+ "id": "42834ed9b0fab346fb485eef504a199e",
+ "groups": {},
+ "checks": {
+ "is status 200": {
+ "name": "is status 200",
+ "path": "::publisher::is status 200",
+ "id": "033b4844056b121c9936aedc4ab12017",
+ "passes": 9000,
+ "fails": 0
+ }
+ }
+ },
+ "setup": {
+ "name": "setup",
+ "path": "::setup",
+ "id": "5c0f8025f7e0b6654089e5b00e950f1a",
+ "groups": {},
+ "checks": {}
+ },
+ "subscriber": {
+ "name": "subscriber",
+ "path": "::subscriber",
+ "id": "8cd2fe283a36cc055d74315ba7db7039",
+ "groups": {
+ "ack": {
+ "name": "ack",
+ "path": "::subscriber::ack",
+ "id": "896b291f5e9f4a0b4a9d63c5bd0aa610",
+ "groups": {},
+ "checks": {
+ "is status 204": {
+ "name": "is status 204",
+ "path": "::subscriber::ack::is status 204",
+ "id": "1675e96687d722c0ea932d5d2fbfd93f",
+ "passes": 17923,
+ "fails": 0
+ }
+ }
+ },
+ "fetch": {
+ "name": "fetch",
+ "path": "::subscriber::fetch",
+ "id": "219dd6d5879771ae4590fb2c3826ad84",
+ "groups": {},
+ "checks": {
+ "has messages array": {
+ "name": "has messages array",
+ "path": "::subscriber::fetch::has messages array",
+ "id": "8bcfe7144bfdfba71334cca76e6453c6",
+ "passes": 18175,
+ "fails": 0
+ },
+ "is status 200": {
+ "name": "is status 200",
+ "path": "::subscriber::fetch::is status 200",
+ "id": "bee4378527eb142a3168d665c6ac821a",
+ "passes": 18175,
+ "fails": 0
+ }
+ }
+ }
+ },
+ "checks": {
+ "is status 200": {
+ "name": "is status 200",
+ "path": "::subscriber::is status 200",
+ "id": "96fdc221e3cd9ed60329154e5bb89f28",
+ "passes": 300,
+ "fails": 0
+ }
+ }
+ }
+ },
+ "checks": {}
+ }
+}
diff --git a/loadtest/experiments/20201229-on-macbook/result/channels-200/logs/dsps.log.gz b/loadtest/experiments/20201229-on-macbook/result/channels-200/logs/dsps.log.gz
new file mode 100644
index 0000000..e718653
Binary files /dev/null and b/loadtest/experiments/20201229-on-macbook/result/channels-200/logs/dsps.log.gz differ
diff --git a/loadtest/experiments/20201229-on-macbook/result/channels-200/logs/k6.log.gz b/loadtest/experiments/20201229-on-macbook/result/channels-200/logs/k6.log.gz
new file mode 100644
index 0000000..9a2e08a
Binary files /dev/null and b/loadtest/experiments/20201229-on-macbook/result/channels-200/logs/k6.log.gz differ
diff --git a/loadtest/experiments/20201229-on-macbook/result/channels-200/logs/redis1.log.gz b/loadtest/experiments/20201229-on-macbook/result/channels-200/logs/redis1.log.gz
new file mode 100644
index 0000000..b29ac87
Binary files /dev/null and b/loadtest/experiments/20201229-on-macbook/result/channels-200/logs/redis1.log.gz differ
diff --git a/loadtest/experiments/20201229-on-macbook/result/channels-200/logs/redis2.log.gz b/loadtest/experiments/20201229-on-macbook/result/channels-200/logs/redis2.log.gz
new file mode 100644
index 0000000..8543608
Binary files /dev/null and b/loadtest/experiments/20201229-on-macbook/result/channels-200/logs/redis2.log.gz differ
diff --git a/loadtest/experiments/20201229-on-macbook/result/channels-200/result/data.json.gz b/loadtest/experiments/20201229-on-macbook/result/channels-200/result/data.json.gz
new file mode 100644
index 0000000..962c674
Binary files /dev/null and b/loadtest/experiments/20201229-on-macbook/result/channels-200/result/data.json.gz differ
diff --git a/loadtest/experiments/20201229-on-macbook/result/channels-200/result/summary.json b/loadtest/experiments/20201229-on-macbook/result/channels-200/result/summary.json
new file mode 100644
index 0000000..5c0b5a7
--- /dev/null
+++ b/loadtest/experiments/20201229-on-macbook/result/channels-200/result/summary.json
@@ -0,0 +1,224 @@
+{
+ "metrics": {
+ "checks": {
+ "fails": 0,
+ "passes": 125487,
+ "thresholds": {
+ "rate >= 1.0": false
+ },
+ "value": 0
+ },
+ "data_received": {
+ "count": 32677893,
+ "rate": 989706.1201036749
+ },
+ "data_sent": {
+ "count": 18125801,
+ "rate": 548971.0178523846
+ },
+ "dsps_fetched_messages": {
+ "count": 53096,
+ "rate": 1608.1035626447742,
+ "thresholds": {
+ "count >= 21600": false
+ }
+ },
+ "dsps_msg_delay_ms": {
+ "avg": 26.38835317161368,
+ "max": 1228,
+ "med": 22,
+ "min": 0,
+ "p(90)": 33,
+ "p(95)": 42
+ },
+ "dsps_ttfb_ms_ack": {
+ "avg": 2.0270198414944773,
+ "max": 70.381,
+ "med": 0.992,
+ "min": 0.188,
+ "p(90)": 4.14,
+ "p(95)": 7.0865499999999955
+ },
+ "dsps_ttfb_ms_publish": {
+ "avg": 8.488173544438867,
+ "max": 877.705,
+ "med": 1.98,
+ "min": 0.284,
+ "p(90)": 6.754,
+ "p(95)": 10.3185
+ },
+ "group_duration": {
+ "avg": 389.2914580048516,
+ "max": 3021.040137,
+ "med": 22.5484945,
+ "min": 0.000606,
+ "p(90)": 990.2148010000001,
+ "p(95)": 1007.7678539999999
+ },
+ "http_req_blocked": {
+ "avg": 0.37921010109945,
+ "max": 166.643,
+ "med": 0.002,
+ "min": 0.001,
+ "p(90)": 0.004,
+ "p(95)": 0.007
+ },
+ "http_req_connecting": {
+ "avg": 0.3732484433235874,
+ "max": 155.589,
+ "med": 0,
+ "min": 0,
+ "p(90)": 0,
+ "p(95)": 0
+ },
+ "http_req_duration": {
+ "avg": 209.19263639609758,
+ "max": 3010.212,
+ "med": 1.995,
+ "min": 0.215,
+ "p(90)": 960.69,
+ "p(95)": 966.1507
+ },
+ "http_req_receiving": {
+ "avg": 0.06560775102104156,
+ "max": 75.403,
+ "med": 0.023,
+ "min": 0.008,
+ "p(90)": 0.052,
+ "p(95)": 0.095
+ },
+ "http_req_sending": {
+ "avg": 0.09644968420114108,
+ "max": 43.129,
+ "med": 0.011,
+ "min": 0.004,
+ "p(90)": 0.056,
+ "p(95)": 0.14734999999999127
+ },
+ "http_req_tls_handshaking": {
+ "avg": 0,
+ "max": 0,
+ "med": 0,
+ "min": 0,
+ "p(90)": 0,
+ "p(95)": 0
+ },
+ "http_req_waiting": {
+ "avg": 209.0305789608755,
+ "max": 3010.117,
+ "med": 1.9,
+ "min": 0.188,
+ "p(90)": 960.61,
+ "p(95)": 966.06635
+ },
+ "http_reqs": {
+ "count": 89614,
+ "rate": 2714.1139193696094
+ },
+ "iteration_duration": {
+ "avg": 610.1495090745699,
+ "max": 3021.062741,
+ "med": 975.550473,
+ "min": 0.159508,
+ "p(90)": 1009.905706,
+ "p(95)": 1020.092668
+ },
+ "iterations": {
+ "count": 41810,
+ "rate": 1266.2876667579103
+ },
+ "vus": {
+ "max": 800,
+ "min": 0,
+ "value": 0
+ },
+ "vus_max": {
+ "max": 800,
+ "min": 800,
+ "value": 800
+ }
+ },
+ "root_group": {
+ "name": "",
+ "path": "",
+ "id": "d41d8cd98f00b204e9800998ecf8427e",
+ "groups": {
+ "publisher": {
+ "name": "publisher",
+ "path": "::publisher",
+ "id": "42834ed9b0fab346fb485eef504a199e",
+ "groups": {},
+ "checks": {
+ "is status 200": {
+ "name": "is status 200",
+ "path": "::publisher::is status 200",
+ "id": "033b4844056b121c9936aedc4ab12017",
+ "passes": 17811,
+ "fails": 0
+ }
+ }
+ },
+ "setup": {
+ "name": "setup",
+ "path": "::setup",
+ "id": "5c0f8025f7e0b6654089e5b00e950f1a",
+ "groups": {},
+ "checks": {}
+ },
+ "subscriber": {
+ "name": "subscriber",
+ "path": "::subscriber",
+ "id": "8cd2fe283a36cc055d74315ba7db7039",
+ "groups": {
+ "ack": {
+ "name": "ack",
+ "path": "::subscriber::ack",
+ "id": "896b291f5e9f4a0b4a9d63c5bd0aa610",
+ "groups": {},
+ "checks": {
+ "is status 204": {
+ "name": "is status 204",
+ "path": "::subscriber::ack::is status 204",
+ "id": "1675e96687d722c0ea932d5d2fbfd93f",
+ "passes": 35330,
+ "fails": 0
+ }
+ }
+ },
+ "fetch": {
+ "name": "fetch",
+ "path": "::subscriber::fetch",
+ "id": "219dd6d5879771ae4590fb2c3826ad84",
+ "groups": {},
+ "checks": {
+ "has messages array": {
+ "name": "has messages array",
+ "path": "::subscriber::fetch::has messages array",
+ "id": "8bcfe7144bfdfba71334cca76e6453c6",
+ "passes": 35873,
+ "fails": 0
+ },
+ "is status 200": {
+ "name": "is status 200",
+ "path": "::subscriber::fetch::is status 200",
+ "id": "bee4378527eb142a3168d665c6ac821a",
+ "passes": 35873,
+ "fails": 0
+ }
+ }
+ }
+ },
+ "checks": {
+ "is status 200": {
+ "name": "is status 200",
+ "path": "::subscriber::is status 200",
+ "id": "96fdc221e3cd9ed60329154e5bb89f28",
+ "passes": 600,
+ "fails": 0
+ }
+ }
+ }
+ },
+ "checks": {}
+ }
+}
diff --git a/loadtest/experiments/20201229-on-macbook/result/channels-300/logs/dsps.log.gz b/loadtest/experiments/20201229-on-macbook/result/channels-300/logs/dsps.log.gz
new file mode 100644
index 0000000..0bc30e7
Binary files /dev/null and b/loadtest/experiments/20201229-on-macbook/result/channels-300/logs/dsps.log.gz differ
diff --git a/loadtest/experiments/20201229-on-macbook/result/channels-300/logs/k6.log.gz b/loadtest/experiments/20201229-on-macbook/result/channels-300/logs/k6.log.gz
new file mode 100644
index 0000000..ddaf86a
Binary files /dev/null and b/loadtest/experiments/20201229-on-macbook/result/channels-300/logs/k6.log.gz differ
diff --git a/loadtest/experiments/20201229-on-macbook/result/channels-300/logs/redis1.log.gz b/loadtest/experiments/20201229-on-macbook/result/channels-300/logs/redis1.log.gz
new file mode 100644
index 0000000..c76173d
Binary files /dev/null and b/loadtest/experiments/20201229-on-macbook/result/channels-300/logs/redis1.log.gz differ
diff --git a/loadtest/experiments/20201229-on-macbook/result/channels-300/logs/redis2.log.gz b/loadtest/experiments/20201229-on-macbook/result/channels-300/logs/redis2.log.gz
new file mode 100644
index 0000000..1b05bdc
Binary files /dev/null and b/loadtest/experiments/20201229-on-macbook/result/channels-300/logs/redis2.log.gz differ
diff --git a/loadtest/experiments/20201229-on-macbook/result/channels-300/result/data.json.gz b/loadtest/experiments/20201229-on-macbook/result/channels-300/result/data.json.gz
new file mode 100644
index 0000000..1600050
Binary files /dev/null and b/loadtest/experiments/20201229-on-macbook/result/channels-300/result/data.json.gz differ
diff --git a/loadtest/experiments/20201229-on-macbook/result/channels-300/result/summary.json b/loadtest/experiments/20201229-on-macbook/result/channels-300/result/summary.json
new file mode 100644
index 0000000..505ec03
--- /dev/null
+++ b/loadtest/experiments/20201229-on-macbook/result/channels-300/result/summary.json
@@ -0,0 +1,224 @@
+{
+ "metrics": {
+ "checks": {
+ "fails": 0,
+ "passes": 177470,
+ "thresholds": {
+ "rate >= 1.0": false
+ },
+ "value": 0
+ },
+ "data_received": {
+ "count": 46270831,
+ "rate": 1400545.4007832198
+ },
+ "data_sent": {
+ "count": 25667488,
+ "rate": 776914.5591540919
+ },
+ "dsps_fetched_messages": {
+ "count": 75267,
+ "rate": 2278.2139071751403,
+ "thresholds": {
+ "count >= 32400": false
+ }
+ },
+ "dsps_msg_delay_ms": {
+ "avg": 59.32956009937954,
+ "max": 6172,
+ "med": 23,
+ "min": 0,
+ "p(90)": 41,
+ "p(95)": 62
+ },
+ "dsps_ttfb_ms_ack": {
+ "avg": 2.524096623794175,
+ "max": 114.377,
+ "med": 1.075,
+ "min": 0.187,
+ "p(90)": 5.319199999999997,
+ "p(95)": 9.230049999999997
+ },
+ "dsps_ttfb_ms_publish": {
+ "avg": 23.279035686274398,
+ "max": 5773.416,
+ "med": 1.599,
+ "min": 0.272,
+ "p(90)": 7.466100000000003,
+ "p(95)": 12.309099999999997
+ },
+ "group_duration": {
+ "avg": 406.69089204222087,
+ "max": 7047.806164,
+ "med": 23.290035,
+ "min": 0.00026,
+ "p(90)": 990.4311532,
+ "p(95)": 1008.7664149999999
+ },
+ "http_req_blocked": {
+ "avg": 0.7481098608179423,
+ "max": 110.081,
+ "med": 0.003,
+ "min": 0.001,
+ "p(90)": 0.005,
+ "p(95)": 0.01
+ },
+ "http_req_connecting": {
+ "avg": 0.7397475140953348,
+ "max": 107.905,
+ "med": 0,
+ "min": 0,
+ "p(90)": 0,
+ "p(95)": 0
+ },
+ "http_req_duration": {
+ "avg": 224.87377527106796,
+ "max": 5775.351,
+ "med": 2.07,
+ "min": 0.209,
+ "p(90)": 959.9616,
+ "p(95)": 964.2076
+ },
+ "http_req_receiving": {
+ "avg": 0.0891607223120533,
+ "max": 106.81,
+ "med": 0.027,
+ "min": 0.008,
+ "p(90)": 0.051,
+ "p(95)": 0.093
+ },
+ "http_req_sending": {
+ "avg": 0.15000894215966185,
+ "max": 77.247,
+ "med": 0.013,
+ "min": 0.004,
+ "p(90)": 0.068,
+ "p(95)": 0.261
+ },
+ "http_req_tls_handshaking": {
+ "avg": 0,
+ "max": 0,
+ "med": 0,
+ "min": 0,
+ "p(90)": 0,
+ "p(95)": 0
+ },
+ "http_req_waiting": {
+ "avg": 224.63460560659286,
+ "max": 5773.416,
+ "med": 1.95,
+ "min": 0.187,
+ "p(90)": 959.8792000000001,
+ "p(95)": 964.0862999999999
+ },
+ "http_reqs": {
+ "count": 126815,
+ "rate": 3838.490927477054
+ },
+ "iteration_duration": {
+ "avg": 647.7388498626557,
+ "max": 7083.069628,
+ "med": 975.034647,
+ "min": 0.074195,
+ "p(90)": 1011.2709435,
+ "p(95)": 1040.8787275
+ },
+ "iterations": {
+ "count": 59155,
+ "rate": 1790.5289659338812
+ },
+ "vus": {
+ "max": 1200,
+ "min": 6,
+ "value": 6
+ },
+ "vus_max": {
+ "max": 1200,
+ "min": 1200,
+ "value": 1200
+ }
+ },
+ "root_group": {
+ "name": "",
+ "path": "",
+ "id": "d41d8cd98f00b204e9800998ecf8427e",
+ "groups": {
+ "publisher": {
+ "name": "publisher",
+ "path": "::publisher",
+ "id": "42834ed9b0fab346fb485eef504a199e",
+ "groups": {},
+ "checks": {
+ "is status 200": {
+ "name": "is status 200",
+ "path": "::publisher::is status 200",
+ "id": "033b4844056b121c9936aedc4ab12017",
+ "passes": 25500,
+ "fails": 0
+ }
+ }
+ },
+ "setup": {
+ "name": "setup",
+ "path": "::setup",
+ "id": "5c0f8025f7e0b6654089e5b00e950f1a",
+ "groups": {},
+ "checks": {}
+ },
+ "subscriber": {
+ "name": "subscriber",
+ "path": "::subscriber",
+ "id": "8cd2fe283a36cc055d74315ba7db7039",
+ "groups": {
+ "ack": {
+ "name": "ack",
+ "path": "::subscriber::ack",
+ "id": "896b291f5e9f4a0b4a9d63c5bd0aa610",
+ "groups": {},
+ "checks": {
+ "is status 204": {
+ "name": "is status 204",
+ "path": "::subscriber::ack::is status 204",
+ "id": "1675e96687d722c0ea932d5d2fbfd93f",
+ "passes": 49760,
+ "fails": 0
+ }
+ }
+ },
+ "fetch": {
+ "name": "fetch",
+ "path": "::subscriber::fetch",
+ "id": "219dd6d5879771ae4590fb2c3826ad84",
+ "groups": {},
+ "checks": {
+ "has messages array": {
+ "name": "has messages array",
+ "path": "::subscriber::fetch::has messages array",
+ "id": "8bcfe7144bfdfba71334cca76e6453c6",
+ "passes": 50655,
+ "fails": 0
+ },
+ "is status 200": {
+ "name": "is status 200",
+ "path": "::subscriber::fetch::is status 200",
+ "id": "bee4378527eb142a3168d665c6ac821a",
+ "passes": 50655,
+ "fails": 0
+ }
+ }
+ }
+ },
+ "checks": {
+ "is status 200": {
+ "name": "is status 200",
+ "path": "::subscriber::is status 200",
+ "id": "96fdc221e3cd9ed60329154e5bb89f28",
+ "passes": 900,
+ "fails": 0
+ }
+ }
+ }
+ },
+ "checks": {}
+ }
+}
diff --git a/loadtest/experiments/20201229-on-macbook/result/channels-400/logs/dsps.log.gz b/loadtest/experiments/20201229-on-macbook/result/channels-400/logs/dsps.log.gz
new file mode 100644
index 0000000..8110b67
Binary files /dev/null and b/loadtest/experiments/20201229-on-macbook/result/channels-400/logs/dsps.log.gz differ
diff --git a/loadtest/experiments/20201229-on-macbook/result/channels-400/logs/k6.log.gz b/loadtest/experiments/20201229-on-macbook/result/channels-400/logs/k6.log.gz
new file mode 100644
index 0000000..65343bb
Binary files /dev/null and b/loadtest/experiments/20201229-on-macbook/result/channels-400/logs/k6.log.gz differ
diff --git a/loadtest/experiments/20201229-on-macbook/result/channels-400/logs/redis1.log.gz b/loadtest/experiments/20201229-on-macbook/result/channels-400/logs/redis1.log.gz
new file mode 100644
index 0000000..c41643e
Binary files /dev/null and b/loadtest/experiments/20201229-on-macbook/result/channels-400/logs/redis1.log.gz differ
diff --git a/loadtest/experiments/20201229-on-macbook/result/channels-400/logs/redis2.log.gz b/loadtest/experiments/20201229-on-macbook/result/channels-400/logs/redis2.log.gz
new file mode 100644
index 0000000..301af4b
Binary files /dev/null and b/loadtest/experiments/20201229-on-macbook/result/channels-400/logs/redis2.log.gz differ
diff --git a/loadtest/experiments/20201229-on-macbook/result/channels-400/result/data.json.gz b/loadtest/experiments/20201229-on-macbook/result/channels-400/result/data.json.gz
new file mode 100644
index 0000000..90a4bc2
Binary files /dev/null and b/loadtest/experiments/20201229-on-macbook/result/channels-400/result/data.json.gz differ
diff --git a/loadtest/experiments/20201229-on-macbook/result/channels-400/result/summary.json b/loadtest/experiments/20201229-on-macbook/result/channels-400/result/summary.json
new file mode 100644
index 0000000..73ca4ac
--- /dev/null
+++ b/loadtest/experiments/20201229-on-macbook/result/channels-400/result/summary.json
@@ -0,0 +1,224 @@
+{
+ "metrics": {
+ "checks": {
+ "fails": 0,
+ "passes": 209445,
+ "thresholds": {
+ "rate >= 1.0": false
+ },
+ "value": 0
+ },
+ "data_received": {
+ "count": 54519978,
+ "rate": 1648488.9243541933
+ },
+ "data_sent": {
+ "count": 30326543,
+ "rate": 916966.0752513728
+ },
+ "dsps_fetched_messages": {
+ "count": 87198,
+ "rate": 2636.5553050266626,
+ "thresholds": {
+ "count >= 43200": false
+ }
+ },
+ "dsps_msg_delay_ms": {
+ "avg": 131.50333723250534,
+ "max": 6328,
+ "med": 36,
+ "min": 0,
+ "p(90)": 215,
+ "p(95)": 326
+ },
+ "dsps_ttfb_ms_ack": {
+ "avg": 16.62900326103567,
+ "max": 180.732,
+ "med": 5.199,
+ "min": 0.188,
+ "p(90)": 49.53140000000001,
+ "p(95)": 70.51739999999995
+ },
+ "dsps_ttfb_ms_publish": {
+ "avg": 63.543295283272414,
+ "max": 5847.173,
+ "med": 7.86,
+ "min": 0.306,
+ "p(90)": 61.267900000000004,
+ "p(95)": 90.32489999999999
+ },
+ "group_duration": {
+ "avg": 437.76350004147906,
+ "max": 7103.337582,
+ "med": 78.11372750000001,
+ "min": 0.000274,
+ "p(90)": 1020.9506247,
+ "p(95)": 1128.4400069
+ },
+ "http_req_blocked": {
+ "avg": 1.4159386056933243,
+ "max": 190.06,
+ "med": 0.003,
+ "min": 0.001,
+ "p(90)": 0.006,
+ "p(95)": 0.011
+ },
+ "http_req_connecting": {
+ "avg": 1.4059587563409375,
+ "max": 168.663,
+ "med": 0,
+ "min": 0,
+ "p(90)": 0,
+ "p(95)": 0
+ },
+ "http_req_duration": {
+ "avg": 246.63950601177567,
+ "max": 5847.267,
+ "med": 14.694,
+ "min": 0.22,
+ "p(90)": 959.3646000000001,
+ "p(95)": 977.8367999999999
+ },
+ "http_req_receiving": {
+ "avg": 0.22845847897718707,
+ "max": 126.862,
+ "med": 0.029,
+ "min": 0.007,
+ "p(90)": 0.059,
+ "p(95)": 0.149
+ },
+ "http_req_sending": {
+ "avg": 0.8592670311383076,
+ "max": 174.212,
+ "med": 0.015,
+ "min": 0.004,
+ "p(90)": 0.267,
+ "p(95)": 2.677899999999994
+ },
+ "http_req_tls_handshaking": {
+ "avg": 0,
+ "max": 0,
+ "med": 0,
+ "min": 0,
+ "p(90)": 0,
+ "p(95)": 0
+ },
+ "http_req_waiting": {
+ "avg": 245.55178050166296,
+ "max": 5847.173,
+ "med": 13.797,
+ "min": 0.188,
+ "p(90)": 959.1416,
+ "p(95)": 977.0154999999999
+ },
+ "http_reqs": {
+ "count": 149623,
+ "rate": 4524.063790499832
+ },
+ "iteration_duration": {
+ "avg": 721.0031081017922,
+ "max": 7103.37327,
+ "med": 950.465135,
+ "min": 0.07901,
+ "p(90)": 1151.317182,
+ "p(95)": 1307.4926759999998
+ },
+ "iterations": {
+ "count": 69730,
+ "rate": 2108.3855297083555
+ },
+ "vus": {
+ "max": 1600,
+ "min": 26,
+ "value": 26
+ },
+ "vus_max": {
+ "max": 1600,
+ "min": 1600,
+ "value": 1600
+ }
+ },
+ "root_group": {
+ "name": "",
+ "path": "",
+ "id": "d41d8cd98f00b204e9800998ecf8427e",
+ "groups": {
+ "publisher": {
+ "name": "publisher",
+ "path": "::publisher",
+ "id": "42834ed9b0fab346fb485eef504a199e",
+ "groups": {},
+ "checks": {
+ "is status 200": {
+ "name": "is status 200",
+ "path": "::publisher::is status 200",
+ "id": "033b4844056b121c9936aedc4ab12017",
+ "passes": 29724,
+ "fails": 0
+ }
+ }
+ },
+ "setup": {
+ "name": "setup",
+ "path": "::setup",
+ "id": "5c0f8025f7e0b6654089e5b00e950f1a",
+ "groups": {},
+ "checks": {}
+ },
+ "subscriber": {
+ "name": "subscriber",
+ "path": "::subscriber",
+ "id": "8cd2fe283a36cc055d74315ba7db7039",
+ "groups": {
+ "ack": {
+ "name": "ack",
+ "path": "::subscriber::ack",
+ "id": "896b291f5e9f4a0b4a9d63c5bd0aa610",
+ "groups": {},
+ "checks": {
+ "is status 204": {
+ "name": "is status 204",
+ "path": "::subscriber::ack::is status 204",
+ "id": "1675e96687d722c0ea932d5d2fbfd93f",
+ "passes": 58877,
+ "fails": 0
+ }
+ }
+ },
+ "fetch": {
+ "name": "fetch",
+ "path": "::subscriber::fetch",
+ "id": "219dd6d5879771ae4590fb2c3826ad84",
+ "groups": {},
+ "checks": {
+ "has messages array": {
+ "name": "has messages array",
+ "path": "::subscriber::fetch::has messages array",
+ "id": "8bcfe7144bfdfba71334cca76e6453c6",
+ "passes": 59822,
+ "fails": 0
+ },
+ "is status 200": {
+ "name": "is status 200",
+ "path": "::subscriber::fetch::is status 200",
+ "id": "bee4378527eb142a3168d665c6ac821a",
+ "passes": 59822,
+ "fails": 0
+ }
+ }
+ }
+ },
+ "checks": {
+ "is status 200": {
+ "name": "is status 200",
+ "path": "::subscriber::is status 200",
+ "id": "96fdc221e3cd9ed60329154e5bb89f28",
+ "passes": 1200,
+ "fails": 0
+ }
+ }
+ }
+ },
+ "checks": {}
+ }
+}
diff --git a/loadtest/experiments/20201229-on-macbook/result/channels-50/logs/dsps.log.gz b/loadtest/experiments/20201229-on-macbook/result/channels-50/logs/dsps.log.gz
new file mode 100644
index 0000000..ca8ea8a
Binary files /dev/null and b/loadtest/experiments/20201229-on-macbook/result/channels-50/logs/dsps.log.gz differ
diff --git a/loadtest/experiments/20201229-on-macbook/result/channels-50/logs/k6.log.gz b/loadtest/experiments/20201229-on-macbook/result/channels-50/logs/k6.log.gz
new file mode 100644
index 0000000..026c085
Binary files /dev/null and b/loadtest/experiments/20201229-on-macbook/result/channels-50/logs/k6.log.gz differ
diff --git a/loadtest/experiments/20201229-on-macbook/result/channels-50/logs/redis1.log.gz b/loadtest/experiments/20201229-on-macbook/result/channels-50/logs/redis1.log.gz
new file mode 100644
index 0000000..dcb280b
Binary files /dev/null and b/loadtest/experiments/20201229-on-macbook/result/channels-50/logs/redis1.log.gz differ
diff --git a/loadtest/experiments/20201229-on-macbook/result/channels-50/logs/redis2.log.gz b/loadtest/experiments/20201229-on-macbook/result/channels-50/logs/redis2.log.gz
new file mode 100644
index 0000000..b085861
Binary files /dev/null and b/loadtest/experiments/20201229-on-macbook/result/channels-50/logs/redis2.log.gz differ
diff --git a/loadtest/experiments/20201229-on-macbook/result/channels-50/result/data.json.gz b/loadtest/experiments/20201229-on-macbook/result/channels-50/result/data.json.gz
new file mode 100644
index 0000000..37ec2cc
Binary files /dev/null and b/loadtest/experiments/20201229-on-macbook/result/channels-50/result/data.json.gz differ
diff --git a/loadtest/experiments/20201229-on-macbook/result/channels-50/result/summary.json b/loadtest/experiments/20201229-on-macbook/result/channels-50/result/summary.json
new file mode 100644
index 0000000..133164c
--- /dev/null
+++ b/loadtest/experiments/20201229-on-macbook/result/channels-50/result/summary.json
@@ -0,0 +1,224 @@
+{
+ "metrics": {
+ "checks": {
+ "fails": 0,
+ "passes": 31815,
+ "thresholds": {
+ "rate >= 1.0": false
+ },
+ "value": 0
+ },
+ "data_received": {
+ "count": 8263517,
+ "rate": 250731.13455005726
+ },
+ "data_sent": {
+ "count": 4570917,
+ "rate": 138690.48800215987
+ },
+ "dsps_fetched_messages": {
+ "count": 13403,
+ "rate": 406.6730178414854,
+ "thresholds": {
+ "count >= 5400": false
+ }
+ },
+ "dsps_msg_delay_ms": {
+ "avg": 21.508617473699918,
+ "max": 1030,
+ "med": 22,
+ "min": 0,
+ "p(90)": 27,
+ "p(95)": 29
+ },
+ "dsps_ttfb_ms_ack": {
+ "avg": 1.1608371859296513,
+ "max": 12.231,
+ "med": 0.833,
+ "min": 0.194,
+ "p(90)": 2.2272000000000007,
+ "p(95)": 2.965299999999992
+ },
+ "dsps_ttfb_ms_publish": {
+ "avg": 4.095637555555554,
+ "max": 717.688,
+ "med": 1.152,
+ "min": 0.265,
+ "p(90)": 2.8922999999999996,
+ "p(95)": 3.8911500000000006
+ },
+ "group_duration": {
+ "avg": 386.6544554704826,
+ "max": 3017.305062,
+ "med": 22.01059,
+ "min": 0.000292,
+ "p(90)": 984.0920726,
+ "p(95)": 1003.8870944
+ },
+ "http_req_blocked": {
+ "avg": 0.21584222809341708,
+ "max": 48.728,
+ "med": 0.003,
+ "min": 0.001,
+ "p(90)": 0.004,
+ "p(95)": 0.007
+ },
+ "http_req_connecting": {
+ "avg": 0.2108453985028623,
+ "max": 48.7,
+ "med": 0,
+ "min": 0,
+ "p(90)": 0,
+ "p(95)": 0
+ },
+ "http_req_duration": {
+ "avg": 206.91370981946235,
+ "max": 3005.639,
+ "med": 1.343,
+ "min": 0.222,
+ "p(90)": 959.6564,
+ "p(95)": 961.9601
+ },
+ "http_req_receiving": {
+ "avg": 0.03806081021576389,
+ "max": 24.412,
+ "med": 0.025,
+ "min": 0.01,
+ "p(90)": 0.053,
+ "p(95)": 0.078
+ },
+ "http_req_sending": {
+ "avg": 0.03413535887274189,
+ "max": 20.404,
+ "med": 0.012,
+ "min": 0.005,
+ "p(90)": 0.037,
+ "p(95)": 0.09
+ },
+ "http_req_tls_handshaking": {
+ "avg": 0,
+ "max": 0,
+ "med": 0,
+ "min": 0,
+ "p(90)": 0,
+ "p(95)": 0
+ },
+ "http_req_waiting": {
+ "avg": 206.84151365037357,
+ "max": 3005.472,
+ "med": 1.2805,
+ "min": 0.194,
+ "p(90)": 959.5852,
+ "p(95)": 961.89265
+ },
+ "http_reqs": {
+ "count": 22710,
+ "rate": 689.0654506588176
+ },
+ "iteration_duration": {
+ "avg": 603.0194496801789,
+ "max": 3017.34404,
+ "med": 978.654395,
+ "min": 0.103627,
+ "p(90)": 1004.9525134999999,
+ "p(95)": 1007.383953
+ },
+ "iterations": {
+ "count": 10605,
+ "rate": 321.77627055203703
+ },
+ "vus": {
+ "max": 200,
+ "min": 150,
+ "value": 150
+ },
+ "vus_max": {
+ "max": 200,
+ "min": 200,
+ "value": 200
+ }
+ },
+ "root_group": {
+ "name": "",
+ "path": "",
+ "id": "d41d8cd98f00b204e9800998ecf8427e",
+ "groups": {
+ "publisher": {
+ "name": "publisher",
+ "path": "::publisher",
+ "id": "42834ed9b0fab346fb485eef504a199e",
+ "groups": {},
+ "checks": {
+ "is status 200": {
+ "name": "is status 200",
+ "path": "::publisher::is status 200",
+ "id": "033b4844056b121c9936aedc4ab12017",
+ "passes": 4500,
+ "fails": 0
+ }
+ }
+ },
+ "setup": {
+ "name": "setup",
+ "path": "::setup",
+ "id": "5c0f8025f7e0b6654089e5b00e950f1a",
+ "groups": {},
+ "checks": {}
+ },
+ "subscriber": {
+ "name": "subscriber",
+ "path": "::subscriber",
+ "id": "8cd2fe283a36cc055d74315ba7db7039",
+ "groups": {
+ "ack": {
+ "name": "ack",
+ "path": "::subscriber::ack",
+ "id": "896b291f5e9f4a0b4a9d63c5bd0aa610",
+ "groups": {},
+ "checks": {
+ "is status 204": {
+ "name": "is status 204",
+ "path": "::subscriber::ack::is status 204",
+ "id": "1675e96687d722c0ea932d5d2fbfd93f",
+ "passes": 8955,
+ "fails": 0
+ }
+ }
+ },
+ "fetch": {
+ "name": "fetch",
+ "path": "::subscriber::fetch",
+ "id": "219dd6d5879771ae4590fb2c3826ad84",
+ "groups": {},
+ "checks": {
+ "has messages array": {
+ "name": "has messages array",
+ "path": "::subscriber::fetch::has messages array",
+ "id": "8bcfe7144bfdfba71334cca76e6453c6",
+ "passes": 9105,
+ "fails": 0
+ },
+ "is status 200": {
+ "name": "is status 200",
+ "path": "::subscriber::fetch::is status 200",
+ "id": "bee4378527eb142a3168d665c6ac821a",
+ "passes": 9105,
+ "fails": 0
+ }
+ }
+ }
+ },
+ "checks": {
+ "is status 200": {
+ "name": "is status 200",
+ "path": "::subscriber::is status 200",
+ "id": "96fdc221e3cd9ed60329154e5bb89f28",
+ "passes": 150,
+ "fails": 0
+ }
+ }
+ }
+ },
+ "checks": {}
+ }
+}
diff --git a/loadtest/loadtest.k6.js b/loadtest/loadtest.k6.js
new file mode 100644
index 0000000..db3a2b3
--- /dev/null
+++ b/loadtest/loadtest.k6.js
@@ -0,0 +1,173 @@
+import http from 'k6/http';
+import { group, sleep, check } from 'k6';
+import { Counter, Trend } from 'k6/metrics';
+
+//
+// To run in local, use ./experiment-local.sh
+// See experiment logbook (e.g. ./experiments/20201229-on-macbook/README.md) for steps to reproduce.
+//
+
+
+//
+// --- Compute client role & parameter ---
+//
+
+const settings = {
+ BASE_URL: __ENV.BASE_URL || "http://localhost:3000",
+
+ TEST_RAMPUP_SEC: __ENV.TEST_RAMPUP_SEC || 1,
+ TEST_DURATION_SEC: __ENV.TEST_DURATION_SEC || 3,
+ TEST_RAMPDOWN_SEC: __ENV.TEST_RAMPUP_SEC || 0,
+
+ CHANNELS: +(__ENV.CHANNELS) || 50,
+ PUBLISHER_PER_CHANEL: +(__ENV.PUBLISHER_PER_CHANEL) || 1,
+ SUBSCRIBER_PER_PUBLISHER: +(__ENV.SUBSCRIBER_PER_PUBLISHER) || 3,
+
+ PUBLISH_MESSAGES_PER_ITERATION: +(__ENV.PUBLISH_MESSAGES_PER_ITERATION) || 3,
+
+ PUBLISH_INTERVAL_SEC: +(__ENV.PUBLISH_INTERVAL_SEC) || 1,
+ SUBSCRIBE_ACK_WAIT_SEC: +(__ENV.SUBSCRIBE_INERVAL_SEC) || 0.01,
+ SUBSCRIBE_INERVAL_SEC: +(__ENV.SUBSCRIBE_INERVAL_SEC) || 0.01,
+}
+const clientsPerChannel = (1 + settings.SUBSCRIBER_PER_PUBLISHER) * settings.PUBLISHER_PER_CHANEL;
+const targetVU = settings.CHANNELS * clientsPerChannel;
+if (__VU === 1) console.log(`clientsPerChannel: ${clientsPerChannel}, targetVU: ${targetVU}, settings: ${JSON.stringify(settings, null, 2)}`);
+
+const channelNumber = Math.floor((__VU - 1) / clientsPerChannel);
+const isPublisher = (((__VU - 1) % clientsPerChannel) < settings.PUBLISHER_PER_CHANEL);
+
+//
+// --- Custom metrics ---
+//
+const fetchedMessagesCounter = new Counter('dsps_fetched_messages');
+const ttfbTrends = { // TTFB = time to first byte
+ publish: new Trend('dsps_ttfb_ms_publish'),
+ ack: new Trend('dsps_ttfb_ms_ack'),
+};
+const messageDelayTrends = new Trend('dsps_msg_delay_ms');
+
+//
+// --- k6 config ---
+//
+export const options = {
+ stages: [
+ { duration: `${settings.TEST_RAMPUP_SEC}s`, target: targetVU },
+ { duration: `${settings.TEST_DURATION_SEC}s`, target: targetVU },
+ { duration: `${settings.TEST_RAMPDOWN_SEC}s`, target: 0 },
+ ],
+ thresholds: { // https://k6.io/docs/using-k6/thresholds
+ checks: ['rate >= 1.0'],
+ dsps_fetched_messages: [`count >= ${0.9 * (targetVU * settings.TEST_DURATION_SEC) / settings.PUBLISH_INTERVAL_SEC}`],
+ },
+};
+
+//
+// --- Load test implementation ---
+//
+export function setup() {
+ const chars = "abcdefghijklmnopqrstuvwxyz".split("");
+ let randomID = "";
+ for (let i = 0; i < 16; i++) {
+ randomID += chars[Math.floor(chars.length * Math.random())];
+ }
+
+ const data = {
+ randomID: randomID,
+ };
+ console.log(`data: ${JSON.stringify(data)}`);
+ return data;
+}
+
+export default function (data) {
+ const { randomID } = data;
+ const channelID = `${randomID}-${channelNumber}`;
+ const subscriberID = `sbsc-${__VU}`;
+
+ if (isPublisher) {
+ group("publisher", () => {
+ publisherScenario(channelID);
+ });
+ } else {
+ group("subscriber", () => {
+ subscriberScenario(channelID, subscriberID);
+ });
+ }
+}
+
+function publisherScenario(channelID) {
+ for (let i = 0; i < settings.PUBLISH_MESSAGES_PER_ITERATION; i++) {
+ publishMessage(channelID, `${__VU}-${__ITER}-${i}`);
+ }
+ sleep(settings.PUBLISH_INTERVAL_SEC);
+}
+
+function subscriberScenario(channelID, subscriberID) {
+ if (__ITER === 0) {
+ createSubscription(channelID, subscriberID);
+ }
+
+ let ackHandle;
+ group("fetch", () => {
+ ackHandle = fetchMessages(channelID, subscriberID);
+ });
+ group("ack", () => {
+ if (ackHandle) {
+ sleep(settings.SUBSCRIBE_ACK_WAIT_SEC);
+ consumeAckHandle(channelID, subscriberID, ackHandle);
+ }
+ });
+
+ sleep(settings.SUBSCRIBE_INERVAL_SEC);
+}
+
+function publishMessage(channelID, messageID) {
+ const message = createMessage();
+ const res = http.put(`${settings.BASE_URL}/channel/${channelID}/message/${messageID}`, message, { tags: { endpoint: "publish" } });
+ check(res, {
+ "is status 200": (r) => r.status === 200,
+ });
+ ttfbTrends.publish.add(res.timings.waiting);
+}
+
+function createSubscription(channelID, subscriberID) {
+ const res = http.put(`${settings.BASE_URL}/channel/${channelID}/subscription/polling/${subscriberID}`, undefined, { tags: { endpoint: "createSubscription" } });
+ check(res, {
+ "is status 200": (r) => r.status === 200,
+ });
+}
+
+function fetchMessages(channelID, subscriberID) {
+ const res = http.get(`${settings.BASE_URL}/channel/${channelID}/subscription/polling/${subscriberID}?timeout=3s`, undefined, { tags: { endpoint: "fetch" } });
+ const receivedAt = new Date();
+
+ const body = res.json();
+ check(res, {
+ "is status 200": (r) => r.status === 200,
+ "has messages array": (r) => (typeof (body.messages) === "object" && typeof (body.messages.length) === "number"),
+ });
+ if (body.messages) {
+ fetchedMessagesCounter.add(body.messages.length);
+ for (let i = 0; i < body.messages.length; i++) {
+ readMessage(receivedAt, body.messages[i]);
+ }
+ }
+ return body.ackHandle;
+}
+
+function consumeAckHandle(channelID, subscriberID, ackHandle) {
+ const res = http.del(`${settings.BASE_URL}/channel/${channelID}/subscription/polling/${subscriberID}/message?ackHandle=${ackHandle}`, undefined, { tags: { endpoint: "ack" } });
+ check(res, {
+ "is status 204": (r) => r.status === 204,
+ });
+ ttfbTrends.ack.add(res.timings.waiting);
+}
+
+function createMessage() {
+ return JSON.stringify({
+ at: (new Date()).getTime(),
+ });
+}
+
+function readMessage(receivedAt, msg) {
+ messageDelayTrends.add(receivedAt.getTime() - msg.content.at);
+}
diff --git a/loadtest/log/20yymmdd-redis-jsclient/.keep b/loadtest/log/20yymmdd-redis-jsclient/.keep
deleted file mode 100644
index e69de29..0000000
diff --git a/loadtest/package.json b/loadtest/package.json
new file mode 100644
index 0000000..be46d36
--- /dev/null
+++ b/loadtest/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "@dsps/load-test",
+ "version": "0.0.0",
+ "private": true,
+ "license": "MIT",
+ "description": "DSPS load testing code",
+ "main": "loadtest.k6.js",
+ "devDependencies": {
+ "@types/k6": "^0.28.3",
+ "typescript": "^4.1.2"
+ }
+}
diff --git a/loadtest/yarn.lock b/loadtest/yarn.lock
new file mode 100644
index 0000000..a12b04e
--- /dev/null
+++ b/loadtest/yarn.lock
@@ -0,0 +1,13 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@types/k6@^0.28.3":
+ version "0.28.3"
+ resolved "https://registry.yarnpkg.com/@types/k6/-/k6-0.28.3.tgz#69ab758f450e558722dff553ab3eb5542892a440"
+ integrity sha512-qUOneA7Zn8erpBxhFkkshr6lRNKYIBGo0MjNQWaepGcbegfQaQy0fVIlHYjTJhlqxnmwDZPNkKbIrTpL0VHb6A==
+
+typescript@^4.1.2:
+ version "4.1.3"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.3.tgz#519d582bd94cba0cf8934c7d8e8467e473f53bb7"
+ integrity sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==
diff --git a/server/storage/redis/internal/pubsub/dispatcher_test.go b/server/storage/redis/internal/pubsub/dispatcher_test.go
index 925c9ff..e2849d2 100644
--- a/server/storage/redis/internal/pubsub/dispatcher_test.go
+++ b/server/storage/redis/internal/pubsub/dispatcher_test.go
@@ -49,6 +49,7 @@ func TestDispatcherAwaitAfterShutdown(t *testing.T) {
pubsub := newRedisRawPubSubStub(t).EnqueueDefaultSubscribeMessage().EnqueuePingResultForever(nil)
dispatcher, pubsubActivated := newDispatcher(t, pubsub)
<-pubsubActivated
+ time.Sleep(10 * time.Millisecond) // Await reconcile completion
dispatcher.Shutdown(ctx)
await, cancel := dispatcher.Await(ctx, "ch-1")
diff --git a/server/storage/redis/internal/pubsub/redis_test.go b/server/storage/redis/internal/pubsub/redis_test.go
index 4200e6d..3579252 100644
--- a/server/storage/redis/internal/pubsub/redis_test.go
+++ b/server/storage/redis/internal/pubsub/redis_test.go
@@ -20,6 +20,7 @@ type redisRawPubSubStub struct {
closeResult error
pingResultQueue chan error
+ fixedPingResult error
messageQueue chan interface{}
channelInit sync.Once
@@ -47,15 +48,8 @@ func (s *redisRawPubSubStub) EnqueuePingResult(nCalls int, result error) *redisR
}
func (s *redisRawPubSubStub) EnqueuePingResultForever(result error) *redisRawPubSubStub {
- go func() {
- for {
- select {
- case s.pingResultQueue <- result:
- case <-s.closed:
- return
- }
- }
- }()
+ s.fixedPingResult = result
+ close(s.pingResultQueue)
return s
}
@@ -63,7 +57,10 @@ func (s *redisRawPubSubStub) Ping(ctx context.Context, payload ...string) error
select {
case <-s.closed:
return errors.New("stub RedisRawPubSub closed")
- case result := <-s.pingResultQueue:
+ case result, open := <-s.pingResultQueue:
+ if !open {
+ return s.fixedPingResult
+ }
return result
default:
assert.FailNow(s.t, "Unexpected ping call")