-
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
Refactor group and check to not be tightly coupled #3750
Conversation
Group and check were tightly coupled between them and also to the end of test summary and the REST API. This change the tight coupling is removed. Group and check no longer interact with each other while being run. In order to keep the same end of test summary and REST API behavior the code makes the same aggregation it used to be but *only* based on the metrics emitted by `check` and `group`. There are several breaking changes still in it as a bunch of things are no longer useful, and technically not implementable if we want to remove the tight coupling. The only one that is relevant though is that `lib.State` no longer has `Group`. There are 2 extensions using that and both of them use it to use the "magic" that tight `group` and `check` together. Fixes #2869
|
||
// GroupSummaryDescription is the description of the GroupSummary used to identify and ignore it | ||
// for the purposes of the cli descriptions. | ||
const GroupSummaryDescription = "Internal Group Summary output" | ||
|
||
// GroupSummary is an internal output implementation that facilitates the aggregation of | ||
// group and check metrics for the purposes of the end of test summary and the REST API | ||
type GroupSummary struct { | ||
group *Group // TODO(@mstoykov): move the whole type outside of lib later | ||
ch chan []metrics.SampleContainer | ||
stopped chan struct{} | ||
logger logrus.FieldLogger | ||
} | ||
|
||
// NewGroupSummary returns new GroupSummary ready to be started. |
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 kind of wanted to move the whole Group, Check and GroupSummary somewhere else, but then I decided that is quite a big change and will make the review even harder.
It also turned out to not be easy.
So if we have any good places to move them I would prefer to do them in a separate PR.
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.
Ideally, I would use this opportunity to move them to startup an internal
package and move GroupSummary there. This is my unique request, mostly to reflect what the comment is mentioning.
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 am not against this - but that will create a much bigger change that will make following what happens ever harder. So I will prefer to keep it in a separate PR.
Not certain internal
is needed as well so 🤷 on that particular part. But it is probably a good usage as well.
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.
Yeah, especially with the v1.0 on the road, would be great to start considering hiding internal details behind an internal
package, to reduce the public surface ( 👋🏻 say hello to Hyrum's Law).
That said, I fully agree on doing so on a separate PR, to avoid making this even harder to read, follow and review.
// Current group; all emitted metrics are tagged with this. | ||
Group *Group | ||
|
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 break 2 extensions:
xk6-fasthttp/checks.go
41: check, err := state.Group.Check("check status is " + strconv.FormatInt(int64(wantStatus), 10))
xk6-g0/g0/addon/assert.go
67: check, err := state.Group.Check(name)
I don't really think that keeping this compatible is possible, and even more importantly warranted.
As you can see both extensions basically hit exactly the same issue with this being the way to create a new check instead of just emitting the metrics.
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #3750 +/- ##
==========================================
- Coverage 70.85% 70.83% -0.02%
==========================================
Files 289 290 +1
Lines 21159 21191 +32
==========================================
+ Hits 14992 15011 +19
- Misses 5210 5215 +5
- Partials 957 965 +8
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
|
||
if sample.Metric.Name != "checks" { |
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.
if sample.Metric.Name != "checks" { | |
// In case of checks we need to also generate the check and add it values. Otherwise we are done. | |
if sample.Metric.Name != "checks" { |
groupTag, ok := state.Tags.GetCurrentValues().Tags.Get("group") | ||
require.True(t, ok) | ||
assert.Equal(t, groupTag, root.Name) | ||
assert.Equal(t, groupTag, "") |
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.
assert.Equal(t, groupTag, "") | |
assert.Equal(t, groupTag, lib.RootGroupPath) |
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 actually purposefully left it here not using the constant to catch it if ever changes - I think there is one more place where this is done in the tests as well.
I am not particularly attached to this, but as changing this constant will be a breaking change I kind of prefer if changing it breaks a few tests for sure.
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.
Yes, you're right, sometimes you used ""
, sometimes the constant.
but as changing this constant will be a breaking change I kind of prefer if changing it breaks a few tests for sure
I fully agree with end goal, it was me just probably confused about where and for which tests it makes more sense to use the constant and for which not. Let's keep it as-is.
That said, as I had previous negative experiences with this kind of tradeoff: "de-coupling" the test from the constant, to notice any change, would make sense to also leave a comment together with the constant, stating that explicitly, for the ourselves of the future, who might forget/omit it by just updating the tests anyway?
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.
Looks good despite of the minor comments/suggestions!
Co-authored-by: Joan López de la Franca Beltran <[email protected]>
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.
Approved! ✅ Although I guess we need a to reach a consensus around the breaking changes to move forward. Nevertheless, I don't see many alternatives at this point.
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 🚀 👏🏻
I concur with what has been said about starting an internal
package too, as it's inline with our ongoing plans for v1.
// GroupSummary is an internal output implementation that facilitates the aggregation of | ||
// group and check metrics for the purposes of the end of test summary and the REST API | ||
type GroupSummary struct { | ||
group *Group // TODO(@mstoykov): move the whole type outside of lib later |
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.
group *Group // TODO(@mstoykov): move the whole type outside of lib later | |
group *Group // TODO(@mstoykov): move the whole type outside of lib later |
Where do you plan to move it? Under output
? It would like to have a suggestion directly in the code so we have shared knowledge on ideas/plans.
|
||
// AddMetricSamples is part of the output.Output interface | ||
func (gs *GroupSummary) AddMetricSamples(samples []metrics.SampleContainer) { | ||
gs.ch <- samples |
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.
Are we sure we can serve the rate expected and we don't block here? Is the buffer size of the channel enough?
See grafana/k6#3750 for more info.
What?
Group and check were tightly coupled between them and also to the end of test summary and the REST API.
This change the tight coupling is removed. Group and check no longer interact with each other while being run.
In order to keep the same end of test summary and REST API behavior the code makes the same aggregation it used to be but only based on the metrics emitted by
check
andgroup
.There are several breaking changes still in it as a bunch of things are no longer useful, and technically not implementable if we want to remove the tight coupling.
The only one that is relevant though is that
lib.State
no longer hasGroup
.There are 2 extensions using that and both of them use it to use the "magic" that tight
group
andcheck
together.Why?
The previous code apart from having problems code smell wise, meant that you can't implement this functionality in JS.
Which also means that they act very strangely especially as if you do not look closely it just seems like they set a few tags and emit metrics.
While working on #2728 it became apparent that it is unlikely we will get a perfect solution and one of the big problems is that you can't even experiment without having to write a fairly complicated async go code.
So this also makes that issue a bit more actionable. And definitely a thing that now people can have not perfect or even 80%, but still workable for them solutions.
Checklist
make lint
) and all checks pass.make tests
) and all tests pass.Related PR(s)/Issue(s)
#2869