constellation/coordinator/store/stdstore.go
datosh 4abb483902 Ref/store ectd (#45)
Improved unit & integration tests for store, by making them independent and test a single thing.
2022-04-12 09:38:10 +02:00

198 lines
4.2 KiB
Go

package store
import (
"strings"
"sync"
)
// StdStore is the standard implementation of the Store interface.
type StdStore struct {
data map[string]string
mut, txmut sync.Mutex
}
// NewStdStore creates and initializes a new StdStore object.
func NewStdStore() *StdStore {
s := &StdStore{
data: make(map[string]string),
}
return s
}
// Get retrieves a value from StdStore by Type and Name.
func (s *StdStore) Get(request string) ([]byte, error) {
s.mut.Lock()
value, ok := s.data[request]
s.mut.Unlock()
if ok {
return []byte(value), nil
}
return nil, &ValueUnsetError{requestedValue: request}
}
// Put saves a value in StdStore by Type and Name.
func (s *StdStore) Put(request string, requestData []byte) error {
tx, err := s.BeginTransaction()
if err != nil {
return err
}
defer tx.Rollback()
if err := tx.Put(request, requestData); err != nil {
return err
}
return tx.Commit()
}
func (s *StdStore) Delete(key string) error {
tx, err := s.BeginTransaction()
if err != nil {
return err
}
defer tx.Rollback()
if err := tx.Delete(key); err != nil {
return err
}
return tx.Commit()
}
// Iterator returns an iterator for keys saved in StdStore with a given prefix.
// For an empty prefix this is an iterator for all keys in StdStore.
func (s *StdStore) Iterator(prefix string) (Iterator, error) {
keys := make([]string, 0)
s.mut.Lock()
for k := range s.data {
if strings.HasPrefix(k, prefix) {
keys = append(keys, k)
}
}
s.mut.Unlock()
return &StdIterator{0, keys}, nil
}
// BeginTransaction starts a new transaction.
func (s *StdStore) BeginTransaction() (Transaction, error) {
tx := stdTransaction{
store: s,
data: map[string]string{},
ongoingTransaction: true,
}
s.txmut.Lock()
s.mut.Lock()
for k, v := range s.data {
tx.data[k] = v
}
s.mut.Unlock()
return &tx, nil
}
func (s *StdStore) commit(data map[string]string) error {
s.mut.Lock()
s.data = data
s.mut.Unlock()
s.txmut.Unlock()
return nil
}
func (s *StdStore) Transfer(newstore Store) error {
s.mut.Lock()
// copy key:value pairs from the old storage into etcd
for key, value := range s.data {
if err := newstore.Put(key, []byte(value)); err != nil {
return err
}
}
s.mut.Unlock()
return nil
}
type stdTransaction struct {
store *StdStore
data map[string]string
ongoingTransaction bool
}
// Get retrieves a value.
func (t *stdTransaction) Get(request string) ([]byte, error) {
if !t.ongoingTransaction {
return nil, &TransactionAlreadyCommittedError{op: "Get"}
}
if value, ok := t.data[request]; ok {
return []byte(value), nil
}
return nil, &ValueUnsetError{requestedValue: request}
}
// Put saves a value.
func (t *stdTransaction) Put(request string, requestData []byte) error {
if !t.ongoingTransaction {
return &TransactionAlreadyCommittedError{op: "Put"}
}
t.data[request] = string(requestData)
return nil
}
func (t *stdTransaction) Delete(key string) error {
if !t.ongoingTransaction {
return &TransactionAlreadyCommittedError{op: "Delete"}
}
delete(t.data, key)
return nil
}
// Iterator returns an iterator for all keys in the transaction with a given prefix.
func (t *stdTransaction) Iterator(prefix string) (Iterator, error) {
keys := make([]string, 0)
for k := range t.data {
if strings.HasPrefix(k, prefix) {
keys = append(keys, k)
}
}
return &StdIterator{0, keys}, nil
}
// Commit ends a transaction and persists the changes.
func (t *stdTransaction) Commit() error {
if err := t.store.commit(t.data); err != nil {
return err
}
t.store = nil
t.ongoingTransaction = false
return nil
}
// Rollback aborts a transaction.
func (t *stdTransaction) Rollback() {
if t.store != nil {
t.store.txmut.Unlock()
}
}
// StdIterator is the standard Iterator implementation.
type StdIterator struct {
idx int
keys []string
}
// GetNext returns the next element of the iterator.
func (i *StdIterator) GetNext() (string, error) {
if i.idx >= len(i.keys) {
return "", &NoElementsLeftError{idx: i.idx}
}
key := i.keys[i.idx]
i.idx++
return key, nil
}
// HasNext returns true if there are elements left to get with GetNext().
func (i *StdIterator) HasNext() bool {
return i.idx < len(i.keys)
}