move memory implementations to core micro repo
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
This commit is contained in:
199
store/memory.go
Normal file
199
store/memory.go
Normal file
@@ -0,0 +1,199 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/patrickmn/go-cache"
|
||||
)
|
||||
|
||||
// NewStore returns a memory store
|
||||
func NewStore(opts ...Option) Store {
|
||||
return &memoryStore{
|
||||
opts: NewOptions(opts...),
|
||||
store: cache.New(cache.NoExpiration, 5*time.Minute),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *memoryStore) Connect(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *memoryStore) Disconnect(ctx context.Context) error {
|
||||
m.store.Flush()
|
||||
return nil
|
||||
}
|
||||
|
||||
type memoryStore struct {
|
||||
opts Options
|
||||
store *cache.Cache
|
||||
}
|
||||
|
||||
func (m *memoryStore) key(prefix, key string) string {
|
||||
return filepath.Join(prefix, key)
|
||||
}
|
||||
|
||||
func (m *memoryStore) prefix(database, table string) string {
|
||||
if len(database) == 0 {
|
||||
database = m.opts.Database
|
||||
}
|
||||
if len(table) == 0 {
|
||||
table = m.opts.Table
|
||||
}
|
||||
return filepath.Join(database, table)
|
||||
}
|
||||
|
||||
func (m *memoryStore) exists(prefix, key string) error {
|
||||
key = m.key(prefix, key)
|
||||
|
||||
_, found := m.store.Get(key)
|
||||
if !found {
|
||||
return ErrNotFound
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *memoryStore) get(prefix, key string, val interface{}) error {
|
||||
key = m.key(prefix, key)
|
||||
|
||||
r, found := m.store.Get(key)
|
||||
if !found {
|
||||
return ErrNotFound
|
||||
}
|
||||
|
||||
buf, ok := r.([]byte)
|
||||
if !ok {
|
||||
return ErrNotFound
|
||||
}
|
||||
|
||||
if err := m.opts.Codec.Unmarshal(buf, val); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *memoryStore) delete(prefix, key string) {
|
||||
key = m.key(prefix, key)
|
||||
m.store.Delete(key)
|
||||
}
|
||||
|
||||
func (m *memoryStore) list(prefix string, limit, offset uint) []string {
|
||||
allItems := m.store.Items()
|
||||
allKeys := make([]string, len(allItems))
|
||||
i := 0
|
||||
|
||||
for k := range allItems {
|
||||
if !strings.HasPrefix(k, prefix+"/") {
|
||||
continue
|
||||
}
|
||||
allKeys[i] = strings.TrimPrefix(k, prefix+"/")
|
||||
i++
|
||||
}
|
||||
|
||||
if limit != 0 || offset != 0 {
|
||||
sort.Slice(allKeys, func(i, j int) bool { return allKeys[i] < allKeys[j] })
|
||||
sort.Slice(allKeys, func(i, j int) bool { return allKeys[i] < allKeys[j] })
|
||||
end := len(allKeys)
|
||||
if limit > 0 {
|
||||
calcLimit := int(offset + limit)
|
||||
if calcLimit < end {
|
||||
end = calcLimit
|
||||
}
|
||||
}
|
||||
|
||||
if int(offset) >= end {
|
||||
return nil
|
||||
}
|
||||
return allKeys[offset:end]
|
||||
}
|
||||
|
||||
return allKeys
|
||||
}
|
||||
|
||||
func (m *memoryStore) Init(opts ...Option) error {
|
||||
for _, o := range opts {
|
||||
o(&m.opts)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *memoryStore) String() string {
|
||||
return "memory"
|
||||
}
|
||||
|
||||
func (m *memoryStore) Name() string {
|
||||
return m.opts.Name
|
||||
}
|
||||
|
||||
func (m *memoryStore) Exists(ctx context.Context, key string, opts ...ExistsOption) error {
|
||||
prefix := m.prefix(m.opts.Database, m.opts.Table)
|
||||
return m.exists(prefix, key)
|
||||
}
|
||||
|
||||
func (m *memoryStore) Read(ctx context.Context, key string, val interface{}, opts ...ReadOption) error {
|
||||
readOpts := NewReadOptions(opts...)
|
||||
prefix := m.prefix(readOpts.Database, readOpts.Table)
|
||||
return m.get(prefix, key, val)
|
||||
}
|
||||
|
||||
func (m *memoryStore) Write(ctx context.Context, key string, val interface{}, opts ...WriteOption) error {
|
||||
writeOpts := NewWriteOptions(opts...)
|
||||
|
||||
prefix := m.prefix(writeOpts.Database, writeOpts.Table)
|
||||
|
||||
key = m.key(prefix, key)
|
||||
|
||||
buf, err := m.opts.Codec.Marshal(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.store.Set(key, buf, writeOpts.TTL)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *memoryStore) Delete(ctx context.Context, key string, opts ...DeleteOption) error {
|
||||
deleteOptions := NewDeleteOptions(opts...)
|
||||
|
||||
prefix := m.prefix(deleteOptions.Database, deleteOptions.Table)
|
||||
m.delete(prefix, key)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *memoryStore) Options() Options {
|
||||
return m.opts
|
||||
}
|
||||
|
||||
func (m *memoryStore) List(ctx context.Context, opts ...ListOption) ([]string, error) {
|
||||
listOptions := NewListOptions(opts...)
|
||||
|
||||
prefix := m.prefix(listOptions.Database, listOptions.Table)
|
||||
keys := m.list(prefix, listOptions.Limit, listOptions.Offset)
|
||||
|
||||
if len(listOptions.Prefix) > 0 {
|
||||
var prefixKeys []string
|
||||
for _, k := range keys {
|
||||
if strings.HasPrefix(k, listOptions.Prefix) {
|
||||
prefixKeys = append(prefixKeys, k)
|
||||
}
|
||||
}
|
||||
keys = prefixKeys
|
||||
}
|
||||
|
||||
if len(listOptions.Suffix) > 0 {
|
||||
var suffixKeys []string
|
||||
for _, k := range keys {
|
||||
if strings.HasSuffix(k, listOptions.Suffix) {
|
||||
suffixKeys = append(suffixKeys, k)
|
||||
}
|
||||
}
|
||||
keys = suffixKeys
|
||||
}
|
||||
|
||||
return keys, nil
|
||||
}
|
67
store/memory_test.go
Normal file
67
store/memory_test.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package store_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/unistack-org/micro/v3/store"
|
||||
)
|
||||
|
||||
func TestMemoryReInit(t *testing.T) {
|
||||
s := store.NewStore(store.Table("aaa"))
|
||||
s.Init(store.Table(""))
|
||||
if len(s.Options().Table) > 0 {
|
||||
t.Error("Init didn't reinitialise the store")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMemoryBasic(t *testing.T) {
|
||||
s := store.NewStore()
|
||||
s.Init()
|
||||
basictest(s, t)
|
||||
}
|
||||
|
||||
func TestMemoryPrefix(t *testing.T) {
|
||||
s := store.NewStore()
|
||||
s.Init(store.Table("some-prefix"))
|
||||
basictest(s, t)
|
||||
}
|
||||
|
||||
func TestMemoryNamespace(t *testing.T) {
|
||||
s := store.NewStore()
|
||||
s.Init(store.Database("some-namespace"))
|
||||
basictest(s, t)
|
||||
}
|
||||
|
||||
func TestMemoryNamespacePrefix(t *testing.T) {
|
||||
s := store.NewStore()
|
||||
s.Init(store.Table("some-prefix"), store.Database("some-namespace"))
|
||||
basictest(s, t)
|
||||
}
|
||||
|
||||
func basictest(s store.Store, t *testing.T) {
|
||||
ctx := context.Background()
|
||||
if len(os.Getenv("IN_TRAVIS_CI")) == 0 {
|
||||
t.Logf("Testing store %s, with options %#+v\n", s.String(), s.Options())
|
||||
}
|
||||
// Read and Write an expiring Record
|
||||
if err := s.Write(ctx, "Hello", "World", store.WriteTTL(time.Millisecond*100)); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
var val []byte
|
||||
if err := s.Read(ctx, "Hello", &val); err != nil {
|
||||
t.Error(err)
|
||||
} else {
|
||||
if string(val) != "World" {
|
||||
t.Errorf("Expected %s, got %s", "World", val)
|
||||
}
|
||||
}
|
||||
time.Sleep(time.Millisecond * 200)
|
||||
if err := s.Read(ctx, "Hello", &val); err != store.ErrNotFound {
|
||||
t.Errorf("Expected %# v, got %# v", store.ErrNotFound, err)
|
||||
}
|
||||
|
||||
s.Disconnect(ctx) // reset the store
|
||||
}
|
@@ -1,69 +0,0 @@
|
||||
package store
|
||||
|
||||
import "context"
|
||||
|
||||
type noopStore struct {
|
||||
opts Options
|
||||
}
|
||||
|
||||
func NewStore(opts ...Option) Store {
|
||||
return &noopStore{opts: NewOptions(opts...)}
|
||||
}
|
||||
|
||||
// Init initialize store
|
||||
func (n *noopStore) Init(opts ...Option) error {
|
||||
for _, o := range opts {
|
||||
o(&n.opts)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Options returns Options struct
|
||||
func (n *noopStore) Options() Options {
|
||||
return n.opts
|
||||
}
|
||||
|
||||
// Name
|
||||
func (n *noopStore) Name() string {
|
||||
return n.opts.Name
|
||||
}
|
||||
|
||||
// String returns string representation
|
||||
func (n *noopStore) String() string {
|
||||
return "noop"
|
||||
}
|
||||
|
||||
// Read reads store value by key
|
||||
func (n *noopStore) Exists(ctx context.Context, key string, opts ...ExistsOption) error {
|
||||
return ErrNotFound
|
||||
}
|
||||
|
||||
// Read reads store value by key
|
||||
func (n *noopStore) Read(ctx context.Context, key string, val interface{}, opts ...ReadOption) error {
|
||||
return ErrNotFound
|
||||
}
|
||||
|
||||
// Write writes store record
|
||||
func (n *noopStore) Write(ctx context.Context, key string, val interface{}, opts ...WriteOption) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete removes store value by key
|
||||
func (n *noopStore) Delete(ctx context.Context, key string, opts ...DeleteOption) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// List lists store
|
||||
func (n *noopStore) List(ctx context.Context, opts ...ListOption) ([]string, error) {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
// Connect connects to store
|
||||
func (n *noopStore) Connect(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Disconnect disconnects from store
|
||||
func (n *noopStore) Disconnect(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
Reference in New Issue
Block a user