Skip to content
This repository has been archived by the owner on Nov 3, 2023. It is now read-only.

Commit

Permalink
add support for cached private repository (#206)
Browse files Browse the repository at this point in the history
  • Loading branch information
ortuman authored Feb 4, 2022
1 parent e8b2c1d commit 16783ba
Show file tree
Hide file tree
Showing 15 changed files with 393 additions and 116 deletions.
13 changes: 8 additions & 5 deletions pkg/storage/cached/cached.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,19 @@ type Cache interface {

// Get retrieves k value from the cache store.
// If not present nil will be returned.
Get(ctx context.Context, k string) ([]byte, error)
Get(ctx context.Context, ns, key string) ([]byte, error)

// Put stores a value into the cache store.
Put(ctx context.Context, k string, val []byte) error
Put(ctx context.Context, ns, key string, val []byte) error

// Del removes k value from the cache store.
Del(ctx context.Context, k string) error
Del(ctx context.Context, ns string, keys ...string) error

// DelNS removes all keys contained under a given namespace from the cache store.
DelNS(ctx context.Context, ns string) error

// HasKey tells whether k is present in the cache store.
HasKey(ctx context.Context, k string) (bool, error)
HasKey(ctx context.Context, ns, key string) (bool, error)

// Start starts Cache component.
Start(ctx context.Context) error
Expand Down Expand Up @@ -84,8 +87,8 @@ func New(cfg Config, rep repository.Repository, logger kitlog.Logger) (repositor
User: &cachedUserRep{c: c, rep: rep},
Last: &cachedLastRep{c: c, rep: rep},
Capabilities: &cachedCapsRep{c: c, rep: rep},
Private: &cachedPrivateRep{c: c, rep: rep},
BlockList: rep,
Private: rep,
Roster: rep,
VCard: &cachedVCardRep{c: c, rep: rep},
Offline: rep,
Expand Down
21 changes: 13 additions & 8 deletions pkg/storage/cached/capabilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import (
"github.com/ortuman/jackal/pkg/storage/repository"
)

const capsKey = "caps"

type capsCodec struct {
val *capsmodel.Capabilities
}
Expand Down Expand Up @@ -51,8 +53,9 @@ type cachedCapsRep struct {

func (c *cachedCapsRep) UpsertCapabilities(ctx context.Context, caps *capsmodel.Capabilities) error {
op := updateOp{
c: c.c,
key: capsKey(caps.Node, caps.Ver),
c: c.c,
namespace: capsNS(caps.Node, caps.Ver),
invalidKeys: []string{capsKey},
updateFn: func(ctx context.Context) error {
return c.rep.UpsertCapabilities(ctx, caps)
},
Expand All @@ -62,8 +65,9 @@ func (c *cachedCapsRep) UpsertCapabilities(ctx context.Context, caps *capsmodel.

func (c *cachedCapsRep) CapabilitiesExist(ctx context.Context, node, ver string) (bool, error) {
op := existsOp{
c: c.c,
key: capsKey(node, ver),
c: c.c,
namespace: capsNS(node, ver),
key: capsKey,
missFn: func(ctx context.Context) (bool, error) {
return c.rep.CapabilitiesExist(ctx, node, ver)
},
Expand All @@ -73,9 +77,10 @@ func (c *cachedCapsRep) CapabilitiesExist(ctx context.Context, node, ver string)

func (c *cachedCapsRep) FetchCapabilities(ctx context.Context, node, ver string) (*capsmodel.Capabilities, error) {
op := fetchOp{
c: c.c,
key: capsKey(node, ver),
codec: &capsCodec{},
c: c.c,
namespace: capsNS(node, ver),
key: capsKey,
codec: &capsCodec{},
missFn: func(ctx context.Context) (interface{}, error) {
return c.rep.FetchCapabilities(ctx, node, ver)
},
Expand All @@ -90,6 +95,6 @@ func (c *cachedCapsRep) FetchCapabilities(ctx context.Context, node, ver string)
return nil, nil
}

func capsKey(node, ver string) string {
func capsNS(node, ver string) string {
return fmt.Sprintf("caps:%s:%s", node, ver)
}
18 changes: 10 additions & 8 deletions pkg/storage/cached/capabilities_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@ import (

func TestCachedCapsRep_UpsertCaps(t *testing.T) {
// given
var cacheKey string
var cacheNS, cacheKey string

cacheMock := &cacheMock{}
cacheMock.DelFunc = func(ctx context.Context, k string) error {
cacheKey = k
cacheMock.DelFunc = func(ctx context.Context, ns string, keys ...string) error {
cacheNS = ns
cacheKey = keys[0]
return nil
}

Expand All @@ -50,15 +51,16 @@ func TestCachedCapsRep_UpsertCaps(t *testing.T) {

// then
require.NoError(t, err)
require.Equal(t, capsKey("n1", "v1"), cacheKey)
require.Equal(t, capsNS("n1", "v1"), cacheNS)
require.Equal(t, capsKey, cacheKey)
require.Len(t, repMock.UpsertCapabilitiesCalls(), 1)
}

func TestCachedCapsRep_CapsExist(t *testing.T) {
// given
cacheMock := &cacheMock{}
cacheMock.HasKeyFunc = func(ctx context.Context, k string) (bool, error) {
if k == capsKey("n1", "v1") {
cacheMock.HasKeyFunc = func(ctx context.Context, ns, k string) (bool, error) {
if ns == capsNS("n1", "v1") && k == capsKey {
return true, nil
}
return false, nil
Expand Down Expand Up @@ -94,10 +96,10 @@ func TestCachedCapsRep_CapsExist(t *testing.T) {
func TestCachedCapsRep_FetchCaps(t *testing.T) {
// given
cacheMock := &cacheMock{}
cacheMock.GetFunc = func(ctx context.Context, k string) ([]byte, error) {
cacheMock.GetFunc = func(ctx context.Context, ns, k string) ([]byte, error) {
return nil, nil
}
cacheMock.PutFunc = func(ctx context.Context, k string, val []byte) error {
cacheMock.PutFunc = func(ctx context.Context, ns, k string, val []byte) error {
return nil
}

Expand Down
21 changes: 13 additions & 8 deletions pkg/storage/cached/last.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import (
"github.com/ortuman/jackal/pkg/storage/repository"
)

const lastKey = "lst"

type lastCodec struct {
val *lastmodel.Last
}
Expand Down Expand Up @@ -51,8 +53,9 @@ type cachedLastRep struct {

func (c *cachedLastRep) UpsertLast(ctx context.Context, last *lastmodel.Last) error {
op := updateOp{
c: c.c,
key: lastKey(last.Username),
c: c.c,
namespace: lastNS(last.Username),
invalidKeys: []string{lastKey},
updateFn: func(ctx context.Context) error {
return c.rep.UpsertLast(ctx, last)
},
Expand All @@ -62,9 +65,10 @@ func (c *cachedLastRep) UpsertLast(ctx context.Context, last *lastmodel.Last) er

func (c *cachedLastRep) FetchLast(ctx context.Context, username string) (*lastmodel.Last, error) {
op := fetchOp{
c: c.c,
key: lastKey(username),
codec: &lastCodec{},
c: c.c,
namespace: lastNS(username),
key: lastKey,
codec: &lastCodec{},
missFn: func(ctx context.Context) (interface{}, error) {
return c.rep.FetchLast(ctx, username)
},
Expand All @@ -81,15 +85,16 @@ func (c *cachedLastRep) FetchLast(ctx context.Context, username string) (*lastmo

func (c *cachedLastRep) DeleteLast(ctx context.Context, username string) error {
op := updateOp{
c: c.c,
key: lastKey(username),
c: c.c,
namespace: lastNS(username),
invalidKeys: []string{lastKey},
updateFn: func(ctx context.Context) error {
return c.rep.DeleteLast(ctx, username)
},
}
return op.do(ctx)
}

func lastKey(username string) string {
func lastNS(username string) string {
return fmt.Sprintf("lst:%s", username)
}
24 changes: 14 additions & 10 deletions pkg/storage/cached/last_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,12 @@ import (

func TestCachedLastRep_UpsertLast(t *testing.T) {
// given
var cacheKey string
var cacheNS, cacheKey string

cacheMock := &cacheMock{}
cacheMock.DelFunc = func(ctx context.Context, k string) error {
cacheKey = k
cacheMock.DelFunc = func(ctx context.Context, ns string, keys ...string) error {
cacheNS = ns
cacheKey = keys[0]
return nil
}

Expand All @@ -47,17 +48,19 @@ func TestCachedLastRep_UpsertLast(t *testing.T) {

// then
require.NoError(t, err)
require.Equal(t, lastKey("u1"), cacheKey)
require.Equal(t, lastNS("u1"), cacheNS)
require.Equal(t, lastKey, cacheKey)
require.Len(t, repMock.UpsertLastCalls(), 1)
}

func TestCachedLastRep_DeleteLast(t *testing.T) {
// given
var cacheKey string
var cacheNS, cacheKey string

cacheMock := &cacheMock{}
cacheMock.DelFunc = func(ctx context.Context, k string) error {
cacheKey = k
cacheMock.DelFunc = func(ctx context.Context, ns string, keys ...string) error {
cacheNS = ns
cacheKey = keys[0]
return nil
}

Expand All @@ -75,17 +78,18 @@ func TestCachedLastRep_DeleteLast(t *testing.T) {

// then
require.NoError(t, err)
require.Equal(t, lastKey("v1"), cacheKey)
require.Equal(t, lastNS("v1"), cacheNS)
require.Equal(t, lastKey, cacheKey)
require.Len(t, repMock.DeleteLastCalls(), 1)
}

func TestCachedLastRep_FetchLast(t *testing.T) {
// given
cacheMock := &cacheMock{}
cacheMock.GetFunc = func(ctx context.Context, k string) ([]byte, error) {
cacheMock.GetFunc = func(ctx context.Context, ns, k string) ([]byte, error) {
return nil, nil
}
cacheMock.PutFunc = func(ctx context.Context, k string, val []byte) error {
cacheMock.PutFunc = func(ctx context.Context, ns, k string, val []byte) error {
return nil
}

Expand Down
41 changes: 26 additions & 15 deletions pkg/storage/cached/op.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,14 @@ type codec interface {
}

type existsOp struct {
c Cache
key string
missFn func(context.Context) (bool, error)
c Cache
namespace string
key string
missFn func(context.Context) (bool, error)
}

func (op existsOp) do(ctx context.Context) (bool, error) {
ok, err := op.c.HasKey(ctx, op.key)
ok, err := op.c.HasKey(ctx, op.namespace, op.key)
if err != nil {
return false, err
}
Expand All @@ -42,27 +43,37 @@ func (op existsOp) do(ctx context.Context) (bool, error) {
}

type updateOp struct {
c Cache
key string
updateFn func(context.Context) error
c Cache
namespace string
invalidKeys []string
updateFn func(context.Context) error
}

func (op updateOp) do(ctx context.Context) error {
if err := op.c.Del(ctx, op.key); err != nil {
return err
switch {
case len(op.invalidKeys) > 0:
if err := op.c.Del(ctx, op.namespace, op.invalidKeys...); err != nil {
return err
}

default:
if err := op.c.DelNS(ctx, op.namespace); err != nil {
return err
}
}
return op.updateFn(ctx)
}

type fetchOp struct {
c Cache
key string
codec codec
missFn func(context.Context) (interface{}, error)
c Cache
namespace string
key string
codec codec
missFn func(context.Context) (interface{}, error)
}

func (op fetchOp) do(ctx context.Context) (interface{}, error) {
b, err := op.c.Get(ctx, op.key)
b, err := op.c.Get(ctx, op.namespace, op.key)
if err != nil {
return nil, err
}
Expand All @@ -78,7 +89,7 @@ func (op fetchOp) do(ctx context.Context) (interface{}, error) {
if err != nil {
return nil, err
}
if err := op.c.Put(ctx, op.key, b); err != nil {
if err := op.c.Put(ctx, op.namespace, op.key, b); err != nil {
return nil, err
}
return obj, nil
Expand Down
18 changes: 9 additions & 9 deletions pkg/storage/cached/op_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ import (
func TestCachedRepository_ExistsOp(t *testing.T) {
// given
cacheMock := &cacheMock{}
cacheMock.HasKeyFunc = func(ctx context.Context, k string) (bool, error) {
if k == "k0" {
cacheMock.HasKeyFunc = func(ctx context.Context, ns, k string) (bool, error) {
if ns == "n0" && k == "k0" {
return true, nil
}
return false, nil
Expand All @@ -37,8 +37,8 @@ func TestCachedRepository_ExistsOp(t *testing.T) {
}

// when
op0 := existsOp{c: cacheMock, key: "k0", missFn: missFn}
op1 := existsOp{c: cacheMock, key: "k1", missFn: missFn}
op0 := existsOp{c: cacheMock, namespace: "n0", key: "k0", missFn: missFn}
op1 := existsOp{c: cacheMock, namespace: "n1", key: "k1", missFn: missFn}

ok0, _ := op0.do(context.Background())
ok1, _ := op1.do(context.Background())
Expand All @@ -53,7 +53,7 @@ func TestCachedRepository_UpdateOp(t *testing.T) {
var output string

cacheMock := &cacheMock{}
cacheMock.DelFunc = func(ctx context.Context, k string) error {
cacheMock.DelFunc = func(ctx context.Context, ns string, keys ...string) error {
output += "del"
return nil
}
Expand All @@ -63,7 +63,7 @@ func TestCachedRepository_UpdateOp(t *testing.T) {
}

// when
op := updateOp{c: cacheMock, key: "k0", updateFn: updateFn}
op := updateOp{c: cacheMock, invalidKeys: []string{"k0"}, updateFn: updateFn}
_ = op.do(context.Background())

// then
Expand All @@ -73,7 +73,7 @@ func TestCachedRepository_UpdateOp(t *testing.T) {
func TestCachedRepository_FetchOpHit(t *testing.T) {
// given
cacheMock := &cacheMock{}
cacheMock.GetFunc = func(ctx context.Context, k string) ([]byte, error) {
cacheMock.GetFunc = func(ctx context.Context, ns, k string) ([]byte, error) {
return []byte{255}, nil
}

Expand Down Expand Up @@ -107,10 +107,10 @@ func TestCachedRepository_FetchOpHit(t *testing.T) {
func TestCachedRepository_FetchOpMiss(t *testing.T) {
// given
cacheMock := &cacheMock{}
cacheMock.GetFunc = func(ctx context.Context, k string) ([]byte, error) {
cacheMock.GetFunc = func(ctx context.Context, ns, k string) ([]byte, error) {
return nil, nil
}
cacheMock.PutFunc = func(ctx context.Context, k string, val []byte) error {
cacheMock.PutFunc = func(ctx context.Context, ns, k string, val []byte) error {
return nil
}

Expand Down
Loading

0 comments on commit 16783ba

Please sign in to comment.