Changes AlbumCacheCover interface

This commit is contained in:
2016-01-25 15:40:33 +01:00
parent 2a46e44b2e
commit 21e6b41f95
2 changed files with 57 additions and 29 deletions

View File

@@ -2,7 +2,9 @@ package main
import ( import (
"encoding/json" "encoding/json"
"fmt"
"io" "io"
"sync"
"time" "time"
"github.com/peterbourgon/diskv" "github.com/peterbourgon/diskv"
@@ -12,6 +14,8 @@ import (
type AlbumCoverCache struct { type AlbumCoverCache struct {
dv *diskv.Diskv dv *diskv.Diskv
getter HTTPGetter 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 // time to live of the cache, data which is older than this TTL will be automatically removed
TTL time.Duration TTL time.Duration
} }
@@ -23,70 +27,85 @@ func NewAlbumCoverCache(basepath string, maxRequest uint, window time.Duration)
BasePath: basepath, BasePath: basepath,
CacheSizeMax: 100 * 1024 * 1024, // 100 Mb CacheSizeMax: 100 * 1024 * 1024, // 100 Mb
}), }),
URLs: make(map[AlbumID]string),
getter: NewRateLimitedGetter(maxRequest, window), getter: NewRateLimitedGetter(maxRequest, window),
TTL: 3 * 31 * 24 * time.Hour, // 3 Months TTL: 3 * 31 * 24 * time.Hour, // 3 Months
} }
return res return res
} }
func (c *AlbumCoverCache) fetch(a *Album) (io.ReadCloser, error) { func (c *AlbumCoverCache) fetch(ID AlbumID) (io.ReadCloser, error) {
resp, err := c.getter.Get(a.CoverURL) URL, ok := c.URLs[ID]
if err != nil { if ok == false {
return nil, err 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 { if err != nil {
return nil, err 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{ md := coverMetadata{
Age: time.Now(), Age: time.Now(),
} }
mdbyte, err := json.Marshal(&md) mdbyte, err := json.Marshal(&md)
if err == nil { if err == nil {
err = c.dv.Write(c.metadataKey(a), mdbyte) err = c.dv.Write(c.metadataKey(ID), mdbyte)
if err != nil { if err != nil {
return nil, err return nil, err
} }
} }
return c.dv.ReadStream(c.dataKey(a), false) return c.dv.ReadStream(c.dataKey(ID), false)
} }
type coverMetadata struct { type coverMetadata struct {
Age time.Time Age time.Time
} }
func (c *AlbumCoverCache) metadataKey(a *Album) string { func (c *AlbumCoverCache) metadataKey(ID AlbumID) string {
return AlbumIDString(a.ID) + ".metadata" return AlbumIDString(ID) + ".metadata"
} }
func (c *AlbumCoverCache) dataKey(a *Album) string { func (c *AlbumCoverCache) dataKey(ID AlbumID) string {
return AlbumIDString(a.ID) + ".data" 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 // 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) { func (c *AlbumCoverCache) GetCover(ID AlbumID) (io.ReadCloser, error) {
// c.lock.RLock()
if c.dv.Has(c.metadataKey(a)) == false { defer c.lock.RUnlock()
return c.fetch(a)
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 { if err != nil {
return c.fetch(a) return c.fetch(ID)
} }
var md coverMetadata var md coverMetadata
err = json.Unmarshal(mdbytes, &md) err = json.Unmarshal(mdbytes, &md)
if err != nil { if err != nil {
return c.fetch(a) return c.fetch(ID)
} }
// check TTL // check TTL
if time.Now().Add(-c.TTL).After(md.Age) == true { 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)
} }

View File

@@ -11,7 +11,9 @@ import (
. "gopkg.in/check.v1" . "gopkg.in/check.v1"
) )
type AlbumCoverCacheSuite struct{} type AlbumCoverCacheSuite struct {
cache *AlbumCoverCache
}
var _ = Suite(&AlbumCoverCacheSuite{}) 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") 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) { func (s *AlbumCoverCacheSuite) TestCanFetchCache(c *C) {
cache := NewAlbumCoverCache(c.MkDir(), 10, 10*time.Second)
cache.getter = testGetter
var resData = []bytes.Buffer{} var resData = []bytes.Buffer{}
start := time.Now() start := time.Now()
for _, a := range albumsDataTest { for _, a := range albumsDataTest {
cover, err := cache.GetCover(&a) cover, err := s.cache.GetCover(a.ID)
var buf bytes.Buffer var buf bytes.Buffer
if c.Check(err, IsNil) == true { if c.Check(err, IsNil) == true {
_, err := io.Copy(&buf, cover) _, err := io.Copy(&buf, cover)
@@ -39,12 +48,12 @@ func (s *AlbumCoverCacheSuite) TestCanFetchCache(c *C) {
resData = append(resData, buf) resData = append(resData, buf)
} }
cache.getter = &errorGetter{} s.cache.getter = &errorGetter{}
// now we check that we get it again, but from the disk, not // now we check that we get it again, but from the disk, not
// hitting the web // hitting the web
for i, a := range albumsDataTest { for i, a := range albumsDataTest {
cover, err := cache.GetCover(&a) cover, err := s.cache.GetCover(a.ID)
var buf bytes.Buffer var buf bytes.Buffer
if c.Check(err, IsNil) == true { if c.Check(err, IsNil) == true {
_, err := io.Copy(&buf, cover) _, 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 // now if we it the TTL, we will reftech and get error
cache.TTL = 0 s.cache.TTL = 0
for _, a := range albumsDataTest { for _, a := range albumsDataTest {
cover, err := cache.GetCover(&a) cover, err := s.cache.GetCover(a.ID)
c.Check(cover, IsNil) c.Check(cover, IsNil)
c.Check(err, ErrorMatches, "I will always have an error") c.Check(err, ErrorMatches, "I will always have an error")
} }