-
Notifications
You must be signed in to change notification settings - Fork 4.6k
endpointsharding: Allow children to remain idle if configured #8031
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
endpointsharding: Allow children to remain idle if configured #8031
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #8031 +/- ##
==========================================
+ Coverage 82.22% 82.26% +0.03%
==========================================
Files 383 384 +1
Lines 38688 38926 +238
==========================================
+ Hits 31813 32024 +211
- Misses 5555 5575 +20
- Partials 1320 1327 +7
|
| bal := child.(*balancerWrapper) | ||
| if _, ok := newChildren.Get(e); !ok { | ||
| bal.Close() | ||
| bal.isClosed = true |
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.
Can we move this into balancerWrapper's Close method?
Also since calls into a balancer.Balancer must not happen concurrently, I don't think it requires a 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.
Added a Close method and moved this into the Close method.
| func (bw *balancerWrapper) ExitIdle() { | ||
| if ei, ok := bw.Balancer.(balancer.ExitIdler); ok { | ||
| go func() { | ||
| bw.es.childMu.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.
Because we're calling into the child from a goroutine here:
- All calls into the child need to grab
bw.es.childMuto avoid concurrent calls in, - That means
Close()needs it (and also needed it before you added the method explicitly here, so good that we caught it), and - That means we shouldn't embed the child
Balancer, since directly calling it is wrong.
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.
That means Close() needs it (and also needed it before you added the method explicitly here, so good that we caught it)
Before this PR, calls to balancerWrapper.Close() and updates to endpointsharding.closedwere holding es.ChildMu:
In endpointshardng.Close():
| func (es *endpointSharding) Close() { |
In endpointshardng.UpdateClientConnState():
grpc-go/balancer/endpointsharding/endpointsharding.go
Lines 104 to 105 in 9d4fa67
| es.childMu.Lock() | |
| defer es.childMu.Unlock() |
grpc-go/balancer/endpointsharding/endpointsharding.go
Lines 150 to 157 in 9d4fa67
| // Delete old children that are no longer present. | |
| for _, e := range children.Keys() { | |
| child, _ := children.Get(e) | |
| bal := child.(balancer.Balancer) | |
| if _, ok := newChildren.Get(e); !ok { | |
| bal.Close() | |
| } | |
| } |
Maybe I'm not understanding this point correctly?
That means we shouldn't embed the child Balancer, since directly calling it is wrong.
I've removed the embedded balancer.Balancer from balancerWrapper and provided methods named updateClientConnStateLocked and closeLocked and that expect the caller to lock es.ChildMu.
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.
Before this PR, calls to balancerWrapper.Close() and updates to endpointsharding.closed were holding es.ChildMu:
Oh, I see... I missed where it grabbed the lock in Close and thought it was just delegating directly to the wrappers.
This now seems good and makes it explicit what is required.
79159f2 to
a38a6ce
Compare
a38a6ce to
b95ae1d
Compare
| inhibitChildUpdates atomic.Bool | ||
|
|
||
| mu sync.Mutex // Sync updateState callouts and childState recent state updates | ||
| // mu synchronizes access to the stored children balancer states. |
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.
| // mu synchronizes access to the stored children balancer states. | |
| // mu synchronizes access to the stored children balancer states in children. |
?
Or "in the children field" or something to be more specific.
| // update. | ||
| continue | ||
| } | ||
| var bal *balancerWrapper |
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.
Could you rename this local to childBalancerWrapper or something more clear -- at least child or something? bal to me makes me think this is the endpointsharding balancer itself when I see it later on. I guess we use es for that, but the more common convention is that the Balancer implementation within the package is simply named balancer, and b is the receiver.
| for _, e := range children.Keys() { | ||
| child, _ := children.Get(e) | ||
| bal := child.(balancer.Balancer) | ||
| bal := child.(*balancerWrapper) |
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.
Similar nit about the name of things.. This local isn't even necessary:
child, _ := children.Get(e)
if _, ok := newChildren.Get(e); !ok {
child.(*balancerWrapper).closeLocked()
}| func (bw *balancerWrapper) ExitIdle() { | ||
| if ei, ok := bw.Balancer.(balancer.ExitIdler); ok { | ||
| go func() { | ||
| bw.es.childMu.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.
Before this PR, calls to balancerWrapper.Close() and updates to endpointsharding.closed were holding es.ChildMu:
Oh, I see... I missed where it grabbed the lock in Close and thought it was just delegating directly to the wrappers.
This now seems good and makes it explicit what is required.
| // The following fields are initialized at build time and read-only after | ||
| // that and therefore do not need to be guarded by a mutex. | ||
|
|
||
| // child contains the wrapped balancer. Access it's methods only through |
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.
*its
This change allows the children of endpointsharding to remain in IDLE state. This allows lazy initialization of children and therefore lazy creation of subchannels for endpoints. This enables the usage of endpointsharding in the ringhash balancer for managing pickfirst children.
RELEASE NOTES: N/A