From 6928a9c409f24ee1b9c6de79d7736ebd1bfe9513 Mon Sep 17 00:00:00 2001 From: "A.Unger" Date: Wed, 14 Oct 2020 12:50:45 +0200 Subject: [PATCH] add bounds to index --- .../pkg/indexer/index/disk/autoincrement.go | 26 +++--- .../indexer/index/disk/autoincrement_test.go | 79 ++++++++++++++++++- accounts/pkg/indexer/option/option.go | 15 ++++ 3 files changed, 108 insertions(+), 12 deletions(-) diff --git a/accounts/pkg/indexer/index/disk/autoincrement.go b/accounts/pkg/indexer/index/disk/autoincrement.go index 77d544215d..5e6c29db07 100644 --- a/accounts/pkg/indexer/index/disk/autoincrement.go +++ b/accounts/pkg/indexer/index/disk/autoincrement.go @@ -24,6 +24,8 @@ type Autoincrement struct { indexBaseDir string indexRootDir string entity interface{} + + bound *option.Bound } // - Creating an autoincrement index has to be thread safe. @@ -55,6 +57,7 @@ func NewAutoincrementIndex(o ...option.Option) index.Index { indexBy: opts.IndexBy, typeName: opts.TypeName, filesDir: opts.FilesDir, + bound: opts.Bound, indexBaseDir: path.Join(opts.DataDir, "index.disk"), indexRootDir: path.Join(path.Join(opts.DataDir, "index.disk"), strings.Join([]string{"autoincrement", opts.TypeName, opts.IndexBy}, ".")), } @@ -100,14 +103,18 @@ func (idx Autoincrement) Lookup(v string) ([]string, error) { } func (idx Autoincrement) Add(id, v string) (string, error) { + nextID, err := idx.next() + if err != nil { + return "", err + } oldName := filepath.Join(idx.filesDir, id) - var newName string + newName := filepath.Join(idx.indexRootDir, strconv.Itoa(nextID)) if v == "" { - newName = filepath.Join(idx.indexRootDir, strconv.Itoa(idx.next())) + newName = filepath.Join(idx.indexRootDir, strconv.Itoa(nextID)) } else { newName = filepath.Join(idx.indexRootDir, v) } - err := os.Symlink(oldName, newName) + err = os.Symlink(oldName, newName) if errors.Is(err, os.ErrExist) { return "", &idxerrs.AlreadyExistsErr{TypeName: idx.typeName, Key: idx.indexBy, Value: v} } @@ -216,19 +223,20 @@ func readDir(dirname string) ([]os.FileInfo, error) { return list, nil } -func (idx Autoincrement) next() int { +func (idx Autoincrement) next() (int, error) { files, err := readDir(idx.indexRootDir) if err != nil { - // hello handle me pls. + return -1, err } if len(files) == 0 { - return 0 + return int(idx.bound.Lower), nil } - latest, err := strconv.Atoi(path.Base(files[len(files)-1].Name())) // would returning a string be a better interface? + latest, err := strconv.Atoi(path.Base(files[len(files)-1].Name())) if err != nil { - // handle me daddy + return -1, err } - return latest + 1 + + return latest + 1, nil } diff --git a/accounts/pkg/indexer/index/disk/autoincrement_test.go b/accounts/pkg/indexer/index/disk/autoincrement_test.go index c951d5da16..def0304bec 100644 --- a/accounts/pkg/indexer/index/disk/autoincrement_test.go +++ b/accounts/pkg/indexer/index/disk/autoincrement_test.go @@ -116,6 +116,10 @@ func TestNext(t *testing.T) { assert.NoError(t, err) i := NewAutoincrementIndex( + option.WithBounds(&option.Bound{ + Lower: 0, + Upper: 0, + }), option.WithDataDir(tmpDir), option.WithFilesDir(filepath.Join(tmpDir, "data")), option.WithEntity(scenario.entity), @@ -132,15 +136,80 @@ func TestNext(t *testing.T) { oldName, err := i.Add("test-example", "") assert.NoError(t, err) - assert.Equal(t, filepath.Base(oldName), 0) + assert.Equal(t, "0", filepath.Base(oldName)) oldName, err = i.Add("test-example", "") assert.NoError(t, err) - assert.Equal(t, filepath.Base(oldName), 1) + assert.Equal(t, "1", filepath.Base(oldName)) oldName, err = i.Add("test-example", "") assert.NoError(t, err) - assert.Equal(t, filepath.Base(oldName), 2) + assert.Equal(t, "2", filepath.Base(oldName)) + t.Log(oldName) + + _ = os.RemoveAll(tmpDir) + }) + } +} + +func TestLowerBound(t *testing.T) { + scenarios := []struct { + name string + expected int + indexBy string + entity interface{} + }{ + { + name: "get next value with a lower bound specified", + expected: 0, + indexBy: "Number", + entity: struct { + Number int + Name string + NumberFloat float32 + }{ + Name: "tesy-mc-testace", + }, + }, + } + + for _, scenario := range scenarios { + t.Run(scenario.name, func(t *testing.T) { + tmpDir, err := createTmpDirStr() + assert.NoError(t, err) + + err = os.MkdirAll(filepath.Join(tmpDir, "data"), 0777) + assert.NoError(t, err) + + i := NewAutoincrementIndex( + option.WithBounds(&option.Bound{ + Lower: 1000, + }), + option.WithDataDir(tmpDir), + option.WithFilesDir(filepath.Join(tmpDir, "data")), + option.WithEntity(scenario.entity), + option.WithTypeName("LambdaType"), + option.WithIndexBy(scenario.indexBy), + ) + + err = i.Init() + assert.NoError(t, err) + + tmpFile, err := os.Create(filepath.Join(tmpDir, "data", "test-example")) + assert.NoError(t, err) + assert.NoError(t, tmpFile.Close()) + + oldName, err := i.Add("test-example", "") + assert.NoError(t, err) + assert.Equal(t, "1000", filepath.Base(oldName)) + + oldName, err = i.Add("test-example", "") + assert.NoError(t, err) + assert.Equal(t, "1001", filepath.Base(oldName)) + + oldName, err = i.Add("test-example", "") + assert.NoError(t, err) + assert.Equal(t, "1002", filepath.Base(oldName)) t.Log(oldName) _ = os.RemoveAll(tmpDir) @@ -160,6 +229,10 @@ func BenchmarkAdd(b *testing.B) { assert.NoError(b, tmpFile.Close()) i := NewAutoincrementIndex( + option.WithBounds(&option.Bound{ + Lower: 0, + Upper: 0, + }), option.WithDataDir(tmpDir), option.WithFilesDir(filepath.Join(tmpDir, "data")), option.WithEntity(struct { diff --git a/accounts/pkg/indexer/option/option.go b/accounts/pkg/indexer/option/option.go index af578886f5..dd3c6d9b78 100644 --- a/accounts/pkg/indexer/option/option.go +++ b/accounts/pkg/indexer/option/option.go @@ -3,6 +3,13 @@ package option // Option defines a single option function. type Option func(o *Options) +// Bound represents a lower and upper bound range for an index. +// todo: if we would like to provide an upper bound then we would need to deal with ranges, in which case this is why the +// upper bound attribute is here. +type Bound struct { + Lower, Upper int64 +} + // Options defines the available options for this package. type Options struct { // Disk Options @@ -13,6 +20,7 @@ type Options struct { DataDir string EntityDirName string Entity interface{} + Bound *Bound // CS3 options DataURL string @@ -21,6 +29,13 @@ type Options struct { ProviderAddr string } +// WithBounds sets the Bounds field. +func WithBounds(val *Bound) Option { + return func(o *Options) { + o.Bound = val + } +} + // WithEntity sets the Entity field. func WithEntity(val interface{}) Option { return func(o *Options) {