mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
cache now implements models.Datastore by just embedding one and then changing GetApp and GetRoute to have the cache inside. this makes it really flexible for things like testing, so now the agent doesn't automagically do caching, now it must be passed a datastore that was wrapped with a cache datastore. the datastore in the server can remain separate and not use the cache still, and then now the agent when running fn 'for real' is configured with the cache baked in. this seems a lot cleaner than what we had and gets the cache out of the way and it's easier to swap in / out / extend.
72 lines
1.7 KiB
Go
72 lines
1.7 KiB
Go
package cache
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"github.com/fnproject/fn/api/common/singleflight"
|
|
"github.com/fnproject/fn/api/models"
|
|
"github.com/patrickmn/go-cache"
|
|
)
|
|
|
|
type cacheDB struct {
|
|
models.Datastore
|
|
|
|
cache *cache.Cache
|
|
singleflight singleflight.SingleFlight // singleflight assists Datastore
|
|
}
|
|
|
|
// Wrap implements models.Datastore by wrapping an existing datastore and
|
|
// adding caching around certain methods. At present, GetApp and GetRoute add
|
|
// caching.
|
|
func Wrap(ds models.Datastore) models.Datastore {
|
|
return &cacheDB{
|
|
Datastore: ds,
|
|
cache: cache.New(5*time.Second, 1*time.Minute), // TODO configurable from env
|
|
}
|
|
}
|
|
|
|
func (c *cacheDB) GetApp(ctx context.Context, appName string) (*models.App, error) {
|
|
key := appCacheKey(appName)
|
|
app, ok := c.cache.Get(key)
|
|
if ok {
|
|
return app.(*models.App), nil
|
|
}
|
|
|
|
resp, err := c.singleflight.Do(key,
|
|
func() (interface{}, error) { return c.Datastore.GetApp(ctx, appName) },
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
app = resp.(*models.App)
|
|
c.cache.Set(key, app, cache.DefaultExpiration)
|
|
return app.(*models.App), nil
|
|
}
|
|
|
|
func (c *cacheDB) GetRoute(ctx context.Context, appName, path string) (*models.Route, error) {
|
|
key := routeCacheKey(appName, path)
|
|
route, ok := c.cache.Get(key)
|
|
if ok {
|
|
return route.(*models.Route), nil
|
|
}
|
|
|
|
resp, err := c.singleflight.Do(key,
|
|
func() (interface{}, error) { return c.Datastore.GetRoute(ctx, appName, path) },
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
route = resp.(*models.Route)
|
|
c.cache.Set(key, route, cache.DefaultExpiration)
|
|
return route.(*models.Route), nil
|
|
}
|
|
|
|
func routeCacheKey(appname, path string) string {
|
|
return "r:" + appname + "\x00" + path
|
|
}
|
|
|
|
func appCacheKey(appname string) string {
|
|
return "a:" + appname
|
|
}
|