Change the store interface to remove variadic args (#1095)

This commit is contained in:
Asim Aslam 2020-01-08 22:23:14 +00:00 committed by GitHub
parent 78aed5beed
commit a90a74c9e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 356 additions and 378 deletions

View File

@ -106,15 +106,19 @@ func (w *workersKV) Init(opts ...store.Option) error {
return nil return nil
} }
// In the cloudflare workers KV implemention, List() doesn't guarantee func (w *workersKV) list(prefix string) ([]string, error) {
// anything as the workers API is eventually consistent.
func (w *workersKV) List() ([]*store.Record, error) {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel() defer cancel()
path := fmt.Sprintf("accounts/%s/storage/kv/namespaces/%s/keys", w.account, w.namespace) path := fmt.Sprintf("accounts/%s/storage/kv/namespaces/%s/keys", w.account, w.namespace)
response, _, _, err := w.request(ctx, http.MethodGet, path, nil, make(http.Header)) body := make(map[string]string)
if len(prefix) > 0 {
body["prefix"] = prefix
}
response, _, _, err := w.request(ctx, http.MethodGet, path, body, make(http.Header))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -138,13 +142,51 @@ func (w *workersKV) List() ([]*store.Record, error) {
keys = append(keys, r.Name) keys = append(keys, r.Name)
} }
return w.Read(keys...) return keys, nil
} }
func (w *workersKV) Read(keys ...string) ([]*store.Record, error) { // In the cloudflare workers KV implemention, List() doesn't guarantee
// anything as the workers API is eventually consistent.
func (w *workersKV) List() ([]*store.Record, error) {
keys, err := w.list("")
if err != nil {
return nil, err
}
var gerr error
var records []*store.Record
for _, key := range keys {
r, err := w.Read(key)
if err != nil {
gerr = err
continue
}
records = append(records, r...)
}
return records, gerr
}
func (w *workersKV) Read(key string, opts ...store.ReadOption) ([]*store.Record, error) {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel() defer cancel()
var options store.ReadOptions
for _, o := range opts {
o(&options)
}
keys := []string{key}
if options.Prefix {
k, err := w.list(key)
if err != nil {
return nil, err
}
keys = k
}
//nolint:prealloc //nolint:prealloc
var records []*store.Record var records []*store.Record
@ -174,11 +216,10 @@ func (w *workersKV) Read(keys ...string) ([]*store.Record, error) {
return records, nil return records, nil
} }
func (w *workersKV) Write(records ...*store.Record) error { func (w *workersKV) Write(r *store.Record) error {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel() defer cancel()
for _, r := range records {
path := fmt.Sprintf("accounts/%s/storage/kv/namespaces/%s/values/%s", w.account, w.namespace, url.PathEscape(r.Key)) path := fmt.Sprintf("accounts/%s/storage/kv/namespaces/%s/values/%s", w.account, w.namespace, url.PathEscape(r.Key))
if r.Expiry != 0 { if r.Expiry != 0 {
// Minimum cloudflare TTL is 60 Seconds // Minimum cloudflare TTL is 60 Seconds
@ -205,17 +246,15 @@ func (w *workersKV) Write(records ...*store.Record) error {
} }
return errors.New(messages) return errors.New(messages)
} }
}
return nil return nil
} }
func (w *workersKV) Delete(keys ...string) error { func (w *workersKV) Delete(key string) error {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel() defer cancel()
for _, k := range keys { path := fmt.Sprintf("accounts/%s/storage/kv/namespaces/%s/values/%s", w.account, w.namespace, url.PathEscape(key))
path := fmt.Sprintf("accounts/%s/storage/kv/namespaces/%s/values/%s", w.account, w.namespace, url.PathEscape(k))
resp, _, _, err := w.request(ctx, http.MethodDelete, path, nil, make(http.Header)) resp, _, _, err := w.request(ctx, http.MethodDelete, path, nil, make(http.Header))
if err != nil { if err != nil {
return err return err
@ -233,7 +272,6 @@ func (w *workersKV) Delete(keys ...string) error {
} }
return errors.New(messages) return errors.New(messages)
} }
}
return nil return nil
} }

View File

@ -33,17 +33,18 @@ func TestCloudflare(t *testing.T) {
t.Log("Listed " + strconv.Itoa(len(records)) + " records") t.Log("Listed " + strconv.Itoa(len(records)) + " records")
} }
err = wkv.Write( err = wkv.Write(&store.Record{
&store.Record{
Key: randomK, Key: randomK,
Value: []byte(randomV), Value: []byte(randomV),
}, })
&store.Record{ if err != nil {
t.Errorf("Write: %s", err.Error())
}
err = wkv.Write(&store.Record{
Key: "expirationtest", Key: "expirationtest",
Value: []byte("This message will self destruct"), Value: []byte("This message will self destruct"),
Expiry: 75 * time.Second, Expiry: 75 * time.Second,
}, })
)
if err != nil { if err != nil {
t.Errorf("Write: %s", err.Error()) t.Errorf("Write: %s", err.Error())
} }

View File

@ -80,14 +80,22 @@ func (s *sqlStore) List() ([]*store.Record, error) {
} }
// Read all records with keys // Read all records with keys
func (s *sqlStore) Read(keys ...string) ([]*store.Record, error) { func (s *sqlStore) Read(key string, opts ...store.ReadOption) ([]*store.Record, error) {
var options store.ReadOptions
for _, o := range opts {
o(&options)
}
// TODO: make use of options.Prefix using WHERE key LIKE = ?
q, err := s.db.Prepare(fmt.Sprintf("SELECT key, value, expiry FROM %s.%s WHERE key = $1;", s.database, s.table)) q, err := s.db.Prepare(fmt.Sprintf("SELECT key, value, expiry FROM %s.%s WHERE key = $1;", s.database, s.table))
if err != nil { if err != nil {
return nil, err return nil, err
} }
var records []*store.Record var records []*store.Record
var timehelper pq.NullTime var timehelper pq.NullTime
for _, key := range keys {
row := q.QueryRow(key) row := q.QueryRow(key)
record := &store.Record{} record := &store.Record{}
if err := row.Scan(&record.Key, &record.Value, &timehelper); err != nil { if err := row.Scan(&record.Key, &record.Value, &timehelper); err != nil {
@ -107,12 +115,12 @@ func (s *sqlStore) Read(keys ...string) ([]*store.Record, error) {
} else { } else {
records = append(records, record) records = append(records, record)
} }
}
return records, nil return records, nil
} }
// Write records // Write records
func (s *sqlStore) Write(rec ...*store.Record) error { func (s *sqlStore) Write(r *store.Record) error {
q, err := s.db.Prepare(fmt.Sprintf(`INSERT INTO %s.%s(key, value, expiry) q, err := s.db.Prepare(fmt.Sprintf(`INSERT INTO %s.%s(key, value, expiry)
VALUES ($1, $2::bytea, $3) VALUES ($1, $2::bytea, $3)
ON CONFLICT (key) ON CONFLICT (key)
@ -121,28 +129,27 @@ func (s *sqlStore) Write(rec ...*store.Record) error {
if err != nil { if err != nil {
return err return err
} }
for _, r := range rec {
var err error
if r.Expiry != 0 { if r.Expiry != 0 {
_, err = q.Exec(r.Key, r.Value, time.Now().Add(r.Expiry)) _, err = q.Exec(r.Key, r.Value, time.Now().Add(r.Expiry))
} else { } else {
_, err = q.Exec(r.Key, r.Value, nil) _, err = q.Exec(r.Key, r.Value, nil)
} }
if err != nil { if err != nil {
return errors.Wrap(err, "Couldn't insert record "+r.Key) return errors.Wrap(err, "Couldn't insert record "+r.Key)
} }
}
return nil return nil
} }
// Delete records with keys // Delete records with keys
func (s *sqlStore) Delete(keys ...string) error { func (s *sqlStore) Delete(key string) error {
q, err := s.db.Prepare(fmt.Sprintf("DELETE FROM %s.%s WHERE key = $1;", s.database, s.table)) q, err := s.db.Prepare(fmt.Sprintf("DELETE FROM %s.%s WHERE key = $1;", s.database, s.table))
if err != nil { if err != nil {
return err return err
} }
for _, key := range keys {
result, err := q.Exec(key) result, err := q.Exec(key)
if err != nil { if err != nil {
return err return err
@ -151,7 +158,7 @@ func (s *sqlStore) Delete(keys ...string) error {
if err != nil { if err != nil {
return err return err
} }
}
return nil return nil
} }

View File

@ -44,10 +44,20 @@ func TestSQL(t *testing.T) {
Key: "test", Key: "test",
Value: []byte("foo"), Value: []byte("foo"),
}, },
)
if err != nil {
t.Error(err)
}
err = sqlStore.Write(
&store.Record{ &store.Record{
Key: "bar", Key: "bar",
Value: []byte("baz"), Value: []byte("baz"),
}, },
)
if err != nil {
t.Error(err)
}
err = sqlStore.Write(
&store.Record{ &store.Record{
Key: "qux", Key: "qux",
Value: []byte("aasad"), Value: []byte("aasad"),

View File

@ -6,7 +6,6 @@ import (
"log" "log"
client "github.com/coreos/etcd/clientv3" client "github.com/coreos/etcd/clientv3"
"github.com/coreos/etcd/mvcc/mvccpb"
"github.com/micro/go-micro/store" "github.com/micro/go-micro/store"
) )
@ -22,12 +21,20 @@ func (e *ekv) Init(opts ...store.Option) error {
return nil return nil
} }
func (e *ekv) Read(keys ...string) ([]*store.Record, error) { func (e *ekv) Read(key string, opts ...store.ReadOption) ([]*store.Record, error) {
//nolint:prealloc var options store.ReadOptions
var values []*mvccpb.KeyValue for _, o := range opts {
o(&options)
}
for _, key := range keys { var etcdOpts []client.OpOption
keyval, err := e.kv.Get(context.Background(), key)
// set options prefix
if options.Prefix {
etcdOpts = append(etcdOpts, client.WithPrefix())
}
keyval, err := e.kv.Get(context.Background(), key, etcdOpts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -36,12 +43,9 @@ func (e *ekv) Read(keys ...string) ([]*store.Record, error) {
return nil, store.ErrNotFound return nil, store.ErrNotFound
} }
values = append(values, keyval.Kvs...) records := make([]*store.Record, 0, len(keyval.Kvs))
}
records := make([]*store.Record, 0, len(values)) for _, kv := range keyval.Kvs {
for _, kv := range values {
records = append(records, &store.Record{ records = append(records, &store.Record{
Key: string(kv.Key), Key: string(kv.Key),
Value: kv.Value, Value: kv.Value,
@ -52,27 +56,15 @@ func (e *ekv) Read(keys ...string) ([]*store.Record, error) {
return records, nil return records, nil
} }
func (e *ekv) Delete(keys ...string) error { func (e *ekv) Delete(key string) error {
var gerr error
for _, key := range keys {
_, err := e.kv.Delete(context.Background(), key) _, err := e.kv.Delete(context.Background(), key)
if err != nil { return err
gerr = err
}
}
return gerr
} }
func (e *ekv) Write(records ...*store.Record) error { func (e *ekv) Write(record *store.Record) error {
var gerr error
for _, record := range records {
// TODO create lease to expire keys // TODO create lease to expire keys
_, err := e.kv.Put(context.Background(), record.Key, string(record.Value)) _, err := e.kv.Put(context.Background(), record.Key, string(record.Value))
if err != nil { return err
gerr = err
}
}
return gerr
} }
func (e *ekv) List() ([]*store.Record, error) { func (e *ekv) List() ([]*store.Record, error) {

View File

@ -2,6 +2,7 @@
package memory package memory
import ( import (
"strings"
"sync" "sync"
"time" "time"
@ -55,19 +56,37 @@ func (m *memoryStore) List() ([]*store.Record, error) {
return values, nil return values, nil
} }
func (m *memoryStore) Read(keys ...string) ([]*store.Record, error) { func (m *memoryStore) Read(key string, opts ...store.ReadOption) ([]*store.Record, error) {
m.RLock() m.RLock()
defer m.RUnlock() defer m.RUnlock()
//nolint:prealloc var options store.ReadOptions
var records []*store.Record
for _, key := range keys { for _, o := range opts {
o(&options)
}
var vals []*memoryRecord
if !options.Prefix {
v, ok := m.values[key] v, ok := m.values[key]
if !ok { if !ok {
return nil, store.ErrNotFound return nil, store.ErrNotFound
} }
vals = []*memoryRecord{v}
} else {
for _, v := range m.values {
if !strings.HasPrefix(v.r.Key, key) {
continue
}
vals = append(vals, v)
}
}
//nolint:prealloc
var records []*store.Record
for _, v := range vals {
// get expiry // get expiry
d := v.r.Expiry d := v.r.Expiry
t := time.Since(v.c) t := time.Since(v.c)
@ -88,29 +107,25 @@ func (m *memoryStore) Read(keys ...string) ([]*store.Record, error) {
return records, nil return records, nil
} }
func (m *memoryStore) Write(records ...*store.Record) error { func (m *memoryStore) Write(r *store.Record) error {
m.Lock() m.Lock()
defer m.Unlock() defer m.Unlock()
for _, r := range records {
// set the record // set the record
m.values[r.Key] = &memoryRecord{ m.values[r.Key] = &memoryRecord{
r: r, r: r,
c: time.Now(), c: time.Now(),
} }
}
return nil return nil
} }
func (m *memoryStore) Delete(keys ...string) error { func (m *memoryStore) Delete(key string) error {
m.Lock() m.Lock()
defer m.Unlock() defer m.Unlock()
for _, key := range keys {
// delete the value // delete the value
delete(m.values, key) delete(m.values, key)
}
return nil return nil
} }

View File

@ -1,107 +0,0 @@
// Code generated by mockery v1.0.0. DO NOT EDIT.
package mock
import mock "github.com/stretchr/testify/mock"
import store "github.com/micro/go-micro/store"
// Store is an autogenerated mock type for the Store type
type Store struct {
mock.Mock
}
func (_m *Store) Init(...store.Option) error {
return nil
}
// Delete provides a mock function with given fields: key
func (_m *Store) Delete(key ...string) error {
_va := make([]interface{}, len(key))
for _i := range key {
_va[_i] = key[_i]
}
var _ca []interface{}
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 error
if rf, ok := ret.Get(0).(func(...string) error); ok {
r0 = rf(key...)
} else {
r0 = ret.Error(0)
}
return r0
}
// List provides a mock function with given fields:
func (_m *Store) List() ([]*store.Record, error) {
ret := _m.Called()
var r0 []*store.Record
if rf, ok := ret.Get(0).(func() []*store.Record); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*store.Record)
}
}
var r1 error
if rf, ok := ret.Get(1).(func() error); ok {
r1 = rf()
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Read provides a mock function with given fields: key
func (_m *Store) Read(key ...string) ([]*store.Record, error) {
_va := make([]interface{}, len(key))
for _i := range key {
_va[_i] = key[_i]
}
var _ca []interface{}
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 []*store.Record
if rf, ok := ret.Get(0).(func(...string) []*store.Record); ok {
r0 = rf(key...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*store.Record)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(...string) error); ok {
r1 = rf(key...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Write provides a mock function with given fields: rec
func (_m *Store) Write(rec ...*store.Record) error {
_va := make([]interface{}, len(rec))
for _i := range rec {
_va[_i] = rec[_i]
}
var _ca []interface{}
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 error
if rf, ok := ret.Get(0).(func(...*store.Record) error); ok {
r0 = rf(rec...)
} else {
r0 = ret.Error(0)
}
return r0
}

View File

@ -38,3 +38,10 @@ func Namespace(ns string) Option {
o.Namespace = ns o.Namespace = ns
} }
} }
// ReadPrefix uses the key as a prefix
func ReadPrefix() ReadOption {
return func(o *ReadOptions) {
o.Prefix = true
}
}

View File

@ -78,8 +78,48 @@ func (m *Record) GetExpiry() int64 {
return 0 return 0
} }
type ReadOptions struct {
Prefix bool `protobuf:"varint,1,opt,name=prefix,proto3" json:"prefix,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ReadOptions) Reset() { *m = ReadOptions{} }
func (m *ReadOptions) String() string { return proto.CompactTextString(m) }
func (*ReadOptions) ProtoMessage() {}
func (*ReadOptions) Descriptor() ([]byte, []int) {
return fileDescriptor_f84ccc98e143ed3e, []int{1}
}
func (m *ReadOptions) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ReadOptions.Unmarshal(m, b)
}
func (m *ReadOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ReadOptions.Marshal(b, m, deterministic)
}
func (m *ReadOptions) XXX_Merge(src proto.Message) {
xxx_messageInfo_ReadOptions.Merge(m, src)
}
func (m *ReadOptions) XXX_Size() int {
return xxx_messageInfo_ReadOptions.Size(m)
}
func (m *ReadOptions) XXX_DiscardUnknown() {
xxx_messageInfo_ReadOptions.DiscardUnknown(m)
}
var xxx_messageInfo_ReadOptions proto.InternalMessageInfo
func (m *ReadOptions) GetPrefix() bool {
if m != nil {
return m.Prefix
}
return false
}
type ReadRequest struct { type ReadRequest struct {
Keys []string `protobuf:"bytes,1,rep,name=keys,proto3" json:"keys,omitempty"` Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
Options *ReadOptions `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"` XXX_sizecache int32 `json:"-"`
@ -89,7 +129,7 @@ func (m *ReadRequest) Reset() { *m = ReadRequest{} }
func (m *ReadRequest) String() string { return proto.CompactTextString(m) } func (m *ReadRequest) String() string { return proto.CompactTextString(m) }
func (*ReadRequest) ProtoMessage() {} func (*ReadRequest) ProtoMessage() {}
func (*ReadRequest) Descriptor() ([]byte, []int) { func (*ReadRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_f84ccc98e143ed3e, []int{1} return fileDescriptor_f84ccc98e143ed3e, []int{2}
} }
func (m *ReadRequest) XXX_Unmarshal(b []byte) error { func (m *ReadRequest) XXX_Unmarshal(b []byte) error {
@ -110,9 +150,16 @@ func (m *ReadRequest) XXX_DiscardUnknown() {
var xxx_messageInfo_ReadRequest proto.InternalMessageInfo var xxx_messageInfo_ReadRequest proto.InternalMessageInfo
func (m *ReadRequest) GetKeys() []string { func (m *ReadRequest) GetKey() string {
if m != nil { if m != nil {
return m.Keys return m.Key
}
return ""
}
func (m *ReadRequest) GetOptions() *ReadOptions {
if m != nil {
return m.Options
} }
return nil return nil
} }
@ -128,7 +175,7 @@ func (m *ReadResponse) Reset() { *m = ReadResponse{} }
func (m *ReadResponse) String() string { return proto.CompactTextString(m) } func (m *ReadResponse) String() string { return proto.CompactTextString(m) }
func (*ReadResponse) ProtoMessage() {} func (*ReadResponse) ProtoMessage() {}
func (*ReadResponse) Descriptor() ([]byte, []int) { func (*ReadResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_f84ccc98e143ed3e, []int{2} return fileDescriptor_f84ccc98e143ed3e, []int{3}
} }
func (m *ReadResponse) XXX_Unmarshal(b []byte) error { func (m *ReadResponse) XXX_Unmarshal(b []byte) error {
@ -157,7 +204,7 @@ func (m *ReadResponse) GetRecords() []*Record {
} }
type WriteRequest struct { type WriteRequest struct {
Records []*Record `protobuf:"bytes,2,rep,name=records,proto3" json:"records,omitempty"` Record *Record `protobuf:"bytes,1,opt,name=record,proto3" json:"record,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"` XXX_sizecache int32 `json:"-"`
@ -167,7 +214,7 @@ func (m *WriteRequest) Reset() { *m = WriteRequest{} }
func (m *WriteRequest) String() string { return proto.CompactTextString(m) } func (m *WriteRequest) String() string { return proto.CompactTextString(m) }
func (*WriteRequest) ProtoMessage() {} func (*WriteRequest) ProtoMessage() {}
func (*WriteRequest) Descriptor() ([]byte, []int) { func (*WriteRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_f84ccc98e143ed3e, []int{3} return fileDescriptor_f84ccc98e143ed3e, []int{4}
} }
func (m *WriteRequest) XXX_Unmarshal(b []byte) error { func (m *WriteRequest) XXX_Unmarshal(b []byte) error {
@ -188,9 +235,9 @@ func (m *WriteRequest) XXX_DiscardUnknown() {
var xxx_messageInfo_WriteRequest proto.InternalMessageInfo var xxx_messageInfo_WriteRequest proto.InternalMessageInfo
func (m *WriteRequest) GetRecords() []*Record { func (m *WriteRequest) GetRecord() *Record {
if m != nil { if m != nil {
return m.Records return m.Record
} }
return nil return nil
} }
@ -205,7 +252,7 @@ func (m *WriteResponse) Reset() { *m = WriteResponse{} }
func (m *WriteResponse) String() string { return proto.CompactTextString(m) } func (m *WriteResponse) String() string { return proto.CompactTextString(m) }
func (*WriteResponse) ProtoMessage() {} func (*WriteResponse) ProtoMessage() {}
func (*WriteResponse) Descriptor() ([]byte, []int) { func (*WriteResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_f84ccc98e143ed3e, []int{4} return fileDescriptor_f84ccc98e143ed3e, []int{5}
} }
func (m *WriteResponse) XXX_Unmarshal(b []byte) error { func (m *WriteResponse) XXX_Unmarshal(b []byte) error {
@ -227,7 +274,7 @@ func (m *WriteResponse) XXX_DiscardUnknown() {
var xxx_messageInfo_WriteResponse proto.InternalMessageInfo var xxx_messageInfo_WriteResponse proto.InternalMessageInfo
type DeleteRequest struct { type DeleteRequest struct {
Keys []string `protobuf:"bytes,1,rep,name=keys,proto3" json:"keys,omitempty"` Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"` XXX_sizecache int32 `json:"-"`
@ -237,7 +284,7 @@ func (m *DeleteRequest) Reset() { *m = DeleteRequest{} }
func (m *DeleteRequest) String() string { return proto.CompactTextString(m) } func (m *DeleteRequest) String() string { return proto.CompactTextString(m) }
func (*DeleteRequest) ProtoMessage() {} func (*DeleteRequest) ProtoMessage() {}
func (*DeleteRequest) Descriptor() ([]byte, []int) { func (*DeleteRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_f84ccc98e143ed3e, []int{5} return fileDescriptor_f84ccc98e143ed3e, []int{6}
} }
func (m *DeleteRequest) XXX_Unmarshal(b []byte) error { func (m *DeleteRequest) XXX_Unmarshal(b []byte) error {
@ -258,11 +305,11 @@ func (m *DeleteRequest) XXX_DiscardUnknown() {
var xxx_messageInfo_DeleteRequest proto.InternalMessageInfo var xxx_messageInfo_DeleteRequest proto.InternalMessageInfo
func (m *DeleteRequest) GetKeys() []string { func (m *DeleteRequest) GetKey() string {
if m != nil { if m != nil {
return m.Keys return m.Key
} }
return nil return ""
} }
type DeleteResponse struct { type DeleteResponse struct {
@ -275,7 +322,7 @@ func (m *DeleteResponse) Reset() { *m = DeleteResponse{} }
func (m *DeleteResponse) String() string { return proto.CompactTextString(m) } func (m *DeleteResponse) String() string { return proto.CompactTextString(m) }
func (*DeleteResponse) ProtoMessage() {} func (*DeleteResponse) ProtoMessage() {}
func (*DeleteResponse) Descriptor() ([]byte, []int) { func (*DeleteResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_f84ccc98e143ed3e, []int{6} return fileDescriptor_f84ccc98e143ed3e, []int{7}
} }
func (m *DeleteResponse) XXX_Unmarshal(b []byte) error { func (m *DeleteResponse) XXX_Unmarshal(b []byte) error {
@ -297,8 +344,6 @@ func (m *DeleteResponse) XXX_DiscardUnknown() {
var xxx_messageInfo_DeleteResponse proto.InternalMessageInfo var xxx_messageInfo_DeleteResponse proto.InternalMessageInfo
type ListRequest struct { type ListRequest struct {
// optional key
Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"` XXX_sizecache int32 `json:"-"`
@ -308,7 +353,7 @@ func (m *ListRequest) Reset() { *m = ListRequest{} }
func (m *ListRequest) String() string { return proto.CompactTextString(m) } func (m *ListRequest) String() string { return proto.CompactTextString(m) }
func (*ListRequest) ProtoMessage() {} func (*ListRequest) ProtoMessage() {}
func (*ListRequest) Descriptor() ([]byte, []int) { func (*ListRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_f84ccc98e143ed3e, []int{7} return fileDescriptor_f84ccc98e143ed3e, []int{8}
} }
func (m *ListRequest) XXX_Unmarshal(b []byte) error { func (m *ListRequest) XXX_Unmarshal(b []byte) error {
@ -329,13 +374,6 @@ func (m *ListRequest) XXX_DiscardUnknown() {
var xxx_messageInfo_ListRequest proto.InternalMessageInfo var xxx_messageInfo_ListRequest proto.InternalMessageInfo
func (m *ListRequest) GetKey() string {
if m != nil {
return m.Key
}
return ""
}
type ListResponse struct { type ListResponse struct {
Records []*Record `protobuf:"bytes,1,rep,name=records,proto3" json:"records,omitempty"` Records []*Record `protobuf:"bytes,1,rep,name=records,proto3" json:"records,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
@ -347,7 +385,7 @@ func (m *ListResponse) Reset() { *m = ListResponse{} }
func (m *ListResponse) String() string { return proto.CompactTextString(m) } func (m *ListResponse) String() string { return proto.CompactTextString(m) }
func (*ListResponse) ProtoMessage() {} func (*ListResponse) ProtoMessage() {}
func (*ListResponse) Descriptor() ([]byte, []int) { func (*ListResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_f84ccc98e143ed3e, []int{8} return fileDescriptor_f84ccc98e143ed3e, []int{9}
} }
func (m *ListResponse) XXX_Unmarshal(b []byte) error { func (m *ListResponse) XXX_Unmarshal(b []byte) error {
@ -377,6 +415,7 @@ func (m *ListResponse) GetRecords() []*Record {
func init() { func init() {
proto.RegisterType((*Record)(nil), "go.micro.store.Record") proto.RegisterType((*Record)(nil), "go.micro.store.Record")
proto.RegisterType((*ReadOptions)(nil), "go.micro.store.ReadOptions")
proto.RegisterType((*ReadRequest)(nil), "go.micro.store.ReadRequest") proto.RegisterType((*ReadRequest)(nil), "go.micro.store.ReadRequest")
proto.RegisterType((*ReadResponse)(nil), "go.micro.store.ReadResponse") proto.RegisterType((*ReadResponse)(nil), "go.micro.store.ReadResponse")
proto.RegisterType((*WriteRequest)(nil), "go.micro.store.WriteRequest") proto.RegisterType((*WriteRequest)(nil), "go.micro.store.WriteRequest")
@ -392,26 +431,28 @@ func init() {
} }
var fileDescriptor_f84ccc98e143ed3e = []byte{ var fileDescriptor_f84ccc98e143ed3e = []byte{
// 333 bytes of a gzipped FileDescriptorProto // 364 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x92, 0x4d, 0x4f, 0xc2, 0x40, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x53, 0x4d, 0x4b, 0xc3, 0x40,
0x10, 0x86, 0x59, 0x0a, 0x35, 0x0c, 0x1f, 0x92, 0x89, 0x21, 0x0d, 0x7e, 0xd5, 0x7a, 0xe9, 0xc5, 0x10, 0x6d, 0x9a, 0x36, 0xd5, 0x49, 0x5b, 0xcb, 0x22, 0x25, 0xd4, 0x0f, 0xe2, 0x82, 0x90, 0x8b,
0x42, 0xf0, 0x0f, 0x98, 0xf8, 0x11, 0x4d, 0x3c, 0xad, 0x07, 0xcf, 0x08, 0x13, 0xd2, 0x80, 0x2e, 0x69, 0xa9, 0x78, 0x15, 0xc1, 0x0f, 0x14, 0x04, 0x61, 0x05, 0x3d, 0xd7, 0x76, 0x2c, 0xc1, 0xda,
0xee, 0x16, 0x62, 0xff, 0x90, 0xbf, 0xd3, 0xec, 0x6e, 0xab, 0xc5, 0x42, 0x62, 0xbc, 0xcd, 0xee, 0x8d, 0xbb, 0x69, 0x69, 0xff, 0x90, 0xbf, 0x53, 0xb2, 0xbb, 0xd1, 0x94, 0x34, 0x17, 0x6f, 0x33,
0xbc, 0xf3, 0xec, 0xdb, 0x79, 0x0b, 0xd1, 0x6b, 0x3c, 0x91, 0x62, 0x30, 0x13, 0x17, 0xb6, 0x50, 0xfb, 0xde, 0xbc, 0x99, 0x79, 0xc3, 0x42, 0xf8, 0x19, 0x8d, 0x05, 0xef, 0x4f, 0xf9, 0x99, 0x0e,
0x89, 0x90, 0x34, 0x50, 0x24, 0xd7, 0xf1, 0x84, 0x06, 0x4b, 0x29, 0x92, 0xec, 0x2e, 0x32, 0x35, 0x64, 0xc2, 0x05, 0xf6, 0x25, 0x8a, 0x65, 0x34, 0xc6, 0x7e, 0x2c, 0x78, 0x62, 0xde, 0x42, 0x15,
0x76, 0x66, 0xc2, 0x8e, 0x44, 0xe6, 0x36, 0xb8, 0x07, 0x97, 0xd3, 0x44, 0xc8, 0x29, 0x76, 0xc1, 0x93, 0xf6, 0x94, 0xeb, 0x92, 0x50, 0xbd, 0xd2, 0x7b, 0x70, 0x18, 0x8e, 0xb9, 0x98, 0x90, 0x0e,
0x99, 0x53, 0xea, 0x31, 0x9f, 0x85, 0x0d, 0xae, 0x4b, 0x3c, 0x80, 0xfa, 0x7a, 0xbc, 0x58, 0x91, 0xd8, 0x1f, 0xb8, 0xf6, 0x2c, 0xdf, 0x0a, 0x76, 0x59, 0x1a, 0x92, 0x7d, 0xa8, 0x2f, 0x47, 0xb3,
0x57, 0xf5, 0x59, 0xd8, 0xe2, 0xf6, 0x80, 0x3d, 0x70, 0xe9, 0x63, 0x19, 0xcb, 0xd4, 0x73, 0x7c, 0x05, 0x7a, 0x55, 0xdf, 0x0a, 0x9a, 0x4c, 0x27, 0xa4, 0x0b, 0x0e, 0xae, 0xe2, 0x48, 0xac, 0x3d,
0x16, 0x3a, 0x3c, 0x3b, 0x05, 0x67, 0xd0, 0xe4, 0x34, 0x9e, 0x72, 0x7a, 0x5f, 0x91, 0x4a, 0x10, 0xdb, 0xb7, 0x02, 0x9b, 0x99, 0x8c, 0x9e, 0x82, 0xcb, 0x70, 0x34, 0x79, 0x8a, 0x93, 0x88, 0xcf,
0xa1, 0x36, 0xa7, 0x54, 0x79, 0xcc, 0x77, 0xc2, 0x06, 0x37, 0x75, 0x70, 0x05, 0x2d, 0x2b, 0x51, 0x65, 0x4a, 0x8b, 0x05, 0xbe, 0x47, 0x2b, 0xa5, 0xb8, 0xc3, 0x4c, 0x46, 0x5f, 0x34, 0x8d, 0xe1,
0x4b, 0xf1, 0xa6, 0x08, 0x87, 0xb0, 0x27, 0xcd, 0xe3, 0x56, 0xd6, 0x1c, 0xf5, 0xa2, 0x4d, 0x7b, 0xd7, 0x02, 0x65, 0xb2, 0xa5, 0xeb, 0x05, 0x34, 0xb8, 0xd6, 0x50, 0x7d, 0xdd, 0xe1, 0x41, 0xb8,
0x91, 0xf5, 0xc6, 0x73, 0x99, 0x26, 0x3c, 0xcb, 0x38, 0xa1, 0xfc, 0x95, 0x02, 0xa1, 0xfa, 0x37, 0x39, 0x73, 0x98, 0x6b, 0xc3, 0x32, 0x2e, 0xbd, 0x82, 0xa6, 0xd6, 0x95, 0x31, 0x9f, 0x4b, 0x24,
0xc2, 0x3e, 0xb4, 0x33, 0x82, 0x35, 0x11, 0x9c, 0x43, 0xfb, 0x86, 0x16, 0xf4, 0xc3, 0xdc, 0xe6, 0x03, 0x68, 0x08, 0xb5, 0x98, 0xf4, 0x2c, 0xdf, 0x0e, 0xdc, 0x61, 0xb7, 0x28, 0x93, 0xc2, 0x2c,
0xbc, 0x0b, 0x9d, 0x5c, 0x94, 0x8d, 0x9d, 0x42, 0xf3, 0x31, 0x56, 0x49, 0x3e, 0x54, 0xda, 0x9e, 0xa3, 0xd1, 0x4b, 0x68, 0xbe, 0x8a, 0x28, 0xc1, 0x6c, 0xb4, 0x10, 0x1c, 0x0d, 0xa9, 0xe9, 0xca,
0xb6, 0x6a, 0x05, 0xff, 0xfd, 0xd8, 0xd1, 0x67, 0x15, 0xea, 0x4f, 0xba, 0x83, 0xb7, 0x50, 0xd3, 0x05, 0x0c, 0x8b, 0xee, 0x41, 0xcb, 0xd4, 0xeb, 0x11, 0xe8, 0x09, 0xb4, 0x6e, 0x70, 0x86, 0x7f,
0x2c, 0x3c, 0xfc, 0x3d, 0x52, 0xb0, 0xd0, 0x3f, 0xda, 0xde, 0xcc, 0xfc, 0x56, 0x86, 0x0c, 0xaf, 0x8a, 0x85, 0x65, 0x69, 0x07, 0xda, 0x19, 0xc5, 0x14, 0xb5, 0xc0, 0x7d, 0x8c, 0x64, 0x62, 0x4a,
0xa1, 0xa6, 0xf7, 0x5f, 0xc6, 0x14, 0x82, 0x2b, 0x63, 0x8a, 0x91, 0x05, 0x15, 0xbc, 0x83, 0xba, 0xd2, 0xb5, 0x74, 0xfa, 0xdf, 0xb5, 0x86, 0xdf, 0x55, 0xa8, 0x3f, 0xa7, 0x08, 0xb9, 0x85, 0x5a,
0x59, 0x20, 0x96, 0x84, 0xc5, 0x64, 0xfa, 0xc7, 0x3b, 0xba, 0xdf, 0x9c, 0x07, 0x70, 0xed, 0x4a, 0xaa, 0x45, 0x0a, 0x86, 0xe6, 0x1a, 0xf6, 0x0e, 0xb7, 0x83, 0x66, 0xba, 0xca, 0xc0, 0x22, 0xd7,
0xb1, 0x24, 0xdd, 0xc8, 0xa3, 0x7f, 0xb2, 0xab, 0x9d, 0xa3, 0x5e, 0x5c, 0xf3, 0x6f, 0x5f, 0x7e, 0x50, 0x4b, 0x9d, 0x26, 0x5b, 0xef, 0x52, 0x2a, 0x93, 0x3f, 0x0e, 0xad, 0x90, 0x3b, 0xa8, 0x2b,
0x05, 0x00, 0x00, 0xff, 0xff, 0x30, 0x48, 0x25, 0x2d, 0x0d, 0x03, 0x00, 0x00, 0xb3, 0x48, 0x81, 0x98, 0xbf, 0x41, 0xef, 0xa8, 0x04, 0xfd, 0xd5, 0x79, 0x00, 0x47, 0x1b, 0x48,
0x0a, 0xd4, 0x0d, 0xef, 0x7b, 0xc7, 0x65, 0x70, 0x26, 0xf5, 0xe6, 0xa8, 0x1f, 0x72, 0xfe, 0x13,
0x00, 0x00, 0xff, 0xff, 0xf9, 0x20, 0x54, 0x71, 0x53, 0x03, 0x00, 0x00,
} }

View File

@ -18,8 +18,13 @@ message Record {
int64 expiry = 3; int64 expiry = 3;
} }
message ReadOptions {
bool prefix = 1;
}
message ReadRequest { message ReadRequest {
repeated string keys = 1; string key = 1;
ReadOptions options = 2;
} }
message ReadResponse { message ReadResponse {
@ -27,21 +32,18 @@ message ReadResponse {
} }
message WriteRequest { message WriteRequest {
repeated Record records = 2; Record record = 1;
} }
message WriteResponse {} message WriteResponse {}
message DeleteRequest { message DeleteRequest {
repeated string keys = 1; string key = 1;
} }
message DeleteResponse {} message DeleteResponse {}
message ListRequest { message ListRequest {}
// optional key
string key = 1;
}
message ListResponse { message ListResponse {
repeated Record records = 1; repeated Record records = 1;

View File

@ -73,6 +73,7 @@ func (s *serviceStore) List() ([]*store.Record, error) {
if err != nil { if err != nil {
return records, err return records, err
} }
for _, record := range rsp.Records { for _, record := range rsp.Records {
records = append(records, &store.Record{ records = append(records, &store.Record{
Key: record.Key, Key: record.Key,
@ -86,15 +87,24 @@ func (s *serviceStore) List() ([]*store.Record, error) {
} }
// Read a record with key // Read a record with key
func (s *serviceStore) Read(keys ...string) ([]*store.Record, error) { func (s *serviceStore) Read(key string, opts ...store.ReadOption) ([]*store.Record, error) {
var options store.ReadOptions
for _, o := range opts {
o(&options)
}
rsp, err := s.Client.Read(s.Context(), &pb.ReadRequest{ rsp, err := s.Client.Read(s.Context(), &pb.ReadRequest{
Keys: keys, Key: key,
Options: &pb.ReadOptions{
Prefix: options.Prefix,
},
}, client.WithAddress(s.Nodes...)) }, client.WithAddress(s.Nodes...))
if err != nil { if err != nil {
return nil, err return nil, err
} }
records := make([]*store.Record, 0, len(rsp.Records)) records := make([]*store.Record, 0, len(rsp.Records))
for _, val := range rsp.Records { for _, val := range rsp.Records {
records = append(records, &store.Record{ records = append(records, &store.Record{
Key: val.Key, Key: val.Key,
@ -102,32 +112,27 @@ func (s *serviceStore) Read(keys ...string) ([]*store.Record, error) {
Expiry: time.Duration(val.Expiry) * time.Second, Expiry: time.Duration(val.Expiry) * time.Second,
}) })
} }
return records, nil return records, nil
} }
// Write a record // Write a record
func (s *serviceStore) Write(recs ...*store.Record) error { func (s *serviceStore) Write(record *store.Record) error {
records := make([]*pb.Record, 0, len(recs)) _, err := s.Client.Write(s.Context(), &pb.WriteRequest{
Record: &pb.Record{
for _, record := range recs {
records = append(records, &pb.Record{
Key: record.Key, Key: record.Key,
Value: record.Value, Value: record.Value,
Expiry: int64(record.Expiry.Seconds()), Expiry: int64(record.Expiry.Seconds()),
}) },
}
_, err := s.Client.Write(s.Context(), &pb.WriteRequest{
Records: records,
}, client.WithAddress(s.Nodes...)) }, client.WithAddress(s.Nodes...))
return err return err
} }
// Delete a record with key // Delete a record with key
func (s *serviceStore) Delete(keys ...string) error { func (s *serviceStore) Delete(key string) error {
_, err := s.Client.Delete(s.Context(), &pb.DeleteRequest{ _, err := s.Client.Delete(s.Context(), &pb.DeleteRequest{
Keys: keys, Key: key,
}, client.WithAddress(s.Nodes...)) }, client.WithAddress(s.Nodes...))
return err return err
} }

View File

@ -20,11 +20,11 @@ type Store interface {
// List all the known records // List all the known records
List() ([]*Record, error) List() ([]*Record, error)
// Read records with keys // Read records with keys
Read(key ...string) ([]*Record, error) Read(key string, opts ...ReadOption) ([]*Record, error)
// Write records // Write records
Write(rec ...*Record) error Write(*Record) error
// Delete records with keys // Delete records with keys
Delete(key ...string) error Delete(key string) error
} }
// Record represents a data record // Record represents a data record
@ -34,6 +34,13 @@ type Record struct {
Expiry time.Duration Expiry time.Duration
} }
type ReadOptions struct {
// Read key as a prefix
Prefix bool
}
type ReadOption func(o *ReadOptions)
type noop struct{} type noop struct{}
func (n *noop) Init(...Option) error { func (n *noop) Init(...Option) error {
@ -44,14 +51,14 @@ func (n *noop) List() ([]*Record, error) {
return nil, nil return nil, nil
} }
func (n *noop) Read(key ...string) ([]*Record, error) { func (n *noop) Read(key string, opts ...ReadOption) ([]*Record, error) {
return nil, nil return nil, nil
} }
func (n *noop) Write(rec ...*Record) error { func (n *noop) Write(rec *Record) error {
return nil return nil
} }
func (n *noop) Delete(key ...string) error { func (n *noop) Delete(key string) error {
return nil return nil
} }

View File

@ -1,40 +0,0 @@
package sync
import (
"testing"
"time"
"github.com/micro/go-micro/store"
store_mock "github.com/micro/go-micro/store/mock"
mem_lock "github.com/micro/go-micro/sync/lock/memory"
"github.com/stretchr/testify/mock"
)
func TestIterate(t *testing.T) {
recA := &store.Record{
Key: "A",
Value: nil,
}
recB := &store.Record{
Key: "B",
Value: nil,
}
s1 := &store_mock.Store{}
s2 := &store_mock.Store{}
s1.On("List").Return([]*store.Record{recA, recB}, nil)
s2.On("List").Return([]*store.Record{recB, recA}, nil)
s1.On("Write", mock.Anything).Return(nil)
s2.On("Write", mock.Anything).Return(nil)
f := func(key, val interface{}) error {
time.Sleep(1 * time.Millisecond)
return nil
}
l := mem_lock.NewLock()
m1 := NewMap(WithStore(s1), WithLock(l))
m2 := NewMap(WithStore(s2), WithLock(l))
go func() {
m2.Iterate(f)
}()
m1.Iterate(f)
}