-
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
Parse and validate threshold during init #2356
Conversation
c7777e5
to
db73489
Compare
CI has been useful once again, and outlined a number of issues (probably caused by a failed rebase). I really need to sort my git hooks out to catch those before. I'll make the branch green again and ping you reviewers once it's fixed and ready for review again. Sorry for the noise 🙇🏻 |
2a6632f
to
2a91012
Compare
@na-- @olegbespalov @mstoykov @codebien Alrighty, this is green again 🟢 and ready to review 🦖 |
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.
In general, PR looks good to me, I left one small comment.
Special thanks for changing the short variable names to the more readable ❤️
2aeebfc
to
3aee96d
Compare
…from_threshold_calcultations Fix/1443 remove the JS runtime from threshold calculations
This commit makes some minor modifications to the `stats` package API. Namely, it makes `stats.ThresholdExpression` and `stats.token*` symbols public. It also makes `stats.Threshold.parsed` public. These changes are made in order to facilitate validation of thresholds from outside the `stats` package. Having access to both the parsed Threshold, and the aggregation methods symbols will allow comparing them and asserting their meaningfulness in a context where we have typed metrics available. ref #2330
This commit adds a `validateThresholdConfig` function to `cmd/config`, and integrates it as part of the `validateConfig` operations. From now on, `validateConfig` takes a `metrics.Registry` as input, and validates that thresholds defined in the config apply to existing metrics, and use methods that are valid for the metric they apply to. As a side effect, this commit adds a `Get` method to `metrics.Registry` in order to be able to query registered metrics, regardless of whether they are custom or builtin metrics. As another side effect, this commit introduces a `lib.Contains` helper function allowing to check if a slice of strings contains a given string. This is used to simplify the matching of supported aggregation methods on metrics in the `validateThresholdConfig` function. ref #2330
This commit makes sure that the threshold configuration (as passed in the script exported options for instance) is valid, before starting the execution. A valid threshold must pass the following assertion: - Its expression is syntaxically correct and is parsable - It applies to a metrics that's known to k6, either builtin or custom - Its expression's aggregation method is valid for the metric it applies to Threshold validation will be made in the context of the `run`, `cloud`, `archive`, `inspect`, and `archive` commands. If a threshold definition is invalid, the k6 program will exit with a status code of 104. ref #2330
3aee96d
to
c510664
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.
👍
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.
As far as I can see (and test) you are still parsing the thresholds way too early so if you have a threshold that is just not with valid syntax to begin with it will abort even earlier (and irregardless of --no-thresholds
). this is waht bea617a did very ... bluntly basically and I think somethign like that is required even now.
I see that in the issue this is listed as "undecided" but I have some memory that this is more or less required to merge the whole PR (especially for the cloud use case)
WDYT @na--
// If there are thresholds to validate, the registry paramater is not allowed to be nil. | ||
// Note that the reason for passing it as a pointer in the first place is | ||
// because it holds a Mutex, which effectively forbids passing it by value. | ||
if conf.Thresholds != nil && len(conf.Thresholds) > 0 && registry == nil { | ||
err := fmt.Errorf( | ||
"unable to validate thresholds configuration; " + | ||
"reason: provided registry is nil", | ||
) | ||
errList = append(errList, err) | ||
return consolidateErrorMessage(errList, "there were problems while validating the specified script configuration: ") | ||
} |
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.
nit: this seems like defensive programming to me - there is no case where registry
should be nil
to begin with IMO.
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.
I agree, this is defensive programming; however, I would like to keep it that way. I'm happy to discuss how we could improve this, and it's probably my C programming habits kicking in, but the pointer should be checked. It's defensive in the sense that I don't want to risk a potential segfault, or security issue to go through to production because my future self did something stupid and somehow ended up passing nil in there :)
if !ok { | ||
// The defined threshold applies to a non-existing metrics | ||
err := fmt.Errorf("invalid threshold defined on %s; reason: no metric named %s found", thresholdName, thresholdName) | ||
return errext.WithExitCodeIfNone(err, exitcodes.InvalidConfig) |
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.
This (and the below) should just add to the list so we get all errors in one go
stats.TokenMin, | ||
stats.TokenMax, | ||
stats.TokenMed, | ||
stats.TokenPercentile, |
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.
this doesn't really check if the percentile is valid
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.
I'm uncertain if I understand why, could you elaborate?
Some context: in the current state of the PR, one could argue the validation is done in two phases: parsing, which happens during bundling as of this PR's state (expression format validation), and actual validation which happens in config validation: "does the metric to you apply that expression to supports the operation?".
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.
I guess you are correct, I probably commented on this before I made the general comment that currently parsing of a threshold will still fail the test.
for thresholdName, thresholds := range conf.Thresholds { | ||
// Fetch metric matching the threshold's name | ||
metric, ok := registry.Get(thresholdName) | ||
if !ok { |
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.
This will also break this example even though in practice all the thresholds are valid, they just should be evaluated differently from how they are currently.
The problem here is that thresholdName
includes the tags while we can only check the name of the treshold not the name+ the tags. This is the error:
ERRO[0000] invalid threshold defined on http_reqs{key:""}; reason: no metric named http_reqs{key:""} found
p.s. The thresholds also are evaluated wrongly with this PR as well - a tag with empty value in threshold is always "matched" even if it doesn't have the tag at all
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.
Thanks for pointing the issue out. Would you have pointers or a proposal to make this work, or improve 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.
🤷 I guess something a kin to
Lines 106 to 114 in 737dfa7
e.submetrics = make(map[string][]*stats.Submetric) | |
for name := range e.thresholds { | |
if !strings.Contains(name, "{") { | |
continue | |
} | |
parent, sm := stats.NewSubmetric(name) | |
e.submetrics[parent] = append(e.submetrics[parent], sm) | |
} |
@oleiade, sorry 🙇 . This was shared internally around the time we did revert the previous PR. I though you have seen it, but apperantly not ;( . |
The scope and implementation has evolved quite a bit, to the point where it was more convinient to start over. Closing in favor of #2463 |
What this PR does
This Pull Request addresses the scope described in #2330. Namely, it reintroduces the changes from #1443/#2251, and builds upon them to ensure that thresholds are parsed and validated before the execution starts.
Scope
What it does
Following this Pull Request:
104 - Invalid Config
.What it does not
It was discussed in the context of the #2330 issue that k6 should also make sure to handle metrics properly when in an archiving/cloud execution context. Some changes were proposed to how k6 handles the
metadata.json
file.In order to keep this Pull Request reviewable and avoid scope creep, I decided to address that issue separately. If you have arguments towards doing otherwise: shoot 🚀.
Notable changes
The most notable change I can think of is that
deriveAndValidateConfig
now takes ametrics.Registry
as input. Another important one is that threshold parsing code now declares an explicit sentinel error typeErrThresholdParsing
which the caller code depends on in order to decide which exit code it should use.Review guide
stats
package.Dependency
ref #2330
ref #1443
ref #2251
Please let me know if any more information is needed on your side, I'll happily add it 🙇🏻