mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-02-25 06:58:59 -06:00
add bounds to index
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user