Add NewFrom, and deprecate Save, SaveFile, Load, and LoadFile

This commit is contained in:
Patrick Mylund Nielsen 2014-12-22 01:12:10 -05:00
parent ffd0b9e3ec
commit 73a30b2033
2 changed files with 84 additions and 14 deletions

View File

@ -821,6 +821,9 @@ func (c *cache) DeleteExpired() {
}
// Write the cache's items (using Gob) to an io.Writer.
//
// NOTE: This method is deprecated in favor of c.Items() and NewFrom() (see the
// documentation for NewFrom.)
func (c *cache) Save(w io.Writer) (err error) {
enc := gob.NewEncoder(w)
defer func() {
@ -839,6 +842,9 @@ func (c *cache) Save(w io.Writer) (err error) {
// Save the cache's items to the given filename, creating the file if it
// doesn't exist, and overwriting it if it does.
//
// NOTE: This method is deprecated in favor of c.Items() and NewFrom() (see the
// documentation for NewFrom.)
func (c *cache) SaveFile(fname string) error {
fp, err := os.Create(fname)
if err != nil {
@ -854,6 +860,9 @@ func (c *cache) SaveFile(fname string) error {
// Add (Gob-serialized) cache items from an io.Reader, excluding any items with
// keys that already exist (and haven't expired) in the current cache.
//
// NOTE: This method is deprecated in favor of c.Items() and NewFrom() (see the
// documentation for NewFrom.)
func (c *cache) Load(r io.Reader) error {
dec := gob.NewDecoder(r)
items := map[string]*Item{}
@ -873,6 +882,9 @@ func (c *cache) Load(r io.Reader) error {
// Load and add cache items from the given filename, excluding any items with
// keys that already exist in the current cache.
//
// NOTE: This method is deprecated in favor of c.Items() and NewFrom() (see the
// documentation for NewFrom.)
func (c *cache) LoadFile(fname string) error {
fp, err := os.Open(fname)
if err != nil {
@ -943,33 +955,63 @@ func runJanitor(c *cache, ci time.Duration) {
go j.Run(c)
}
func newCache(de time.Duration) *cache {
func newCache(de time.Duration, m map[string]*Item) *cache {
if de == 0 {
de = -1
}
c := &cache{
defaultExpiration: de,
items: map[string]*Item{},
items: m,
}
return c
}
func newCacheWithJanitor(de time.Duration, ci time.Duration, m map[string]*Item) *Cache {
c := newCache(de, m)
// This trick ensures that the janitor goroutine (which--granted it
// was enabled--is running DeleteExpired on c forever) does not keep
// the returned C object from being garbage collected. When it is
// garbage collected, the finalizer stops the janitor goroutine, after
// which c can be collected.
C := &Cache{c}
if ci > 0 {
runJanitor(c, ci)
runtime.SetFinalizer(C, stopJanitor)
}
return C
}
// Return a new cache with a given default expiration duration and cleanup
// interval. If the expiration duration is less than 1, the items in the cache
// never expire (by default), and must be deleted manually. If the cleanup
// interval is less than one, expired items are not deleted from the cache
// before calling DeleteExpired.
func New(defaultExpiration, cleanupInterval time.Duration) *Cache {
c := newCache(defaultExpiration)
// This trick ensures that the janitor goroutine (which--granted it
// was enabled--is running DeleteExpired on c forever) does not keep
// the returned C object from being garbage collected. When it is
// garbage collected, the finalizer stops the janitor goroutine, after
// which c can be collected.
C := &Cache{c}
if cleanupInterval > 0 {
runJanitor(c, cleanupInterval)
runtime.SetFinalizer(C, stopJanitor)
items := make(map[string]*Item)
return newCacheWithJanitor(defaultExpiration, cleanupInterval, items)
}
return C
// Return a new cache with a given default expiration duration and cleanup
// interval. If the expiration duration is less than 1, the items in the cache
// never expire (by default), and must be deleted manually. If the cleanup
// interval is less than one, expired items are not deleted from the cache
// before calling DeleteExpired.
//
// NewFrom also accepts an items map which will serve as the underlying map
// for the cache. This is useful for deserializing a cache (serialized using
// e.g. gob.Encode on c.Items()), or setting a starting size by passing in e.g.
// make(map[string]*Item, 500) to avoid repeat initial resizing of a map that's
// expected to reach a certain minimum size.
//
// Only the cache's methods synchronize access to this map, so it is not
// recommended to keep any references to the map around after creating a cache.
// If need be, the map can be accessed at a later point using c.Items() (with
// the same caveats.)
//
// Note regarding serialization: When using e.g. gob, make sure to gob.Register
// the individual types stored in the cache before encoding a map retrieved with
// c.Items(), and to register those same types before decoding a blob containing
// an items map.
func NewFrom(defaultExpiration, cleanupInterval time.Duration, items map[string]*Item) *Cache {
return newCacheWithJanitor(defaultExpiration, cleanupInterval, items)
}

View File

@ -106,6 +106,34 @@ func TestCacheTimes(t *testing.T) {
}
}
func TestNewFrom(t *testing.T) {
m := map[string]*Item{
"a": &Item{
Object: 1,
Expiration: nil,
},
"b": &Item{
Object: 2,
Expiration: nil,
},
}
tc := NewFrom(0, 0, m)
a, found := tc.Get("a")
if !found {
t.Fatal("Did not find a")
}
if a.(int) != 1 {
t.Fatal("a is not 1")
}
b, found := tc.Get("b")
if !found {
t.Fatal("Did not find b")
}
if b.(int) != 2 {
t.Fatal("b is not 2")
}
}
func TestStorePointerToStruct(t *testing.T) {
tc := New(0, 0)
tc.Set("foo", &TestStruct{Num: 1}, 0)