Skip to content

Commit

Permalink
Add functions for clearing specific cache entries. (#96)
Browse files Browse the repository at this point in the history
  • Loading branch information
xStrom authored Oct 21, 2021
1 parent 1c698b6 commit 21ceed9
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 0 deletions.
74 changes: 74 additions & 0 deletions goon.go
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,80 @@ func (g *Goon) FlushLocalCache() {
g.cache.Flush()
}

// ClearCache removes the provided entity from cache.
// Takes either *S or *datastore.Key.
// The 'mem' and 'local' booleans dictate the type of caches to clear.
func (g *Goon) ClearCache(src interface{}, mem, local bool) error {
if !mem && !local {
// Nothing to clear ..
return nil
}
var srcs interface{}
if key, ok := src.(*datastore.Key); ok {
srcs = []*datastore.Key{key}
} else {
v := reflect.ValueOf(src)
if v.Kind() != reflect.Ptr {
return fmt.Errorf("goon: expected pointer to a struct, got %#v", src)
}
srcs = []interface{}{src}
}
err := g.ClearCacheMulti(srcs, mem, local)
if err != nil {
// Look for an embedded error if it's multi
if me, ok := err.(appengine.MultiError); ok {
return me[0]
}
}
return err
}

// ClearCacheMulti removes the provided entities from cache.
// Takes either []*S or []*datastore.Key.
// The 'mem' and 'local' booleans dictate the type of caches to clear.
func (g *Goon) ClearCacheMulti(src interface{}, mem, local bool) error {
if !mem && !local {
// Nothing to clear ..
return nil
}
keys, ok := src.([]*datastore.Key)
if !ok {
var err error
keys, err = g.extractKeys(src, false) // don't allow incomplete keys on a Clear request
if err != nil {
return err
}
}
if len(keys) == 0 {
return nil
// not an error, and it was "successful", so return nil
}
cachekeys := make([]string, 0, len(keys))
for _, key := range keys {
cachekeys = append(cachekeys, cacheKey(key))
}
if g.inTransaction {
g.txnCacheLock.Lock()
for _, ck := range cachekeys {
if local {
g.toDelete[ck] = struct{}{}
}
if mem {
g.toDeleteMC[ck] = struct{}{}
}
}
g.txnCacheLock.Unlock()
} else {
if local {
g.cache.DeleteMulti(cachekeys)
}
if mem {
g.memcacheDeleteError(memcache.DeleteMulti(g.Context, cachekeys))
}
}
return nil
}

type memcacheTask struct {
items []*memcache.Item
size int
Expand Down
57 changes: 57 additions & 0 deletions goon_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1865,6 +1865,63 @@ func TestCaches(t *testing.T) {
if !reflect.DeepEqual(*phid, ghids[0]) {
t.Fatalf("Invalid result!\n%s", getDiff(*phid, ghids[0], "*phid", "ghids[0]"))
}

// Do a sneaky save straight to the datastore
mhid := &HasId{Id: phid.Id, Name: "modified"}
if _, err := datastore.Put(c, datastore.NewKey(c, "HasId", "", phid.Id, nil), mhid); err != nil {
t.Fatalf("Unexpected error on datastore.Put: %v", err)
}

// Clear the memcache entry specifically
if err := g.ClearCache(phid, true, false); err != nil {
t.Fatalf("Failed to clear cache: %v", err)
}

// fetch *struct{} from cache
ghid = &HasId{Id: phid.Id}
err = g.Get(ghid)
if err != nil {
t.Fatalf("Unexpected error on get - %v", err)
}
if !reflect.DeepEqual(phid, ghid) {
t.Fatalf("Invalid result!\n%s", getDiff(phid, ghid, "phid", "ghid"))
}

// Clear the local cache entry specifically
if err := g.ClearCache(phid, false, true); err != nil {
t.Fatalf("Failed to clear cache: %v", err)
}

// fetch *struct{} from datastore
ghid = &HasId{Id: phid.Id}
err = g.Get(ghid)
if err != nil {
t.Fatalf("Unexpected error on get - %v", err)
}
if !reflect.DeepEqual(mhid, ghid) {
t.Fatalf("Invalid result!\n%s", getDiff(mhid, ghid, "mhid", "ghid"))
}

// Do a sneaky save straight to the datastore
nhid := &HasId{Id: phid.Id, Name: "nudged"}
if _, err := datastore.Put(c, datastore.NewKey(c, "HasId", "", phid.Id, nil), nhid); err != nil {
t.Fatalf("Unexpected error on datastore.Put: %v", err)
}

// Clear the local cache entry specifically
if err := g.ClearCache(phid, false, true); err != nil {
t.Fatalf("Failed to clear cache: %v", err)
}

// fetch *struct{} from memcache
ghid = &HasId{Id: phid.Id}
err = g.Get(ghid)
if err != nil {
t.Fatalf("Unexpected error on get - %v", err)
}
if !reflect.DeepEqual(mhid, ghid) {
t.Fatalf("Invalid result!\n%s", getDiff(mhid, ghid, "mhid", "ghid"))
}
}

func TestGoon(t *testing.T) {
Expand Down

0 comments on commit 21ceed9

Please sign in to comment.