Expiring cache
First, we're still using the var cache cmap.ConcurrentMap
Second, we need a new field to store, the expire information. So, let's save a struct as a value for the cache item.
type CacheItem struct { Key string Value []byte Expire time.Time }Adding an item to cache is almost the same, let's just take the expiring into account and also the set maximum size of the cache.
func AddItemToCache(key string, value []byte, expire time.Duration) { if cache.Count() <= cacheSize { cache.Set(key, CacheItem{Key: key, Value: value, Expire: time.Now().Add(expire)}) } else { panic("cacheSize reached") } }Getting an item from the cache is trivial. Again, let's take the expired items into account by removing them.
func GetItemFromCache(key string) *CacheItem { if cache.Has(key) { if tmp, ok := cache.Get(key); ok { item := tmp.(CacheItem) if item.Expire.After(time.Now()) { return &item } removeItem(item.Key) } } return nil }
Usages of an expiring cache
cacheItem := GetItemFromCache(weatherURLs[1]) if cacheItem != nil { w.Write(cacheItem.Value) } else { if weather := fetchWeather(weatherURLs[1]); weather != nil { j, _ := json.Marshal(&weather.Query.Results.Channel.Item) AddItemToCache(weatherURLs[1], j, time.Minute) w.Write(j) } else { json.NewEncoder(w).Encode("not found") } }
Removing stale items
In addition to removing stale items when bumping into them while getting an item from cache, we can remove stale items by runningfunc removeStaleCacheItems() { for _, item := range cache.Items() { cItem := item.(CacheItem) if time.Now().After(cItem.Expire) { removeItem(cItem.Key) } } }once in a minute for example. We loop all items and check if the current time is after expiration time. This is started as a background job in the init function.
func init() { go doEvery(time.Minute, removeStaleCacheItems) } func doEvery(d time.Duration, f func()) { for range time.Tick(d) { f() } }
Testing
Testing is otherwise about the same, but we have the new expiring test. Easy enough, just give a little time for an added item and then sleep a bit more than that. When getting the first added item after expiring, you get nil back as supposed to.func TestExpire(t *testing.T) { assert := assert.New(t) AddItemToCache("item", []byte("value"), time.Second) assert.Equal(string(GetItemFromCache("item").Value), "value", "should be equal") time.Sleep(time.Second * 2) assert.True(GetItemFromCache("item") == nil, "should be equal") }
The whole source code can be found from github.










