Skip to content

feat: Add k6 connection and message rate limiter tests#556

Merged
cameri merged 5 commits intocameri:mainfrom
saniddhyaDubey:perf/issue-469-k6-redis-testing
May 1, 2026
Merged

feat: Add k6 connection and message rate limiter tests#556
cameri merged 5 commits intocameri:mainfrom
saniddhyaDubey:perf/issue-469-k6-redis-testing

Conversation

@saniddhyaDubey
Copy link
Copy Markdown
Collaborator

Add k6 Load Testing Suite for Rate Limiters

Description

Adds k6 load tests to validate connection and message rate limiting behavior. (#469)

Tests Added:

  1. connection-rate-limiter.ts - Tests 12 conn/sec limit across 4 load stages
  2. message-rate-limiter.ts - Tests 3 msg/min limit with continuous REQ messages

Both tests require a running Docker instance and output formatted metrics showing success/rejection rates.

Motivation

Automate rate limiter testing. Previously manual (wscat only). Provides:

  • Repeatable load testing under controlled conditions
  • Quantified rejection rates vs configured limits
  • Foundation for future tests (event limits, subscriptions, etc.)

How to Test

docker-compose up -d
npm run test:connection
npm run test:message

Metrics output shows connection/message success rates and type breakdown.

Screenshots:

Message Rate Limiter Test:
Tests relay's ability to reject excess REQ messages at 3 msg/min limit. Shows 40 NOTICE rejections, 16 EOSE acceptances, and 72 EVENT results (62.50% success rate), with Redis message-specific rate limit keys stored alongside connection limits.
Screenshot 2026-04-20 at 11 27 18 PM

Connection Rate Limiter Test:
Tests relay's ability to reject excess connections at 12 conn/sec limit. Shows 350 successful connections vs 350 rate-limited rejections (50% rejection at 2x load), with Redis storing rate limit state visible in Docker sidebar.
Screenshot 2026-04-20 at 11 25 16 PM

Types of changes

  • New feature
  • Non-functional (foundation setup)

Checklist

  • Code follows project style
  • Tests added and passing
  • Docs added

@socket-security
Copy link
Copy Markdown

socket-security Bot commented Apr 21, 2026

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​types/​k6@​1.7.01001007989100

View full report

@coveralls
Copy link
Copy Markdown
Collaborator

coveralls commented Apr 21, 2026

Coverage Status

coverage: 64.58% (-0.03%) from 64.608% — saniddhyaDubey:perf/issue-469-k6-redis-testing into cameri:main

@cameri cameri requested a review from Copilot April 21, 2026 03:38
@cameri cameri self-assigned this Apr 21, 2026
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a k6-based load testing suite intended to validate relay connection and message rate limiting behavior under concurrent WebSocket load.

Changes:

  • Added k6 WebSocket load test scripts for message and connection limiting.
  • Added npm scripts to run the new k6 tests (with a Docker container check).
  • Added @types/k6 and a changeset entry for the release.

Reviewed changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
test/integration/performance/message-limiting-k6.ts New k6 script to send REQ messages continuously and summarize NOTICE/EOSE/EVENT outcomes.
test/integration/performance/connection-limiting-k6.ts New k6 script to create many WebSocket connections and summarize success vs. rate-limited outcomes.
package.json Adds test:connection / test:message scripts and @types/k6.
package-lock.json Locks @types/k6 dependency.
.changeset/jolly-canyons-glow.md Release note for adding the k6 tests.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread package.json Outdated
Comment thread .changeset/jolly-canyons-glow.md Outdated
Comment on lines +25 to +39
const res = ws.connect(relayUrl, {}, function (socket) {
socket.on('close', () => {
socketClosed = true;
connectionRateLimited.add(1);
});

socket.on('open', () => {
connectionSuccess.add(1);
});

socket.setTimeout(() => {
if (!socketClosed) {
socket.close();
}
}, 3000);
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

connection_rate_limited is incremented on every socket close, including the intentional close triggered by setTimeout. This will misclassify successful connections as rate-limited and also misses true handshake rejections (e.g. HTTP 429), where the close/open handlers never run. Consider classifying rate-limited connections based on the ws.connect() response status (count non-101 responses) and only treating early/abnormal closes as rate limiting if you can reliably identify them (e.g. close code/reason).

Copilot uses AI. Check for mistakes.
Comment thread test/performance/connection-limiting-k6.ts
Comment thread test/integration/performance/message-limiting-k6.ts Outdated
Comment on lines +43 to +75
} catch (e: any) {
errorCounter.add(1);
console.error('Failed to parse message:', e.message);
}
});

socket.setTimeout(function () {
socket.close();
}, 9000);
});

check(res, {
'status 101': (r) => r && r.status === 101,
});
}

export function handleSummary(data: any) {
const notices = data.metrics?.notice_messages?.values?.count || 0;
const eoses = data.metrics?.eose_messages?.values?.count || 0;
const events = data.metrics?.event_messages?.values?.count || 0;
const iterations = data.metrics?.iterations?.values?.count || 0;
const wsSessions = data.metrics?.ws_sessions?.values?.count || 0;
const msgsSent = data.metrics?.ws_msgs_sent?.values?.count || 0;
const msgsReceived = data.metrics?.ws_msgs_received?.values?.count || 0;
const dataReceived = data.metrics?.data_received?.values?.count || 0;
const checks = data.metrics?.checks?.values?.passes || 0;

const totalMessages = notices + eoses + events;
const successRate = totalMessages > 0 ? ((eoses + events) / totalMessages * 100).toFixed(2) : 0;

const rate = parseFloat(successRate as string);
const successStatus = rate >= 80 ? '✓ GOOD' : rate >= 50 ? '⚠ MODERATE' : '✗ POOR';
const rateLimitStatus = notices > 0 ? '⚠ ACTIVE' : '✓ INACTIVE';
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file contains TypeScript-only syntax (catch (e: any), data: any, successRate as string). k6 run executes JavaScript and will fail to parse TS annotations unless you add a transpilation step. Either remove TS-only syntax / use JSDoc types and rename to .js, or update the npm scripts to transpile before running k6.

Copilot uses AI. Check for mistakes.
Comment thread test/performance/connection-limiting-k6.ts
Comment thread package.json Outdated
Comment thread package.json Outdated
Comment thread package.json
@saniddhyaDubey saniddhyaDubey force-pushed the perf/issue-469-k6-redis-testing branch from 3dba290 to 449acf6 Compare April 30, 2026 22:45
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 30, 2026

🦋 Changeset detected

Latest commit: 0a33009

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
nostream Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@saniddhyaDubey
Copy link
Copy Markdown
Collaborator Author

Rebased onto latest main and addressed all review feedback.

  • Tests moved to test/performance/ and integrated into the nostream CLI as nostream dev test:perf:connection and nostream dev test:perf:message. Also added npm scripts following the suggested naming: test:performance:connection-rate-limit and test:performance:message-rate-limit.

  • Fixed the connection test counting bug where intentional socket closes were being miscounted as rate-limited rejections. Fixed ws.connect params and made relay URL configurable via __ENV.RELAY_URL.

  • Updated CONTRIBUTING.md with k6 prerequisite and usage docs. Fixed changeset wording and moved to pnpm properly.

  • Both tests passing locally — connection rate limiter showing ~54% success rate at 2x load and message rate limiter showing ~60% success rate with active NOTICE rejections.

  • Event rate limiter tests, EWMA vs sliding-window comparison, and CI thresholds planned for follow-up PRs.

@cameri cameri merged commit d8f62b4 into cameri:main May 1, 2026
14 of 16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants