Skip to content

Commit 21ceed9

Browse files
authored
Add functions for clearing specific cache entries. (#96)
1 parent 1c698b6 commit 21ceed9

File tree

2 files changed

+131
-0
lines changed

2 files changed

+131
-0
lines changed

goon.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,80 @@ func (g *Goon) FlushLocalCache() {
412412
g.cache.Flush()
413413
}
414414

415+
// ClearCache removes the provided entity from cache.
416+
// Takes either *S or *datastore.Key.
417+
// The 'mem' and 'local' booleans dictate the type of caches to clear.
418+
func (g *Goon) ClearCache(src interface{}, mem, local bool) error {
419+
if !mem && !local {
420+
// Nothing to clear ..
421+
return nil
422+
}
423+
var srcs interface{}
424+
if key, ok := src.(*datastore.Key); ok {
425+
srcs = []*datastore.Key{key}
426+
} else {
427+
v := reflect.ValueOf(src)
428+
if v.Kind() != reflect.Ptr {
429+
return fmt.Errorf("goon: expected pointer to a struct, got %#v", src)
430+
}
431+
srcs = []interface{}{src}
432+
}
433+
err := g.ClearCacheMulti(srcs, mem, local)
434+
if err != nil {
435+
// Look for an embedded error if it's multi
436+
if me, ok := err.(appengine.MultiError); ok {
437+
return me[0]
438+
}
439+
}
440+
return err
441+
}
442+
443+
// ClearCacheMulti removes the provided entities from cache.
444+
// Takes either []*S or []*datastore.Key.
445+
// The 'mem' and 'local' booleans dictate the type of caches to clear.
446+
func (g *Goon) ClearCacheMulti(src interface{}, mem, local bool) error {
447+
if !mem && !local {
448+
// Nothing to clear ..
449+
return nil
450+
}
451+
keys, ok := src.([]*datastore.Key)
452+
if !ok {
453+
var err error
454+
keys, err = g.extractKeys(src, false) // don't allow incomplete keys on a Clear request
455+
if err != nil {
456+
return err
457+
}
458+
}
459+
if len(keys) == 0 {
460+
return nil
461+
// not an error, and it was "successful", so return nil
462+
}
463+
cachekeys := make([]string, 0, len(keys))
464+
for _, key := range keys {
465+
cachekeys = append(cachekeys, cacheKey(key))
466+
}
467+
if g.inTransaction {
468+
g.txnCacheLock.Lock()
469+
for _, ck := range cachekeys {
470+
if local {
471+
g.toDelete[ck] = struct{}{}
472+
}
473+
if mem {
474+
g.toDeleteMC[ck] = struct{}{}
475+
}
476+
}
477+
g.txnCacheLock.Unlock()
478+
} else {
479+
if local {
480+
g.cache.DeleteMulti(cachekeys)
481+
}
482+
if mem {
483+
g.memcacheDeleteError(memcache.DeleteMulti(g.Context, cachekeys))
484+
}
485+
}
486+
return nil
487+
}
488+
415489
type memcacheTask struct {
416490
items []*memcache.Item
417491
size int

goon_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1865,6 +1865,63 @@ func TestCaches(t *testing.T) {
18651865
if !reflect.DeepEqual(*phid, ghids[0]) {
18661866
t.Fatalf("Invalid result!\n%s", getDiff(*phid, ghids[0], "*phid", "ghids[0]"))
18671867
}
1868+
1869+
// Do a sneaky save straight to the datastore
1870+
mhid := &HasId{Id: phid.Id, Name: "modified"}
1871+
if _, err := datastore.Put(c, datastore.NewKey(c, "HasId", "", phid.Id, nil), mhid); err != nil {
1872+
t.Fatalf("Unexpected error on datastore.Put: %v", err)
1873+
}
1874+
1875+
// Clear the memcache entry specifically
1876+
if err := g.ClearCache(phid, true, false); err != nil {
1877+
t.Fatalf("Failed to clear cache: %v", err)
1878+
}
1879+
1880+
// fetch *struct{} from cache
1881+
ghid = &HasId{Id: phid.Id}
1882+
err = g.Get(ghid)
1883+
if err != nil {
1884+
t.Fatalf("Unexpected error on get - %v", err)
1885+
}
1886+
if !reflect.DeepEqual(phid, ghid) {
1887+
t.Fatalf("Invalid result!\n%s", getDiff(phid, ghid, "phid", "ghid"))
1888+
}
1889+
1890+
// Clear the local cache entry specifically
1891+
if err := g.ClearCache(phid, false, true); err != nil {
1892+
t.Fatalf("Failed to clear cache: %v", err)
1893+
}
1894+
1895+
// fetch *struct{} from datastore
1896+
ghid = &HasId{Id: phid.Id}
1897+
err = g.Get(ghid)
1898+
if err != nil {
1899+
t.Fatalf("Unexpected error on get - %v", err)
1900+
}
1901+
if !reflect.DeepEqual(mhid, ghid) {
1902+
t.Fatalf("Invalid result!\n%s", getDiff(mhid, ghid, "mhid", "ghid"))
1903+
}
1904+
1905+
// Do a sneaky save straight to the datastore
1906+
nhid := &HasId{Id: phid.Id, Name: "nudged"}
1907+
if _, err := datastore.Put(c, datastore.NewKey(c, "HasId", "", phid.Id, nil), nhid); err != nil {
1908+
t.Fatalf("Unexpected error on datastore.Put: %v", err)
1909+
}
1910+
1911+
// Clear the local cache entry specifically
1912+
if err := g.ClearCache(phid, false, true); err != nil {
1913+
t.Fatalf("Failed to clear cache: %v", err)
1914+
}
1915+
1916+
// fetch *struct{} from memcache
1917+
ghid = &HasId{Id: phid.Id}
1918+
err = g.Get(ghid)
1919+
if err != nil {
1920+
t.Fatalf("Unexpected error on get - %v", err)
1921+
}
1922+
if !reflect.DeepEqual(mhid, ghid) {
1923+
t.Fatalf("Invalid result!\n%s", getDiff(mhid, ghid, "mhid", "ghid"))
1924+
}
18681925
}
18691926

18701927
func TestGoon(t *testing.T) {

0 commit comments

Comments
 (0)