Index: filter/dscache/globalconfig.go |
diff --git a/filter/dscache/globalconfig.go b/filter/dscache/globalconfig.go |
new file mode 100644 |
index 0000000000000000000000000000000000000000..74212e50340207449f3b36b9dad4b47f0bc305d0 |
--- /dev/null |
+++ b/filter/dscache/globalconfig.go |
@@ -0,0 +1,107 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package dscache |
+ |
+import ( |
+ "sync" |
+ "time" |
+ |
+ "github.com/luci/gae/service/datastore" |
+ "github.com/luci/gae/service/info" |
+ "github.com/luci/gae/service/memcache" |
+ "github.com/luci/luci-go/common/clock" |
+ "golang.org/x/net/context" |
+) |
+ |
+type GlobalConfig struct { |
+ _id int64 `gae:"$id,1"` |
+ _kind string `gae:"$kind,dscache"` |
+ |
+ Enable bool |
+} |
+ |
+var ( |
+ globalEnabledLock = sync.RWMutex{} |
+ |
+ // globalEnabled is whether or not memcache has been globally enabled. It is |
+ // populated by IsGloballyEnabled when SetGlobalEnable has been set to |
+ // true. |
+ globalEnabled = true |
+ |
+ // globalEnabledNextCheck is IsGloballyEnabled's last successful check of the |
+ // global disable key. |
+ globalEnabledNextCheck = time.Time{} |
+) |
+ |
+// IsGloballyEnabled checks to see if this filter is enabled globally. |
+// |
+// This checks InstanceEnabledStatic, as well as polls the datastore entity |
+// /dscache,1 (a GlobalConfig instance) |
+// Once every GlobalEnabledCheckInterval. |
+// |
+// For correctness, any error encountered returns true. If this assumed false, |
+// then Put operations might incorrectly invalidate the cache. |
+func IsGloballyEnabled(c context.Context) bool { |
+ if !InstanceEnabledStatic { |
+ return false |
+ } |
+ |
+ now := clock.Now(c) |
+ |
+ globalEnabledLock.RLock() |
+ nextCheck := globalEnabledNextCheck |
+ enabledVal := globalEnabled |
+ globalEnabledLock.RUnlock() |
+ |
+ if now.Before(nextCheck) { |
+ return enabledVal |
+ } |
+ |
+ globalEnabledLock.Lock() |
+ defer globalEnabledLock.Unlock() |
+ // just in case we raced |
+ if now.Before(globalEnabledNextCheck) { |
+ return globalEnabled |
+ } |
+ |
+ // always go to the default namespace |
+ c, err := info.Get(c).Namespace("") |
+ if err != nil { |
+ return true |
+ } |
+ cfg := &GlobalConfig{Enable: true} |
+ if err := datastore.Get(c).Get(cfg); err != nil && err != datastore.ErrNoSuchEntity { |
+ return true |
+ } |
+ globalEnabled = cfg.Enable |
+ globalEnabledNextCheck = now.Add(GlobalEnabledCheckInterval) |
+ return globalEnabled |
+} |
+ |
+func SetGlobalEnable(c context.Context, memcacheEnabled bool) error { |
+ // always go to the default namespace |
+ c, err := info.Get(c).Namespace("") |
+ if err != nil { |
+ return err |
+ } |
+ return datastore.Get(c).RunInTransaction(func(c context.Context) error { |
+ ds := datastore.Get(c) |
+ cfg := &GlobalConfig{Enable: true} |
+ if err := ds.Get(cfg); err != nil && err != datastore.ErrNoSuchEntity { |
+ return err |
+ } |
+ if cfg.Enable == memcacheEnabled { |
+ return nil |
+ } |
+ cfg.Enable = memcacheEnabled |
+ if memcacheEnabled { |
+ // when going false -> true, wipe memcache. |
+ if err := memcache.Get(c).Flush(); err != nil { |
+ return err |
+ } |
+ } |
+ return ds.Put(cfg) |
+ }, nil) |
+} |