Skip to content

Instantly share code, notes, and snippets.

@imjasonh
Last active October 28, 2021 06:18
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save imjasonh/e846785003b0509be05f to your computer and use it in GitHub Desktop.
Save imjasonh/e846785003b0509be05f to your computer and use it in GitHub Desktop.
appengine datastore/memcache wrapper
// TODO: support for *Multi methods, with special care about handling MultiErrors
// TODO: support for Query, which mostly depends on serializing Query structs
// TODO: support for (or at least intelligently ignore) PropertyLoadSavers
// TODO: tests?
// TODO: benchmarks?
package dsmem
import (
"errors"
"time"
"appengine"
"appengine/datastore"
"appengine/memcache"
)
var (
// Codec is the codec to use to serialize entities when caching.
// By default, this uses gob encoding.
Codec = memcache.Gob
// Expiration is the amount of time that cached entities should be kept.
// By default, there is no expiration.
Expiration time.Duration
// ErrInconsistentCache is the error that is returned by operations that would
// leave the cache in an inconsistent state with the datastore.
ErrInconsistentCache = errors.New("invalid cache state")
)
func Get(ctx appengine.Context, key *datastore.Key, dst interface{}) error {
if _, err := Codec.Get(ctx, key.Encode(), dst); err != nil {
// TODO: what if err != ErrCacheMiss? log something?
return datastore.Get(ctx, key, dst)
}
return nil
}
func Put(ctx appengine.Context, key *datastore.Key, src interface{}) (*datastore.Key, error) {
if err := Codec.Set(ctx, &memcache.Item{
Key: key.Encode(),
Object: src,
Expiration: Expiration,
}); err != nil {
// If we can't update the cache, at least try to make sure we don't cache inconsistently.
// If invalidating the cache fails, fail the entire operation.
if err := memcache.Delete(ctx, key.Encode()); err != nil && err != memcache.ErrCacheMiss {
return nil, ErrInconsistentCache
}
}
return datastore.Put(ctx, key, src)
}
func Delete(ctx appengine.Context, key *datastore.Key) error {
if err := memcache.Delete(ctx, key.Encode()); err != nil && err != memcache.ErrCacheMiss {
// If we can't remove the item from the cache, fail the entire operation.
return ErrInconsistentCache
}
return datastore.Delete(ctx, key)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment