-
Notifications
You must be signed in to change notification settings - Fork 38.7k
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
Couple code cleanups for APF code #103820
Conversation
go func() { | ||
defer runtime.HandleCrash() | ||
qs.goroutineDoneOrBlocked() |
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.
One more thing I want to do in subsequent PR is to ensure that production code isn't really tracking goroutines at all (it doesn't use this information) by using non-tracking promise in prod and tracking one (build on top of non-tracking one) in tests.
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 production code already is not really tracking goroutines --- see https://github.com/kubernetes/apiserver/blob/b94b411ffc0d129451a850a78bd39b9457a15518/pkg/util/flowcontrol/apf_filter.go#L85
By removing the updates through the counter interface, this makes it impossible to test queueset with a fake clock. It is important to be able to test with a fake clock:
- so that we can control timing precisely, and
- so that we can run tests faster.
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.
Sorry - I wasn't clear exactly.
I'm aware we're using no-op tracker in production code.
But I'm really not a fan of having test-only related code in production code. This complicates production code.
All the test-related code should really be separate out from the code that is running in production.
By removing the updates through the counter interface, this makes it impossible to test queueset with a fake clock. It is important to be able to test with a fake clock:
I disagree with this one. Currently none of the tests is exercising this. But even if at some point we extend our testing to take advantage of it, what we should do is:
- make a class function from this goroutine [and leave it without counter]
- in tests, define our own
TestingQueuset
(or whatever name we choose) that will be basically inheriting everything from the QueueSet, we will just redefing this single function
I can live with complicate tests, but I really want to have production code as simple as possible [it's not just about P&F of course].
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.
If we factor out this goroutine plus old line 298, the resulting surface seems odder (less simple) to me than the one we have now. What is the meaning of this goroutine plus old line 298? It's just a random bit of implementation. Counting active goroutines, on the other hand, is a simple self-contained semantic.
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.
https://github.com/kubernetes/apiserver/blob/b94b411ffc0d129451a850a78bd39b9457a15518/pkg/util/flowcontrol/fairqueuing/queueset/queueset_test.go#L653 does exercise this.
It exercises that code path, but it doesn't exercise the counter:
- clk is not used anywhere after initialization
- counter is only updated through the text but never check
[as the test shows - it passed consistently after removing it]
If we factor out this goroutine plus old line 298, the resulting surface seems odder (less simple) to me than the one we have now. What is the meaning of this goroutine plus old line 298? It's just a random bit of implementation. Counting active goroutines, on the other hand, is a simple self-contained semantic.
I disagree with this. When I'm reading the code I would like to be able to quickly understand what exactly is happening in production. And mixing test code with production code and just initializing it differently is not the best practice.
|
||
// WriteOnceOnly represents a variable that is initially not set and | ||
// can be set once. | ||
type WriteOnceOnly interface { |
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 case anyone is not aware, the Go language has improved in a relevant way here since these interfaces were defined. In the bad old days, Go did not allow a diamond-shaped inheritance graph among interfaces. That is why this is written in mixin-style. Now, Go supports diamon-shaped inheritance. It would be possible to maintain the producer/consumer distinction here with fewer interface definitions.
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 think we shouldn't be introducing interfaces for the sake of introducing them. Let's not overcomplicate stuff and not introduce things that we don't even use/need anywhere.
} | ||
} | ||
|
||
func TestLockingWriteOnce(t *testing.T) { |
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.
Why delete this test code?
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.
Of couse it shouldn't be deleted - reverted. Thanks for catching.
go func() { | ||
defer runtime.HandleCrash() | ||
qs.goroutineDoneOrBlocked() |
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 production code already is not really tracking goroutines --- see https://github.com/kubernetes/apiserver/blob/b94b411ffc0d129451a850a78bd39b9457a15518/pkg/util/flowcontrol/apf_filter.go#L85
By removing the updates through the counter interface, this makes it impossible to test queueset with a fake clock. It is important to be able to test with a fake clock:
- so that we can control timing precisely, and
- so that we can run tests faster.
86653e8
to
19cb29b
Compare
/triage accepted |
@@ -125,8 +125,7 @@ type configController struct { | |||
requestWaitLimit time.Duration | |||
|
|||
// This must be locked while accessing flowSchemas or | |||
// priorityLevelStates. It is the lock involved in | |||
// LockingWriteMultiple. |
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 remark
It is the lock involved in LockingWriteMultiple
got misplaced in past refactoring. It belongs on the lock in queueSet
and refers to the promise in every request
in the queueSet. It wouldn't hurt to reiterate the point in the comment on the decision
field of request
.
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 went step further and simplified it further (we don't really take advantage of "locking" part of the interface anywhere).
So to make it clear for everyone - I updated the interface itself to not be thread-safe [which is how it is used - we care about protecting it ourselves in queueset]
c63d9e9
to
44e5d94
Compare
44e5d94
to
b97b42f
Compare
@MikeSpreitzer - PTAL |
lock.Lock() | ||
defer lock.Unlock() | ||
if !wr.IsSet() { | ||
t.Error("IsSet()==false after second Set") |
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.
s/second Set/second Get/
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.
done
decision promise.LockingWriteOnce | ||
// | ||
// The field is NOT thread-safe and should be protected by the | ||
// external lock. |
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.
s/external/queueset's/
(this type is not designed or available for use outside this package)
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.
done
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 OK except for a couple of minor comments.
b97b42f
to
9f735e7
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.
Thanks @MikeSpreitzer - PTAL
lock.Lock() | ||
defer lock.Unlock() | ||
if !wr.IsSet() { | ||
t.Error("IsSet()==false after second Set") |
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.
done
decision promise.LockingWriteOnce | ||
// | ||
// The field is NOT thread-safe and should be protected by the | ||
// external lock. |
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.
done
/retest |
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
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: MikeSpreitzer, wojtek-t The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
/retest |
/kind cleanup
/priority important-longterm
/sig api-machinery
/assign @MikeSpreitzer @tkashem