Proxy accesstoken cache store (#5829)

* refactor middleware options

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* use ocmemstore micro store implementaiton for token cache

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* refactor ocis store options, support redis sentinel

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* align cache configuration

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* database and tabe are used to build prefixes for inmemory stores

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* add global persistent store options to userlog config

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* log cache errors but continue

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* drup unnecessary type conversion

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* Better description for the default userinfo ttl

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* use global cache options for even more caches

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* don't log userinfo cache misses

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* default to stock memory store

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* use correct mem store typo string

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* split cache options, doc cleanup

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* mint and write userinfo to cache async

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* use hashed token as key

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* go mod tidy

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* update docs

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* update cache store naming

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* bring back depreceted ocis-pkg/store package for backwards compatability

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* update changelog

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* Apply suggestions from code review

Co-authored-by: kobergj <jkoberg@owncloud.com>

* revert ocis-pkg/cache to store rename

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* add waiting for each step 50 milliseconds

* starlack check

---------

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
Co-authored-by: kobergj <jkoberg@owncloud.com>
Co-authored-by: Viktor Scharf <scharf.vi@gmail.com>
This commit is contained in:
Jörn Friedrich Dreyer
2023-03-22 15:21:57 +01:00
committed by GitHub
co-authored by kobergj Viktor Scharf
parent 688d07e297
commit 6bec87f582
37 changed files with 468 additions and 461 deletions
-36
View File
@@ -1,36 +0,0 @@
package roles
import (
"time"
"github.com/owncloud/ocis/v2/ocis-pkg/sync"
settingsmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/settings/v0"
)
// cache is a cache implementation for roles, keyed by roleIDs.
type cache struct {
sc sync.Cache
ttl time.Duration
}
// newCache returns a new instance of Cache.
func newCache(capacity int, ttl time.Duration) cache {
return cache{
ttl: ttl,
sc: sync.NewCache(capacity),
}
}
// get gets a role-bundle by a given `roleID`.
func (c *cache) get(roleID string) *settingsmsg.Bundle {
if ce := c.sc.Load(roleID); ce != nil {
return ce.V.(*settingsmsg.Bundle)
}
return nil
}
// set sets a roleID / role-bundle.
func (c *cache) set(roleID string, value *settingsmsg.Bundle) {
c.sc.Store(roleID, value, time.Now().Add(c.ttl))
}
-55
View File
@@ -1,55 +0,0 @@
package roles
import (
"strconv"
"sync"
"testing"
"time"
settingsmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/settings/v0"
"github.com/stretchr/testify/assert"
)
func cacheRunner(size int, ttl time.Duration) (*cache, func(f func(v string))) {
c := newCache(size, ttl)
run := func(f func(v string)) {
wg := sync.WaitGroup{}
for i := 0; i < size; i++ {
wg.Add(1)
go func(i int) {
f(strconv.Itoa(i))
wg.Done()
}(i)
}
wg.Wait()
}
return &c, run
}
func BenchmarkCache(b *testing.B) {
b.ReportAllocs()
size := 1024
c, cr := cacheRunner(size, 100*time.Millisecond)
cr(func(v string) { c.set(v, &settingsmsg.Bundle{}) })
cr(func(v string) { c.get(v) })
}
func TestCache(t *testing.T) {
size := 1024
ttl := 100 * time.Millisecond
c, cr := cacheRunner(size, ttl)
cr(func(v string) {
c.set(v, &settingsmsg.Bundle{Id: v})
})
assert.Equal(t, "50", c.get("50").Id, "it returns the right bundle")
assert.Nil(t, c.get("unknown"), "unknown bundle ist nil")
time.Sleep(ttl + 1)
// roles cache has no access to evict, adding new items triggers a cleanup
c.set("evict", nil)
assert.Nil(t, c.get("50"), "old bundles get removed")
}
+11 -11
View File
@@ -5,23 +5,23 @@ import (
"time"
"github.com/owncloud/ocis/v2/ocis-pkg/log"
ocisstore "github.com/owncloud/ocis/v2/ocis-pkg/store"
"github.com/owncloud/ocis/v2/ocis-pkg/store"
settingsmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/settings/v0"
settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0"
"go-micro.dev/v4/store"
microstore "go-micro.dev/v4/store"
"google.golang.org/protobuf/encoding/protojson"
)
const (
cacheDatabase = "ocis-pkg"
cacheTableName = "ocis-pkg/roles"
cacheTableName = "roles"
cacheTTL = time.Hour
)
// Manager manages a cache of roles by fetching unknown roles from the settings.RoleService.
type Manager struct {
logger log.Logger
cache store.Store
roleCache microstore.Store
roleService settingssvc.RoleService
}
@@ -29,9 +29,9 @@ type Manager struct {
func NewManager(o ...Option) Manager {
opts := newOptions(o...)
nStore := ocisstore.Create(opts.storeOptions...)
nStore := store.Create(opts.storeOptions...)
return Manager{
cache: nStore,
roleCache: nStore,
roleService: opts.roleService,
}
}
@@ -42,7 +42,7 @@ func (m *Manager) List(ctx context.Context, roleIDs []string) []*settingsmsg.Bun
result := make([]*settingsmsg.Bundle, 0)
lookup := make([]string, 0)
for _, roleID := range roleIDs {
if records, err := m.cache.Read(roleID, store.ReadFrom(cacheDatabase, cacheTableName)); err != nil {
if records, err := m.roleCache.Read(roleID, microstore.ReadFrom(cacheDatabase, cacheTableName)); err != nil {
lookup = append(lookup, roleID)
} else {
role := &settingsmsg.Bundle{}
@@ -77,15 +77,15 @@ func (m *Manager) List(ctx context.Context, roleIDs []string) []*settingsmsg.Bun
}
for _, role := range res.Bundles {
jsonbytes, _ := protojson.Marshal(role)
record := &store.Record{
record := &microstore.Record{
Key: role.Id,
Value: jsonbytes,
Expiry: cacheTTL,
}
err := m.cache.Write(
err := m.roleCache.Write(
record,
store.WriteTo(cacheDatabase, cacheTableName),
store.WriteTTL(cacheTTL),
microstore.WriteTo(cacheDatabase, cacheTableName),
microstore.WriteTTL(cacheTTL),
)
if err != nil {
m.logger.Debug().Err(err).Msg("failed to cache roles")
+3 -3
View File
@@ -2,13 +2,13 @@ package roles
import (
"github.com/owncloud/ocis/v2/ocis-pkg/log"
ocisstore "github.com/owncloud/ocis/v2/ocis-pkg/store"
settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0"
"go-micro.dev/v4/store"
)
// Options are all the possible options.
type Options struct {
storeOptions []ocisstore.Option
storeOptions []store.Option
logger log.Logger
roleService settingssvc.RoleService
}
@@ -31,7 +31,7 @@ func RoleService(rs settingssvc.RoleService) Option {
}
// StoreOptions are the options for the store
func StoreOptions(storeOpts []ocisstore.Option) Option {
func StoreOptions(storeOpts []store.Option) Option {
return func(o *Options) {
o.storeOptions = storeOpts
}