From 60263da2776533fb6560c6a321ca11d60ebe8d68 Mon Sep 17 00:00:00 2001 From: Nate Roiger Date: Mon, 18 Jul 2022 14:15:05 -0500 Subject: [PATCH 1/7] Refactor persistent storage API Signed-off-by: Nate Roiger --- pkg/manager-nnf/file_share.go | 6 +- pkg/manager-nnf/file_system.go | 7 +- pkg/manager-nnf/manager.go | 10 +- pkg/manager-nnf/persistent.go | 7 +- pkg/manager-nnf/storage_group.go | 7 +- pkg/manager-nnf/storage_pool.go | 6 +- pkg/manager-nvme/nvme_mock_persistence.go | 12 +- .../persistent}/debug/kvdebug.go | 8 +- .../kvstore => pkg/persistent}/kvstore.go | 94 +++-------- .../persistent}/kvstore_test.go | 2 +- pkg/persistent/local_storage.go | 155 ++++++++++++++++++ pkg/persistent/storage.go | 54 ++++++ 12 files changed, 261 insertions(+), 107 deletions(-) rename {internal/kvstore => pkg/persistent}/debug/kvdebug.go (86%) rename {internal/kvstore => pkg/persistent}/kvstore.go (71%) rename {internal/kvstore => pkg/persistent}/kvstore_test.go (99%) create mode 100644 pkg/persistent/local_storage.go create mode 100644 pkg/persistent/storage.go diff --git a/pkg/manager-nnf/file_share.go b/pkg/manager-nnf/file_share.go index 9e81b3e..0cdddc8 100644 --- a/pkg/manager-nnf/file_share.go +++ b/pkg/manager-nnf/file_share.go @@ -24,7 +24,7 @@ import ( "fmt" "strings" - "github.com/NearNodeFlash/nnf-ec/internal/kvstore" + "github.com/NearNodeFlash/nnf-ec/pkg/persistent" sf "github.com/NearNodeFlash/nnf-ec/pkg/rfsf/pkg/models" ) @@ -134,13 +134,13 @@ type fileShareRecoveryRegistry struct { storageService *StorageService } -func NewFileShareRecoveryRegistry(s *StorageService) kvstore.Registry { +func NewFileShareRecoveryRegistry(s *StorageService) persistent.Registry { return &fileShareRecoveryRegistry{storageService: s} } func (r *fileShareRecoveryRegistry) Prefix() string { return fileShareRegistryPrefix } -func (r *fileShareRecoveryRegistry) NewReplay(id string) kvstore.ReplayHandler { +func (r *fileShareRecoveryRegistry) NewReplay(id string) persistent.ReplayHandler { ids := strings.SplitN(id, ":", 2) return &fileShareRecoveryReplayHandler{ diff --git a/pkg/manager-nnf/file_system.go b/pkg/manager-nnf/file_system.go index e1f3a55..389a963 100644 --- a/pkg/manager-nnf/file_system.go +++ b/pkg/manager-nnf/file_system.go @@ -26,8 +26,7 @@ import ( server "github.com/NearNodeFlash/nnf-ec/pkg/manager-server" sf "github.com/NearNodeFlash/nnf-ec/pkg/rfsf/pkg/models" - - "github.com/NearNodeFlash/nnf-ec/internal/kvstore" + "github.com/NearNodeFlash/nnf-ec/pkg/persistent" ) type FileSystem struct { @@ -162,13 +161,13 @@ type fileSystemRecoveryRegistry struct { storageService *StorageService } -func NewFileSystemRecoveryRegistry(s *StorageService) kvstore.Registry { +func NewFileSystemRecoveryRegistry(s *StorageService) persistent.Registry { return &fileSystemRecoveryRegistry{storageService: s} } func (*fileSystemRecoveryRegistry) Prefix() string { return fileSystemRegistryPrefix } -func (r *fileSystemRecoveryRegistry) NewReplay(id string) kvstore.ReplayHandler { +func (r *fileSystemRecoveryRegistry) NewReplay(id string) persistent.ReplayHandler { return &fileSystemRecoveryReplyHandler{ fileSystemId: id, storageService: r.storageService, diff --git a/pkg/manager-nnf/manager.go b/pkg/manager-nnf/manager.go index c09b785..f89a140 100644 --- a/pkg/manager-nnf/manager.go +++ b/pkg/manager-nnf/manager.go @@ -28,7 +28,7 @@ import ( "github.com/google/uuid" log "github.com/sirupsen/logrus" - "github.com/NearNodeFlash/nnf-ec/internal/kvstore" + "github.com/NearNodeFlash/nnf-ec/pkg/persistent" ec "github.com/NearNodeFlash/nnf-ec/pkg/ec" event "github.com/NearNodeFlash/nnf-ec/pkg/manager-event" fabric "github.com/NearNodeFlash/nnf-ec/pkg/manager-fabric" @@ -51,7 +51,7 @@ type StorageService struct { health sf.ResourceHealth config *ConfigFile - store *kvstore.Store + store *persistent.Store serverControllerProvider server.ServerControllerProvider persistentController PersistentControllerApi @@ -74,7 +74,7 @@ func (s *StorageService) OdataIdRef(ref string) sf.OdataV4IdRef { return sf.OdataV4IdRef{OdataId: fmt.Sprintf("%s%s", s.OdataId(), ref)} } -func (s *StorageService) GetStore() *kvstore.Store { +func (s *StorageService) GetStore() *persistent.Store { return s.store } @@ -424,12 +424,12 @@ func (*StorageService) Initialize(ctrl NnfControllerInterface) error { // Create the key-value storage database { - s.store, err = kvstore.Open("nnf.db", false) + s.store, err = persistent.Open("nnf.db", false) if err != nil { return err } - s.store.Register([]kvstore.Registry{ + s.store.Register([]persistent.Registry{ NewStoragePoolRecoveryRegistry(s), NewStorageGroupRecoveryRegistry(s), NewFileSystemRecoveryRegistry(s), diff --git a/pkg/manager-nnf/persistent.go b/pkg/manager-nnf/persistent.go index 6e79a42..6e7292a 100644 --- a/pkg/manager-nnf/persistent.go +++ b/pkg/manager-nnf/persistent.go @@ -20,7 +20,7 @@ package nnf import ( - "github.com/NearNodeFlash/nnf-ec/internal/kvstore" + "github.com/NearNodeFlash/nnf-ec/pkg/persistent" log "github.com/sirupsen/logrus" ) @@ -37,8 +37,9 @@ func NewDefaultPersistentController() PersistentControllerApi { return &DefaultPersistentController{} } +// Persistent Store Provider provides an interface for supplying a Key-Value Store type PersistentStoreProvider interface { - GetStore() *kvstore.Store + GetStore() *persistent.Store } // Persistent Object API provides interface for creating or updating a persistent object. @@ -107,7 +108,7 @@ func (*DefaultPersistentController) DeletePersistentObject(obj PersistentObjectA return ledger.Close(true) } -func executePersistentObjectTransaction(ledger *kvstore.Ledger, obj PersistentObjectApi, updateFunc func() error, startingState, endingState uint32) error { +func executePersistentObjectTransaction(ledger *persistent.Ledger, obj PersistentObjectApi, updateFunc func() error, startingState, endingState uint32) error { data, err := obj.GenerateStateData(startingState) if err != nil { diff --git a/pkg/manager-nnf/storage_group.go b/pkg/manager-nnf/storage_group.go index 956d342..2556621 100644 --- a/pkg/manager-nnf/storage_group.go +++ b/pkg/manager-nnf/storage_group.go @@ -25,8 +25,7 @@ import ( nvme "github.com/NearNodeFlash/nnf-ec/pkg/manager-nvme" server "github.com/NearNodeFlash/nnf-ec/pkg/manager-server" - - "github.com/NearNodeFlash/nnf-ec/internal/kvstore" + "github.com/NearNodeFlash/nnf-ec/pkg/persistent" sf "github.com/NearNodeFlash/nnf-ec/pkg/rfsf/pkg/models" ) @@ -156,13 +155,13 @@ type storageGroupRecoveryRegistry struct { storageService *StorageService } -func NewStorageGroupRecoveryRegistry(s *StorageService) kvstore.Registry { +func NewStorageGroupRecoveryRegistry(s *StorageService) persistent.Registry { return &storageGroupRecoveryRegistry{storageService: s} } func (r *storageGroupRecoveryRegistry) Prefix() string { return storageGroupRegistryPrefix } -func (r *storageGroupRecoveryRegistry) NewReplay(id string) kvstore.ReplayHandler { +func (r *storageGroupRecoveryRegistry) NewReplay(id string) persistent.ReplayHandler { return &storageGroupRecoveryReplyHandler{id: id, storageService: r.storageService} } diff --git a/pkg/manager-nnf/storage_pool.go b/pkg/manager-nnf/storage_pool.go index 7d49e0e..d83002e 100644 --- a/pkg/manager-nnf/storage_pool.go +++ b/pkg/manager-nnf/storage_pool.go @@ -25,7 +25,7 @@ import ( "github.com/google/uuid" - "github.com/NearNodeFlash/nnf-ec/internal/kvstore" + "github.com/NearNodeFlash/nnf-ec/pkg/persistent" nvme2 "github.com/NearNodeFlash/nnf-ec/internal/switchtec/pkg/nvme" nvme "github.com/NearNodeFlash/nnf-ec/pkg/manager-nvme" sf "github.com/NearNodeFlash/nnf-ec/pkg/rfsf/pkg/models" @@ -230,13 +230,13 @@ type storagePoolRecoveryRegistry struct { storageService *StorageService } -func NewStoragePoolRecoveryRegistry(s *StorageService) kvstore.Registry { +func NewStoragePoolRecoveryRegistry(s *StorageService) persistent.Registry { return &storagePoolRecoveryRegistry{storageService: s} } func (*storagePoolRecoveryRegistry) Prefix() string { return storagePoolRegistryPrefix } -func (r *storagePoolRecoveryRegistry) NewReplay(id string) kvstore.ReplayHandler { +func (r *storagePoolRecoveryRegistry) NewReplay(id string) persistent.ReplayHandler { return &storagePoolRecoveryReplayHandler{storageService: r.storageService, storagePool: StoragePool{id: id}} } diff --git a/pkg/manager-nvme/nvme_mock_persistence.go b/pkg/manager-nvme/nvme_mock_persistence.go index be848ab..900b8b6 100644 --- a/pkg/manager-nvme/nvme_mock_persistence.go +++ b/pkg/manager-nvme/nvme_mock_persistence.go @@ -24,7 +24,7 @@ import ( "errors" "fmt" - "github.com/NearNodeFlash/nnf-ec/internal/kvstore" + "github.com/NearNodeFlash/nnf-ec/pkg/persistent" "github.com/NearNodeFlash/nnf-ec/internal/switchtec/pkg/nvme" ) @@ -47,7 +47,7 @@ const ( var errDeviceNotFound = errors.New("Device Not Found") type MockNvmePersistenceManager struct { - store *kvstore.Store + store *persistent.Store replays []mockNvmePersistenceReplay } @@ -55,12 +55,12 @@ type MockNvmePersistenceManager struct { // the Mock NVMe Persistence Registry to handle any Replays in the database. func (mgr *MockNvmePersistenceManager) initialize() (err error) { - mgr.store, err = kvstore.Open("mock.db", false) + mgr.store, err = persistent.Open("mock.db", false) if err != nil { panic(err) } - mgr.store.Register([]kvstore.Registry{newMockNvmePersistenceRegistry(mgr)}) + mgr.store.Register([]persistent.Registry{newMockNvmePersistenceRegistry(mgr)}) if err := mgr.store.Replay(); err != nil { panic(err) @@ -202,7 +202,7 @@ type mockNvmePersistenceRegistry struct { mgr *MockNvmePersistenceManager } -func newMockNvmePersistenceRegistry(mgr *MockNvmePersistenceManager) kvstore.Registry { +func newMockNvmePersistenceRegistry(mgr *MockNvmePersistenceManager) persistent.Registry { return &mockNvmePersistenceRegistry{mgr: mgr} } @@ -210,7 +210,7 @@ func (reg *mockNvmePersistenceRegistry) Prefix() string { return mockNvmePersistenceRegistryPrefix } -func (reg *mockNvmePersistenceRegistry) NewReplay(id string) kvstore.ReplayHandler { +func (reg *mockNvmePersistenceRegistry) NewReplay(id string) persistent.ReplayHandler { reg.mgr.replays = append(reg.mgr.replays, mockNvmePersistenceReplay{id: id}) return ®.mgr.replays[len(reg.mgr.replays)-1] } diff --git a/internal/kvstore/debug/kvdebug.go b/pkg/persistent/debug/kvdebug.go similarity index 86% rename from internal/kvstore/debug/kvdebug.go rename to pkg/persistent/debug/kvdebug.go index 7830f72..b1cff9f 100644 --- a/internal/kvstore/debug/kvdebug.go +++ b/pkg/persistent/debug/kvdebug.go @@ -23,7 +23,7 @@ import ( "flag" "fmt" - "github.com/NearNodeFlash/nnf-ec/internal/kvstore" + "github.com/NearNodeFlash/nnf-ec/pkg/persistent" ) func main() { @@ -32,12 +32,12 @@ func main() { flag.Parse() fmt.Printf("Debug KVStore Tool. Path: '%s'\n", path) - store, err := kvstore.Open(path, true) + store, err := persistent.Open(path, true) if err != nil { panic(err) } - store.Register([]kvstore.Registry{&debugRegistry{}}) + store.Register([]persistent.Registry{&debugRegistry{}}) if err := store.Replay(); err != nil { panic(err) @@ -47,7 +47,7 @@ func main() { type debugRegistry struct{} func (*debugRegistry) Prefix() string { return "" } -func (*debugRegistry) NewReplay(id string) kvstore.ReplayHandler { return &debugReplayHandler{id: id} } +func (*debugRegistry) NewReplay(id string) persistent.ReplayHandler { return &debugReplayHandler{id: id} } type debugReplayHandler struct { id string diff --git a/internal/kvstore/kvstore.go b/pkg/persistent/kvstore.go similarity index 71% rename from internal/kvstore/kvstore.go rename to pkg/persistent/kvstore.go index dd2d158..7aaab36 100644 --- a/internal/kvstore/kvstore.go +++ b/pkg/persistent/kvstore.go @@ -17,7 +17,7 @@ * limitations under the License. */ -package kvstore +package persistent import ( "encoding/binary" @@ -25,8 +25,6 @@ import ( "fmt" "math" "strings" - - "github.com/dgraph-io/badger/v3" ) const ( @@ -35,37 +33,16 @@ const ( type Store struct { path string - db *badger.DB + storage PersistentStorageApi registries []Registry } func Open(path string, readOnly bool) (*Store, error) { - - opts := badger.DefaultOptions(path) - opts.SyncWrites = true - //opts.ReadOnly = readOnly // Causes ErrLogTruncate - opts.BypassLockGuard = readOnly - - // Shrink the in-memory and on-disk size to a more manageable 8 MiB and 16 MiB, respectively; - // We use very little data and the 64 MiB and 256 MiB defaults will cause OOM issues in kubernetes. - // 8MiB seems to be the lower limit within badger, anything smaller and badger will complain with - // """ - // Valuethreshold 1048576 greater than max batch size of 629145. Either reduce opt.ValueThreshold - // or increase opt.MaxTableSize. - // """ - opts.MemTableSize = 8 << 20 - opts.BlockCacheSize = 16 << 20 - - db, err := badger.Open(opts) - - if err != nil { - return nil, err - } - - return &Store{path: path, db: db, registries: make([]Registry, 0)}, nil + s, err := StorageProvider.NewPersistentStorageInterface(path, readOnly) + return &Store{path: path, storage: s, registries: make([]Registry, 0)}, err } -func (s *Store) Close() error { return s.db.Close() } +func (s *Store) Close() error { return s.storage.Close() } func (s *Store) Register(registries []Registry) { for _, registry := range registries { @@ -84,25 +61,15 @@ func (s *Store) Replay() error { for _, r := range s.registries { deleteKeys := make([]string, 0) - err := s.db.View(func(txn *badger.Txn) error { - opts := badger.DefaultIteratorOptions - if len(r.Prefix()) != 0 { - opts.Prefix = []byte(r.Prefix()) - } - itr := txn.NewIterator(opts) + err := s.storage.View(func(txn PersistentStorageTransactionApi) error { + itr := txn.NewIterator(r.Prefix()) defer itr.Close() for itr.Rewind(); itr.Valid(); itr.Next() { - item := itr.Item() - key := item.Key() - value := []byte{} - - err := item.Value(func(val []byte) error { - value = append([]byte{}, val...) - return nil - }) + key := itr.Key() + value, err := itr.Value() if err != nil { return err } @@ -151,18 +118,14 @@ func (s *Store) NewKey(key string, metadata []byte) (*Ledger, error) { // Create the Metadata TLV tlv := newTlv(metadataTlvType, metadata) - err := s.db.Update(func(txn *badger.Txn) error { - return txn.Set([]byte(key), tlv.bytes()) + err := s.storage.Update(func(txn PersistentStorageTransactionApi) error { + return txn.Set(key, tlv.bytes()) }) if err != nil { return nil, err } - if err := s.db.Sync(); err != nil { - return nil, err - } - return s.newKeyLedger(key, tlv.bytes()), nil } } @@ -175,26 +138,19 @@ func (s *Store) OpenKey(key string) (*Ledger, error) { if strings.HasPrefix(key, r.Prefix()) { ledger := s.existingKeyLedger(key) - err := s.db.View(func(txn *badger.Txn) error { - item, err := txn.Get([]byte(key)) + err := s.storage.View(func(txn PersistentStorageTransactionApi) error { + value, err := txn.Get(key) if err != nil { return err } - - return item.Value(func(val []byte) error { - ledger.bytes = append([]byte{}, val...) - return nil - }) + ledger.bytes = value + return nil }) if err != nil { return nil, err } - if err := s.db.Sync(); err != nil { - return nil, err - } - return ledger, nil } } @@ -218,7 +174,7 @@ type Registry interface { NewReplay(id string) ReplayHandler } -func (s *Store) runReply(registry Registry, key []byte, data []byte) (delete bool, err error) { +func (s *Store) runReply(registry Registry, key string, data []byte) (delete bool, err error) { id := string(key[len(registry.Prefix()):]) it := newIterator(data) replay := registry.NewReplay(string(id)) @@ -301,27 +257,17 @@ func (l *Ledger) Log(t uint32, v []byte) error { tlv := newTlv(t, v) - err := l.s.db.Update(func(txn *badger.Txn) error { + err := l.s.storage.Update(func(txn PersistentStorageTransactionApi) error { l.bytes = append(l.bytes, tlv.bytes()...) - return txn.Set([]byte(l.key), l.bytes) + return txn.Set(l.key, l.bytes) }) - if err != nil { - return err - } - - return l.s.db.Sync() + return err } func (l *Ledger) Close(delete bool) error { if delete { - - txn := l.s.db.NewTransaction(true) - if err := txn.Delete([]byte(l.key)); err != nil { - return err - } - - return txn.Commit() + return l.s.storage.Delete(l.key) } return nil } diff --git a/internal/kvstore/kvstore_test.go b/pkg/persistent/kvstore_test.go similarity index 99% rename from internal/kvstore/kvstore_test.go rename to pkg/persistent/kvstore_test.go index 9bc21e0..d2c37bc 100644 --- a/internal/kvstore/kvstore_test.go +++ b/pkg/persistent/kvstore_test.go @@ -17,7 +17,7 @@ * limitations under the License. */ -package kvstore +package persistent import ( "fmt" diff --git a/pkg/persistent/local_storage.go b/pkg/persistent/local_storage.go new file mode 100644 index 0000000..aa9f5ad --- /dev/null +++ b/pkg/persistent/local_storage.go @@ -0,0 +1,155 @@ +/* + * Copyright 2020, 2021, 2022 Hewlett Packard Enterprise Development LP + * Other additional copyright holders may be indicated within. + * + * The entirety of this work is licensed under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package persistent + +import ( + "github.com/dgraph-io/badger/v3" +) + +func NewLocalPersistentStorageProvider() PersistentStorageProvider { + return &localPersistentStorageProvider{} +} + +type localPersistentStorageProvider struct{} + +// NewPersistentStorageInterface implements PersistentStorageProvider +func (*localPersistentStorageProvider) NewPersistentStorageInterface(path string, readOnly bool) (PersistentStorageApi, error) { + s := &localPersistentStorage{} + return s, s.open(path, readOnly) +} + +type localPersistentStorage struct { + *badger.DB +} + +func (s *localPersistentStorage) open(path string, readOnly bool) (err error) { + opts := badger.DefaultOptions(path) + opts.SyncWrites = true + //opts.ReadOnly = readOnly // Causes ErrLogTruncate + opts.BypassLockGuard = readOnly + + // Shrink the in-memory and on-disk size to a more manageable 8 MiB and 16 MiB, respectively; + // We use very little data and the 64 MiB and 256 MiB defaults will cause OOM issues in kubernetes. + // 8MiB seems to be the lower limit within badger, anything smaller and badger will complain with + // """ + // Valuethreshold 1048576 greater than max batch size of 629145. Either reduce opt.ValueThreshold + // or increase opt.MaxTableSize. + // """ + opts.MemTableSize = 8 << 20 + opts.BlockCacheSize = 16 << 20 + + s.DB, err = badger.Open(opts) + return err +} + +func (s *localPersistentStorage) Close() error { + return s.DB.Close() +} + +func (s *localPersistentStorage) View(fn func(PersistentStorageTransactionApi) error) error { + return s.DB.View(func(txn *badger.Txn) error { + return fn(&localPersistentStorageTransaction{txn}) + }) +} + +func (s *localPersistentStorage) Update(fn func(PersistentStorageTransactionApi) error) error { + return s.DB.Update(func(txn *badger.Txn) error { + return fn(&localPersistentStorageTransaction{txn}) + }) +} + +func (s *localPersistentStorage) Delete(key string) error { + txn := s.DB.NewTransaction(true) + if err := txn.Delete([]byte(key)); err != nil { + return err + } + + return txn.Commit() +} + +type localPersistentStorageTransaction struct { + *badger.Txn +} + +func (txn *localPersistentStorageTransaction) NewIterator(prefix string) PersistentStorageIteratorApi { + opts := badger.DefaultIteratorOptions + if len(prefix) != 0 { + opts.Prefix = []byte(prefix) + } + + return &localPersistentStorageIterator{txn.Txn.NewIterator(opts)} +} + +func (txn *localPersistentStorageTransaction) Set(key string, value []byte) error { + return txn.Txn.Set([]byte(key), []byte(value)) +} + +func (txn *localPersistentStorageTransaction) Get(key string) ([]byte, error) { + value := []byte{} + item, err := txn.Txn.Get([]byte(key)) + if err != nil { + return value, err + } + + + err = item.Value(func(val []byte) error { + value = append(value, val...) + return nil + }) + + return value, err + +} + +type localPersistentStorageIterator struct { + *badger.Iterator +} + +func (itr *localPersistentStorageIterator) Rewind() { + itr.Iterator.Rewind() +} + +func (itr *localPersistentStorageIterator) Valid() bool { + return itr.Iterator.Valid() +} + +func (itr *localPersistentStorageIterator) Next() { + itr.Iterator.Next() +} + +func (itr *localPersistentStorageIterator) Key() string { + return string(itr.Iterator.Item().Key()) +} + +func (itr *localPersistentStorageIterator) Value() ([]byte, error) { + item := itr.Iterator.Item() + value := []byte{} + + err := item.Value(func(val []byte) error { + value = append(value, val...) + return nil + }) + + return value, err +} + +func (itr *localPersistentStorageIterator) Close() { + itr.Iterator.Close() +} diff --git a/pkg/persistent/storage.go b/pkg/persistent/storage.go new file mode 100644 index 0000000..59f3bf5 --- /dev/null +++ b/pkg/persistent/storage.go @@ -0,0 +1,54 @@ +/* + * Copyright 2020, 2021, 2022 Hewlett Packard Enterprise Development LP + * Other additional copyright holders may be indicated within. + * + * The entirety of this work is licensed under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package persistent + +var StorageProvider = NewLocalPersistentStorageProvider() + +type PersistentStorageProvider interface { + NewPersistentStorageInterface(path string, readOnly bool) (PersistentStorageApi, error) +} + +// Persistent Storage API provides an interface for interacting with persistent storage +type PersistentStorageApi interface { + View(func(txn PersistentStorageTransactionApi) error) error + Update(func(txn PersistentStorageTransactionApi) error) error + Delete(key string) error + + Close() error +} + +// Persistent Storage Transaction API provides an interface for interacting with persistent storage transactions +type PersistentStorageTransactionApi interface { + NewIterator(prefix string) PersistentStorageIteratorApi + Set(key string, value []byte) error + Get(key string) ([]byte, error) +} + +// Persistent Storage Iterator API provides an iterface for interacting with persistent storage iterators +type PersistentStorageIteratorApi interface { + Rewind() + Valid() bool + Next() + + Key() string + Value() ([]byte, error) + + Close() +} From 73ee1cd89e9cc59fcfff9cbc3282c25cac128bdd Mon Sep 17 00:00:00 2001 From: Nate Roiger Date: Tue, 19 Jul 2022 12:27:13 -0500 Subject: [PATCH 2/7] Add support for base64 transaction & iterator. Organize storage* files Signed-off-by: Nate Roiger --- pkg/persistent/{storage.go => storage_api.go} | 0 pkg/persistent/storage_base64.go | 95 +++++++++++++++++++ .../{local_storage.go => storage_local.go} | 0 3 files changed, 95 insertions(+) rename pkg/persistent/{storage.go => storage_api.go} (100%) create mode 100644 pkg/persistent/storage_base64.go rename pkg/persistent/{local_storage.go => storage_local.go} (100%) diff --git a/pkg/persistent/storage.go b/pkg/persistent/storage_api.go similarity index 100% rename from pkg/persistent/storage.go rename to pkg/persistent/storage_api.go diff --git a/pkg/persistent/storage_base64.go b/pkg/persistent/storage_base64.go new file mode 100644 index 0000000..d714157 --- /dev/null +++ b/pkg/persistent/storage_base64.go @@ -0,0 +1,95 @@ +/* + * Copyright 2020, 2021, 2022 Hewlett Packard Enterprise Development LP + * Other additional copyright holders may be indicated within. + * + * The entirety of this work is licensed under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package persistent + +import ( + "encoding/base64" + "fmt" + "strings" +) + +type base64PersistentStorageTransaction struct { + data map[string]string +} + +func NewBase64PersistentStorageTransaction(data map[string]string) PersistentStorageTransactionApi { + return &base64PersistentStorageTransaction{data: data} +} + +func (txn *base64PersistentStorageTransaction) Get(key string) ([]byte, error) { + value, found := txn.data[key] + if !found { + return nil, fmt.Errorf("Key %s not found", key) + } + + return base64.StdEncoding.DecodeString(value) +} + +func (txn *base64PersistentStorageTransaction) Set(key string, value []byte) error { + txn.data[key] = base64.StdEncoding.EncodeToString(value) + return nil +} + +func (txn *base64PersistentStorageTransaction) NewIterator(prefix string) PersistentStorageIteratorApi { + itr := base64PersistentStorageIterator{ + keys: make([]string, 0), + data: txn.data, + index: 0, + } + + for key := range txn.data { + if strings.HasPrefix(key, prefix) { + itr.keys = append(itr.keys, key) + } + } + + return &itr +} + +type base64PersistentStorageIterator struct { + keys []string + data map[string]string + index int +} + +func (itr *base64PersistentStorageIterator) Close() { + itr.keys = []string{} +} + +func (itr *base64PersistentStorageIterator) Key() string { + return itr.keys[itr.index] +} + +func (itr *base64PersistentStorageIterator) Rewind() { + itr.index = 0 +} + +func (itr *base64PersistentStorageIterator) Valid() bool { + return itr.index < len(itr.keys) +} + +func (itr *base64PersistentStorageIterator) Next() { + itr.index += 1 +} + +func (itr *base64PersistentStorageIterator) Value() ([]byte, error) { + key := itr.keys[itr.index] + return base64.StdEncoding.DecodeString(itr.data[key]) +} diff --git a/pkg/persistent/local_storage.go b/pkg/persistent/storage_local.go similarity index 100% rename from pkg/persistent/local_storage.go rename to pkg/persistent/storage_local.go From f4e15f4ef94b7194b0bf18775c9ba82299574b40 Mon Sep 17 00:00:00 2001 From: Nate Roiger Date: Tue, 19 Jul 2022 13:35:16 -0500 Subject: [PATCH 3/7] Debug for JSON files Signed-off-by: Nate Roiger --- pkg/persistent/debug/kvdebug.go | 12 ++++- pkg/persistent/debug/storage_json.go | 73 ++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 pkg/persistent/debug/storage_json.go diff --git a/pkg/persistent/debug/kvdebug.go b/pkg/persistent/debug/kvdebug.go index b1cff9f..1f74fa1 100644 --- a/pkg/persistent/debug/kvdebug.go +++ b/pkg/persistent/debug/kvdebug.go @@ -28,9 +28,15 @@ import ( func main() { var path string + var json string flag.StringVar(&path, "path", "nnf.db", "the kvstore database to display") + flag.StringVar(&json, "json", "", "json file to parse") flag.Parse() + if len(json) != 0 { + persistent.StorageProvider = NewJsonFilePersistentStorageProvider(json) + } + fmt.Printf("Debug KVStore Tool. Path: '%s'\n", path) store, err := persistent.Open(path, true) if err != nil { @@ -46,8 +52,10 @@ func main() { type debugRegistry struct{} -func (*debugRegistry) Prefix() string { return "" } -func (*debugRegistry) NewReplay(id string) persistent.ReplayHandler { return &debugReplayHandler{id: id} } +func (*debugRegistry) Prefix() string { return "" } +func (*debugRegistry) NewReplay(id string) persistent.ReplayHandler { + return &debugReplayHandler{id: id} +} type debugReplayHandler struct { id string diff --git a/pkg/persistent/debug/storage_json.go b/pkg/persistent/debug/storage_json.go new file mode 100644 index 0000000..331483a --- /dev/null +++ b/pkg/persistent/debug/storage_json.go @@ -0,0 +1,73 @@ +/* + * Copyright 2020, 2021, 2022 Hewlett Packard Enterprise Development LP + * Other additional copyright holders may be indicated within. + * + * The entirety of this work is licensed under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "encoding/json" + "io/ioutil" + + "github.com/NearNodeFlash/nnf-ec/pkg/persistent" +) + +func NewJsonFilePersistentStorageProvider(filename string) persistent.PersistentStorageProvider { + return &jsonFilePersisentStorageProvider{filename: filename} +} + +type jsonFilePersisentStorageProvider struct { + filename string +} + +func (p *jsonFilePersisentStorageProvider) NewPersistentStorageInterface(name string, readOnly bool) (persistent.PersistentStorageApi, error) { + content, err := ioutil.ReadFile(p.filename) + if err != nil { + return nil, err + } + + var payload map[string]map[string]string + if err := json.Unmarshal(content, &payload); err != nil { + return nil, err + } + + return &jsonPersistentStorageInterface{data: payload[name]}, nil +} + +type jsonPersistentStorageInterface struct { + data map[string]string +} + +func (psi *jsonPersistentStorageInterface) View(fn func(persistent.PersistentStorageTransactionApi) error) error { + return fn(persistent.NewBase64PersistentStorageTransaction(psi.data)) +} + +func (*jsonPersistentStorageInterface) Update(func(txn persistent.PersistentStorageTransactionApi) error) error { + panic("unimplemented") +} + +func (*jsonPersistentStorageInterface) Delete(key string) error { + panic("unimplemented") +} + +func (*jsonPersistentStorageInterface) Close() error { + panic("unimplemented") +} + + + + From c067dd257886fe09b92da521edd94da3c1df251f Mon Sep 17 00:00:00 2001 From: Nate Roiger Date: Tue, 19 Jul 2022 14:03:07 -0500 Subject: [PATCH 4/7] Fix bug in kvstore's ledger logic that caused duplicate entries when an update is retried Signed-off-by: Nate Roiger --- pkg/persistent/kvstore.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/persistent/kvstore.go b/pkg/persistent/kvstore.go index 7aaab36..68661e0 100644 --- a/pkg/persistent/kvstore.go +++ b/pkg/persistent/kvstore.go @@ -256,9 +256,9 @@ type Ledger struct { func (l *Ledger) Log(t uint32, v []byte) error { tlv := newTlv(t, v) + l.bytes = append(l.bytes, tlv.bytes()...) err := l.s.storage.Update(func(txn PersistentStorageTransactionApi) error { - l.bytes = append(l.bytes, tlv.bytes()...) return txn.Set(l.key, l.bytes) }) From d7e848fe49f541018bab4ec5ee8eafbfdaaa4bcd Mon Sep 17 00:00:00 2001 From: Nate Roiger Date: Tue, 19 Jul 2022 14:27:51 -0500 Subject: [PATCH 5/7] Allow starting of nnf-ec with a json database Signed-off-by: Nate Roiger --- pkg/controller.go | 6 ++++++ pkg/persistent/debug/kvdebug.go | 2 +- pkg/persistent/{debug => }/storage_json.go | 13 ++++++------- 3 files changed, 13 insertions(+), 8 deletions(-) rename pkg/persistent/{debug => }/storage_json.go (73%) diff --git a/pkg/controller.go b/pkg/controller.go index e4b1849..7d019fc 100644 --- a/pkg/controller.go +++ b/pkg/controller.go @@ -40,6 +40,7 @@ import ( nnf "github.com/NearNodeFlash/nnf-ec/pkg/manager-nnf" nvme "github.com/NearNodeFlash/nnf-ec/pkg/manager-nvme" telemetry "github.com/NearNodeFlash/nnf-ec/pkg/manager-telemetry" + "github.com/NearNodeFlash/nnf-ec/pkg/persistent" log "github.com/sirupsen/logrus" ) @@ -54,6 +55,7 @@ type Options struct { cli bool // Enable CLI commands instead of binary persistence bool // Enable persistent object storage; used during crash/reboot recovery + json string // Initialize the element controller with the provided json file direct string // Enable direct management of NVMe devices matching this regexp pattern } @@ -71,6 +73,7 @@ func BindFlags(fs *flag.FlagSet) *Options { fs.BoolVar(&opts.mock, "mock", opts.mock, "Enable mock (simulated) environment.") fs.BoolVar(&opts.cli, "cli", opts.cli, "Enable CLI interfaces with devices, instead of raw binary.") fs.BoolVar(&opts.persistence, "persistence", opts.persistence, "Enable persistent object storage (used during crash/reboot recovery)") + fs.StringVar(&opts.json, "json", "", "Initialize database with provided json file") fs.StringVar(&opts.direct, "direct", opts.direct, "Enable direct management of NVMe block devices matching this regexp pattern. Implies Mock.") nvme.BindFlags(fs) @@ -106,7 +109,10 @@ func NewController(opts *Options) *ec.Controller { } else { nnfCtrl = nnf.NewMockNnfController(opts.persistence) } + } + if len(opts.json) != 0 { + persistent.StorageProvider = persistent.NewJsonFilePersistentStorageProvider(opts.json) } return &ec.Controller{ diff --git a/pkg/persistent/debug/kvdebug.go b/pkg/persistent/debug/kvdebug.go index 1f74fa1..4857be0 100644 --- a/pkg/persistent/debug/kvdebug.go +++ b/pkg/persistent/debug/kvdebug.go @@ -34,7 +34,7 @@ func main() { flag.Parse() if len(json) != 0 { - persistent.StorageProvider = NewJsonFilePersistentStorageProvider(json) + persistent.StorageProvider = persistent.NewJsonFilePersistentStorageProvider(json) } fmt.Printf("Debug KVStore Tool. Path: '%s'\n", path) diff --git a/pkg/persistent/debug/storage_json.go b/pkg/persistent/storage_json.go similarity index 73% rename from pkg/persistent/debug/storage_json.go rename to pkg/persistent/storage_json.go index 331483a..f96353c 100644 --- a/pkg/persistent/debug/storage_json.go +++ b/pkg/persistent/storage_json.go @@ -17,16 +17,15 @@ * limitations under the License. */ -package main +package persistent import ( "encoding/json" "io/ioutil" - "github.com/NearNodeFlash/nnf-ec/pkg/persistent" ) -func NewJsonFilePersistentStorageProvider(filename string) persistent.PersistentStorageProvider { +func NewJsonFilePersistentStorageProvider(filename string) PersistentStorageProvider { return &jsonFilePersisentStorageProvider{filename: filename} } @@ -34,7 +33,7 @@ type jsonFilePersisentStorageProvider struct { filename string } -func (p *jsonFilePersisentStorageProvider) NewPersistentStorageInterface(name string, readOnly bool) (persistent.PersistentStorageApi, error) { +func (p *jsonFilePersisentStorageProvider) NewPersistentStorageInterface(name string, readOnly bool) (PersistentStorageApi, error) { content, err := ioutil.ReadFile(p.filename) if err != nil { return nil, err @@ -52,11 +51,11 @@ type jsonPersistentStorageInterface struct { data map[string]string } -func (psi *jsonPersistentStorageInterface) View(fn func(persistent.PersistentStorageTransactionApi) error) error { - return fn(persistent.NewBase64PersistentStorageTransaction(psi.data)) +func (psi *jsonPersistentStorageInterface) View(fn func(PersistentStorageTransactionApi) error) error { + return fn(NewBase64PersistentStorageTransaction(psi.data)) } -func (*jsonPersistentStorageInterface) Update(func(txn persistent.PersistentStorageTransactionApi) error) error { +func (*jsonPersistentStorageInterface) Update(func(PersistentStorageTransactionApi) error) error { panic("unimplemented") } From 5bb9544b9bb379438e7c45a5d0bebd801e50998c Mon Sep 17 00:00:00 2001 From: Nate Roiger Date: Tue, 19 Jul 2022 15:44:14 -0500 Subject: [PATCH 6/7] Ensure the nnf manager is initialized to a disabled state Signed-off-by: Nate Roiger --- pkg/manager-nnf/manager.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pkg/manager-nnf/manager.go b/pkg/manager-nnf/manager.go index f89a140..c26a81d 100644 --- a/pkg/manager-nnf/manager.go +++ b/pkg/manager-nnf/manager.go @@ -28,18 +28,21 @@ import ( "github.com/google/uuid" log "github.com/sirupsen/logrus" - "github.com/NearNodeFlash/nnf-ec/pkg/persistent" ec "github.com/NearNodeFlash/nnf-ec/pkg/ec" event "github.com/NearNodeFlash/nnf-ec/pkg/manager-event" fabric "github.com/NearNodeFlash/nnf-ec/pkg/manager-fabric" msgreg "github.com/NearNodeFlash/nnf-ec/pkg/manager-message-registry/registries" nvme "github.com/NearNodeFlash/nnf-ec/pkg/manager-nvme" server "github.com/NearNodeFlash/nnf-ec/pkg/manager-server" + "github.com/NearNodeFlash/nnf-ec/pkg/persistent" openapi "github.com/NearNodeFlash/nnf-ec/pkg/rfsf/pkg/common" sf "github.com/NearNodeFlash/nnf-ec/pkg/rfsf/pkg/models" ) -var storageService = StorageService{} +var storageService = StorageService{ + id: DefaultStorageServiceId, + state: sf.DISABLED_RST, +} func NewDefaultStorageService() StorageServiceApi { return NewAerService(&storageService) // Wrap the default storage service with Advanced Error Reporting capabilities From d6f36002c38c0514b3fdeb8cfc380a31dd03bee1 Mon Sep 17 00:00:00 2001 From: Nate Roiger Date: Wed, 20 Jul 2022 10:48:36 -0500 Subject: [PATCH 7/7] Correct copyright headers for new files Signed-off-by: Nate Roiger --- pkg/persistent/storage_api.go | 2 +- pkg/persistent/storage_base64.go | 2 +- pkg/persistent/storage_json.go | 2 +- pkg/persistent/storage_local.go | 3 +-- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/pkg/persistent/storage_api.go b/pkg/persistent/storage_api.go index 59f3bf5..b6c69ed 100644 --- a/pkg/persistent/storage_api.go +++ b/pkg/persistent/storage_api.go @@ -1,5 +1,5 @@ /* - * Copyright 2020, 2021, 2022 Hewlett Packard Enterprise Development LP + * Copyright 2022 Hewlett Packard Enterprise Development LP * Other additional copyright holders may be indicated within. * * The entirety of this work is licensed under the Apache License, diff --git a/pkg/persistent/storage_base64.go b/pkg/persistent/storage_base64.go index d714157..c0c14dc 100644 --- a/pkg/persistent/storage_base64.go +++ b/pkg/persistent/storage_base64.go @@ -1,5 +1,5 @@ /* - * Copyright 2020, 2021, 2022 Hewlett Packard Enterprise Development LP + * Copyright 2022 Hewlett Packard Enterprise Development LP * Other additional copyright holders may be indicated within. * * The entirety of this work is licensed under the Apache License, diff --git a/pkg/persistent/storage_json.go b/pkg/persistent/storage_json.go index f96353c..1f9e5f8 100644 --- a/pkg/persistent/storage_json.go +++ b/pkg/persistent/storage_json.go @@ -1,5 +1,5 @@ /* - * Copyright 2020, 2021, 2022 Hewlett Packard Enterprise Development LP + * Copyright 2022 Hewlett Packard Enterprise Development LP * Other additional copyright holders may be indicated within. * * The entirety of this work is licensed under the Apache License, diff --git a/pkg/persistent/storage_local.go b/pkg/persistent/storage_local.go index aa9f5ad..8a788d3 100644 --- a/pkg/persistent/storage_local.go +++ b/pkg/persistent/storage_local.go @@ -1,5 +1,5 @@ /* - * Copyright 2020, 2021, 2022 Hewlett Packard Enterprise Development LP + * Copyright 2022 Hewlett Packard Enterprise Development LP * Other additional copyright holders may be indicated within. * * The entirety of this work is licensed under the Apache License, @@ -108,7 +108,6 @@ func (txn *localPersistentStorageTransaction) Get(key string) ([]byte, error) { return value, err } - err = item.Value(func(val []byte) error { value = append(value, val...) return nil