From 72d4de5a5ff850cae914c907000af832acb40003 Mon Sep 17 00:00:00 2001 From: Gabriel Handford Date: Wed, 20 Jan 2016 11:48:45 -0800 Subject: [PATCH] Skip cached updater singleflight result We don't want to re-apply the previous singleflighted update. Temporarily copying singleflight here until we add in behavior to the golang package: https://github.com/golang/groupcache/issues/61 --- go/updater/singleflight.go | 66 ++++++++++++++++++++++++++++++++++++++ go/updater/updater.go | 10 ++++-- 2 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 go/updater/singleflight.go diff --git a/go/updater/singleflight.go b/go/updater/singleflight.go new file mode 100644 index 000000000000..5ab0a26df5ef --- /dev/null +++ b/go/updater/singleflight.go @@ -0,0 +1,66 @@ +/* +Copyright 2012 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// This is a copy of https://github.com/golang/groupcache with a minor change +// being discussed here: https://github.com/golang/groupcache/issues/61 +// This is temporary! + +package updater + +import "sync" + +// call is an in-flight or completed Do call +type call struct { + wg sync.WaitGroup + val interface{} + err error +} + +// Group represents a class of work and forms a namespace in which +// units of work can be executed with duplicate suppression. +type Group struct { + mu sync.Mutex // protects m + m map[string]*call // lazily initialized +} + +// Do executes and returns the results of the given function, making +// sure that only one execution is in-flight for a given key at a +// time. If a duplicate comes in, the duplicate caller waits for the +// original to complete and receives the same results. +func (g *Group) Do(key string, fn func() (interface{}, error)) (interface{}, bool, error) { + g.mu.Lock() + if g.m == nil { + g.m = make(map[string]*call) + } + if c, ok := g.m[key]; ok { + g.mu.Unlock() + c.wg.Wait() + return c.val, true, c.err + } + c := new(call) + c.wg.Add(1) + g.m[key] = c + g.mu.Unlock() + + c.val, c.err = fn() + c.wg.Done() + + g.mu.Lock() + delete(g.m, key) + g.mu.Unlock() + + return c.val, false, c.err +} diff --git a/go/updater/updater.go b/go/updater/updater.go index c9fe89ac82c2..0d974f19d63d 100644 --- a/go/updater/updater.go +++ b/go/updater/updater.go @@ -16,7 +16,6 @@ import ( "time" "github.com/blang/semver" - "github.com/golang/groupcache/singleflight" "github.com/keybase/client/go/libkb" "github.com/keybase/client/go/logger" keybase1 "github.com/keybase/client/go/protocol" @@ -30,7 +29,7 @@ type Updater struct { source sources.UpdateSource config Config log logger.Logger - callGroup singleflight.Group + callGroup Group } type UI interface { @@ -351,7 +350,12 @@ func (u *Updater) Update(ui UI, force bool, requested bool) (*keybase1.Update, e do := func() (interface{}, error) { return u.update(ui, force, requested) } - any, err := u.callGroup.Do("update", do) + any, cached, err := u.callGroup.Do("update", do) + + if cached { + u.log.Info("Update call was singleflighted and returned cached result, skipping") + return nil, nil + } update, ok := any.(*keybase1.Update) if !ok {