From 21e6b41f959e45f40e62a40e00fc4d25cd9ebecf Mon Sep 17 00:00:00 2001 From: Alexandre Tuleu Date: Mon, 25 Jan 2016 15:40:33 +0100 Subject: [PATCH] Changes AlbumCacheCover interface --- album_cover_cache.go | 61 +++++++++++++++++++++++++-------------- album_cover_cache_test.go | 25 +++++++++++----- 2 files changed, 57 insertions(+), 29 deletions(-) diff --git a/album_cover_cache.go b/album_cover_cache.go index a41de71..759b44d 100644 --- a/album_cover_cache.go +++ b/album_cover_cache.go @@ -2,7 +2,9 @@ package main import ( "encoding/json" + "fmt" "io" + "sync" "time" "github.com/peterbourgon/diskv" @@ -12,6 +14,8 @@ import ( type AlbumCoverCache struct { dv *diskv.Diskv getter HTTPGetter + URLs map[AlbumID]string + lock sync.RWMutex // time to live of the cache, data which is older than this TTL will be automatically removed TTL time.Duration } @@ -23,70 +27,85 @@ func NewAlbumCoverCache(basepath string, maxRequest uint, window time.Duration) BasePath: basepath, CacheSizeMax: 100 * 1024 * 1024, // 100 Mb }), + URLs: make(map[AlbumID]string), getter: NewRateLimitedGetter(maxRequest, window), TTL: 3 * 31 * 24 * time.Hour, // 3 Months } return res } -func (c *AlbumCoverCache) fetch(a *Album) (io.ReadCloser, error) { - resp, err := c.getter.Get(a.CoverURL) - if err != nil { - return nil, err +func (c *AlbumCoverCache) fetch(ID AlbumID) (io.ReadCloser, error) { + URL, ok := c.URLs[ID] + if ok == false { + return nil, fmt.Errorf("Cover URL for Album %d is not registered", ID) } - defer closeOrPanic(resp.Body, "GET "+a.CoverURL) - err = c.dv.WriteStream(c.dataKey(a), resp.Body, false) + resp, err := c.getter.Get(URL) if err != nil { return nil, err } + defer closeOrPanic(resp.Body, "GET "+URL) + + err = c.dv.WriteStream(c.dataKey(ID), resp.Body, false) + if err != nil { + return nil, err + } + md := coverMetadata{ Age: time.Now(), } mdbyte, err := json.Marshal(&md) if err == nil { - err = c.dv.Write(c.metadataKey(a), mdbyte) + err = c.dv.Write(c.metadataKey(ID), mdbyte) if err != nil { return nil, err } } - return c.dv.ReadStream(c.dataKey(a), false) + return c.dv.ReadStream(c.dataKey(ID), false) } type coverMetadata struct { Age time.Time } -func (c *AlbumCoverCache) metadataKey(a *Album) string { - return AlbumIDString(a.ID) + ".metadata" +func (c *AlbumCoverCache) metadataKey(ID AlbumID) string { + return AlbumIDString(ID) + ".metadata" } -func (c *AlbumCoverCache) dataKey(a *Album) string { - return AlbumIDString(a.ID) + ".data" +func (c *AlbumCoverCache) dataKey(ID AlbumID) string { + return AlbumIDString(ID) + ".data" +} + +func (c *AlbumCoverCache) RegisterCover(ID AlbumID, URL string) { + c.lock.Lock() + defer c.lock.Unlock() + c.URLs[ID] = URL } // GetCover retrieves from the cache or either from www.bedetheque.com the Cover of an album -func (c *AlbumCoverCache) GetCover(a *Album) (io.ReadCloser, error) { - // - if c.dv.Has(c.metadataKey(a)) == false { - return c.fetch(a) +func (c *AlbumCoverCache) GetCover(ID AlbumID) (io.ReadCloser, error) { + c.lock.RLock() + defer c.lock.RUnlock() + + if c.dv.Has(c.metadataKey(ID)) == false { + return c.fetch(ID) } - mdbytes, err := c.dv.Read(c.metadataKey(a)) + mdbytes, err := c.dv.Read(c.metadataKey(ID)) if err != nil { - return c.fetch(a) + return c.fetch(ID) } var md coverMetadata err = json.Unmarshal(mdbytes, &md) if err != nil { - return c.fetch(a) + return c.fetch(ID) } // check TTL if time.Now().Add(-c.TTL).After(md.Age) == true { - return c.fetch(a) + return c.fetch(ID) } - return c.dv.ReadStream(c.dataKey(a), false) + return c.dv.ReadStream(c.dataKey(ID), false) } diff --git a/album_cover_cache_test.go b/album_cover_cache_test.go index 78b4e32..1f43ab8 100644 --- a/album_cover_cache_test.go +++ b/album_cover_cache_test.go @@ -11,7 +11,9 @@ import ( . "gopkg.in/check.v1" ) -type AlbumCoverCacheSuite struct{} +type AlbumCoverCacheSuite struct { + cache *AlbumCoverCache +} var _ = Suite(&AlbumCoverCacheSuite{}) @@ -22,14 +24,21 @@ func (g *errorGetter) Get(URL string) (*http.Response, error) { return nil, fmt.Errorf("I will always have an error") } +func (s *AlbumCoverCacheSuite) SetUpSuite(c *C) { + s.cache = NewAlbumCoverCache(c.MkDir(), 10, 10*time.Second) + s.cache.getter = testGetter + + for _, a := range albumsDataTest { + s.cache.RegisterCover(a.ID, a.CoverURL) + } +} + func (s *AlbumCoverCacheSuite) TestCanFetchCache(c *C) { - cache := NewAlbumCoverCache(c.MkDir(), 10, 10*time.Second) - cache.getter = testGetter var resData = []bytes.Buffer{} start := time.Now() for _, a := range albumsDataTest { - cover, err := cache.GetCover(&a) + cover, err := s.cache.GetCover(a.ID) var buf bytes.Buffer if c.Check(err, IsNil) == true { _, err := io.Copy(&buf, cover) @@ -39,12 +48,12 @@ func (s *AlbumCoverCacheSuite) TestCanFetchCache(c *C) { resData = append(resData, buf) } - cache.getter = &errorGetter{} + s.cache.getter = &errorGetter{} // now we check that we get it again, but from the disk, not // hitting the web for i, a := range albumsDataTest { - cover, err := cache.GetCover(&a) + cover, err := s.cache.GetCover(a.ID) var buf bytes.Buffer if c.Check(err, IsNil) == true { _, err := io.Copy(&buf, cover) @@ -56,10 +65,10 @@ func (s *AlbumCoverCacheSuite) TestCanFetchCache(c *C) { } // now if we it the TTL, we will reftech and get error - cache.TTL = 0 + s.cache.TTL = 0 for _, a := range albumsDataTest { - cover, err := cache.GetCover(&a) + cover, err := s.cache.GetCover(a.ID) c.Check(cover, IsNil) c.Check(err, ErrorMatches, "I will always have an error") }