feat(state): introduce cache e.g. to skip GitHub API requests for the same version information over and over again
This commit is contained in:
parent
fee941a0e4
commit
174ce3f39a
|
@ -220,7 +220,11 @@ func (a *App) initBasic(ctx context.Context) (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
return a.Collection.With(services.WithVault(v), services.WithStateStore(s))
|
||||
return a.Collection.With(
|
||||
services.WithVault(v),
|
||||
services.WithStateStore(s),
|
||||
services.WithCache(state.NewStateCache(a.appCfg.Cache.TTL, s)),
|
||||
)
|
||||
}
|
||||
|
||||
func (a *App) RunModule(ctx context.Context, cat modules.Category, name string) error {
|
||||
|
|
|
@ -29,7 +29,7 @@ func MockContext() *hcl.EvalContext {
|
|||
string_helpers.RegisterInContext(evalctx)
|
||||
hclhelpers.RegisterInContext(evalctx)
|
||||
vaultHelpers.RegisterInContext(evalctx, vaultHelpers.MockGetter("<mocked>"))
|
||||
github.RegisterInContext(context.Background(), evalctx, nil)
|
||||
github.RegisterInContext(context.Background(), evalctx, nil, nil)
|
||||
|
||||
return evalctx
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ func FullContext(ctx context.Context, parent *hcl.EvalContext, svc *services.Col
|
|||
evalctx.Variables = make(map[string]cty.Value)
|
||||
}
|
||||
|
||||
github.RegisterInContext(ctx, evalctx, svc.GitHubClient())
|
||||
github.RegisterInContext(ctx, evalctx, svc.GitHubClient(), svc.Cache())
|
||||
|
||||
return evalctx
|
||||
}
|
||||
|
|
|
@ -52,6 +52,13 @@ func WithStateStore(store state.Store) CollectionOption {
|
|||
})
|
||||
}
|
||||
|
||||
func WithCache(cache state.Cache) CollectionOption {
|
||||
return collectionOptionFunc(func(svc *Collection) error {
|
||||
svc.cache = cache
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func WithDockerClientFromEnv(ctx context.Context) CollectionOption {
|
||||
return collectionOptionFunc(func(svc *Collection) error {
|
||||
cli, err := client.NewClientWithOpts(
|
||||
|
@ -100,6 +107,7 @@ type Collection struct {
|
|||
dockerClient *client.Client
|
||||
ignorer *ignore.Ignorer
|
||||
stateStore state.Store
|
||||
cache state.Cache
|
||||
}
|
||||
|
||||
func (c *Collection) With(opts ...CollectionOption) error {
|
||||
|
@ -116,6 +124,10 @@ func (c *Collection) StateStore() state.Store {
|
|||
return c.stateStore
|
||||
}
|
||||
|
||||
func (c *Collection) Cache() state.Cache {
|
||||
return c.cache
|
||||
}
|
||||
|
||||
func (c *Collection) GitHubClient() *gh.Client {
|
||||
return c.ghClient
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@ import (
|
|||
"context"
|
||||
"time"
|
||||
|
||||
"code.icb4dc0.de/buildr/buildr/modules/state"
|
||||
|
||||
"github.com/google/go-github/v50/github"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
|
@ -11,7 +13,7 @@ import (
|
|||
|
||||
const DefaultAPITimeout = 30 * time.Second
|
||||
|
||||
func GetLatestReleaseTag(ctx context.Context, cli *github.Client) function.Function {
|
||||
func GetLatestReleaseTag(ctx context.Context, cli *github.Client, cache state.Cache) function.Function {
|
||||
return function.New(&function.Spec{
|
||||
Description: "Get tag of latest release of Github project",
|
||||
Params: []function.Parameter{
|
||||
|
@ -31,6 +33,15 @@ func GetLatestReleaseTag(ctx context.Context, cli *github.Client) function.Funct
|
|||
owner := args[0].AsString()
|
||||
repo := args[1].AsString()
|
||||
|
||||
cacheKey := state.KeyOfStrings("latest_github_release", owner, repo)
|
||||
|
||||
if cache != nil {
|
||||
val, _, err := cache.Get(ctx, cacheKey)
|
||||
if err == nil && len(val) > 0 {
|
||||
return cty.StringVal(string(val)), nil
|
||||
}
|
||||
}
|
||||
|
||||
if cli == nil {
|
||||
return cty.StringVal("<mocked>"), nil
|
||||
}
|
||||
|
@ -43,6 +54,8 @@ func GetLatestReleaseTag(ctx context.Context, cli *github.Client) function.Funct
|
|||
return cty.Value{}, err
|
||||
}
|
||||
|
||||
_ = cache.Set(ctx, cacheKey, []byte(release.GetTagName()))
|
||||
|
||||
return cty.StringVal(release.GetTagName()), nil
|
||||
},
|
||||
})
|
||||
|
|
|
@ -3,10 +3,12 @@ package github
|
|||
import (
|
||||
"context"
|
||||
|
||||
"code.icb4dc0.de/buildr/buildr/modules/state"
|
||||
|
||||
"github.com/google/go-github/v50/github"
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
)
|
||||
|
||||
func RegisterInContext(ctx context.Context, evalCtx *hcl.EvalContext, cli *github.Client) {
|
||||
evalCtx.Functions["gh_latest_release"] = GetLatestReleaseTag(ctx, cli)
|
||||
func RegisterInContext(ctx context.Context, evalCtx *hcl.EvalContext, cli *github.Client, cache state.Cache) {
|
||||
evalCtx.Functions["gh_latest_release"] = GetLatestReleaseTag(ctx, cli, cache)
|
||||
}
|
||||
|
|
|
@ -32,3 +32,12 @@ type StoreWriter interface {
|
|||
type StoreReader interface {
|
||||
Get(ctx context.Context, key Key) (state []byte, meta Metadata, err error)
|
||||
}
|
||||
|
||||
type CacheWriter interface {
|
||||
Set(ctx context.Context, key Key, state []byte) error
|
||||
}
|
||||
|
||||
type Cache interface {
|
||||
CacheWriter
|
||||
StoreReader
|
||||
}
|
||||
|
|
|
@ -12,12 +12,8 @@ func (f EntryOptionFunc) applyToEntry(e *ent.KVEntryCreate) {
|
|||
f(e)
|
||||
}
|
||||
|
||||
func WithTTL(t time.Time) EntryOption {
|
||||
func WithTTL(ttl time.Duration) EntryOption {
|
||||
return EntryOptionFunc(func(e *ent.KVEntryCreate) {
|
||||
if t.Location() != time.UTC {
|
||||
t = t.UTC()
|
||||
}
|
||||
|
||||
e.SetTTL(t)
|
||||
e.SetTTL(time.Now().UTC().Add(ttl))
|
||||
})
|
||||
}
|
||||
|
|
31
modules/state/state_cache.go
Normal file
31
modules/state/state_cache.go
Normal file
|
@ -0,0 +1,31 @@
|
|||
package state
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
func NewStateCache(ttl time.Duration, store Store) *StateCache {
|
||||
return &StateCache{
|
||||
TTL: ttl,
|
||||
Store: store,
|
||||
}
|
||||
}
|
||||
|
||||
var _ Cache = (*StateCache)(nil)
|
||||
|
||||
type StateCache struct {
|
||||
TTL time.Duration
|
||||
Store Store
|
||||
}
|
||||
|
||||
func (s *StateCache) Set(ctx context.Context, key Key, state []byte) error {
|
||||
return s.Store.Set(ctx, key, state, WithTTL(s.TTL))
|
||||
}
|
||||
|
||||
func (s *StateCache) Get(ctx context.Context, key Key) (state []byte, meta Metadata, err error) {
|
||||
if s == nil {
|
||||
return nil, Metadata{}, nil
|
||||
}
|
||||
return s.Store.Get(ctx, key)
|
||||
}
|
|
@ -28,7 +28,7 @@ func NewEntStore(ctx context.Context, stateFilePath string) (*EntStore, error) {
|
|||
|
||||
_, err = client.KVEntry.
|
||||
Delete().
|
||||
Where(kventry.TTLGT(time.Now().UTC())).
|
||||
Where(kventry.TTLLTE(time.Now().UTC())).
|
||||
Exec(ctx)
|
||||
|
||||
if err != nil {
|
||||
|
@ -63,7 +63,7 @@ func (s *EntStore) Get(ctx context.Context, key Key) (state []byte, meta Metadat
|
|||
return nil, Metadata{}, err
|
||||
}
|
||||
|
||||
if kvEntry.TTL != nil && kvEntry.TTL.After(time.Now().UTC()) {
|
||||
if kvEntry.TTL != nil && kvEntry.TTL.Before(time.Now().UTC()) {
|
||||
return nil, Metadata{}, nil
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue