featurebase/dbshard_internal_test.go
pokeeffe-molecula fb40cdc2cb
fb-1729 Enriched Table Metadata (#2255)
enriched metadata for tables

added support for the concept of a table and field owners in metadata; mechanism to derive owner from http request metadata; metadata for table description
2022-10-26 11:23:40 -05:00

281 lines
8.1 KiB
Go

// Copyright 2021 Molecula Corp. All rights reserved.
package pilosa
import (
"os"
"path/filepath"
"strings"
"testing"
"github.com/molecula/featurebase/v3/rbf"
"github.com/molecula/featurebase/v3/shardwidth"
txkey "github.com/molecula/featurebase/v3/short_txkey"
"github.com/molecula/featurebase/v3/testhook"
. "github.com/molecula/featurebase/v3/vprint" // nolint:staticcheck
)
// Shard per db evaluation
func TestShardPerDB_SetBit(t *testing.T) {
f, idx, tx := mustOpenFragment(t)
_ = idx
defer f.Clean(t)
// Set bits on the fragment.
if _, err := f.setBit(tx, 120, 1); err != nil {
t.Fatal(err)
} else if _, err := f.setBit(tx, 120, 6); err != nil {
t.Fatal(err)
} else if _, err := f.setBit(tx, 121, 0); err != nil {
t.Fatal(err)
}
// should have two containers set in the fragment.
// Verify counts on rows.
if n := f.mustRow(tx, 120).Count(); n != 2 {
t.Fatalf("unexpected count: %d", n)
} else if n := f.mustRow(tx, 121).Count(); n != 1 {
t.Fatalf("unexpected count: %d", n)
}
// commit the change, and verify it is still there
PanicOn(tx.Commit())
// Close and reopen the fragment & verify the data.
err := f.Reopen() // roaring data not being flushed? red on roaring
if err != nil {
t.Fatal(err)
}
tx = idx.holder.txf.NewTx(Txo{Write: writable, Index: idx, Fragment: f, Shard: f.shard})
defer tx.Rollback()
if n := f.mustRow(tx, 120).Count(); n != 2 {
t.Fatalf("unexpected count (reopen): %d", n)
} else if n := f.mustRow(tx, 121).Count(); n != 1 {
t.Fatalf("unexpected count (reopen): %d", n)
}
}
// test that we find all *local* shards
func Test_DBPerShard_GetShardsForIndex_LocalOnly(t *testing.T) {
tmpdir, err := testhook.TempDir(t, "Test_DBPerShard_GetShardsForIndex_LocalOnly")
PanicOn(err)
defer os.RemoveAll(tmpdir)
v2s := NewFieldView2Shards()
stdShardSet := newShardSet()
for _, shard := range []uint64{93, 223, 221, 215, 219, 217} {
stdShardSet.add(shard)
}
for _, field := range []string{"f", "_exists"} {
v2s.addViewShardSet(txkey.FieldView{Field: field, View: "standard"}, stdShardSet)
}
for _, src := range []string{"rbf"} {
holder := newTestHolder(t)
index := "rick"
idx := makeSampleRoaringDir(t, tmpdir, index, src, 1, holder, v2s)
if idx == nil {
idx, err = NewIndex(holder, filepath.Join(tmpdir, index), index)
PanicOn(err)
}
std := "rick/fields/f/views/standard"
shards, err := holder.txf.GetShardsForIndex(idx, tmpdir+sep+std, false)
PanicOn(err)
for _, shard := range []uint64{93, 223, 221, 215, 219, 217} {
if _, ok := shards[shard]; !ok {
t.Fatalf("missing shard=%v from shards='%#v'", shard, shards)
}
}
for _, shard := range []uint64{93, 223, 221, 215, 219, 217} {
tx := idx.holder.txf.NewTx(Txo{Write: !writable, Index: idx, Shard: shard})
fvs, err := tx.GetSortedFieldViewList(idx, shard)
PanicOn(err)
// expect these same two field/views for all 6 shards
expect0 := txkey.FieldView{Field: "_exists", View: "standard"}
expect1 := txkey.FieldView{Field: "f", View: "standard"}
if len(fvs) != 2 {
t.Fatalf("fvs should be len 2, got '%#v' (%s)", fvs, src)
}
if fvs[0] != expect0 {
t.Fatalf("expected fvs[0]='%#v', but got '%#v'", expect0, fvs[0])
}
if fvs[1] != expect1 {
t.Fatalf("expected fvs[1]='%#v', but got '%#v'", expect1, fvs[1])
}
tx.Rollback()
}
}
}
// data for Test_DBPerShard_GetShardsForIndex
var sampleRoaringDirList = map[string]string{"roaring": `
rick/fields/f/views/standard/fragments/215.cache
rick/fields/f/views/standard/fragments/221.cache
rick/fields/f/views/standard/fragments/223.cache
rick/fields/f/views/standard/fragments/93.cache
rick/fields/f/views/standard/fragments/217.cache
rick/fields/f/views/standard/fragments/219.cache
rick/fields/f/views/standard/fragments/217
rick/fields/f/views/standard/fragments/219
rick/fields/f/views/standard/fragments/215
rick/fields/f/views/standard/fragments/221
rick/fields/f/views/standard/fragments/223
rick/fields/f/views/standard/fragments/93
rick/fields/_exists/views/standard/fragments/221
rick/fields/_exists/views/standard/fragments/215
rick/fields/_exists/views/standard/fragments/217
rick/fields/_exists/views/standard/fragments/93
rick/fields/_exists/views/standard/fragments/219
rick/fields/_exists/views/standard/fragments/223
`,
"rbf": `
rick/backends/backend-rbf/shard.0093-rbf
rick/backends/backend-rbf/shard.0215-rbf
rick/backends/backend-rbf/shard.0217-rbf
rick/backends/backend-rbf/shard.0219-rbf
rick/backends/backend-rbf/shard.0221-rbf
rick/backends/backend-rbf/shard.0223-rbf
`,
}
func makeSampleRoaringDir(t *testing.T, root, index, backend string, minBytes int, h *Holder, view2shards *FieldView2Shards) (idx *Index) {
shards := []uint64{0, 93, 215, 217, 219, 221, 223}
fns := strings.Split(sampleRoaringDirList[backend], "\n")
firstDone := false
for i, fn := range fns {
// This check is here because in sampleRoaringDirList, the first entry
// of each map value is a line feed, so the strings.Split() above
// results in a blank entry for the first item. This means that the
// slice of shards above has an initial entry "0" which is not used.
if fn == "" {
continue
}
var shard uint64
switch backend {
case "rbf":
shard = shards[i]
idx = helperCreateDBShard(h, index, shard)
// first time only, we'll actually make all the shards at this point because
// view2shards has them all anyway.
if !firstDone {
firstDone = true
makeTxTestDBWithViewsShards(t, h, idx, view2shards)
}
continue
case "roaring":
default:
t.Fatalf("invalid backend: %s", backend)
}
path := root + sep + filepath.Dir(fn)
PanicOn(os.MkdirAll(path, 0755))
fd, err := os.Create(root + sep + fn)
PanicOn(err)
if minBytes > 0 {
_, err := fd.Write(make([]byte, minBytes))
PanicOn(err)
}
fd.Close()
}
return
}
func helperCreateDBShard(h *Holder, index string, shard uint64) *Index {
idx, err := h.CreateIndexIfNotExists(index, "", IndexOptions{})
PanicOn(err)
// TODO: It's not clear that this is actually doing anything.
dbs, err := h.txf.dbPerShard.GetDBShard(index, shard, idx)
PanicOn(err)
_ = dbs
return idx
}
// keep the ocd linter happy
var _ = makeRBFtestDB
func makeRBFtestDB(path string, h *Holder, shard uint64) {
i := uint64(1)
db := rbf.NewDB(path, nil)
err := db.Open()
PanicOn(err)
defer db.Close()
tx, err := db.Begin(true)
PanicOn(err)
err = tx.CreateBitmap("x")
PanicOn(err)
_, err = tx.Add("x", i)
PanicOn(err)
err = tx.Commit()
PanicOn(err)
}
func makeTxTestDBWithViewsShards(tb testing.TB, holder *Holder, idx *Index, exp *FieldView2Shards) {
// TODO(jea): need date time quantum views!!
for field, viewmap := range exp.m {
for view, shset := range viewmap {
ss := shset.CloneMaybe()
for shard := range ss {
// simply write 1 bit to each shard to force its creation.
bits := []uint64{(shard << shardwidth.Exponent) + 1}
tx := idx.holder.txf.NewTx(Txo{Write: writable, Index: idx, Shard: shard})
changeCount, err := tx.Add(idx.name, field, view, shard, bits...)
PanicOn(err)
if changeCount != len(bits) {
tb.Fatalf("writing field '%v', view '%v' shard '%v', expected changeCount to equal len bits = %v but was %v", field, view, shard, len(bits), changeCount)
}
PanicOn(tx.Commit())
}
}
}
}
// test that rbf can give us a map[view]*shardSet
func Test_DBPerShard_GetFieldView2Shards_map_from_RBF(t *testing.T) {
holder := newTestHolder(t)
index := "rick"
field := "f"
idx, err := holder.CreateIndex(index, "", IndexOptions{})
if err != nil {
t.Fatalf("creating index: %v", err)
}
exp := NewFieldView2Shards()
stdShardSet := newShardSet()
stdShardSet.add(12)
stdShardSet.add(15)
exp.addViewShardSet(txkey.FieldView{Field: field, View: "standard"}, stdShardSet)
hrShardSet := newShardSet()
hrShardSet.add(7)
exp.addViewShardSet(txkey.FieldView{Field: field, View: "standard_2019092416"}, hrShardSet)
makeTxTestDBWithViewsShards(t, holder, idx, exp)
// setup is done
view2shard, err := holder.txf.GetFieldView2ShardsMapForIndex(idx)
PanicOn(err)
// compare against setup
if !view2shard.equals(exp) {
t.Fatalf("expected '%v' but got view2shard '%v'", exp, view2shard)
}
}