-
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
Fix/1443 remove the JS runtime from threshold calculations #2251
Fix/1443 remove the JS runtime from threshold calculations #2251
Conversation
I'd be really interested in your thoughts on how I approached testing @inancgumus; I'm all ears for any recommendations on that topic specifically 👂🏻 🙏🏻 |
1b251b1
to
f24b04d
Compare
f24b04d
to
2e4adb5
Compare
The Pull Request makes the engine's |
2e4adb5
to
7569d94
Compare
a78bc1b
to
f3a2663
Compare
I haven't gone through the code, yet, but I would expect expressions like Given that, for the purposes of the tests, you can rewrite (as you seem to have done) in a way that you get the test to pass -where it requires that the threshold fails just to write a threshold that will fail when evaluated but still based on the metric. |
f3a2663
to
28c66d8
Compare
@oleiade I've only started digging into this, hopefully will finish tomorrow 😅 But I have a preliminary question: the work you reference in the PR description (Jeffhail benthos' bloblang) -- should it be referenced in the code as well? I imagine it depends on how much you adapted it... Also, CI failure shows:
This doesn't look like a flaky one... Did it happen before? I can't repeat it on the current master 😕 |
28c66d8
to
5a98912
Compare
@yorugac Sorry for the huge chunk of code I've just dropped 🙏🏻 I prefer to make smaller iterative PRs in general, but I found it hard to avoid in this case. You're right about mentioning Jeff Hail's work somehow in the code. I wanted to find out what would be the best way to do that, and it somehow slipped out of my mind. I've adapted and extended the API, and modified some implementation here and there, but a big part of the code is still verbatim. I would like to add a copy of his copyright license at the top of the file (as is the MIT license etiquette, I think?); and mention that the base code comes from bloblang's implementation. Does that sound like a good approach. Regarding the failing test, as I haven't touched the code since yesterday, and only rebased my PR on master, I can only assume that either the failing test comes from master, or that it impacted my code's behavior somehow. I'll look into it, thanks for pointing it out! 👍🏻 |
4f61eb4
to
e5c8ad7
Compare
@yorugac I've modified the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the clarification on licenses, @oleiade ! It is now clear enough to me what happens with the code adaptation in this PR 👍 But I'm not sure about how it should be done in k6 code base. I.e. should there be 2 full license headers as is currently done in combinators.go
: it takes a lot of space and IIRC, there was some talk about dropping all of those headers? The only precedent in k6 that I know of ATM is the tc39 files and those are left without a header.
@na--, @mstoykov, could you please add your opinion on what should be done in this case with license / authorship?
On the review part. combinators
pkg looks quite nice and Result
is reminding me of some other languages 😄 I'm not sure about its name though: maybe rename it to just parser
? I currently assume "combinators" came from the original code but it doesn't make much sense in k6 context IMHO. I don't have a strong opinion about pkg
folder except there is only lib
at the moment: are we moving lib
-> pkg
? It doesn't make sense to me to have 2 folders without understanding the difference between them.
Overall, I like how this change shapes up a lot! Theo, please see the comments: there are some suggestions about renamings and some requests about logic and clarifications. And one more thing I've been wondering about: should some benchmarking be done here?
@yorugac Thanks for the review! I'll look into your comments and address them when I can. Regarding the naming, I don't think Regarding benchmarking, I've thought about it, without never actually getting to implementing it. Now that you ask about it, I'll add some :) |
28cd94b
to
a73759a
Compare
Note that I will make sure to squash all "Update", and "fixup!" commits before merging 👍🏻 |
c992f76
to
685cdbf
Compare
In this commit we use the parser combinator library introduced in the previous commit to build a threshold expression parser. We match the existing supported expression format, without any modifications or additions.
In this commit we replace the previously existing thresholds condition evaluation, which was depending on Goja's Runtime, with a new pure-Go one. Thresholds are now parsed, and evaluated in o, and no JS rutimes are involved in the process anymore. It is built upong the thresholds parser, and parser combinators library introduced in previous commits.
685cdbf
to
81cccd9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! According to the current "thresholds plan" as was discussed in this PR 😄
// expression | ||
lhs, ok := sinks[t.parsed.AggregationMethod] | ||
if !ok { | ||
return false, fmt.Errorf("unable to apply threshold %s over metrics; reason: "+ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The next step here, in another PR, would be to tie the threshold validation with the metrics in our metrics registry. This validation is awesome, much better than what existed (or rather, what didn't exist 😅) before, but we can do it right after the initial execution of the init context. Right now, this script:
import { sleep } from 'k6';
import { Counter } from 'k6/metrics';
import exec from 'k6/execution';
const myCounter = new Counter('my_counter');
export const options = {
vus: 5,
iterations: 30,
thresholds: {
my_counter: ['p(95)<200'], // error, Counters do not have p()
},
};
export default function () {
console.log(`[t=${(new Date()) - exec.scenario.startTime}ms] VU{${exec.vu.idInTest}} ran scenario iteration ${exec.scenario.iterationInTest}`);
myCounter.add(1);
sleep(1);
}
will emit something like this:
INFO[0000] [t=0ms] VU{3} ran scenario iteration 0 source=console
INFO[0000] [t=0ms] VU{5} ran scenario iteration 2 source=console
INFO[0000] [t=0ms] VU{2} ran scenario iteration 1 source=console
INFO[0000] [t=0ms] VU{1} ran scenario iteration 3 source=console
INFO[0000] [t=0ms] VU{4} ran scenario iteration 4 source=console
INFO[0001] [t=1001ms] VU{2} ran scenario iteration 7 source=console
INFO[0001] [t=1001ms] VU{4} ran scenario iteration 5 source=console
INFO[0001] [t=1001ms] VU{3} ran scenario iteration 9 source=console
INFO[0001] [t=1001ms] VU{5} ran scenario iteration 6 source=console
INFO[0001] [t=1001ms] VU{1} ran scenario iteration 8 source=console
ERRO[0002] Threshold error component=engine error="threshold 0 run error: unable to apply threshold p(95)<200 over metrics; reason: no metric supporting the p(95) aggregation method found" m=my_counter
but right now there is nothing preventing us from validating the thresholds before we actually start running the script, registry
should have all of the metrics (custom or built-in) and their types right after the last line here:
Lines 113 to 115 in 23575eb
registry := metrics.NewRegistry() | |
builtinMetrics := metrics.RegisterBuiltinMetrics(registry) | |
initRunner, err := newRunner(logger, src, runType, filesystems, runtimeOptions, builtinMetrics, registry) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And, thinking about it, #2320 will also be easier when we don't have to do any validation in Thresholds.Run()
🤔 🎉 Each threshold can be "pre-compiled" by just turning it into a Go lambda or something like that. Once we do the validation once after the init context execution, that a threshold impression is valid for the metric type it is defined for, we shouldn't need any more validation afterwards 🎉
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very good points indeed! I've created #2330 to keep track of it 🎉
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, so much nicer than the JS hack ❤️ 😅 it also unlocks us to implement so many other fixes and improvements like https://github.com/grafana/k6/pull/2251/files#r782875298, #1346 and threshold improvements in general 🎉
Revert "Merge pull request #2251 from grafana/fix/1443_remove_the_js_…
We experienced issue with this feature in the larger, internal, context of K6, and decided to revert it, and delay its release to |
…from_threshold_calcultations Fix/1443 remove the JS runtime from threshold calculations
…from_threshold_calcultations Fix/1443 remove the JS runtime from threshold calculations
…from_threshold_calcultations Fix/1443 remove the JS runtime from threshold calculations
…from_threshold_calcultations Fix/1443 remove the JS runtime from threshold calculations
…from_threshold_calcultations Fix/1443 remove the JS runtime from threshold calculations
…from_threshold_calcultations Fix/1443 remove the JS runtime from threshold calculations
…from_threshold_calcultations Fix/1443 remove the JS runtime from threshold calculations
…from_threshold_calcultations Fix/1443 remove the JS runtime from threshold calculations
…_the_js_runtime_from_threshold_calcultations"" This reverts commit 22a0db3.
This Pull Request addresses the minimum scope described in #1443 and replaces the current thresholds' expression evaluation implementation. It drops the reliance on the Goja JS runtime, in favor of a pure-go based implementation. With this PR, thresholds are parsed, and evaluated directly in Go, and all JS runtime-related threshold code has been dropped. It sticks to the existing format, and does not introduce any of the design and format changes proposed in #1443 and #1441.
Design notes regarding the PR's content:
pkg
. I was not entirely sure what the best place would be for code that's generic enough to be a good independent library, but not big enough to make it into a dedicated repo.pkg
is often used as the place where projects put their public reusable code. I'm not hung onto it at all. Please propose an alternative if you prefer to put it in another place 👀Thresholds
struct, and pass it around to childrenThreshold
instances when they need to be evaluated. It's the simplest thing I could think of, but with my limited understanding of the current design of K6, I might have missed another way of doing it.Let me know what you think 👀 🐻