Do not register types with encoding/gob

Registering the same type multiple times can lead to a panic. While the
panic is handled in Save, the handling prevents saving the cache.

While doing this seems like a good idea (since then the caller wouldn't
have to register the types themselves), the need to deal with duplicate
entries causes issues with Load. If calling Load to initialize a cache
right after startup, encoding/gob will have no information about the
types written in the cache, and thus won't be able to load the cache.

There are also issues w.r.t. how the type is registered in gob, and
if they are registered in a different form inside go-cache than they
were outside, a panic happens due to different paths being generated:
https://github.com/golang/go/blob/master/src/encoding/gob/type.go#L857

Make sure that it's the caller's respansibility to register types with
encoding/gob, to avoid issues with gob's Register and so that they can
both Save and Load without having to Save beforehand in the same instance.
This commit is contained in:
Diogo Franco (Kovensky) 2014-12-11 17:21:32 +09:00
parent 7d1d6d6ae9
commit abe7c4fb56
1 changed files with 12 additions and 8 deletions

View File

@ -869,24 +869,22 @@ 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.
//
// The caller should register any custom types with encoding/gob.Register
// before calling this function.
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() {
if x := recover(); x != nil {
err = fmt.Errorf("Error registering item types with Gob library")
}
}()
c.RLock() c.RLock()
defer c.RUnlock() defer c.RUnlock()
for _, v := range c.items {
gob.Register(v.Object)
}
err = enc.Encode(&c.items) err = enc.Encode(&c.items)
return return
} }
// 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.
//
// The caller should register any custom types with encoding/gob.Register
// before calling this function.
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 {
@ -902,6 +900,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.
//
// The caller should register any custom types with encoding/gob.Register
// before calling this function.
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{}
@ -921,6 +922,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.
//
// The caller should register any custom types with encoding/gob.Register
// before calling this function.
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 {