Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 1ba23e0

Browse files
committed
Protect against race conditions in the service controller.
Re-GET the service object when we process it rather than trusting the delta. This will make for a lot more service get requests given that we resync all the services every 5 minutes, but will avoid re-ordering of updates and continually retrying stale updates, as has been described in a few other issues and PRs.
1 parent cf9d6eb commit 1ba23e0

File tree

1 file changed

+44
-40
lines changed

1 file changed

+44
-40
lines changed

pkg/controller/service/servicecontroller.go

Lines changed: 44 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ func (s *ServiceController) watchServices(serviceQueue *cache.DeltaFIFO) {
196196
// Returns an error if processing the delta failed, along with a boolean
197197
// indicator of whether the processing should be retried.
198198
func (s *ServiceController) processDelta(delta *cache.Delta) (error, bool) {
199-
service, ok := delta.Object.(*api.Service)
199+
deltaService, ok := delta.Object.(*api.Service)
200200
var namespacedName types.NamespacedName
201201
var cachedService *cachedService
202202
if !ok {
@@ -211,70 +211,74 @@ func (s *ServiceController) processDelta(delta *cache.Delta) (error, bool) {
211211
if !ok {
212212
return fmt.Errorf("Service %s not in cache even though the watcher thought it was. Ignoring the deletion.", key), notRetryable
213213
}
214-
service = cachedService.lastState
215-
delta.Object = cachedService.lastState
216-
namespacedName = types.NamespacedName{Namespace: service.Namespace, Name: service.Name}
214+
deltaService = cachedService.lastState
215+
delta.Object = deltaService
216+
namespacedName = types.NamespacedName{Namespace: deltaService.Namespace, Name: deltaService.Name}
217217
} else {
218-
namespacedName.Namespace = service.Namespace
219-
namespacedName.Name = service.Name
218+
namespacedName.Namespace = deltaService.Namespace
219+
namespacedName.Name = deltaService.Name
220220
cachedService = s.cache.getOrCreate(namespacedName.String())
221221
}
222-
glog.V(2).Infof("Got new %s delta for service: %+v", delta.Type, service)
222+
glog.V(2).Infof("Got new %s delta for service: %+v", delta.Type, deltaService)
223223

224224
// Ensure that no other goroutine will interfere with our processing of the
225225
// service.
226226
cachedService.mu.Lock()
227227
defer cachedService.mu.Unlock()
228228

229-
// Update the cached service (used above for populating synthetic deletes)
230-
cachedService.lastState = service
231-
232-
// TODO: Handle added, updated, and sync differently?
233-
switch delta.Type {
234-
case cache.Added:
235-
fallthrough
236-
case cache.Updated:
237-
fallthrough
238-
case cache.Sync:
239-
err, retry := s.createLoadBalancerIfNeeded(namespacedName, service, cachedService.appliedState)
240-
if err != nil {
241-
message := "Error creating load balancer"
242-
if retry {
243-
message += " (will retry): "
244-
} else {
245-
message += " (will not retry): "
246-
}
247-
message += err.Error()
248-
s.eventRecorder.Event(service, "CreatingLoadBalancerFailed", message)
249-
return err, retry
250-
}
251-
// Always update the cache upon success.
252-
// NOTE: Since we update the cached service if and only if we successfully
253-
// processed it, a cached service being nil implies that it hasn't yet
254-
// been successfully processed.
255-
cachedService.appliedState = service
256-
s.cache.set(namespacedName.String(), cachedService)
257-
case cache.Deleted:
229+
// Get the most recent state of the service from the API directly rather than
230+
// trusting the body of the delta. This avoids update re-ordering problems.
231+
// TODO: Handle sync delta types differently rather than doing a get on every
232+
// service every time we sync?
233+
service, err := s.kubeClient.Services(namespacedName.Namespace).Get(namespacedName.Name)
234+
if err != nil && !errors.IsNotFound(err) {
235+
glog.Warningf("Failed to get most recent state of service %v from API (will retry): %v", namespacedName, err)
236+
return err, retryable
237+
} else if errors.IsNotFound(err) {
238+
glog.V(2).Infof("Service %v not found, ensuring load balancer is deleted", namespacedName)
258239
s.eventRecorder.Event(service, "DeletingLoadBalancer", "Deleting load balancer")
259-
err := s.balancer.EnsureTCPLoadBalancerDeleted(s.loadBalancerName(service), s.zone.Region)
240+
err := s.balancer.EnsureTCPLoadBalancerDeleted(s.loadBalancerName(deltaService), s.zone.Region)
260241
if err != nil {
261242
message := "Error deleting load balancer (will retry): " + err.Error()
262243
s.eventRecorder.Event(service, "DeletingLoadBalancerFailed", message)
263244
return err, retryable
264245
}
265246
s.eventRecorder.Event(service, "DeletedLoadBalancer", "Deleted load balancer")
266247
s.cache.delete(namespacedName.String())
267-
default:
268-
glog.Errorf("Unexpected delta type: %v", delta.Type)
248+
return nil, notRetryable
269249
}
250+
251+
// Update the cached service (used above for populating synthetic deletes)
252+
cachedService.lastState = service
253+
254+
err, retry := s.createLoadBalancerIfNeeded(namespacedName, service, cachedService.appliedState)
255+
if err != nil {
256+
message := "Error creating load balancer"
257+
if retry {
258+
message += " (will retry): "
259+
} else {
260+
message += " (will not retry): "
261+
}
262+
message += err.Error()
263+
s.eventRecorder.Event(service, "CreatingLoadBalancerFailed", message)
264+
265+
return err, retry
266+
}
267+
// Always update the cache upon success.
268+
// NOTE: Since we update the cached service if and only if we successfully
269+
// processed it, a cached service being nil implies that it hasn't yet
270+
// been successfully processed.
271+
cachedService.appliedState = service
272+
s.cache.set(namespacedName.String(), cachedService)
273+
270274
return nil, notRetryable
271275
}
272276

273277
// Returns whatever error occurred along with a boolean indicator of whether it
274278
// should be retried.
275279
func (s *ServiceController) createLoadBalancerIfNeeded(namespacedName types.NamespacedName, service, appliedState *api.Service) (error, bool) {
276280
if appliedState != nil && !needsUpdate(appliedState, service) {
277-
glog.Infof("LB already exists and doesn't need update for service %s", namespacedName)
281+
glog.Infof("LB doesn't need update for service %s", namespacedName)
278282
return nil, notRetryable
279283
}
280284

0 commit comments

Comments
 (0)