Skip to content

Instantly share code, notes, and snippets.

@joshrotenberg
Last active October 14, 2015 19:46
Show Gist options
  • Select an option

  • Save joshrotenberg/e9d0274f3dc9b79353cd to your computer and use it in GitHub Desktop.

Select an option

Save joshrotenberg/e9d0274f3dc9b79353cd to your computer and use it in GitHub Desktop.
package groupcache_expire
import (
"log"
"strings"
"time"
"github.com/golang/groupcache"
)
// this is the format that we'll use to prefix our keys
const prefixFormat = "15:04:05"
// wrap up some of the details of cache creation
type Cache struct {
cacheGroup *groupcache.Group
peers *groupcache.HTTPPool
expiration time.Duration
}
// this is really the root of how we handle expiration. you can probably
// think of ten other ways to do it, but Go's time.Truncate() comes
// in really handy if you want to set an explicit TTL style expiration.
func stampKey(key string, expiration time.Duration) string {
tf := time.Now().Truncate(expiration).Format(prefixFormat)
tn := time.Now().Format(prefixFormat)
log.Println(tf, tn)
return strings.Join([]string{tf, key}, "-")
}
func unstampKey(key string) string {
parts := strings.Split(key, "-")
return parts[1]
}
func getter(ctx groupcache.Context, key string, dest groupcache.Sink) error {
log.Printf("key %s not found (stamped: %s)", unstampKey(key), key)
dest.SetString(fakeDb[unstampKey(key)])
return nil
}
func NewCache(name string, url string, expiration time.Duration, size int64) *Cache {
peers := groupcache.NewHTTPPool(url)
var stringcache = groupcache.NewGroup(name, size, groupcache.GetterFunc(getter))
return &Cache{
cacheGroup: stringcache,
peers: peers,
expiration: expiration,
}
}
func (mc Cache) Stats() groupcache.CacheStats {
return mc.cacheGroup.CacheStats(groupcache.MainCache)
}
func (mc Cache) Get(key string) string {
var data string
stampedKey := stampKey(key, mc.expiration)
err := mc.cacheGroup.Get(nil, stampedKey, groupcache.StringSink(&data))
if err != nil {
panic(err)
}
return data
}
package groupcache_expire
import (
"log"
"testing"
"time"
)
var (
// our handy in memory fake NoSQL key value store that holds
// a mapping of english to czech numbers 0-5.
fakeDb = map[string]string{
"zero": "nula",
"one": "jeden",
"two": "dva",
"three": "tři",
"four": "čtyři",
"five": "pět",
}
)
func TestExpire(t *testing.T) {
// if we were running groupcache for real and i was demonstrating
// the distributed nature, this would matter more.
url := "http://localhost:8282"
maxAge := time.Duration(10 * time.Second)
// create a new cache and give it plenty of space
// so we know our misses are caused by key expiration and not
// eviction.
c := NewCache("mycache", url, maxAge, 4096)
for i := 0; i < 10; i++ {
for k, _ := range fakeDb {
c.Get(k)
}
stats := c.Stats()
log.Printf("Items: %d, Gets: %d, Hits: %d",
stats.Items, stats.Gets, stats.Hits)
time.Sleep(1 * time.Second)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment