func (a *MemcacheDatastoreAccessor) uncacheIdiom(c context.Context, idiom *Idiom) error {
	cacheKeys := make([]string, 1+len(idiom.Implementations))
	cacheKeys[0] = fmt.Sprintf("getIdiom(%v)", idiom.Id)
	for i, impl := range idiom.Implementations {
		cacheKeys[1+i] = fmt.Sprintf("getIdiomByImplID(%v)", impl.Id)
	}

	err := memcache.DeleteMulti(c, cacheKeys)
	if err != nil {
		log.Errorf(c, err.Error())
	}
	return err
}
Example #2
0
// RunInTransaction runs f in a transaction. It calls f with a transaction
// context tg that f should use for all App Engine operations. Neither cache nor
// memcache are used or set during a transaction.
//
// Otherwise similar to appengine/datastore.RunInTransaction:
// https://developers.google.com/appengine/docs/go/datastore/reference#RunInTransaction
func (g *Goon) RunInTransaction(f func(tg *Goon) error, opts *datastore.TransactionOptions) error {
	var ng *Goon
	err := datastore.RunInTransaction(g.Context, func(tc context.Context) error {
		ng = &Goon{
			Context:          tc,
			inTransaction:    true,
			toSet:            make(map[string]interface{}),
			toDelete:         make(map[string]bool),
			toDeleteMC:       make(map[string]bool),
			KindNameResolver: g.KindNameResolver,
		}
		return f(ng)
	}, opts)

	if err == nil {
		if len(ng.toDeleteMC) > 0 {
			var memkeys []string
			for k := range ng.toDeleteMC {
				memkeys = append(memkeys, k)
			}
			memcache.DeleteMulti(g.Context, memkeys)
		}

		g.cacheLock.Lock()
		defer g.cacheLock.Unlock()
		for k, v := range ng.toSet {
			g.cache[k] = v
		}

		for k := range ng.toDelete {
			delete(g.cache, k)
		}
	} else {
		g.error(err)
	}

	return err
}
Example #3
0
func (mc *gaeMemcache) deleteMulti(c context.Context, keys []string) error {
	return memcache.DeleteMulti(c, keys)
}
Example #4
0
// DeleteMulti deletes the multiple keys
func (d *Driver) DeleteMulti(keys []string) error {
	return memcache.DeleteMulti(d.ctx, keys)
}
Example #5
0
func TestPutGetDelete(t *testing.T) {
	c, closeFunc := NewContext(t)
	defer closeFunc()

	type testEntity struct {
		IntVal int
	}

	// Check we set memcahce, put datastore and delete memcache.
	seq := make(chan string, 3)
	nds.SetMemcacheSetMulti(func(c context.Context,
		items []*memcache.Item) error {
		seq <- "memcache.SetMulti"
		return memcache.SetMulti(c, items)
	})
	nds.SetDatastorePutMulti(func(c context.Context,
		keys []*datastore.Key, vals interface{}) ([]*datastore.Key, error) {
		seq <- "datastore.PutMulti"
		return datastore.PutMulti(c, keys, vals)
	})
	nds.SetMemcacheDeleteMulti(func(c context.Context,
		keys []string) error {
		seq <- "memcache.DeleteMulti"
		close(seq)
		return memcache.DeleteMulti(c, keys)
	})

	incompleteKey := datastore.NewIncompleteKey(c, "Entity", nil)
	key, err := nds.Put(c, incompleteKey, &testEntity{43})
	if err != nil {
		t.Fatal(err)
	}

	nds.SetMemcacheSetMulti(memcache.SetMulti)
	nds.SetDatastorePutMulti(datastore.PutMulti)
	nds.SetMemcacheDeleteMulti(memcache.DeleteMulti)

	if s := <-seq; s != "memcache.SetMulti" {
		t.Fatal("memcache.SetMulti not", s)
	}
	if s := <-seq; s != "datastore.PutMulti" {
		t.Fatal("datastore.PutMulti not", s)
	}
	if s := <-seq; s != "memcache.DeleteMulti" {
		t.Fatal("memcache.DeleteMulti not", s)
	}
	// Check chan is closed.
	<-seq

	if key.Incomplete() {
		t.Fatal("Key is incomplete")
	}

	te := &testEntity{}
	if err := nds.Get(c, key, te); err != nil {
		t.Fatal(err)
	}

	if te.IntVal != 43 {
		t.Fatal("te.Val != 43", te.IntVal)
	}

	// Get from cache.
	te = &testEntity{}
	if err := nds.Get(c, key, te); err != nil {
		t.Fatal(err)
	}

	if te.IntVal != 43 {
		t.Fatal("te.Val != 43", te.IntVal)
	}

	// Change value.
	if _, err := nds.Put(c, key, &testEntity{64}); err != nil {
		t.Fatal(err)
	}

	// Get from cache.
	te = &testEntity{}
	if err := nds.Get(c, key, te); err != nil {
		t.Fatal(err)
	}

	if te.IntVal != 64 {
		t.Fatal("te.Val != 64", te.IntVal)
	}

	if err := nds.Delete(c, key); err != nil {
		t.Fatal(err)
	}

	if err := nds.Get(c, key, &testEntity{}); err != datastore.ErrNoSuchEntity {
		t.Fatal("expected datastore.ErrNoSuchEntity")
	}
}
Example #6
0
// DeleteMulti is a batch version of Delete.
func (g *Goon) DeleteMulti(keys []*datastore.Key) error {
	if len(keys) == 0 {
		return nil
		// not an error, and it was "successful", so return nil
	}
	memkeys := make([]string, len(keys))

	g.cacheLock.Lock()
	for i, k := range keys {
		mk := memkey(k)
		memkeys[i] = mk

		if g.inTransaction {
			delete(g.toSet, mk)
			g.toDelete[mk] = true
		} else {
			delete(g.cache, mk)
		}
	}
	g.cacheLock.Unlock()

	// Memcache needs to be updated after the datastore to prevent a common race condition,
	// where a concurrent request will fetch the not-yet-updated data from the datastore
	// and populate memcache with it.
	if g.inTransaction {
		for _, mk := range memkeys {
			g.toDeleteMC[mk] = true
		}
	} else {
		defer memcache.DeleteMulti(g.Context, memkeys)
	}

	multiErr, any := make(appengine.MultiError, len(keys)), false
	goroutines := (len(keys)-1)/deleteMultiLimit + 1
	var wg sync.WaitGroup
	wg.Add(goroutines)
	for i := 0; i < goroutines; i++ {
		go func(i int) {
			defer wg.Done()
			lo := i * deleteMultiLimit
			hi := (i + 1) * deleteMultiLimit
			if hi > len(keys) {
				hi = len(keys)
			}
			dmerr := datastore.DeleteMulti(g.Context, keys[lo:hi])
			if dmerr != nil {
				any = true // this flag tells DeleteMulti to return multiErr later
				merr, ok := dmerr.(appengine.MultiError)
				if !ok {
					g.error(dmerr)
					for j := lo; j < hi; j++ {
						multiErr[j] = dmerr
					}
					return
				}
				copy(multiErr[lo:hi], merr)
			}
		}(i)
	}
	wg.Wait()
	if any {
		return realError(multiErr)
	}
	return nil
}
Example #7
0
// PutMulti is a batch version of Put.
//
// src must be a *[]S, *[]*S, *[]I, []S, []*S, or []I, for some struct type S,
// or some interface type I. If *[]I or []I, each element must be a struct pointer.
func (g *Goon) PutMulti(src interface{}) ([]*datastore.Key, error) {
	keys, err := g.extractKeys(src, true) // allow incomplete keys on a Put request
	if err != nil {
		return nil, err
	}

	var memkeys []string
	for _, key := range keys {
		if !key.Incomplete() {
			memkeys = append(memkeys, memkey(key))
		}
	}

	// Memcache needs to be updated after the datastore to prevent a common race condition,
	// where a concurrent request will fetch the not-yet-updated data from the datastore
	// and populate memcache with it.
	if g.inTransaction {
		for _, mk := range memkeys {
			g.toDeleteMC[mk] = true
		}
	} else {
		defer memcache.DeleteMulti(g.Context, memkeys)
	}

	v := reflect.Indirect(reflect.ValueOf(src))
	multiErr, any := make(appengine.MultiError, len(keys)), false
	goroutines := (len(keys)-1)/putMultiLimit + 1
	var wg sync.WaitGroup
	wg.Add(goroutines)
	for i := 0; i < goroutines; i++ {
		go func(i int) {
			defer wg.Done()
			lo := i * putMultiLimit
			hi := (i + 1) * putMultiLimit
			if hi > len(keys) {
				hi = len(keys)
			}
			rkeys, pmerr := datastore.PutMulti(g.Context, keys[lo:hi], v.Slice(lo, hi).Interface())
			if pmerr != nil {
				any = true // this flag tells PutMulti to return multiErr later
				merr, ok := pmerr.(appengine.MultiError)
				if !ok {
					g.error(pmerr)
					for j := lo; j < hi; j++ {
						multiErr[j] = pmerr
					}
					return
				}
				copy(multiErr[lo:hi], merr)
			}

			for i, key := range keys[lo:hi] {
				if multiErr[lo+i] != nil {
					continue // there was an error writing this value, go to next
				}
				vi := v.Index(lo + i).Interface()
				if key.Incomplete() {
					g.setStructKey(vi, rkeys[i])
					keys[i] = rkeys[i]
				}
				if g.inTransaction {
					mk := memkey(rkeys[i])
					delete(g.toDelete, mk)
					g.toSet[mk] = vi
				} else {
					g.putMemory(vi)
				}
			}
		}(i)
	}
	wg.Wait()
	if any {
		return keys, realError(multiErr)
	}
	return keys, nil
}
Example #8
0
// clearCache removes all data for the given namespace from the cache
func clearCache(con *Context, namespace string) error {
	namespace = strings.ToLower(namespace)
	return memcache.DeleteMulti(con.C, []string{"RSS" + namespace, "JSON" + namespace})
}
Example #9
0
func (m mcImpl) DeleteMulti(keys []string, cb mc.RawCB) error {
	return doCB(memcache.DeleteMulti(m.aeCtx, keys), cb)
}