package datastore import ( "fmt" "sync" store "github.com/docker/docker/libnetwork/internal/kvstore" ) type kvMap map[string]KVObject type cache struct { mu sync.Mutex kmm map[string]kvMap ds store.Store } func newCache(ds store.Store) *cache { return &cache{kmm: make(map[string]kvMap), ds: ds} } func (c *cache) kmap(kvObject KVObject) (kvMap, error) { var err error c.mu.Lock() keyPrefix := Key(kvObject.KeyPrefix()...) kmap, ok := c.kmm[keyPrefix] c.mu.Unlock() if ok { return kmap, nil } kmap = kvMap{} kvList, err := c.ds.List(keyPrefix) if err != nil { if err == store.ErrKeyNotFound { // If the store doesn't have anything then there is nothing to // populate in the cache. Just bail out. goto out } return nil, fmt.Errorf("error while populating kmap: %v", err) } for _, kvPair := range kvList { // Ignore empty kvPair values if len(kvPair.Value) == 0 { continue } dstO := kvObject.New() err = dstO.SetValue(kvPair.Value) if err != nil { return nil, err } // Make sure the object has a correct view of the DB index in // case we need to modify it and update the DB. dstO.SetIndex(kvPair.LastIndex) kmap[Key(dstO.Key()...)] = dstO } out: // There may multiple go routines racing to fill the // cache. The one which places the kmap in c.kmm first // wins. The others should just use what the first populated. c.mu.Lock() kmapNew, ok := c.kmm[keyPrefix] if ok { c.mu.Unlock() return kmapNew, nil } c.kmm[keyPrefix] = kmap c.mu.Unlock() return kmap, nil } func (c *cache) add(kvObject KVObject, atomic bool) error { kmap, err := c.kmap(kvObject) if err != nil { return err } c.mu.Lock() // If atomic is true, cache needs to maintain its own index // for atomicity and the add needs to be atomic. if atomic { if prev, ok := kmap[Key(kvObject.Key()...)]; ok { if prev.Index() != kvObject.Index() { c.mu.Unlock() return ErrKeyModified } } // Increment index index := kvObject.Index() index++ kvObject.SetIndex(index) } kmap[Key(kvObject.Key()...)] = kvObject c.mu.Unlock() return nil } func (c *cache) del(kvObject KVObject, atomic bool) error { kmap, err := c.kmap(kvObject) if err != nil { return err } c.mu.Lock() // If atomic is true, cache needs to maintain its own index // for atomicity and del needs to be atomic. if atomic { if prev, ok := kmap[Key(kvObject.Key()...)]; ok { if prev.Index() != kvObject.Index() { c.mu.Unlock() return ErrKeyModified } } } delete(kmap, Key(kvObject.Key()...)) c.mu.Unlock() return nil } func (c *cache) get(kvObject KVObject) error { kmap, err := c.kmap(kvObject) if err != nil { return err } c.mu.Lock() defer c.mu.Unlock() o, ok := kmap[Key(kvObject.Key()...)] if !ok { return ErrKeyNotFound } return o.CopyTo(kvObject) } func (c *cache) list(kvObject KVObject) ([]KVObject, error) { kmap, err := c.kmap(kvObject) if err != nil { return nil, err } c.mu.Lock() defer c.mu.Unlock() var kvol []KVObject for _, v := range kmap { kvol = append(kvol, v) } return kvol, nil }