-
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
Internal module instances per VU #1858
Comments
Hmm yeah, 👍 this definitely deserves some investigation... I wonder if we even have to make it optional? If every VU has its own copy of the module |
I am somewhat against this ... as this makes it kind of impossible to have 1 instance of k6 running 2+ tests in parallel. This is technically impossible now as well, but having to use global variables for this functionality will definitely make it harder in the future. It also makes testing ... harder, and we already are trying to drop global variables so ... let's not design them in something ;). The good news is that technically nothing forbids this even if we make it a requirement. But the code above (where the currently registered struct is checked for a I would argue that regardless we can have it work with the old version (through checking for an I doubt there are all that many xk6 extensions that aren't made by k6 employees, so updating them will probably be a non-issue., but still the above seems like it will just be a few more lines and a link to some documentation -possibly just the changelog or a post in the community forum, although a doc page might not be a bad idea as well |
This is dones so we can now have per VU settign for the HTTP module such as the ResponseCallback This is part of #1858
More on the actual changes that I currently envision for this(hopefully done in v0.32.0):
Why require the interface instead of just checking for it?Consistency and ease of use/expansion. If we just check for it without enforcing it(or even worse check for multiple interfaces), it means that down the line a possible rewrite will be required on the module author part to add per module shared state (both for VU or per test) as was the case in d36ce43, if they don't want to go for global variables 🤢. edit: It is very likely that both the per-vu instance and the per-test instance will be taking some struct/interface as an argument so they can get some parameters that matter for them. What those will include exactly and will one of them be edit 2: maybe the |
I am unconvinced by the necessity of all of this extra complexity, breaking changes and multiple interfaces... Very, very far from convinced, in fact... So, instead, here's a counter suggestion, based on the discussion in #1856 (comment). We only have this code you've already written and a simpler version of the module registry you propose ([a]): https://github.com/loadimpact/k6/blob/fde4130165079de64b417dbad31a6fbf72b163ec/js/internal/modules/modules.go#L34-L49 Though, again, I think the interfaces should be named like this instead of // HasModuleInstancePerVU should be implemented by all native Golang modules that
// would require per-VU state. k6 will call their NewModuleInstance() methods
// every time a VU imports the module and use its result as the returned object.
type HasModuleInstancePerVU interface {
NewModuleInstance() interface{}
} With this, we can have everything we want:
[a] The only problem with testing here are actually the However, I think the solution isn't to introduce more levels of abstraction, it's to fix the func GetJSModules() map[string]interface{}{
return map[string]interface{}{
"k6": k6.New(),
"k6/data": data.NewModule(),
"k6/http": http.NewModule(),
// ...
}
} Not perfect, but actually better than this...Using this will remove the intrinsic global variables we are currently struggling with. It will also be perfectly adequate for integration tests with all built-in JS modules, but without any conflicts or shared registries, and it will remove the So, for now, I'm firmly against most of the complexity you want to introduce, with the exception of this simplified module registry, for which 👍 from me for k6 v0.32.0. The approach above seems to be good enough for all use cases we currently have and would want to handle in the future, without any breaking changes required. And if it isn't, it can be easily extended in a much more Go-idiomatic way (simple optional interfaces), if we ever want to handle more complicated things in the future... To change my mind, I'd need some very good arguments why I'm wrong, as well as concrete use cases where the above approach fails to suffice. |
As stated in #1909, I just want to pitch in that we at some point, when we've agreed on the implementation, should make this accessible to external consumers as well. 😅 |
This allows integration tests to have separate global module instance of all internal js modules. For this to be supported for extension an additional type/interface will be required a bit more added code
This allows integration tests to have separate global module instance of all internal js modules. For this to be supported for extension an additional type/interface will be required a bit more added code
This allows integration tests to have separate global module instance of all internal js modules. For this to be supported for extension an additional type/interface will be required a bit more added code
I think we can close this, since pretty much everything got implemented in #1911? |
Currently, internal modules such as
http
have a single instance of them for the whole k6 instance. This is not how it works for not internal modules - imported js files, which get an instance per VU.This will make it possible for per VU settings (of a single module) such as the callback from #1856 to actually not be in the lib.State. This is also probably true for the RPSLimit and Transport especially if in the future we have multiple transports.
Having both per VU and global store would've also let stuff like #1739 to be implemented only by a single module without other changes in other parts of the code.
This is not to say that
lib.State
should be removed as there are definitely things that need to be in a more shared location between modules such as the tls.Config.Currently all, but HTTP and gRPC use empty structs (both the exceptions use it for constants) and one of the easiest ways to add a way to have this will be to instead of taking an instance to take the
New
method most of those call to get an instance.This though is a breaking change especially with
xk6
in mind. Given that, and the fact that it will also be nice if there is a way to have a "shared" global state per/in module without the usage of global variables such as in xk6-counter maybe it will be better to keep the current behaviour, but being able to add a methodNew
(name up for debate, maybe Instantiate) that will then return a new copy of another object that will be the actual module instance:So for HTTP it will be something like:
And the code in
https://github.com/loadimpact/k6/blob/11811171a3c416daf17f7f0ad182ca30aa9990bb/js/internal/modules/modules.go#L35-L39
or
https://github.com/loadimpact/k6/blob/11811171a3c416daf17f7f0ad182ca30aa9990bb/js/initcontext.go#L142-L148
will have
or similar (code was written in GitHub, so likely has problems ;) )
The text was updated successfully, but these errors were encountered: