-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
Global JS event loops in every VU #882
Comments
Hi na, Do you already have an implementation of a simple event loop in k6 ? I'd be interested in working on a http sse implementation that can callback or send events to main js code. Thanks |
Thanks, @ofauchon! There's a simple event loop implementation from goja's author here, but I haven't investigated how easy it would be to integrate something like it into k6 architecture. I assume there's some substantial refactoring involved... We currently have a very localized event loop in k6 for the websocket handling code here, but unfortunately that type of localized event loop doesn't scale very well and it has some other issues, which is why we're pursuing global event loops. |
After watching this explanation of how the event loop works in V8, I am of the opinion we should probably get started with this in the most simple way possible:
This has the problem that we do not add a way to make an http request asyncly. We should probably just add functions with callbacks on them that does this as is XMLHttpRequest as well as XMLHttpRequest? Alternatively we can make it possible that on golang calls (http.get for example) k6 checks the queue and possibly starts executing from it. This will need some way of ... copying the stack or creating new stacks for the JS VM ? It will also be complicated as we can touch JS values from inside golang's code and we do it .. for various reasons. As a whole this approach will be more golangish and ... arguably better but it will also be very non-JS and I am of the opinion we should NOT do it, but instead be as JSier as possible in order for more libraries to work out of the box. This will mean that we will need to change/add the http and the ws api to be async. This can probably be done by adding a callback function to the parameters of the There is definitely a lot more to be discussed and thought but I think it is about time we make at least some experimental implementation. |
I'd usually agree, but in my head, we should start not with the absolute simplest implementation possible, but figure out roughly what we want and start with an exploratory (i.e. simple and potentially throwaway) implementation that covers our initial list of requirements, so we can see where we're having issues and what's missing. Specifically, due to the current k6 architecture, I think it's likely a hard requirement for us to have a way to restrict the execution of event loop jobs at some point. It's still unclear to me whether that point is after each iteration is done or after a scheduler/executor is done, but there should be some sort of a timeout after which any outstanding event loop jobs are killed and discarded. And maybe a simple Like everything else with this issue, that's still not clear to me, thus I think we'll probably have to throw away our first implementation 😄
This seems reasonable as a start, though I think we should investigate whether there are other opportunities for working on (i.e. executing outstanding events from) the event loop. Specifically, I think it's probably a bad idea to try to clear the event loop when we're doing HTTP requests, simply because k6 is a load testing tool and it's not very acceptable for us to delay HTTP requests. Which can happen, if some event takes longer to complete than the HTTP request during which we scheduled it.
I'm not sure callbacks are the best way to go about here. Instead, if we add a new Unfortunately, for that functionality, we'd probably need to extend goja with native support for promises... That shouldn't be too complicated, given Go's async support, but it won't exactly be easy either 😞 It has merits on it's own though, since having an event loop, but only accessing it with
Actually, I'm not sure that's a problem. Given how the current
Not sure what you mean by that, sorry. |
What I meant is that we do handle (at least) the parameters to all the functions so if you have something like (pseudo code):
If we decide to during But apart from this ... this is not how JS works anywhere else ... and I think it doesn't work like that anywhere else specifically because it will be a nightmare to reason about. The way JS event loop works (and all other event loops AFAIK) is that you get 1 function that is executed that can queue other functions and when it finishes those functions can be executed. Not they queue other functions that might be executed in the middle of the execution of this function. I do agree that SetTimeout(and family) are not particularly user friendly, but I am not proposing that we juts get an eventloop and SetTimeout release it and not touch ever again. I am saying that nomatter how I look at this we do need:
I do agree that ultimately we will probably need http.* (and others) to return Promise in some cases (the params sounds good). But this seem like way too much work as a first experiment and as such I prefer if the first experiment is the bare minimum to get ANY amount of eventloopines. The polyfll mention above also have denodefy which makes a callback based function into a Promise. We can also have
Which although will not help you have more than 1 request being made at the same time will show us (somewhat) how code may look when you try to write async code with |
I found video explaining the event loop and the difference between executing things with p.s. The whole video is interesting, but the linked portion is the more relevant part |
Another use case for asynchronous requests in a single VU: https://community.k6.io/t/run-one-vu-at-a-time-but-have-varying-ramp-up-pattern-within-duration/1377 |
As well as cut down setTimeout implementation A recent update to goja introduced Promise. The catch there is that Promise's then will only be called when goja exits executing js code and it has already been resolved. Also resolving and rejecting Promises needs to happen while no other js code is being executed. This more or less necessitates adding an event loop. Additionally because a call to a k6 modules such as `k6/http` might make a promise to signal when an http request is made, but if (no changes were made) the iteration then finishes before the request completes, nothing would've stopped the start of a *new* iteration (which would probably just again ask k6/http to make a new request and return Promise). This might be a desirable behaviour for some cases but arguably will be very confusing so this commit also adds a way to Reserve(name should be changed) a place on the queue (doesn't reserve an exact spot) so that the event loop will not let the iteration finish until it gets unreserved. Additional abstraction to make a "handled" Promise is added so that k6 js-modules can use it the same way goja.NewPromise but with the key difference that the Promise will be waited to be resolved before the event loop can end. Additionally to that some additional code was needed so there is an event loop for all special functions calls (setup, teardown, handleSummary, default) and the init context. And finally a basic setTimeout implementation was added. There is no way to currently cancel the setTimeout and it doesn't take code as the first argument or additional arguments to be given to the callback later on. fixes #882
As well as cut down setTimeout implementation A recent update to goja introduced Promise. The catch here is that Promise's then will only be called when goja exits executing js code and it has already been resolved. Also resolving and rejecting Promises needs to happen while no other js code is being executed. This more or less necessitates adding an event loop. Additionally because a call to a k6 modules such as `k6/http` might make a promise to signal when an http request is made, but if (no changes were made) the iteration then finishes before the request completes, nothing would've stopped the start of a *new* iteration (which would probably just again ask k6/http to make a new request and return Promise). This might be a desirable behaviour for some cases but arguably will be very confusing so this commit also adds a way to Reserve(name should be changed) a place on the queue (doesn't reserve an exact spot) so that the event loop will not let the iteration finish until it gets unreserved. Additional abstraction to make a "handled" Promise is added so that k6 js-modules can use it the same way goja.NewPromise but with the key the difference that the Promise will be waited to be resolved before the event loop can end. Additionally to that, some additional code was needed so there is an event loop for all special functions calls (setup, teardown, handleSummary, default) and the init context. And finally, a basic setTimeout implementation was added. There is no way to currently cancel the setTimeout and it doesn't take code as the first argument or additional arguments to be given to the callback later on. fixes #882
As well as cut down setTimeout implementation A recent update to goja introduced Promise. The catch here is that Promise's then will only be called when goja exits executing js code and it has already been resolved. Also resolving and rejecting Promises needs to happen while no other js code is being executed. This more or less necessitates adding an event loop. Additionally because a call to a k6 modules such as `k6/http` might make a promise to signal when an http request is made, but if (no changes were made) the iteration then finishes before the request completes, nothing would've stopped the start of a *new* iteration (which would probably just again ask k6/http to make a new request and return Promise). This might be a desirable behaviour for some cases but arguably will be very confusing so this commit also adds a way to Reserve(name should be changed) a place on the queue (doesn't reserve an exact spot) so that the event loop will not let the iteration finish until it gets unreserved. Additional abstraction to make a "handled" Promise is added so that k6 js-modules can use it the same way goja.NewPromise but with the key the difference that the Promise will be waited to be resolved before the event loop can end. Additionally to that, some additional code was needed so there is an event loop for all special functions calls (setup, teardown, handleSummary, default) and the init context. And finally, a basic setTimeout implementation was added. There is no way to currently cancel the setTimeout and it doesn't take code as the first argument or additional arguments to be given to the callback later on. fixes #882
As well as cut down setTimeout implementation A recent update to goja introduced Promise. The catch here is that Promise's then will only be called when goja exits executing js code and it has already been resolved. Also resolving and rejecting Promises needs to happen while no other js code is being executed. This more or less necessitates adding an event loop. Additionally because a call to a k6 modules such as `k6/http` might make a promise to signal when an http request is made, but if (no changes were made) the iteration then finishes before the request completes, nothing would've stopped the start of a *new* iteration (which would probably just again ask k6/http to make a new request and return Promise). This might be a desirable behaviour for some cases but arguably will be very confusing so this commit also adds a way to Reserve(name should be changed) a place on the queue (doesn't reserve an exact spot) so that the event loop will not let the iteration finish until it gets unreserved. Additional abstraction to make a "handled" Promise is added so that k6 js-modules can use it the same way goja.NewPromise but with the key the difference that the Promise will be waited to be resolved before the event loop can end. Additionally to that, some additional code was needed so there is an event loop for all special functions calls (setup, teardown, handleSummary, default) and the init context. And finally, a basic setTimeout implementation was added. There is no way to currently cancel the setTimeout and it doesn't take code as the first argument or additional arguments to be given to the callback later on. fixes #882
As well as cut down setTimeout implementation A recent update to goja introduced Promise. The catch here is that Promise's then will only be called when goja exits executing js code and it has already been resolved. Also resolving and rejecting Promises needs to happen while no other js code is being executed. This more or less necessitates adding an event loop. Additionally because a call to a k6 modules such as `k6/http` might make a promise to signal when an http request is made, but if (no changes were made) the iteration then finishes before the request completes, nothing would've stopped the start of a *new* iteration (which would probably just again ask k6/http to make a new request and return Promise). This might be a desirable behaviour for some cases but arguably will be very confusing so this commit also adds a way to Reserve(name should be changed) a place on the queue (doesn't reserve an exact spot) so that the event loop will not let the iteration finish until it gets unreserved. Additional abstraction to make a "handled" Promise is added so that k6 js-modules can use it the same way goja.NewPromise but with the key the difference that the Promise will be waited to be resolved before the event loop can end. Additionally to that, some additional code was needed so there is an event loop for all special functions calls (setup, teardown, handleSummary, default) and the init context. And finally, a basic setTimeout implementation was added. There is no way to currently cancel the setTimeout and it doesn't take code as the first argument or additional arguments to be given to the callback later on. fixes #882
As well as cut down setTimeout implementation A recent update to goja introduced Promise. The catch here is that Promise's then will only be called when goja exits executing js code and it has already been resolved. Also resolving and rejecting Promises needs to happen while no other js code is being executed. This more or less necessitates adding an event loop. Additionally because a call to a k6 modules such as `k6/http` might make a promise to signal when an http request is made, but if (no changes were made) the iteration then finishes before the request completes, nothing would've stopped the start of a *new* iteration (which would probably just again ask k6/http to make a new request and return Promise). This might be a desirable behaviour for some cases but arguably will be very confusing so this commit also adds a way to Reserve(name should be changed) a place on the queue (doesn't reserve an exact spot) so that the event loop will not let the iteration finish until it gets unreserved. Additional abstraction to make a "handled" Promise is added so that k6 js-modules can use it the same way goja.NewPromise but with the key the difference that the Promise will be waited to be resolved before the event loop can end. Additionally to that, some additional code was needed so there is an event loop for all special functions calls (setup, teardown, handleSummary, default) and the init context. And finally, a basic setTimeout implementation was added. There is no way to currently cancel the setTimeout and it doesn't take code as the first argument or additional arguments to be given to the callback later on. fixes #882
As well as cut down setTimeout implementation A recent update to goja introduced Promise. The catch there is that Promise's then will only be called when goja exits executing js code and it has already been resolved. Also resolving and rejecting Promises needs to happen while no other js code is being executed. This more or less necessitates adding an event loop. Additionally because a call to a k6 modules such as `k6/http` might make a promise to signal when an http request is made, but if (no changes were made) the iteration then finishes before the request completes, nothing would've stopped the start of a *new* iteration (which would probably just again ask k6/http to make a new request and return Promise). This might be a desirable behaviour for some cases but arguably will be very confusing so this commit also adds a way to Reserve(name should be changed) a place on the queue (doesn't reserve an exact spot) so that the event loop will not let the iteration finish until it gets unreserved. Additional abstraction to make a "handled" Promise is added so that k6 js-modules can use it the same way goja.NewPromise but with the key difference that the Promise will be waited to be resolved before the event loop can end. Additionally to that some additional code was needed so there is an event loop for all special functions calls (setup, teardown, handleSummary, default) and the init context. And finally a basic setTimeout implementation was added. There is no way to currently cancel the setTimeout and it doesn't take code as the first argument or additional arguments to be given to the callback later on. fixes #882
As well as cut down setTimeout implementation A recent update to goja introduced Promise. The catch here is that Promise's then will only be called when goja exits executing js code and it has already been resolved. Also resolving and rejecting Promises needs to happen while no other js code is being executed. This more or less necessitates adding an event loop. Additionally because a call to a k6 modules such as `k6/http` might make a promise to signal when an http request is made, but if (no changes were made) the iteration then finishes before the request completes, nothing would've stopped the start of a *new* iteration (which would probably just again ask k6/http to make a new request and return Promise). This might be a desirable behaviour for some cases but arguably will be very confusing so this commit also adds a way to Reserve(name should be changed) a place on the queue (doesn't reserve an exact spot) so that the event loop will not let the iteration finish until it gets unreserved. Additional abstraction to make a "handled" Promise is added so that k6 js-modules can use it the same way goja.NewPromise but with the key the difference that the Promise will be waited to be resolved before the event loop can end. Additionally to that, some additional code was needed so there is an event loop for all special functions calls (setup, teardown, handleSummary, default) and the init context. And finally, a basic setTimeout implementation was added. There is no way to currently cancel the setTimeout and it doesn't take code as the first argument or additional arguments to be given to the callback later on. fixes #882
As well as cut down setTimeout implementation A recent update to goja introduced Promise. The catch here is that Promise's then will only be called when goja exits executing js code and it has already been resolved. Also resolving and rejecting Promises needs to happen while no other js code is being executed. This more or less necessitates adding an event loop. Additionally because a call to a k6 modules such as `k6/http` might make a promise to signal when an http request is made, but if (no changes were made) the iteration then finishes before the request completes, nothing would've stopped the start of a *new* iteration (which would probably just again ask k6/http to make a new request and return Promise). This might be a desirable behaviour for some cases but arguably will be very confusing so this commit also adds a way to Reserve(name should be changed) a place on the queue (doesn't reserve an exact spot) so that the event loop will not let the iteration finish until it gets unreserved. Additional abstraction to make a "handled" Promise is added so that k6 js-modules can use it the same way goja.NewPromise but with the key the difference that the Promise will be waited to be resolved before the event loop can end. Additionally to that, some additional code was needed so there is an event loop for all special functions calls (setup, teardown, handleSummary, default) and the init context. And finally, a basic setTimeout implementation was added. There is no way to currently cancel the setTimeout and it doesn't take code as the first argument or additional arguments to be given to the callback later on. fixes #882
As well as cut down setTimeout implementation. A recent update to goja introduced support for ECMAscript Promise. The catch here is that Promise's then will only be called when goja exits executing js code and it has already been resolved. Also resolving and rejecting Promises needs to happen while no other js code is being executed as it will otherwise lead to a data race. This more or less necessitates adding an event loop. Additionally because a call to a k6 modules such as `k6/http` might make a promise to signal when an http request is made, but if (no changes were made) the iteration then finishes before the request completes, nothing would've stopped the start of a *new* iteration, which would probably just again ask k6/http to make a new request and return Promise. This might be a desirable behaviour for some cases but arguably will be very confusing so this commit also adds a way to Reserve(name should be changed) a place on the queue so that the event loop will not let the iteration finish until it gets unreserved. Additionally to that, some additional code was needed so there is an event loop for all special functions calls (setup, teardown, handleSummary, default) and the init context. This also adds handling of rejected promise which don't have a reject handler similar to what deno does. It also adds a per iteration context that gets canceled on the end of each iteration letting other code know that it needs to stop. This is particularly needed here as if an iteration gets aborted by a syntax error (or unhandled promise rejection), a new iteration will start right after that. But this means that any in-flight asynchronous operation (an http requests for example) will *not* get stopped. With a context that gets canceled every time module code can notice that and abort any operation. For this same reason the event loop need to be waited to be *empty* before the iteration ends. This did lead to some ... not very nice code, but a whole package needs a big refactor which will likely happen once common.Bind and co gets removed. And finally, a basic setTimeout implementation was added. There is no way to currently cancel the setTimeout aka no clearTimeout. This likely needs to be extended but this can definitely wait. Or we might decide to actually drop setTimeout. fixes #882
As well as cut down setTimeout implementation. A recent update to goja introduced support for ECMAScript Promise. The catch here is that Promise's then will only be called when goja exits executing js code and it has already been resolved. Also resolving and rejecting Promises needs to happen while no other js code is being executed as it will otherwise lead to a data race. This more or less necessitates adding an event loop. Additionally because a call to a k6 modules such as `k6/http` might make a promise to signal when an http request is made, but if (no changes were made) the iteration then finishes before the request completes, nothing would've stopped the start of a *new* iteration. That new iteration would then probably just again ask k6/http to make a new request with a Promise ... This might be a desirable behaviour for some cases but arguably will be very confusing so this commit also adds a way to RegisterCallback that will return a function to actually queue the callback on the event loop, but prevent the event loop from ending before the callback is queued and possible executed, once RegisterCallback is called. Additionally to that, some additional code was needed so there is an event loop for all special functions calls (setup, teardown, handleSummary, default) and the init context. This also adds handling of rejected promise which don't have a reject handler similar to what deno does. It also adds a per iteration context that gets canceled on the end of each iteration letting other code know that it needs to stop. This is particularly needed here as if an iteration gets aborted by a syntax error (or unhandled promise rejection), a new iteration will start right after that. But this means that any in-flight asynchronous operation (an http requests for example) will *not* get stopped. With a context that gets canceled every time module code can notice that and abort any operation. For this same reason the event loop needs wait to be *empty* before the iteration ends. This did lead to some ... not very nice code, but a whole package needs a big refactor which will likely happen once common.Bind and co gets removed. And finally, a basic setTimeout implementation was added. There is no way to currently cancel the setTimeout - no clearTimeout. This likely needs to be extended but this can definitely wait. Or we might decide to actually drop setTimeout altogether as it isn't particularly useful currently without any async APIs, it just makes testing the event loop functionality possible. fixes #882
As well as cut down setTimeout implementation. A recent update to goja introduced support for ECMAScript Promise. The catch here is that Promise's then will only be called when goja exits executing js code and it has already been resolved. Also resolving and rejecting Promises needs to happen while no other js code is being executed as it will otherwise lead to a data race. This more or less necessitates adding an event loop. Additionally because a call to a k6 modules such as `k6/http` might make a promise to signal when an http request is made, but if (no changes were made) the iteration then finishes before the request completes, nothing would've stopped the start of a *new* iteration. That new iteration would then probably just again ask k6/http to make a new request with a Promise ... This might be a desirable behaviour for some cases but arguably will be very confusing so this commit also adds a way to RegisterCallback that will return a function to actually queue the callback on the event loop, but prevent the event loop from ending before the callback is queued and possible executed, once RegisterCallback is called. Additionally to that, some additional code was needed so there is an event loop for all special functions calls (setup, teardown, handleSummary, default) and the init context. This also adds handling of rejected promise which don't have a reject handler similar to what deno does. It also adds a per iteration context that gets canceled on the end of each iteration letting other code know that it needs to stop. This is particularly needed here as if an iteration gets aborted by a syntax error (or unhandled promise rejection), a new iteration will start right after that. But this means that any in-flight asynchronous operation (an http requests for example) will *not* get stopped. With a context that gets canceled every time module code can notice that and abort any operation. For this same reason the event loop needs wait to be *empty* before the iteration ends. This did lead to some ... not very nice code, but a whole package needs a big refactor which will likely happen once common.Bind and co gets removed. And finally, a basic setTimeout implementation was added. There is no way to currently cancel the setTimeout - no clearTimeout. This likely needs to be extended but this can definitely wait. Or we might decide to actually drop setTimeout altogether as it isn't particularly useful currently without any async APIs, it just makes testing the event loop functionality possible. fixes #882
As well as cut down setTimeout implementation. A recent update to goja introduced support for ECMAScript Promise. The catch here is that Promise's then will only be called when goja exits executing js code and it has already been resolved. Also resolving and rejecting Promises needs to happen while no other js code is being executed as it will otherwise lead to a data race. This more or less necessitates adding an event loop. Additionally because a call to a k6 modules such as `k6/http` might make a promise to signal when an http request is made, but if (no changes were made) the iteration then finishes before the request completes, nothing would've stopped the start of a *new* iteration. That new iteration would then probably just again ask k6/http to make a new request with a Promise ... This might be a desirable behaviour for some cases but arguably will be very confusing so this commit also adds a way to RegisterCallback that will return a function to actually queue the callback on the event loop, but prevent the event loop from ending before the callback is queued and possible executed, once RegisterCallback is called. Additionally to that, some additional code was needed so there is an event loop for all special functions calls (setup, teardown, handleSummary, default) and the init context. This also adds handling of rejected promise which don't have a reject handler similar to what deno does. It also adds a per iteration context that gets canceled on the end of each iteration letting other code know that it needs to stop. This is particularly needed here as if an iteration gets aborted by a syntax error (or unhandled promise rejection), a new iteration will start right after that. But this means that any in-flight asynchronous operation (an http requests for example) will *not* get stopped. With a context that gets canceled every time module code can notice that and abort any operation. For this same reason the event loop needs wait to be *empty* before the iteration ends. This did lead to some ... not very nice code, but a whole package needs a big refactor which will likely happen once common.Bind and co gets removed. And finally, a basic setTimeout implementation was added. There is no way to currently cancel the setTimeout - no clearTimeout. This likely needs to be extended but this can definitely wait. Or we might decide to actually drop setTimeout altogether as it isn't particularly useful currently without any async APIs, it just makes testing the event loop functionality possible. fixes #882
As well as cut down setTimeout implementation. A recent update to goja introduced support for ECMAScript Promise. The catch here is that Promise's then will only be called when goja exits executing js code and it has already been resolved. Also resolving and rejecting Promises needs to happen while no other js code is being executed as it will otherwise lead to a data race. This more or less necessitates adding an event loop. Additionally because a call to a k6 modules such as `k6/http` might make a promise to signal when an http request is made, but if (no changes were made) the iteration then finishes before the request completes, nothing would've stopped the start of a *new* iteration. That new iteration would then probably just again ask k6/http to make a new request with a Promise ... This might be a desirable behaviour for some cases but arguably will be very confusing so this commit also adds a way to RegisterCallback that will return a function to actually queue the callback on the event loop, but prevent the event loop from ending before the callback is queued and possible executed, once RegisterCallback is called. Additionally to that, some additional code was needed so there is an event loop for all special functions calls (setup, teardown, handleSummary, default) and the init context. This also adds handling of rejected promise which don't have a reject handler similar to what deno does. It also adds a per iteration context that gets canceled on the end of each iteration letting other code know that it needs to stop. This is particularly needed here as if an iteration gets aborted by a syntax error (or unhandled promise rejection), a new iteration will start right after that. But this means that any in-flight asynchronous operation (an http requests for example) will *not* get stopped. With a context that gets canceled every time module code can notice that and abort any operation. For this same reason the event loop needs wait to be *empty* before the iteration ends. This did lead to some ... not very nice code, but a whole package needs a big refactor which will likely happen once common.Bind and co gets removed. And finally, a basic setTimeout implementation was added. There is no way to currently cancel the setTimeout - no clearTimeout. This likely needs to be extended but this can definitely wait. Or we might decide to actually drop setTimeout altogether as it isn't particularly useful currently without any async APIs, it just makes testing the event loop functionality possible. fixes #882
A recent update to goja introduced support for ECMAScript Promise. The catch here is that Promise's then will only be called when goja exits executing js code and it has already been resolved. Also resolving and rejecting Promises needs to happen while no other js code is being executed as it will otherwise lead to a data race. This more or less necessitates adding an event loop. Additionally because a call to a k6 modules such as `k6/http` might make a promise to signal when an http request is made, but if (no changes were made) the iteration then finishes before the request completes, nothing would've stopped the start of a *new* iteration. That new iteration would then probably just again ask k6/http to make a new request with a Promise ... This might be a desirable behaviour for some cases but arguably will be very confusing so this commit also adds a way to RegisterCallback that will return a function to actually queue the callback on the event loop, but prevent the event loop from ending before the callback is queued and possible executed, once RegisterCallback is called. Additionally, to that, some additional code was needed so there is an event loop for all special functions calls (setup, teardown, handleSummary, default) and the init context. This also adds handling of rejected promise which don't have a reject handler similar to what deno does. It also adds a per iteration context that gets canceled on the end of each iteration letting other code know that it needs to stop. This is particularly needed here as if an iteration gets aborted by a syntax error (or unhandled promise rejection), a new iteration will start right after that. But this means that any in-flight asynchronous operation (an http requests for example) will *not* get stopped. With a context that gets canceled every time module code can notice that and abort any operation. For this same reason the event loop needs to wait to be *empty* before the iteration ends. This did lead to some ... not very nice code, but a whole package needs a big refactor which will likely happen once common.Bind and co gets removed. fixes #882
To actually support a lot of cool features and to achieve better compatibility with a ton of JavaScript APIs and libraries out there, we need to be able to support asynchronous execution inside of VUs via a global event loop. We have a localized one in the current websocket implementation, but even that's not enough in a lot of cases.
Things which will benefit from or need an event loop:
http.batch()
equivalent)setTimeout()
,setInterval()
, etc. (Timers API:setTimeout, setInterval, clearTimeout, clearInterval
#332)async
/await
(How to use imported async functions in test script? #779)XMLHttpRequest
orFetch
, so we have a broader support for browser JS libraries and browserified node librariesOf course, the current looping VU model of k6 will necessitate certain restrictions in the way event loops are normally implemented. For one, each VU in k6 has its own separate JS runtime, so each VU will also need to have its own event loop.
Also, events probably shouldn't cross over between iterations of the same VU, since that would be very confusing and likely quite error-prone, especially with the arrival-rate based executor. Memory leaks would also be a concern... And even if cross-iteration events are easy to implement and we decide to allow them, I think they shouldn't be enabled by default. So some type of configurable post-iteration timeout option should probably be introduced that will be used to control how long k6 will wait for queued events to fire and finish up before it discards them and continues to the next iteration.
As a simple event loop implementation we may look to for inspiration and ideas, the author of goja (the JS runtime k6 uses) also has this repo: https://github.com/dop251/goja_nodejs
The text was updated successfully, but these errors were encountered: