From 1999c2b309f4d624a971485d19d02b69a6c43dce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jer=C3=B3nimo=20Albi?= Date: Wed, 13 Dec 2023 18:09:50 +0100 Subject: [PATCH] feat(pkg/cache): support version prefix (#3829) * feat(pkg/cache): support version prefix * chore: update changelog * tests: add versioned storage cache tests --------- Co-authored-by: Danilo Pantani --- changelog.md | 1 + ignite/cmd/cmd.go | 5 +++- ignite/pkg/cache/cache.go | 23 ++++++++++++------ ignite/pkg/cache/cache_test.go | 43 ++++++++++++++++++++++++++++++--- ignite/pkg/cache/options.go | 12 +++++++++ ignite/services/plugin/cache.go | 6 ++++- 6 files changed, 77 insertions(+), 13 deletions(-) create mode 100644 ignite/pkg/cache/options.go diff --git a/changelog.md b/changelog.md index 8d085738dd..7b715484dc 100644 --- a/changelog.md +++ b/changelog.md @@ -10,6 +10,7 @@ - [#3822](https://github.com/ignite/cli/pull/3822) Improve default scaffolded AutoCLI config - [#3838](https://github.com/ignite/cli/pull/3838) Scaffold chain with Cosmos SDK `v0.50.2`, and bump confix and x/upgrade to latest +- [#3829](https://github.com/ignite/cli/pull/3829) Support version prefix for cached values - [#3723](https://github.com/ignite/cli/pull/3723) Create a wrapper for errors ### Fixes diff --git a/ignite/cmd/cmd.go b/ignite/cmd/cmd.go index 8256f85e74..a30811e180 100644 --- a/ignite/cmd/cmd.go +++ b/ignite/cmd/cmd.go @@ -275,7 +275,10 @@ func newCache(cmd *cobra.Command) (cache.Storage, error) { return cache.Storage{}, err } - storage, err := cache.NewStorage(filepath.Join(cacheRootDir, cacheFileName)) + storage, err := cache.NewStorage( + filepath.Join(cacheRootDir, cacheFileName), + cache.WithVersion(version.Version), + ) if err != nil { return cache.Storage{}, err } diff --git a/ignite/pkg/cache/cache.go b/ignite/pkg/cache/cache.go index c447a965d6..211d6f7481 100644 --- a/ignite/pkg/cache/cache.go +++ b/ignite/pkg/cache/cache.go @@ -3,6 +3,7 @@ package cache import ( "bytes" "encoding/gob" + "fmt" "os" "path/filepath" "strings" @@ -17,7 +18,7 @@ var ErrorNotFound = errors.New("no value was found with the provided key") // Storage is meant to be passed around and used by the New function (which provides namespacing and type-safety). type Storage struct { - storagePath string + path, version string } // Cache is a namespaced and type-safe key-value store. @@ -29,16 +30,24 @@ type Cache[T any] struct { // NewStorage sets up the storage needed for later cache usage // path is the full path (including filename) to the database file to use. // It does not need to be closed as this happens automatically in each call to the cache. -func NewStorage(path string) (Storage, error) { +func NewStorage(path string, options ...StorageOption) (Storage, error) { if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { return Storage{}, err } - return Storage{path}, nil + s := Storage{path: path} + for _, apply := range options { + apply(&s) + } + return s, nil } // New creates a namespaced and typesafe key-value Cache. func New[T any](storage Storage, namespace string) Cache[T] { + if storage.version != "" { + namespace = fmt.Sprint(storage.version, namespace) + } + return Cache[T]{ storage: storage, namespace: namespace, @@ -52,7 +61,7 @@ func Key(keyParts ...string) string { // Clear deletes all namespaces and cached values. func (s Storage) Clear() error { - db, err := openDB(s.storagePath) + db, err := openDB(s.path) if err != nil { return err } @@ -68,7 +77,7 @@ func (s Storage) Clear() error { // Put sets key to value within the namespace // If the key already exists, it will be overwritten. func (c Cache[T]) Put(key string, value T) error { - db, err := openDB(c.storage.storagePath) + db, err := openDB(c.storage.path) if err != nil { return err } @@ -93,7 +102,7 @@ func (c Cache[T]) Put(key string, value T) error { // Get fetches the value of key within the namespace. // If no value exists, it will return found == false. func (c Cache[T]) Get(key string) (val T, err error) { - db, err := openDB(c.storage.storagePath) + db, err := openDB(c.storage.path) if err != nil { return val, err } @@ -129,7 +138,7 @@ func (c Cache[T]) Get(key string) (val T, err error) { // Delete removes a value for key within the namespace. func (c Cache[T]) Delete(key string) error { - db, err := openDB(c.storage.storagePath) + db, err := openDB(c.storage.path) if err != nil { return err } diff --git a/ignite/pkg/cache/cache_test.go b/ignite/pkg/cache/cache_test.go index f72b403c8d..817d8665d0 100644 --- a/ignite/pkg/cache/cache_test.go +++ b/ignite/pkg/cache/cache_test.go @@ -14,14 +14,49 @@ type TestStruct struct { } func TestCreateStorage(t *testing.T) { - tmpDir1 := t.TempDir() - tmpDir2 := t.TempDir() + cases := []struct { + name string + options []cache.StorageOption + }{ + { + name: "simple", + }, + { + name: "versioned", + options: []cache.StorageOption{ + cache.WithVersion("v0.1.0"), + }, + }, + } + + for _, tt := range cases { + t.Run(tt.name, func(t *testing.T) { + _, err := cache.NewStorage(filepath.Join(t.TempDir(), "cache.db"), tt.options...) + require.NoError(t, err) + }) + } +} - _, err := cache.NewStorage(filepath.Join(tmpDir1, "test.db")) +func TestStoreWithVersion(t *testing.T) { + path := filepath.Join(t.TempDir(), "cache.db") + storage, err := cache.NewStorage(path, cache.WithVersion("v0.1.0")) require.NoError(t, err) - _, err = cache.NewStorage(filepath.Join(tmpDir2, "test.db")) + nsCache := cache.New[string](storage, "cacheNS") + err = nsCache.Put("myKey", "myValue") require.NoError(t, err) + + v, err := nsCache.Get("myKey") + require.NoError(t, err) + require.Equal(t, "myValue", v) + + // Create a non versioned storage with the same file path + storage, err = cache.NewStorage(path) + require.NoError(t, err) + + nsCache = cache.New[string](storage, "cacheNS") + _, err = nsCache.Get("myKey") + require.ErrorIs(t, err, cache.ErrorNotFound) } func TestStoreString(t *testing.T) { diff --git a/ignite/pkg/cache/options.go b/ignite/pkg/cache/options.go new file mode 100644 index 0000000000..7356a5c510 --- /dev/null +++ b/ignite/pkg/cache/options.go @@ -0,0 +1,12 @@ +package cache + +// StorageOptions configures the cache storage. +type StorageOption func(*Storage) + +// WithVersion sets a version for the storage. +// Version is used as prefix for any cached value. +func WithVersion(version string) StorageOption { + return func(o *Storage) { + o.version = version + } +} diff --git a/ignite/services/plugin/cache.go b/ignite/services/plugin/cache.go index 89fe8d32c6..df16d2e457 100644 --- a/ignite/services/plugin/cache.go +++ b/ignite/services/plugin/cache.go @@ -9,6 +9,7 @@ import ( "github.com/ignite/cli/v28/ignite/pkg/cache" "github.com/ignite/cli/v28/ignite/pkg/errors" + "github.com/ignite/cli/v28/ignite/version" ) const ( @@ -80,7 +81,10 @@ func newCache() (*cache.Cache[hplugin.ReattachConfig], error) { return nil, err } if storageCache == nil { - storage, err := cache.NewStorage(path.Join(cacheRootDir, cacheFileName)) + storage, err := cache.NewStorage( + path.Join(cacheRootDir, cacheFileName), + cache.WithVersion(version.Version), + ) if err != nil { return nil, err }