// release is called right after a successful transaction completion. It's job // is to clear out all the locks, if possible (but if not, no worries, // they'll expire soon). func (s *dsTxnState) release(sc *supportContext) { s.Lock() defer s.Unlock() delKeys := make([]string, 0, len(s.toDelete)) for k := range s.toDelete { delKeys = append(delKeys, k) } if err := errors.Filter(sc.mc.DeleteMulti(delKeys), memcache.ErrCacheMiss); err != nil { (log.Fields{log.ErrorKey: err}).Warningf( sc.c, "dscache: txn.release: memcache.DeleteMulti") } }
func (s *supportContext) mutation(keys []*ds.Key, f func() error) error { lockItems, lockKeys := s.mkAllLockItems(keys) if lockItems == nil { return f() } if err := s.mc.SetMulti(lockItems); err != nil { // this is a hard failure. No mutation can occur if we're unable to set // locks out. See "DANGER ZONE" in the docs. (log.Fields{log.ErrorKey: err}).Errorf( s.c, "dscache: HARD FAILURE: supportContext.mutation(): mc.SetMulti") return err } err := f() if err == nil { if err := errors.Filter(s.mc.DeleteMulti(lockKeys), memcache.ErrCacheMiss); err != nil { (log.Fields{log.ErrorKey: err}).Warningf( s.c, "dscache: mc.DeleteMulti") } } return err }
func (d *dsCache) GetMulti(keys []*ds.Key, metas ds.MultiMetaGetter, cb ds.GetMultiCB) error { lockItems, nonce := d.mkRandLockItems(keys, metas) if len(lockItems) == 0 { return d.RawInterface.GetMulti(keys, metas, cb) } if err := d.mc.AddMulti(lockItems); err != nil { // Ignore this error. Either we couldn't add them because they exist // (so, not an issue), or because memcache is having sad times (in which // case we'll see so in the GetMulti which immediately follows this). } if err := errors.Filter(d.mc.GetMulti(lockItems), memcache.ErrCacheMiss); err != nil { (log.Fields{log.ErrorKey: err}).Warningf( d.c, "dscache: GetMulti: memcache.GetMulti") } p := makeFetchPlan(d.c, d.aid, d.ns, &facts{keys, metas, lockItems, nonce}) if !p.empty() { // looks like we have something to pull from datastore, and maybe some work // to save stuff back to memcache. toCas := []memcache.Item{} j := 0 err := d.RawInterface.GetMulti(p.toGet, p.toGetMeta, func(pm ds.PropertyMap, err error) error { i := p.idxMap[j] toSave := p.toSave[j] j++ data := []byte(nil) // true: save entity to memcache // false: lock entity in memcache forever shouldSave := true if err == nil { p.decoded[i] = pm if toSave != nil { data = encodeItemValue(pm) if len(data) > internalValueSizeLimit { shouldSave = false log.Warningf( d.c, "dscache: encoded entity too big (%d/%d)!", len(data), internalValueSizeLimit) } } } else { p.lme.Assign(i, err) if err != ds.ErrNoSuchEntity { return nil // aka continue to the next entry } } if toSave != nil { if shouldSave { // save mg := metas.GetSingle(i) expSecs := ds.GetMetaDefault(mg, CacheExpirationMeta, CacheTimeSeconds).(int64) toSave.SetFlags(uint32(ItemHasData)) toSave.SetExpiration(time.Duration(expSecs) * time.Second) toSave.SetValue(data) } else { // Set a lock with an infinite timeout. No one else should try to // serialize this item to memcache until something Put/Delete's it. toSave.SetFlags(uint32(ItemHasLock)) toSave.SetExpiration(0) toSave.SetValue(nil) } toCas = append(toCas, toSave) } return nil }) if err != nil { return err } if len(toCas) > 0 { // we have entries to save back to memcache. if err := d.mc.CompareAndSwapMulti(toCas); err != nil { (log.Fields{log.ErrorKey: err}).Warningf( d.c, "dscache: GetMulti: memcache.CompareAndSwapMulti") } } } // finally, run the callback for all of the decoded items and the errors, // if any. for i, dec := range p.decoded { cb(dec, p.lme.GetOne(i)) } return nil }