Skip to content

Commit

Permalink
return struct instead of interface in hash and etcd implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
zbindenren committed Jun 19, 2020
1 parent d1a19d0 commit abd60bc
Show file tree
Hide file tree
Showing 7 changed files with 52 additions and 46 deletions.
5 changes: 4 additions & 1 deletion etcd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,12 @@ const (
DfltSeparator = '/'
)

// verify that *Backend implements store.Backend
var _ store.Backend = &Backend{}

// New configures a new etcd backend. At least Withclientv3 or WithEndoints option
// has to be used.
func New(opts ...Opt) (store.Backend, error) {
func New(opts ...Opt) (*Backend, error) {
e := Backend{
separator: DfltSeparator,
errHandler: func(err error) error { return err },
Expand Down
12 changes: 6 additions & 6 deletions etcd/etcdv3_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,37 +33,37 @@ func TestKeyFunctions(t *testing.T) {
t.Run("absolute key", func(t *testing.T) {
b, err := New(WithClient(cli), WithPrefix(prefix))
assert.NoError(t, err)
assert.Equal(t, b.(*Backend).AbsKey(relkey), abskey)
assert.Equal(t, b.AbsKey(relkey), abskey)
})

t.Run("absolute key from key with leading separator", func(t *testing.T) {
b, err := New(WithClient(cli), WithPrefix(prefix))
assert.NoError(t, err)
assert.NotEqual(t, b.(*Backend).AbsKey("/"+relkey), abskey)
assert.NotEqual(t, b.AbsKey("/"+relkey), abskey)
})

t.Run("relative key", func(t *testing.T) {
b, err := New(WithClient(cli), WithPrefix(prefix))
assert.NoError(t, err)
assert.Equal(t, b.(*Backend).RelKey(abskey), relkey)
assert.Equal(t, b.RelKey(abskey), relkey)
})

t.Run("join key", func(t *testing.T) {
b, err := New(WithClient(cli), WithPrefix(prefix))
assert.NoError(t, err)
assert.Equal(t, b.(*Backend).JoinKey(middle, suffix), relkey)
assert.Equal(t, b.JoinKey(middle, suffix), relkey)
})

t.Run("split key", func(t *testing.T) {
b, err := New(WithClient(cli), WithPrefix(prefix))
assert.NoError(t, err)
assert.Equal(t, b.(*Backend).SplitKey(relkey), []string{middle, suffix})
assert.Equal(t, b.SplitKey(relkey), []string{middle, suffix})
})

t.Run("split key", func(t *testing.T) {
b, err := New(WithClient(cli), WithPrefix(prefix))
assert.NoError(t, err)
assert.Equal(t, b.(*Backend).KeyLeaf(relkey), suffix)
assert.Equal(t, b.KeyLeaf(relkey), suffix)
})
}

Expand Down
15 changes: 9 additions & 6 deletions hash/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@ const (
DfltSeparator = '/'
)

// verify that *Backend implements store.Backend
var _ store.Backend = &Backend{}

// New returns a new store and the function to stop the cache janitor
func New(opts ...Opt) (store.Backend, error) {
h := &Hash{
func New(opts ...Opt) (*Backend, error) {
h := &Backend{
separator: DfltSeparator,
data: make(map[string]entry),
watchKey: make(map[string][]notifyCallbackFunc),
Expand All @@ -33,12 +36,12 @@ func New(opts ...Opt) (store.Backend, error) {
}

// Opt is a functional option to configure backend
type Opt func(*Hash) error
type Opt func(*Backend) error

// WithPrefix is an option to set the global prefix used for the backend
// WithPrefix("global") results in keys prefixed "global" + separator
func WithPrefix(p string) Opt {
return func(h *Hash) error {
return func(h *Backend) error {
h.prefix = p

if h.prefix != "" {
Expand All @@ -51,7 +54,7 @@ func WithPrefix(p string) Opt {

// WithSeparator is an option to overwrite the default separator for keys
func WithSeparator(s rune) Opt {
return func(h *Hash) error {
return func(h *Backend) error {
h.separator = s

if h.prefix != "" {
Expand All @@ -65,7 +68,7 @@ func WithSeparator(s rune) Opt {
// WithTTL is an option to add a time to live for hash entries
// the TTL will be the preset which can be overwritten for each Operation supporting WithTTL
func WithTTL(ttl time.Duration) Opt {
return func(h *Hash) error {
return func(h *Backend) error {
if ttl < 0 {
return errors.New("ttl cannot be < 0")
}
Expand Down
16 changes: 8 additions & 8 deletions hash/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,26 @@ func TestOptions(t *testing.T) {
)

t.Run("WithPrefix", func(t *testing.T) {
h := &Hash{}
h := &Backend{}
assert.NoError(t, WithPrefix(prefix)(h))
assert.EqualValues(t, h.prefix, prefix)
})

t.Run("WithSeparator", func(t *testing.T) {
s := '!'
h := &Hash{}
h := &Backend{}
assert.NoError(t, WithSeparator(s)(h))
assert.EqualValues(t, h.separator, s)
})

t.Run("WithTTL", func(t *testing.T) {
h := &Hash{}
h := &Backend{}
assert.NoError(t, WithTTL(1)(h))
assert.EqualValues(t, h.TTL(), 1)
})

t.Run("WithTTL invalid value", func(t *testing.T) {
h := &Hash{}
h := &Backend{}
assert.Error(t, WithTTL(-1)(h))
assert.EqualValues(t, h.TTL(), 0)
})
Expand All @@ -46,15 +46,15 @@ func TestOptions(t *testing.T) {
t.Run("WithPrefix and default separator", func(t *testing.T) {
h, err := New(WithPrefix(prefix))
assert.NoError(t, err)
assert.EqualValues(t, h.(*Hash).prefix, prefix)
assert.EqualValues(t, h.(*Hash).prefixReady2Use, fmt.Sprintf("%s/", prefix))
assert.EqualValues(t, h.prefix, prefix)
assert.EqualValues(t, h.prefixReady2Use, fmt.Sprintf("%s/", prefix))
})

t.Run("WithPrefix and WithSeparator", func(t *testing.T) {
s := '!'
h, err := New(WithPrefix(prefix), WithSeparator(s))
assert.NoError(t, err)
assert.EqualValues(t, h.(*Hash).separator, s)
assert.EqualValues(t, h.(*Hash).prefixReady2Use, fmt.Sprintf("%s%c", prefix, s))
assert.EqualValues(t, h.separator, s)
assert.EqualValues(t, h.prefixReady2Use, fmt.Sprintf("%s%c", prefix, s))
})
}
34 changes: 17 additions & 17 deletions hash/hash.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ type entry struct {
TTL time.Duration
}

// Hash table backend
type Hash struct {
// Backend table backend
type Backend struct {
sync.RWMutex
prefix string
prefixReady2Use string
Expand All @@ -36,7 +36,7 @@ type Hash struct {
// AbsKey("a/b") with prefix "root" and separator '/' returns "root/a/b"
// AbsKey does not validate the given key.
// Given a faulty relative key returns a faulty absolute key.
func (h *Hash) AbsKey(k string) string {
func (h *Backend) AbsKey(k string) string {
if h.prefix == "" {
return k
}
Expand All @@ -48,7 +48,7 @@ func (h *Hash) AbsKey(k string) string {
// RelKey("root/a/b") with prefix "root" and separator '/' returns "a/b"
// RelKey does not validate the given key.
// Given a faulty absolute key returns a faulty relativ key.
func (h *Hash) RelKey(k string) string {
func (h *Backend) RelKey(k string) string {
if h.prefix == "" {
return k
}
Expand All @@ -58,18 +58,18 @@ func (h *Hash) RelKey(k string) string {

// JoinKey returns a formatted key
// it joins the given elements with the correct delimiter
func (h *Hash) JoinKey(args ...string) string {
func (h *Backend) JoinKey(args ...string) string {
return strings.Join(args, string(h.separator))
}

// SplitKey returns the key elements
// it splits the given key in its elements with the correct delimiter
func (h *Hash) SplitKey(key string) []string {
func (h *Backend) SplitKey(key string) []string {
return strings.Split(strings.Trim(key, string(h.separator)), string(h.separator))
}

// KeyLeaf returns the leave (last) element of a key
func (h *Hash) KeyLeaf(key string) string {
func (h *Backend) KeyLeaf(key string) string {
elems := h.SplitKey(key)
if len(elems) > 0 {
return elems[len(elems)-1]
Expand All @@ -79,12 +79,12 @@ func (h *Hash) KeyLeaf(key string) string {
}

// TTL returns the configured TTL
func (h *Hash) TTL() time.Duration {
func (h *Backend) TTL() time.Duration {
return h.ttl
}

// register a watch key
func (h *Hash) register(key string, prefix bool, f notifyCallbackFunc) {
func (h *Backend) register(key string, prefix bool, f notifyCallbackFunc) {
h.Lock()
if prefix {
if _, ok := h.watchKeyPrefix[key]; !ok {
Expand All @@ -102,7 +102,7 @@ func (h *Hash) register(key string, prefix bool, f notifyCallbackFunc) {
}

// notify all registrants
func (h *Hash) notify(key string, msg changeNotification) {
func (h *Backend) notify(key string, msg changeNotification) {
for _, f := range h.watchKey[key] {
f(msg)
}
Expand All @@ -117,7 +117,7 @@ func (h *Hash) notify(key string, msg changeNotification) {
}

// exists checks if an entry is expired
func (h *Hash) exists(e entry) bool {
func (h *Backend) exists(e entry) bool {
// entry has no TTL or an unexpired TTL
if e.TTL == 0 || e.Updated.Add(e.TTL).After(time.Now()) {
return true
Expand All @@ -129,7 +129,7 @@ func (h *Hash) exists(e entry) bool {
}

// keys returns all keys with prefix
func (h *Hash) keys(prefix string) []string {
func (h *Backend) keys(prefix string) []string {
keys := []string{}

h.RLock()
Expand All @@ -147,7 +147,7 @@ func (h *Hash) keys(prefix string) []string {
// Get returns a list of store entries
// WithPrefix, WithHandler are supported
// WithContext will be ignored
func (h *Hash) Get(key string, ops ...store.GetOption) ([]store.Entry, error) {
func (h *Backend) Get(key string, ops ...store.GetOption) ([]store.Entry, error) {
opts := &store.GetOptions{}

for _, op := range ops {
Expand Down Expand Up @@ -213,7 +213,7 @@ func (h *Hash) Get(key string, ops ...store.GetOption) ([]store.Entry, error) {
// Put insert and/or update an entry in the store
// WithTTL, WithInsert are supported
// WithContext will be ignored
func (h *Hash) Put(e *store.Entry, ops ...store.PutOption) (bool, error) {
func (h *Backend) Put(e *store.Entry, ops ...store.PutOption) (bool, error) {
opts := &store.PutOptions{}

for _, op := range ops {
Expand Down Expand Up @@ -260,7 +260,7 @@ func (h *Hash) Put(e *store.Entry, ops ...store.PutOption) (bool, error) {
// Del an entry from the store
// WithPrefix is supported
// WithContext will be ignored
func (h *Hash) Del(key string, ops ...store.DelOption) (int64, error) {
func (h *Backend) Del(key string, ops ...store.DelOption) (int64, error) {
var count int64

opts := &store.DelOptions{}
Expand Down Expand Up @@ -288,7 +288,7 @@ func (h *Hash) Del(key string, ops ...store.DelOption) (int64, error) {
return count, nil
}

func (h *Hash) del(e store.Entry) {
func (h *Backend) del(e store.Entry) {
absKey := h.AbsKey(e.Key)

h.Lock()
Expand All @@ -302,6 +302,6 @@ func (h *Hash) del(e store.Entry) {

// Close exists to implement the store interface, there
// is no connection to close.
func (h *Hash) Close() error {
func (h *Backend) Close() error {
return nil
}
14 changes: 7 additions & 7 deletions hash/hash_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,42 +26,42 @@ func TestKeyFunctions(t *testing.T) {
t.Run("absolute key", func(t *testing.T) {
h, err := hash.New(hash.WithPrefix(prefix))
assert.NoError(t, err)
assert.Equal(t, h.(*hash.Hash).AbsKey(relkey), abskey)
assert.Equal(t, h.AbsKey(relkey), abskey)
assert.NoError(t, h.Close())
})

t.Run("absolute key from key with leading separator", func(t *testing.T) {
h, err := hash.New(hash.WithPrefix(prefix))
assert.NoError(t, err)
assert.NotEqual(t, h.(*hash.Hash).AbsKey("/"+relkey), abskey)
assert.NotEqual(t, h.AbsKey("/"+relkey), abskey)
assert.NoError(t, h.Close())
})

t.Run("relative key", func(t *testing.T) {
h, err := hash.New(hash.WithPrefix(prefix))
assert.NoError(t, err)
assert.Equal(t, h.(*hash.Hash).RelKey(abskey), relkey)
assert.Equal(t, h.RelKey(abskey), relkey)
assert.NoError(t, h.Close())
})

t.Run("join key", func(t *testing.T) {
h, err := hash.New(hash.WithPrefix(prefix))
assert.NoError(t, err)
assert.Equal(t, h.(*hash.Hash).JoinKey(middle, suffix), relkey)
assert.Equal(t, h.JoinKey(middle, suffix), relkey)
assert.NoError(t, h.Close())
})

t.Run("split key", func(t *testing.T) {
h, err := hash.New(hash.WithPrefix(prefix))
assert.NoError(t, err)
assert.Equal(t, h.(*hash.Hash).SplitKey(relkey), []string{middle, suffix})
assert.Equal(t, h.SplitKey(relkey), []string{middle, suffix})
assert.NoError(t, h.Close())
})

t.Run("split key", func(t *testing.T) {
h, err := hash.New(hash.WithPrefix(prefix))
assert.NoError(t, err)
assert.Equal(t, h.(*hash.Hash).KeyLeaf(relkey), suffix)
assert.Equal(t, h.KeyLeaf(relkey), suffix)
assert.NoError(t, h.Close())
})
}
Expand Down Expand Up @@ -391,7 +391,7 @@ func TestMarshal(t *testing.T) {
}
}

func TestHashWithTTL(t *testing.T) {
func TestBackendWithTTL(t *testing.T) {
for _, p := range []string{"", "root"} {
opts := []hash.Opt{hash.WithTTL(10 * time.Millisecond)}
if p != "" {
Expand Down
2 changes: 1 addition & 1 deletion hash/watch.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (

// Watch a key or prefix
// WithPrefix, WithContext, WithNotifyCreated, WithErrorHandler are supported
func (h *Hash) Watch(key string, w store.Watcher, ops ...store.WatchOption) error {
func (h *Backend) Watch(key string, w store.Watcher, ops ...store.WatchOption) error {
opts := &store.WatchOptions{}

for _, op := range ops {
Expand Down

0 comments on commit abd60bc

Please sign in to comment.