Add NewFrom, and deprecate Save, SaveFile, Load, and LoadFile
This commit is contained in:
parent
ffd0b9e3ec
commit
73a30b2033
68
cache.go
68
cache.go
|
@ -821,6 +821,9 @@ func (c *cache) DeleteExpired() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the cache's items (using Gob) to an io.Writer.
|
// 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) {
|
func (c *cache) Save(w io.Writer) (err error) {
|
||||||
enc := gob.NewEncoder(w)
|
enc := gob.NewEncoder(w)
|
||||||
defer func() {
|
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
|
// Save the cache's items to the given filename, creating the file if it
|
||||||
// doesn't exist, and overwriting it if it does.
|
// 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 {
|
func (c *cache) SaveFile(fname string) error {
|
||||||
fp, err := os.Create(fname)
|
fp, err := os.Create(fname)
|
||||||
if err != nil {
|
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
|
// 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.
|
// 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 {
|
func (c *cache) Load(r io.Reader) error {
|
||||||
dec := gob.NewDecoder(r)
|
dec := gob.NewDecoder(r)
|
||||||
items := map[string]*Item{}
|
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
|
// Load and add cache items from the given filename, excluding any items with
|
||||||
// keys that already exist in the current cache.
|
// 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 {
|
func (c *cache) LoadFile(fname string) error {
|
||||||
fp, err := os.Open(fname)
|
fp, err := os.Open(fname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -943,33 +955,63 @@ func runJanitor(c *cache, ci time.Duration) {
|
||||||
go j.Run(c)
|
go j.Run(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newCache(de time.Duration) *cache {
|
func newCache(de time.Duration, m map[string]*Item) *cache {
|
||||||
if de == 0 {
|
if de == 0 {
|
||||||
de = -1
|
de = -1
|
||||||
}
|
}
|
||||||
c := &cache{
|
c := &cache{
|
||||||
defaultExpiration: de,
|
defaultExpiration: de,
|
||||||
items: map[string]*Item{},
|
items: m,
|
||||||
}
|
}
|
||||||
return c
|
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
|
// 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
|
// 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
|
// 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
|
// interval is less than one, expired items are not deleted from the cache
|
||||||
// before calling DeleteExpired.
|
// before calling DeleteExpired.
|
||||||
func New(defaultExpiration, cleanupInterval time.Duration) *Cache {
|
func New(defaultExpiration, cleanupInterval time.Duration) *Cache {
|
||||||
c := newCache(defaultExpiration)
|
items := make(map[string]*Item)
|
||||||
// This trick ensures that the janitor goroutine (which--granted it
|
return newCacheWithJanitor(defaultExpiration, cleanupInterval, items)
|
||||||
// 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)
|
|
||||||
}
|
}
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
func TestStorePointerToStruct(t *testing.T) {
|
||||||
tc := New(0, 0)
|
tc := New(0, 0)
|
||||||
tc.Set("foo", &TestStruct{Num: 1}, 0)
|
tc.Set("foo", &TestStruct{Num: 1}, 0)
|
||||||
|
|
Loading…
Reference in New Issue