Close WebSocket for VU on graceful rampdown/shutdown

Hi everyone,

I’m currently testing a WebSocket connection using the experimental xk6-websockets API (I also tried using k6/ws).

My k6 version:

k6 v0.45.1 ((devel), go1.21.1, windows/amd64)
Extensions:
  github.com/grafana/xk6-output-influxdb v0.4.1, xk6-influxdb [output]

Built using:
go install go.k6.io/xk6/cmd/xk6@latest
xk6 build --with github.com/grafana/xk6-output-influxdb

I also tested using the default k6 binary: k6 v0.46.0 (2023-08-14T13:23:26+0000/v0.46.0-0-gcbd9e9ad, go1.20.7, windows/amd64)

Code

Here is some example code for what I’m trying to achieve:

Test Setup + Options
import { Options } from 'k6/options';
import ws from 'k6/ws';
import { WebSocket } from 'k6/experimental/websockets';

export const options: Options = {
  scenarios: {
    example: {
      executor: 'ramping-vus',
      startVUs: 0,
      stages: [
        { duration: '10s', target: 10 },
      ],
      gracefulStop: '5s',
      exec: 'test'
    }
  }
};

I have also tried to add a second stage { duration: '5s', target: 0} with a gracefulRampDown for 5s, this did not work either.

k6/ws Test
export const test = () => {
  const headers: Record<string, string> = { "Sec-Websocket-Protocol": "graphql-transport-ws" };
  ws.connect("ws://my.example.com/graphql", {
    headers
  }, (socket) => {
    socket.on('open', () => {
      console.log('open');
      socket.send(
        JSON.stringify({
          type: "connection_init",
          payload: {},
        })
      );
    });

    socket.on('message', () => {
      console.log('message');
    });

    socket.on('error', (ev) => {
      console.log('error:', JSON.stringify(ev));
    });

    socket.on('close', () => {
      console.log('closed');
    })
  });
};
xk6-websockets Test
export const test2 = () => {
  const headers: Record<string, string> = { "Sec-Websocket-Protocol": "graphql-transport-ws" };

  const socket = new WebSocket("ws://my.example.com/graphql", null, {
    headers
  });

  socket.onopen = () => {
    console.log('open!');
    socket.send(
      JSON.stringify({
        type: "connection_init",
        payload: {},
      })
    );
  };

  socket.onmessage = (ev) => {
    console.log('message');
  };

  socket.onerror = (ev) => {
    console.log('error:', JSON.stringify(ev));
  };

  socket.onclose = () => {
    console.log('closed');
  };
};

Issue

As you can see above, I’m testing a GraphQL API (I excluded the queries and subscriptions from the test, as they are not relevant here). My problem is that these session run infinitely (which is intended per se, I want to keep a few subscriptions open while executing other scenarios; this simulates the actual application, which also has a few infinite websockets opened).

I already saw in this post that people solved this with a maximum session length, which is shorter than the gracefulShutDown time. However, I intentionally want these sessions to not have a maximum length and only to close when the VU is being shut or ramped down. This leads to all my iterations being interrupted:

grafik

Question

As already mentioned, I know that the common solution for this is to simply limit the session duration. However, I do wonder whether there is some kind of event system (or similar) which allows to be notified when the gracleful rampdown/shutdown should happen so the virtual user can “clean up” to avoid being forcefully shut down (here: close the WebSocket connection).

Here’s what I already did:

  • I looked up the issue in the forum and on Stack Overflow. All posts had similar solutions to the post linked above.
  • I searched the documentation for Web Sockets and Event Handling. I couldn’t find anything regarding events, and all Web Socket examples close the connection immediatly.
  • I checked the repositories for k6 and the xk6-websockets extension, I couldn’t find any related issues or something interesting on the roadmap.

I’m aware that there might not be a fitting solution for my issue (at the moment), but since I didn’t find this question anywhere else, I thought that it couldn’t hurt to ask :slight_smile: . Thanks in advance for any help or suggestions!

1 Like

Have you tried working with scenarios and in between socket open and close adding some sleep time…?

@rdt.one Regarding the scenarios: I am already using a scenario, or am I missing something important in my example?

Can you elaborate a bit more on the sleep time? I tried using sleep, but this blocks the connection so I don’t see how this is useful for my use case.