diff --git a/soketi-load-test/.env.example b/soketi-load-test/.env.example new file mode 100644 index 0000000..c640217 --- /dev/null +++ b/soketi-load-test/.env.example @@ -0,0 +1,5 @@ +PUSHER_APP_KEY=some-key +PUSHER_WH_HOST=ws://nino.baby +PUSHER_AUTHORIZATION_ENDPOINT=https://810d-59-124-114-73.ngrok-free.app/pusher/auth +PUSHER_CHANNELS='presence-user@5795bd230c562f2783adc27b' +CLIENT_COUNT=3 diff --git a/soketi-load-test/.gitignore b/soketi-load-test/.gitignore new file mode 100644 index 0000000..4c49bd7 --- /dev/null +++ b/soketi-load-test/.gitignore @@ -0,0 +1 @@ +.env diff --git a/soketi-load-test/Dockerfile b/soketi-load-test/Dockerfile new file mode 100644 index 0000000..37cb120 --- /dev/null +++ b/soketi-load-test/Dockerfile @@ -0,0 +1,7 @@ +FROM grafana/k6:latest + +WORKDIR /scripts + +COPY loadtest.js ./ + +CMD ["run", "loadtest.js"] diff --git a/soketi-load-test/README.md b/soketi-load-test/README.md new file mode 100644 index 0000000..b48ee51 --- /dev/null +++ b/soketi-load-test/README.md @@ -0,0 +1,12 @@ +# Soketi load test +Use k6/ws test soketi server + +envars: +- CLIENT_COUNT = Stress test client count +- PUSHER_CHANNELS = Pusher channel names client will connect, space separete string +- PUSHER_APP_KEY = Pusher app key +- PUSHER_WH_HOST = Pusher host +- PUSHER_AUTHORIZATION_ENDPOINT = Pusher authorization endpoint + +# Run test +`docker compose up` diff --git a/soketi-load-test/compose.yaml b/soketi-load-test/compose.yaml new file mode 100644 index 0000000..41de36a --- /dev/null +++ b/soketi-load-test/compose.yaml @@ -0,0 +1,10 @@ +version: '3.8' + +services: + k6-load-test: + build: . + command: ["run", "--verbose", "loadtest.js"] # Add verbose flag + volumes: + - ./loadtest.js:/scripts/loadtest.js + env_file: .env + \ No newline at end of file diff --git a/soketi-load-test/loadtest.js b/soketi-load-test/loadtest.js new file mode 100644 index 0000000..04f8f02 --- /dev/null +++ b/soketi-load-test/loadtest.js @@ -0,0 +1,114 @@ +import ws from 'k6/ws'; +import http from 'k6/http'; +import { check } from 'k6'; + +// Environment variables +const CLIENT_COUNT = __ENV.CLIENT_COUNT || '100'; +const PUSHER_CHANNELS = (__ENV.PUSHER_CHANNELS || '').split(' '); +const PUSHER_WH_HOST = __ENV.PUSHER_WH_HOST; +const PUSHER_APP_KEY = __ENV.PUSHER_APP_KEY; +const PUSHER_AUTHORIZATION_ENDPOINT = __ENV.PUSHER_AUTHORIZATION_ENDPOINT; + + +export const options = { + vus: parseInt(CLIENT_COUNT), + duration: '300s', +}; + +function subscribeToChannels(socket, socketId) { + console.log('subscribing to channels', PUSHER_CHANNELS); + PUSHER_CHANNELS.forEach(async (channel) => { + const authResponse = http.post(PUSHER_AUTHORIZATION_ENDPOINT, { + channel_name: channel, + socket_id: socketId, + }, { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + } + }); + + check(authResponse, { + 'Auth request successful': (r) => r.status === 200, + }); + + const authData = authResponse.json(); + + // Step 3: Subscribe to channel with auth token + socket.send(JSON.stringify({ + event: 'pusher:subscribe', + data: { + channel: channel, + auth: authData.auth, + channel_data: authData.channel_data, + }, + })); + }); +} + +function handleMessageEvent(message, socket) { + message = JSON.parse(message); + + switch (message.event) { + case 'pusher:connection_established': + subscribeToChannels(socket, JSON.parse(message.data).socket_id); + break; + } +} + + +export default function () { + const url = `${PUSHER_WH_HOST}/app/${PUSHER_APP_KEY}`; +// const url = `ws://echo.websocket.org` + const params = { + tags: { my_tag: 'hello' }, + }; + + const response = ws.connect(url, params, function (socket) { + console.log('connection initialized'); + + socket.on('open', function open() { + console.log('connected'); + + socket.setInterval(function timeout() { + socket.send(JSON.stringify({ + event: 'pusher:pong', + data: {}, + })); + console.log('Pinging every 30sec (setInterval test)'); + }, 30000); + }); + + + socket.on('pusher:pong', function () { + console.log('pusher:pong received'); + }); + + socket.on('message', function (message) { + console.log(`Received message: ${message}`); + handleMessageEvent(message, socket); + }); + + socket.on('close', function () { + console.log('disconnected'); + }); + + socket.on('error', function (e) { + if (e.error() != 'websocket: close sent') { + console.log('An unexpected error occured: ', e.error()); + } + }); + + // Pusher events + + socket.on('activity', function () { + console.log('activity'); + }); + + }); + + check(response, { + 'status is 101': (r) => r && r.status === 101, + + }); + +}