2019-12-27 02:15:14 +00:00
|
|
|
package kv_test
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2021-03-30 18:10:02 +00:00
|
|
|
"github.com/influxdata/influxdb/v2/kit/platform/errors"
|
2019-12-27 02:15:14 +00:00
|
|
|
"testing"
|
|
|
|
|
2020-04-03 17:39:20 +00:00
|
|
|
"github.com/influxdata/influxdb/v2/kv"
|
2020-07-01 11:08:20 +00:00
|
|
|
"github.com/influxdata/influxdb/v2/kv/migration"
|
2019-12-27 02:15:14 +00:00
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestIndexStore(t *testing.T) {
|
2019-12-31 03:11:53 +00:00
|
|
|
newStoreBase := func(resource string, bktName []byte, encKeyFn, encBodyFn kv.EncodeEntFn, decFn kv.DecodeBucketValFn, decToEntFn kv.ConvertValToEntFn) *kv.StoreBase {
|
2019-12-27 02:15:14 +00:00
|
|
|
return kv.NewStoreBase(resource, bktName, encKeyFn, encBodyFn, decFn, decToEntFn)
|
|
|
|
}
|
|
|
|
|
2019-12-31 03:11:53 +00:00
|
|
|
newFooIndexStore := func(t *testing.T, bktSuffix string) (*kv.IndexStore, func(), kv.Store) {
|
|
|
|
t.Helper()
|
|
|
|
|
2020-01-06 23:31:50 +00:00
|
|
|
kvStoreStore, done, err := NewTestBoltStore(t)
|
2019-12-31 03:11:53 +00:00
|
|
|
require.NoError(t, err)
|
2019-12-27 02:15:14 +00:00
|
|
|
|
|
|
|
const resource = "foo"
|
|
|
|
|
2020-07-01 11:08:20 +00:00
|
|
|
bucketName := []byte("foo_ent_" + bktSuffix)
|
|
|
|
indexBucketName := []byte("foo_idx+" + bktSuffix)
|
|
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
if err := migration.CreateBuckets("add foo buckets", bucketName, indexBucketName).Up(ctx, kvStoreStore); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2019-12-27 02:15:14 +00:00
|
|
|
indexStore := &kv.IndexStore{
|
|
|
|
Resource: resource,
|
2020-07-01 11:08:20 +00:00
|
|
|
EntStore: newStoreBase(resource, bucketName, kv.EncIDKey, kv.EncBodyJSON, decJSONFooFn, decFooEntFn),
|
|
|
|
IndexStore: kv.NewOrgNameKeyStore(resource, indexBucketName, false),
|
2019-12-27 02:15:14 +00:00
|
|
|
}
|
|
|
|
|
2020-01-06 23:31:50 +00:00
|
|
|
return indexStore, done, kvStoreStore
|
2019-12-27 02:15:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
t.Run("Put", func(t *testing.T) {
|
2020-01-06 23:31:50 +00:00
|
|
|
t.Run("basic", func(t *testing.T) {
|
|
|
|
indexStore, done, kvStore := newFooIndexStore(t, "put")
|
|
|
|
defer done()
|
2019-12-27 02:15:14 +00:00
|
|
|
|
2020-01-06 23:31:50 +00:00
|
|
|
expected := testPutBase(t, kvStore, indexStore, indexStore.EntStore.BktName)
|
2019-12-27 02:15:14 +00:00
|
|
|
|
2020-01-06 23:31:50 +00:00
|
|
|
key, err := indexStore.IndexStore.EntKey(context.TODO(), kv.Entity{
|
|
|
|
UniqueKey: kv.Encode(kv.EncID(expected.OrgID), kv.EncString(expected.Name)),
|
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
rawIndex := getEntRaw(t, kvStore, indexStore.IndexStore.BktName, key)
|
|
|
|
assert.Equal(t, encodeID(t, expected.ID), rawIndex)
|
2019-12-27 02:15:14 +00:00
|
|
|
})
|
|
|
|
|
2020-01-06 23:31:50 +00:00
|
|
|
t.Run("new entity", func(t *testing.T) {
|
|
|
|
indexStore, done, kvStore := newFooIndexStore(t, "put")
|
|
|
|
defer done()
|
|
|
|
|
|
|
|
expected := foo{ID: 3, OrgID: 33, Name: "333"}
|
|
|
|
update(t, kvStore, func(tx kv.Tx) error {
|
|
|
|
ent := newFooEnt(expected.ID, expected.OrgID, expected.Name)
|
|
|
|
return indexStore.Put(context.TODO(), tx, ent, kv.PutNew())
|
|
|
|
})
|
|
|
|
|
|
|
|
key, err := indexStore.IndexStore.EntKey(context.TODO(), kv.Entity{
|
|
|
|
UniqueKey: kv.Encode(kv.EncID(expected.OrgID), kv.EncString(expected.Name)),
|
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
rawIndex := getEntRaw(t, kvStore, indexStore.IndexStore.BktName, key)
|
|
|
|
assert.Equal(t, encodeID(t, expected.ID), rawIndex)
|
|
|
|
})
|
|
|
|
|
2020-03-20 02:00:27 +00:00
|
|
|
t.Run("updating entity that doesn't exist returns not found error", func(t *testing.T) {
|
|
|
|
indexStore, done, kvStore := newFooIndexStore(t, "put")
|
|
|
|
defer done()
|
|
|
|
|
|
|
|
expected := testPutBase(t, kvStore, indexStore, indexStore.EntStore.BktName)
|
|
|
|
|
|
|
|
err := kvStore.Update(context.Background(), func(tx kv.Tx) error {
|
|
|
|
ent := newFooEnt(33333, expected.OrgID, "safe name")
|
|
|
|
return indexStore.Put(context.TODO(), tx, ent, kv.PutUpdate())
|
|
|
|
})
|
|
|
|
require.Error(t, err)
|
2021-03-30 18:10:02 +00:00
|
|
|
assert.Equal(t, errors.ENotFound, errors.ErrorCode(err))
|
2020-03-20 02:00:27 +00:00
|
|
|
})
|
|
|
|
|
2020-01-06 23:31:50 +00:00
|
|
|
t.Run("updating entity with no naming collision succeeds", func(t *testing.T) {
|
|
|
|
indexStore, done, kvStore := newFooIndexStore(t, "put")
|
|
|
|
defer done()
|
|
|
|
|
|
|
|
expected := testPutBase(t, kvStore, indexStore, indexStore.EntStore.BktName)
|
|
|
|
|
|
|
|
update(t, kvStore, func(tx kv.Tx) error {
|
|
|
|
entCopy := newFooEnt(expected.ID, expected.OrgID, "safe name")
|
|
|
|
return indexStore.Put(context.TODO(), tx, entCopy, kv.PutUpdate())
|
|
|
|
})
|
|
|
|
|
|
|
|
err := kvStore.View(context.TODO(), func(tx kv.Tx) error {
|
|
|
|
_, err := indexStore.FindEnt(context.TODO(), tx, kv.Entity{
|
|
|
|
PK: kv.EncID(expected.ID),
|
|
|
|
UniqueKey: kv.Encode(kv.EncID(expected.OrgID), kv.EncString(expected.Name)),
|
|
|
|
})
|
|
|
|
return err
|
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
})
|
|
|
|
|
2020-05-05 00:21:19 +00:00
|
|
|
t.Run("updating an existing entity to a new unique identifier should delete the existing unique key", func(t *testing.T) {
|
|
|
|
indexStore, done, kvStore := newFooIndexStore(t, "put")
|
|
|
|
defer done()
|
|
|
|
|
|
|
|
expected := testPutBase(t, kvStore, indexStore, indexStore.EntStore.BktName)
|
|
|
|
|
|
|
|
update(t, kvStore, func(tx kv.Tx) error {
|
|
|
|
entCopy := newFooEnt(expected.ID, expected.OrgID, "safe name")
|
|
|
|
return indexStore.Put(context.TODO(), tx, entCopy, kv.PutUpdate())
|
|
|
|
})
|
|
|
|
|
|
|
|
update(t, kvStore, func(tx kv.Tx) error {
|
|
|
|
ent := newFooEnt(33, expected.OrgID, expected.Name)
|
|
|
|
return indexStore.Put(context.TODO(), tx, ent, kv.PutNew())
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2020-01-06 23:31:50 +00:00
|
|
|
t.Run("error cases", func(t *testing.T) {
|
|
|
|
t.Run("new entity conflicts with existing", func(t *testing.T) {
|
|
|
|
indexStore, done, kvStore := newFooIndexStore(t, "put")
|
|
|
|
defer done()
|
|
|
|
|
|
|
|
expected := testPutBase(t, kvStore, indexStore, indexStore.EntStore.BktName)
|
|
|
|
|
|
|
|
err := kvStore.Update(context.TODO(), func(tx kv.Tx) error {
|
|
|
|
entCopy := newFooEnt(expected.ID, expected.OrgID, expected.Name)
|
|
|
|
return indexStore.Put(context.TODO(), tx, entCopy, kv.PutNew())
|
|
|
|
})
|
|
|
|
require.Error(t, err)
|
2021-03-30 18:10:02 +00:00
|
|
|
assert.Equal(t, errors.EConflict, errors.ErrorCode(err))
|
2020-01-06 23:31:50 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("updating entity that does not exist", func(t *testing.T) {
|
|
|
|
indexStore, done, kvStore := newFooIndexStore(t, "put")
|
|
|
|
defer done()
|
|
|
|
|
|
|
|
expected := testPutBase(t, kvStore, indexStore, indexStore.EntStore.BktName)
|
|
|
|
|
|
|
|
update(t, kvStore, func(tx kv.Tx) error {
|
|
|
|
ent := newFooEnt(9000, expected.OrgID, "name1")
|
|
|
|
return indexStore.Put(context.TODO(), tx, ent, kv.PutNew())
|
|
|
|
})
|
|
|
|
|
|
|
|
err := kvStore.Update(context.TODO(), func(tx kv.Tx) error {
|
|
|
|
// ent by id does not exist
|
|
|
|
entCopy := newFooEnt(333, expected.OrgID, "name1")
|
|
|
|
return indexStore.Put(context.TODO(), tx, entCopy, kv.PutUpdate())
|
|
|
|
})
|
|
|
|
require.Error(t, err)
|
2021-03-30 18:10:02 +00:00
|
|
|
assert.Equal(t, errors.ENotFound, errors.ErrorCode(err), "got: "+err.Error())
|
2020-01-06 23:31:50 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("updating entity that does collides with an existing entity", func(t *testing.T) {
|
|
|
|
indexStore, done, kvStore := newFooIndexStore(t, "put")
|
|
|
|
defer done()
|
|
|
|
|
|
|
|
expected := testPutBase(t, kvStore, indexStore, indexStore.EntStore.BktName)
|
|
|
|
|
|
|
|
update(t, kvStore, func(tx kv.Tx) error {
|
|
|
|
ent := newFooEnt(9000, expected.OrgID, "name1")
|
|
|
|
return indexStore.Put(context.TODO(), tx, ent, kv.PutNew())
|
|
|
|
})
|
|
|
|
|
|
|
|
err := kvStore.Update(context.TODO(), func(tx kv.Tx) error {
|
|
|
|
// name conflicts
|
|
|
|
entCopy := newFooEnt(expected.ID, expected.OrgID, "name1")
|
|
|
|
return indexStore.Put(context.TODO(), tx, entCopy, kv.PutUpdate())
|
|
|
|
})
|
|
|
|
require.Error(t, err)
|
2021-03-30 18:10:02 +00:00
|
|
|
assert.Equal(t, errors.EConflict, errors.ErrorCode(err))
|
2020-01-06 23:31:50 +00:00
|
|
|
assert.Contains(t, err.Error(), "update conflicts")
|
|
|
|
})
|
|
|
|
})
|
2019-12-27 02:15:14 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("DeleteEnt", func(t *testing.T) {
|
2020-01-06 23:31:50 +00:00
|
|
|
indexStore, done, kvStore := newFooIndexStore(t, "delete_ent")
|
2019-12-31 03:11:53 +00:00
|
|
|
defer done()
|
2019-12-27 02:15:14 +00:00
|
|
|
|
2020-01-06 23:31:50 +00:00
|
|
|
expected := testDeleteEntBase(t, kvStore, indexStore)
|
2019-12-27 02:15:14 +00:00
|
|
|
|
2020-01-06 23:31:50 +00:00
|
|
|
err := kvStore.View(context.TODO(), func(tx kv.Tx) error {
|
2019-12-27 02:15:14 +00:00
|
|
|
_, err := indexStore.IndexStore.FindEnt(context.TODO(), tx, kv.Entity{
|
2020-01-01 00:28:55 +00:00
|
|
|
UniqueKey: expected.UniqueKey,
|
2019-12-27 02:15:14 +00:00
|
|
|
})
|
|
|
|
return err
|
|
|
|
})
|
|
|
|
isNotFoundErr(t, err)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("Delete", func(t *testing.T) {
|
2019-12-31 03:11:53 +00:00
|
|
|
fn := func(t *testing.T, suffix string) (storeBase, func(), kv.Store) {
|
2019-12-27 02:15:14 +00:00
|
|
|
return newFooIndexStore(t, suffix)
|
|
|
|
}
|
|
|
|
|
|
|
|
testDeleteBase(t, fn, func(t *testing.T, kvStore kv.Store, base storeBase, foosLeft []foo) {
|
|
|
|
var expectedIndexIDs []interface{}
|
|
|
|
for _, ent := range foosLeft {
|
|
|
|
expectedIndexIDs = append(expectedIndexIDs, ent.ID)
|
|
|
|
}
|
|
|
|
|
|
|
|
indexStore, ok := base.(*kv.IndexStore)
|
|
|
|
require.True(t, ok)
|
|
|
|
|
|
|
|
// next to verify they are not within the index store
|
|
|
|
var actualIDs []interface{}
|
|
|
|
view(t, kvStore, func(tx kv.Tx) error {
|
|
|
|
return indexStore.IndexStore.Find(context.TODO(), tx, kv.FindOpts{
|
|
|
|
CaptureFn: func(key []byte, decodedVal interface{}) error {
|
|
|
|
actualIDs = append(actualIDs, decodedVal)
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
assert.Equal(t, expectedIndexIDs, actualIDs)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("FindEnt", func(t *testing.T) {
|
|
|
|
t.Run("by ID", func(t *testing.T) {
|
2020-01-06 23:31:50 +00:00
|
|
|
base, done, kvStoreStore := newFooIndexStore(t, "find_ent")
|
2019-12-31 03:11:53 +00:00
|
|
|
defer done()
|
2020-01-06 23:31:50 +00:00
|
|
|
testFindEnt(t, kvStoreStore, base)
|
2019-12-27 02:15:14 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("find by name", func(t *testing.T) {
|
2019-12-31 03:11:53 +00:00
|
|
|
base, done, kvStore := newFooIndexStore(t, "find_ent")
|
|
|
|
defer done()
|
|
|
|
|
2019-12-27 02:15:14 +00:00
|
|
|
expected := newFooEnt(1, 9000, "foo_1")
|
|
|
|
seedEnts(t, kvStore, base, expected)
|
|
|
|
|
|
|
|
var actual interface{}
|
|
|
|
view(t, kvStore, func(tx kv.Tx) error {
|
|
|
|
f, err := base.FindEnt(context.TODO(), tx, kv.Entity{
|
2020-01-01 00:28:55 +00:00
|
|
|
UniqueKey: expected.UniqueKey,
|
2019-12-27 02:15:14 +00:00
|
|
|
})
|
|
|
|
actual = f
|
|
|
|
return err
|
|
|
|
})
|
|
|
|
|
|
|
|
assert.Equal(t, expected.Body, actual)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("Find", func(t *testing.T) {
|
|
|
|
t.Run("base", func(t *testing.T) {
|
2019-12-31 03:11:53 +00:00
|
|
|
fn := func(t *testing.T, suffix string) (storeBase, func(), kv.Store) {
|
2019-12-27 02:15:14 +00:00
|
|
|
return newFooIndexStore(t, suffix)
|
|
|
|
}
|
|
|
|
|
|
|
|
testFind(t, fn)
|
|
|
|
})
|
|
|
|
|
2019-12-31 03:11:53 +00:00
|
|
|
t.Run("with entity filter", func(t *testing.T) {
|
|
|
|
base, done, kvStore := newFooIndexStore(t, "find_index_search")
|
|
|
|
defer done()
|
|
|
|
|
|
|
|
expectedEnts := []kv.Entity{
|
|
|
|
newFooEnt(1, 9000, "foo_0"),
|
|
|
|
newFooEnt(2, 9001, "foo_1"),
|
|
|
|
newFooEnt(3, 9003, "foo_2"),
|
|
|
|
}
|
|
|
|
|
|
|
|
seedEnts(t, kvStore, base, expectedEnts...)
|
|
|
|
|
|
|
|
var actuals []interface{}
|
|
|
|
view(t, kvStore, func(tx kv.Tx) error {
|
|
|
|
return base.Find(context.TODO(), tx, kv.FindOpts{
|
|
|
|
FilterEntFn: func(key []byte, decodedVal interface{}) bool {
|
|
|
|
return decodedVal.(foo).ID < 3
|
|
|
|
},
|
|
|
|
CaptureFn: func(key []byte, decodedVal interface{}) error {
|
|
|
|
actuals = append(actuals, decodedVal)
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
expected := []interface{}{
|
|
|
|
expectedEnts[0].Body,
|
|
|
|
expectedEnts[1].Body,
|
|
|
|
}
|
|
|
|
assert.Equal(t, expected, actuals)
|
|
|
|
})
|
2019-12-27 02:15:14 +00:00
|
|
|
})
|
|
|
|
}
|