-
Notifications
You must be signed in to change notification settings - Fork 36
129 lines (113 loc) · 3.87 KB
/
Copy pathload.yml
File metadata and controls
129 lines (113 loc) · 3.87 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
name: Load test (prices)
# Price endpoints are polled hard by trading bots, so we guard their latency
# under load on a schedule (and on demand). Acceptance criterion: p95 < 80ms at
# 5k RPS, enforced by the k6 thresholds in tests/load/prices.k6.js (the run
# exits non-zero if a threshold is breached).
on:
schedule:
# 04:00 UTC daily — off-peak, so a sustained 5k-RPS flood doesn't collide
# with interactive CI.
- cron: '0 4 * * *'
workflow_dispatch:
inputs:
rate:
description: 'Target requests per second'
required: false
default: '5000'
duration:
description: 'Steady-state duration (k6 syntax, e.g. 1m, 30s)'
required: false
default: '1m'
jobs:
k6:
name: k6 — p95 < 80ms @ 5k RPS
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15-alpine
env:
POSTGRES_USER: lens
POSTGRES_PASSWORD: lens
POSTGRES_DB: lens
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U lens -d lens"
--health-interval 5s
--health-timeout 5s
--health-retries 5
redis:
image: redis:7-alpine
ports:
- 6379:6379
options: >-
--health-cmd "redis-cli ping"
--health-interval 5s
--health-timeout 5s
--health-retries 5
env:
DATABASE_URL: postgresql://lens:lens@localhost:5432/lens
REDIS_URL: redis://localhost:6379
WATCHED_PAIRS: XLM:native/USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5
# Isolate endpoint performance: disable the per-key API auth so the load
# generator can flood without a valid key, and the auth lookup (a DB round
# trip per request) doesn't dominate the measurement.
REQUIRE_API_KEY: 'false'
PORT: '3002'
HOST: '0.0.0.0'
# The limiter would 429 a 5k-RPS flood from a single IP; raise the IP
# fallback well above the target rate so the test measures the endpoint,
# not the limiter.
RATE_LIMIT_IP_MAX: '100000000'
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: npm
- name: Install dependencies
run: npm ci || npm install
- name: Generate Prisma client
run: node node_modules/prisma/build/index.js generate
- name: Build
run: npm run build
- name: Apply database schema
run: node node_modules/prisma/build/index.js db push --accept-data-loss
- name: Start server
run: |
npm start > server.log 2>&1 &
echo "SERVER_PID=$!" >> "$GITHUB_ENV"
- name: Wait for server to be ready
run: |
for i in $(seq 1 60); do
if curl -sf http://localhost:3002/status > /dev/null; then
echo "Server is up."
exit 0
fi
sleep 2
done
echo "Server did not become ready in time. Logs:"
cat server.log
exit 1
- name: Warm the price cache
# Prime Redis so the measured run represents the production hot path
# (bots polling an already-cached price), not a cold first miss.
run: |
curl -sf "http://localhost:3002/price/XLM/USDC" > /dev/null || {
echo "Warmup request failed. Server logs:"
cat server.log
exit 1
}
- name: Install k6
uses: grafana/setup-k6-action@v1
- name: Run k6 load test
uses: grafana/run-k6-action@v1
with:
path: tests/load/prices.k6.js
env:
BASE_URL: http://localhost:3002
RATE: ${{ github.event.inputs.rate || '5000' }}
DURATION: ${{ github.event.inputs.duration || '1m' }}
- name: Dump server logs on failure
if: failure()
run: cat server.log || true