Skip to content

Commit

Permalink
Add in-memory cache for rwp server
Browse files Browse the repository at this point in the history
  • Loading branch information
chocolatkey committed May 29, 2024
1 parent e022f93 commit 7f71e92
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 31 deletions.
69 changes: 38 additions & 31 deletions cmd/rwp/cmd/serve/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/gorilla/mux"
httprange "github.com/gotd/contrib/http_range"
"github.com/pkg/errors"
"github.com/readium/go-toolkit/cmd/rwp/cmd/serve/cache"
"github.com/readium/go-toolkit/pkg/asset"
"github.com/readium/go-toolkit/pkg/manifest"
"github.com/readium/go-toolkit/pkg/pub"
Expand Down Expand Up @@ -53,43 +54,49 @@ func (s *Server) getPublication(filename string) (*pub.Publication, error) {
return nil, err
}

// TODO: cache open publications

cp := filepath.Clean(string(fpath))
pub, err := streamer.New(streamer.Config{
InferA11yMetadata: s.config.InferA11yMetadata,
}).Open(asset.File(filepath.Join(s.config.BaseDirectory, cp)), "")
if err != nil {
return nil, errors.Wrap(err, "failed opening "+cp)
}
dat, ok := s.lfu.Get(cp)
if !ok {
pub, err := streamer.New(streamer.Config{
InferA11yMetadata: s.config.InferA11yMetadata,
}).Open(asset.File(filepath.Join(s.config.BaseDirectory, cp)), "")
if err != nil {
return nil, errors.Wrap(err, "failed opening "+cp)
}

// TODO: Remove this after we make links relative in the go-toolkit
for i, link := range pub.Manifest.Links {
pub.Manifest.Links[i] = makeRelative(link)
}
for i, link := range pub.Manifest.Resources {
pub.Manifest.Resources[i] = makeRelative(link)
}
for i, link := range pub.Manifest.ReadingOrder {
pub.Manifest.ReadingOrder[i] = makeRelative(link)
}
for i, link := range pub.Manifest.TableOfContents {
pub.Manifest.TableOfContents[i] = makeRelative(link)
}
var makeCollectionRelative func(mp manifest.PublicationCollectionMap)
makeCollectionRelative = func(mp manifest.PublicationCollectionMap) {
for i := range mp {
for j := range mp[i] {
for k := range mp[i][j].Links {
mp[i][j].Links[k] = makeRelative(mp[i][j].Links[k])
// TODO: Remove this after we make links relative in the go-toolkit
for i, link := range pub.Manifest.Links {
pub.Manifest.Links[i] = makeRelative(link)
}
for i, link := range pub.Manifest.Resources {
pub.Manifest.Resources[i] = makeRelative(link)
}
for i, link := range pub.Manifest.ReadingOrder {
pub.Manifest.ReadingOrder[i] = makeRelative(link)
}
for i, link := range pub.Manifest.TableOfContents {
pub.Manifest.TableOfContents[i] = makeRelative(link)
}
var makeCollectionRelative func(mp manifest.PublicationCollectionMap)
makeCollectionRelative = func(mp manifest.PublicationCollectionMap) {
for i := range mp {
for j := range mp[i] {
for k := range mp[i][j].Links {
mp[i][j].Links[k] = makeRelative(mp[i][j].Links[k])
}
makeCollectionRelative(mp[i][j].Subcollections)
}
makeCollectionRelative(mp[i][j].Subcollections)
}
}
}
makeCollectionRelative(pub.Manifest.Subcollections)
makeCollectionRelative(pub.Manifest.Subcollections)

return pub, nil
// Cache the publication
encPub := &cache.CachedPublication{Publication: pub}
s.lfu.Set(cp, encPub)

return encPub.Publication, nil
}
return dat.(*cache.CachedPublication).Publication, nil
}

func (s *Server) getManifest(w http.ResponseWriter, req *http.Request) {
Expand Down
90 changes: 90 additions & 0 deletions cmd/rwp/cmd/serve/cache/local.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package cache

// Originally from https://github.com/go-redis/cache/blob/v8.4.3/local.go
// Modified to store interface{} instead of []byte

import (
"sync"
"time"

"github.com/vmihailenco/go-tinylfu"
"golang.org/x/exp/rand"
)

type Evictable interface {
OnEvict()
}

type LocalCache interface {
Set(key string, data Evictable)
Get(key string) (Evictable, bool)
Del(key string)
}

type TinyLFU struct {
mu sync.Mutex
rand *rand.Rand
lfu *tinylfu.T
ttl time.Duration
offset time.Duration
}

var _ LocalCache = (*TinyLFU)(nil)

func NewTinyLFU(size int, ttl time.Duration) *TinyLFU {
const maxOffset = 10 * time.Second

offset := ttl / 10
if offset > maxOffset {
offset = maxOffset
}

return &TinyLFU{
rand: rand.New(rand.NewSource(uint64(time.Now().UnixNano()))),
lfu: tinylfu.New(size, 100000),
ttl: ttl,
offset: offset,
}
}

func (c *TinyLFU) UseRandomizedTTL(offset time.Duration) {
c.offset = offset
}

func (c *TinyLFU) Set(key string, b Evictable) {
c.mu.Lock()
defer c.mu.Unlock()

ttl := c.ttl
if c.offset > 0 {
ttl += time.Duration(c.rand.Int63n(int64(c.offset)))
}

c.lfu.Set(&tinylfu.Item{
Key: key,
Value: b,
ExpireAt: time.Now().Add(ttl),
OnEvict: func() {
b.OnEvict()
},
})
}

func (c *TinyLFU) Get(key string) (Evictable, bool) {
c.mu.Lock()
defer c.mu.Unlock()

val, ok := c.lfu.Get(key)
if !ok {
return nil, false
}

return val.(Evictable), true
}

func (c *TinyLFU) Del(key string) {
c.mu.Lock()
defer c.mu.Unlock()

c.lfu.Del(key)
}
22 changes: 22 additions & 0 deletions cmd/rwp/cmd/serve/cache/pubcache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package cache

import (
"github.com/readium/go-toolkit/pkg/pub"
)

// CachedPublication implements Evictable
type CachedPublication struct {
*pub.Publication
}

func EncapsulatePublication(pub *pub.Publication) *CachedPublication {
cp := &CachedPublication{pub}
return cp
}

func (cp *CachedPublication) OnEvict() {
// Cleanup
if cp.Publication != nil {
cp.Publication.Close()
}
}
8 changes: 8 additions & 0 deletions cmd/rwp/cmd/serve/server.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package serve

import (
"time"

"github.com/gorilla/mux"
"github.com/readium/go-toolkit/cmd/rwp/cmd/serve/cache"
"github.com/readium/go-toolkit/pkg/streamer"
)

Expand All @@ -15,10 +18,15 @@ type ServerConfig struct {
type Server struct {
config ServerConfig
router *mux.Router
lfu *cache.TinyLFU
}

const MaxCachedPublicationAmount = 10
const MaxCachedPublicationTTL = time.Second * time.Duration(600)

func NewServer(config ServerConfig) *Server {
return &Server{
config: config,
lfu: cache.NewTinyLFU(MaxCachedPublicationAmount, MaxCachedPublicationTTL),
}
}
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,17 @@ require (
github.com/stretchr/testify v1.9.0
github.com/trimmer-io/go-xmp v1.0.0
github.com/urfave/negroni v1.0.0
github.com/vmihailenco/go-tinylfu v0.2.2
github.com/zeebo/xxh3 v1.0.2
golang.org/x/exp v0.0.0-20240529005216-23cca8864a10
golang.org/x/net v0.23.0
golang.org/x/text v0.14.0
)

require (
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/antchfx/xpath v1.2.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
Expand Down
7 changes: 7 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
Expand Down Expand Up @@ -292,6 +295,8 @@ github.com/urfave/negroni v1.0.0 h1:kIimOitoypq34K7TG7DUaJ9kq/N4Ofuwi1sjz0KipXc=
github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
github.com/valyala/gozstd v1.20.1 h1:xPnnnvjmaDDitMFfDxmQ4vpx0+3CdTg2o3lALvXTU/g=
github.com/valyala/gozstd v1.20.1/go.mod h1:y5Ew47GLlP37EkTB+B4s7r6A5rdaeB7ftbl9zoYiIPQ=
github.com/vmihailenco/go-tinylfu v0.2.2 h1:H1eiG6HM36iniK6+21n9LLpzx1G9R3DJa2UjUjbynsI=
github.com/vmihailenco/go-tinylfu v0.2.2/go.mod h1:CutYi2Q9puTxfcolkliPq4npPuofg9N9t8JVrjzwa3Q=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
Expand Down Expand Up @@ -333,6 +338,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20240529005216-23cca8864a10 h1:vpzMC/iZhYFAjJzHU0Cfuq+w1vLLsF2vLkDrPjzKYck=
golang.org/x/exp v0.0.0-20240529005216-23cca8864a10/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.11.0 h1:ds2RoQvBvYTiJkwpSFDwCcDFNX7DqjL2WsUgTNk0Ooo=
Expand Down

0 comments on commit 7f71e92

Please sign in to comment.