Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to allow VU to keep working while keeping WebSocket open #865

Closed
AlexGustafsson opened this issue Dec 10, 2018 · 5 comments
Closed

How to allow VU to keep working while keeping WebSocket open #865

AlexGustafsson opened this issue Dec 10, 2018 · 5 comments

Comments

@AlexGustafsson
Copy link

Scenario

I would like to open up WebSockets to a server and keep them open. I'd like to open at least a couple of thousand connections that when open will stay open. I think that this could be done using one VU per socket but I find it painfully slow for thousands of clients. I also think that it may be a bad design choice. I'd therefore want to use VUs to open up a socket each iteration and then closing them in teardown.

Right now, however, VUs don't seem to want to work with another iteration when a socket is still opened. The only way for it to go on to the next iteration seem to be to close the socket as soon as it's opened. In some sense it's smart that the VU doesn't leave any loose ends, but in this scenario I find it troublesome.

My current code looks a bit like this:

export const sockets = [];

export default function(data) {
  const url = 'ws://localhost/channel';

  const response = ws.connect(url, socket => {
    socket.on('open', () => {
      // Unless I close the socket (socket.close()) this VU will never move on
      sockets.push(socket);
    });

    socket.on('error', error => {
      errorRate.add(1);
    });
  });

  check(response, {
    'WS status is 101': (r) => r.status === 101
  }) || errorRate.add(1);
};

export function teardown() {
  for (const socket of sockets)
    socket.close()
}

I'm pretty sure that the "global state" I'm trying here with sockets = [] won't work since it seems to go against documentation, but I think it best shows what I'm trying to do.

Questions

  1. Can a VU move on while still leaving a socket connected or do I need one VU per socket connection?
  2. If it's possible, Is there a global state that can be used like seen above or could I use the data object passed down from setup() to each VU instance to keep track of open sockets to be closed?
@AlexGustafsson AlexGustafsson changed the title Allow VU to keep working while keeping WebSocket open How to allow VU to keep working while keeping WebSocket open Dec 10, 2018
@na--
Copy link
Member

na-- commented Dec 13, 2018

Unfortunately for the moment, unlike other JS runtimes, k6 doesn't have a global event loop, Instead we rely on having multiple parallel runtimes (i.e. VUs) to have concurrency (more info). k6 websockets actually have their own local event loop, but currently, to answer your first question, the VU cannot move on while leaving a socket connected.

I also think that it may be a bad design choice.

I kind of agree... While the current k6 architecture works relatively well for HTTP requests, having a proper even loop will be very helpful if we want more useful websockets or things like HTTP/2 server push, gRPC, and other similar protocols. We plan to introduce a real event loop per VU at some point, albeit likely a bit more restricted than others, simply due to the nature of load testing and VUs... I'll add a separate issue for it later and link this one to it, since once we have that global event loop, we should definitely implement a more flexible way to work with websockets.

If it's possible, Is there a global state that can be used like seen above or could I use the data object passed down from setup() to each VU instance to keep track of open sockets to be closed?

No, and we currently don't plan to add that. We can theoretically have some thread-safe global data store for a single k6 instance, but sharing actual JS objects between different JS runtimes is either extremely hard or just plain impossible. Also, this won't scale well enough in a distributed execution context where there's more than one k6 instance. And in that case it'd likely be more feasible to use an external service like Redis for sharing data between all of the distributed VUs.

@AlexGustafsson
Copy link
Author

AlexGustafsson commented Dec 13, 2018

Thank you for such a thorough explanation. I understand the choices you’ve made and I can’t really see it work any differently. You’ve answered all my questions and more.

@mstoykov
Copy link
Contributor

There is currently an extension that lets you do that. It might at some point be moved to core, but you can use it for now if it's particularly needed by someone.

@mstoykov
Copy link
Contributor

mstoykov commented Sep 8, 2022

The extension is now released as an experimental module in k6 v0.40.0, more info in the relevant section of the release notes

But I am going to keep this open until an API for this is considered stable in k6.

@codebien
Copy link
Contributor

codebien commented Oct 3, 2023

As the previous comment mentions, it has been released a WebSocket experimental module that solves the main issue.
We plan to merge to the core in one of the next releases, track #3185 for getting the updates.

@codebien codebien closed this as completed Oct 3, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants