| Index: logdog/server/service/config/opts.go
 | 
| diff --git a/logdog/server/service/config/opts.go b/logdog/server/service/config/opts.go
 | 
| index d26518b02a089326698795498fb683e1da2b8184..b62de5134dd1f339136f1c1c98c045a015d0c4fb 100644
 | 
| --- a/logdog/server/service/config/opts.go
 | 
| +++ b/logdog/server/service/config/opts.go
 | 
| @@ -7,6 +7,11 @@ package config
 | 
|  import (
 | 
|  	"time"
 | 
|  
 | 
| +	"github.com/luci/luci-go/appengine/datastorecache"
 | 
| +	log "github.com/luci/luci-go/common/logging"
 | 
| +	"github.com/luci/luci-go/common/sync/mutexpool"
 | 
| +	"github.com/luci/luci-go/luci_config/appengine/backend/datastore"
 | 
| +	"github.com/luci/luci-go/luci_config/appengine/gaeconfig"
 | 
|  	"github.com/luci/luci-go/luci_config/server/cfgclient/backend"
 | 
|  	"github.com/luci/luci-go/luci_config/server/cfgclient/backend/caching"
 | 
|  
 | 
| @@ -20,6 +25,12 @@ type CacheOptions struct {
 | 
|  	// If this value is <= 0, no configuration caching will be enabled. This
 | 
|  	// should not be set for production systems.
 | 
|  	CacheExpiration time.Duration
 | 
| +
 | 
| +	// DatastoreCacheAvailable, if true, indicates that requisite services for
 | 
| +	// datastore cache access are installed in the Context. This requires:
 | 
| +	// - A luci/gae datastore service instnace
 | 
| +	// - A settings service instance, presumably gaesettings.
 | 
| +	DatastoreCacheAvailable bool
 | 
|  }
 | 
|  
 | 
|  // WrapBackend wraps the supplied base backend in caching layers and returns a
 | 
| @@ -29,6 +40,33 @@ func (o *CacheOptions) WrapBackend(c context.Context, base backend.B) context.Co
 | 
|  		// Start with our base Backend.
 | 
|  		be := base
 | 
|  
 | 
| +		// If our datastore cache is available, fetch settings to see if it's
 | 
| +		// enabled.
 | 
| +		if o.DatastoreCacheAvailable {
 | 
| +			switch s, err := gaeconfig.FetchCachedSettings(c); err {
 | 
| +			case nil:
 | 
| +				// Successfully fetched settings, is our datastore cache enabled?
 | 
| +				switch s.DatastoreCacheMode {
 | 
| +				case gaeconfig.DSCacheEnabled, gaeconfig.DSCacheStrict:
 | 
| +					// Datastore cache is enabled, do we have an expiration configured?
 | 
| +					exp := time.Duration(s.CacheExpirationSec) * time.Second
 | 
| +					if exp > 0 {
 | 
| +						// The cache enabled enable. Install it into our backend.
 | 
| +						locker := &dsCacheLocker{}
 | 
| +						dsCfg := datastore.Config{
 | 
| +							RefreshInterval: exp,
 | 
| +							FailOpen:        s.DatastoreCacheMode == gaeconfig.DSCacheEnabled,
 | 
| +							LockerFunc:      func(context.Context) datastorecache.Locker { return locker },
 | 
| +						}
 | 
| +						be = dsCfg.Backend(be)
 | 
| +					}
 | 
| +				}
 | 
| +
 | 
| +			default:
 | 
| +				log.WithError(err).Warningf(c, "Failed to fetch datastore cache settings.")
 | 
| +			}
 | 
| +		}
 | 
| +
 | 
|  		// Add a proccache-based config cache.
 | 
|  		if o.CacheExpiration > 0 {
 | 
|  			be = caching.ProcCache(be, o.CacheExpiration)
 | 
| @@ -37,3 +75,14 @@ func (o *CacheOptions) WrapBackend(c context.Context, base backend.B) context.Co
 | 
|  		return be
 | 
|  	})
 | 
|  }
 | 
| +
 | 
| +type dsCacheLocker struct {
 | 
| +	p mutexpool.P
 | 
| +}
 | 
| +
 | 
| +func (l *dsCacheLocker) TryWithLock(c context.Context, key string, fn func(context.Context) error) (err error) {
 | 
| +	l.p.WithMutex(key, func() {
 | 
| +		err = fn(c)
 | 
| +	})
 | 
| +	return
 | 
| +}
 | 
| 
 |