Merge pull request #845 from molecula/rebal_together_rb

fine tune Tx placement, make it lazier so we don't create extra shards.
This commit is contained in:
tgruben 2020-09-14 18:34:15 -05:00 committed by GitHub
commit 9649819c09
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 239 additions and 152 deletions

View file

@ -72,6 +72,9 @@ testvsub-race:
cd ..; \
done
tour:
./tournament.sh
bench:
go test ./... -bench=. -run=NoneZ -timeout=127m $(TESTFLAGS)

View file

@ -290,6 +290,10 @@ func (r *badgerRegistrar) OpenDBWrapper(bpath string, doAllocZero bool) (DBWrapp
// have the -badgerdb suffix.
bpath = badgerPath(bpath)
err := os.MkdirAll(bpath, 0755)
if err != nil {
return nil, err
}
r.mu.Lock()
defer r.mu.Unlock()
w, ok := r.path2db[bpath]
@ -1734,7 +1738,7 @@ func (tx *BadgerTx) countBitsSet(bkey []byte) (n int) {
return
}
func (tx *BadgerTx) Dump(short bool) {
func (tx *BadgerTx) Dump(short bool, shard uint64) {
fmt.Printf("BadgerTx %p Dump: %v\n", tx, stringifiedBadgerKeysTx(tx, short))
}

View file

@ -18,6 +18,7 @@ import (
"bytes"
"fmt"
"io"
"math"
"reflect"
"sort"
"sync"
@ -152,15 +153,15 @@ func (c *blueGreenTx) Type() string {
var blueGreenTxDumpMut sync.Mutex
func (c *blueGreenTx) Dump(short bool) {
func (c *blueGreenTx) Dump(short bool, shard uint64) {
blueGreenTxDumpMut.Lock()
defer blueGreenTxDumpMut.Unlock()
fmt.Printf("%v blueGreenTx.Dump ============== \n", FileLine(2))
fmt.Printf("A(%v) Dump:\n", c.as)
c.a.Dump(short)
c.a.Dump(short, shard)
fmt.Printf("B(%v) Dump:\n", c.bs)
c.b.Dump(short)
c.b.Dump(short, shard)
if !short {
fmt.Printf("dbPerShard.DumpAll(): idx=%p\n", c.idx)
@ -211,21 +212,21 @@ func (c *blueGreenTx) compareTxState(index, field, view string, shard uint64) {
}
if aFound != bFound {
c.Dump(c.short)
c.Dump(c.short, shard)
panic(fmt.Sprintf("compareTxState[%v]: A(%v) ContainerIterator had aFound=%v, but B(%v) had bFound=%v; at '%v'", here, c.as, aFound, c.bs, bFound, stack()))
}
if aErr != nil || bErr != nil {
if aErr != nil && bErr != nil {
c.Dump(c.short)
c.Dump(c.short, shard)
panic(fmt.Sprintf("compareTxState[%v]: A(%v) reported err '%v'; B(%v) reported err '%v' at %v", here, c.as, aErr, c.bs, bErr, stack()))
}
if aErr != nil {
c.Dump(c.short)
c.Dump(c.short, shard)
panic(fmt.Sprintf("compareTxState[%v]: A(%v) reported err %v at %v; but B(%v) did not", here, c.as, aErr, c.bs, stack()))
}
if bErr != nil {
c.Dump(c.short)
c.Dump(c.short, shard)
panic(fmt.Sprintf("compareTxState[%v]: B(%v) reported err %v at %v; but A(%v) did not", here, c.bs, bErr, c.as, stack()))
}
}
@ -234,17 +235,17 @@ func (c *blueGreenTx) compareTxState(index, field, view string, shard uint64) {
if !bIter.Next() {
AlwaysPrintf("compareTxState[%v]: A(%v) found key %v, B(%v) didn't, dump to follow, stack=\n %v\n\n and here is dump:", here, c.as, aKey, c.bs, stack())
c.Dump(c.short)
c.Dump(c.short, shard)
panic(fmt.Sprintf("compareTxState[%v]: A(%v) found key %v, B(%v) didn't, at %v", here, c.as, aKey, c.bs, stack()))
}
bKey, bValue := bIter.Value()
if bKey != aKey {
AlwaysPrintf("problem in caller %v", Caller(2))
c.Dump(c.short)
c.Dump(c.short, shard)
panic(fmt.Sprintf("compareTxState[%v]: A(%v) found key %v, B(%v) found %v, at %v", here, c.as, aKey, c.bs, bKey, stack()))
}
if err := aValue.BitwiseCompare(bValue); err != nil {
c.Dump(c.short)
c.Dump(c.short, shard)
//vv("compareTxState[%v]: key %v differs: %v; A=%v; B=%v; at stack=%v", here, aKey, err, c.as, c.bs, stack())
panic(fmt.Sprintf("compareTxState[%v]: key %v differs: %v; A=%v; B=%v; at stack=%v", here, aKey, err, c.as, c.bs, stack()))
}
@ -253,7 +254,7 @@ func (c *blueGreenTx) compareTxState(index, field, view string, shard uint64) {
// end checking everything in A, but does B have more?
if bIter.Next() {
AlwaysPrintf("bIter has more than it should. problem in caller %v. _sn_ %v", Caller(2), c.Sn())
c.Dump(c.short)
c.Dump(c.short, shard)
bKey, _ := bIter.Value()
panic(fmt.Sprintf("compareTxState[%v]: B(%v) found key %v, A(%v) didn't, (a.sn=%v) (b.sn=%v) at %v", here, c.bs, bKey, c.as, c.a.Sn(), c.b.Sn(), stack()))
}
@ -430,7 +431,7 @@ func (c *blueGreenTx) ImportRoaringBits(index, field, view string, shard uint64,
// ================== begin save comments.
//c.checkDatabase()
////vv("got past database check at TOP of ImportRoaringBits")
//c.Dump(c.short)
//c.Dump(c.short, shard)
////vv("done with top dump; clear=%v", clear)
// ================== end save comments.
defer func() {
@ -789,7 +790,7 @@ func (c *blueGreenTx) CountRange(index, field, view string, shard uint64, start,
c.checker.see(index, field, view, shard)
defer func() {
if r := recover(); r != nil {
c.Dump(c.short)
c.Dump(c.short, shard)
AlwaysPrintf("see CountRange() panic '%v' at '%v'", r, stack())
panic(r)
}
@ -822,7 +823,7 @@ func (c *blueGreenTx) OffsetRange(index, field, view string, shard, offset, star
err = roaringBitmapDiff(a, b)
if err != nil {
c.Dump(false)
c.Dump(false, shard)
panicOn(fmt.Errorf("on _sn_ %v OffsetRange(index='%v', field='%v', view='%v', shard='%v', offset: %v start: %v, end: %v) err: %v", c.Sn(), index, field, view, int(shard), offset, start, end, err))
}
compareErrors(errA, errB)
@ -835,7 +836,7 @@ func (c *blueGreenTx) RoaringBitmapReader(index, field, view string, shard uint6
c.checker.see(index, field, view, shard)
defer func() {
if r := recover(); r != nil {
c.Dump(c.short)
c.Dump(c.short, shard)
AlwaysPrintf("see RoaringBitmapReader() panic '%v' at '%v'", r, stack())
panic(r)
}
@ -895,7 +896,7 @@ func (c *blueGreenTx) SliceOfShards(index, field, view, optionalViewPath string)
//c.checker.see(index, field, view, shard) // don't have shard.
defer func() {
if r := recover(); r != nil {
c.Dump(c.short)
c.Dump(c.short, math.MaxUint64)
AlwaysPrintf("see SliceOfShards() panic '%v' at '%v'", r, stack())
panic(r)
}
@ -921,7 +922,7 @@ func (c *blueGreenTx) SliceOfShards(index, field, view, optionalViewPath string)
for _, kb := range slcB {
if !ma[kb] {
//vv("blueGreenTx SliceOfShards diference! B(%v) had shard %v, but A(%v) did not. cpa='%#v'; cpb='%#v'; in the SliceOfShards returned slice.", c.bs, kb, c.as, cpa, cpb)
c.Dump(c.short)
c.Dump(c.short, math.MaxUint64)
panic(fmt.Sprintf("blueGreenTx SliceOfShards diference! B(%v) had shard %v, but A(%v) did not. cpa='%#v'; cpb='%#v'; in the SliceOfShards returned slice.", c.bs, kb, c.as, cpa, cpb))
}
delete(ma, kb)

View file

@ -58,8 +58,8 @@ func (c *catcherTx) ImportRoaringBits(index, field, view string, shard uint64, r
return c.b.ImportRoaringBits(index, field, view, shard, rit, clear, log, rowSize, data)
}
func (c *catcherTx) Dump(short bool) {
c.b.Dump(short)
func (c *catcherTx) Dump(short bool, shard uint64) {
c.b.Dump(short, shard)
}
func (c *catcherTx) Readonly() bool {

View file

@ -195,7 +195,7 @@ func (dbs *DBShard) DeleteDBPath() (err error) {
type DBPerShard struct {
Mu sync.Mutex
Dir string // holder dir
HolderDir string
dbh *DBHolder
@ -260,7 +260,7 @@ func (txf *TxFactory) NewDBPerShard(types []txtype, holderDir string) (d *DBPerS
d = &DBPerShard{
types: types,
Dir: holderDir,
HolderDir: holderDir,
dbh: NewDBHolder(),
Flatmap: make(map[*DBShard]struct{}),
txf: txf,
@ -340,7 +340,7 @@ func (dbs *DBShard) DumpAll() {
panicOn(err)
defer tx.Rollback()
fmt.Printf("\n============= dumping dbs.W[%v] %v ========\n", i, ty)
tx.Dump(short)
tx.Dump(short, dbs.Shard)
switch ty {
case roaringTxn:
@ -367,15 +367,30 @@ func (per *DBPerShard) DumpAll() {
}
}
if !found1 {
AlwaysPrintf("DBPerShard.DumpAll() sees no databases. dir='%v'", per.Dir)
AlwaysPrintf("DBPerShard.DumpAll() sees no databases. dir='%v'", per.HolderDir)
}
}
// if you know the shard, you can use this
// pathForType and prefixForType must be kept in sync!
func (dbs *DBShard) pathForType(ty txtype) string {
// top level paths will end in "@@"
return dbs.HolderPath + sep + dbs.Index + ".index.txstores@@@" + sep + "store" + ty.FileSuffix() + "@" + sep + fmt.Sprintf("shard.%04v", dbs.Shard)
// what here for roaring? well, roaringRegistrar.OpenDBWrapper()
// is a no-op anyhow. so doesn't need to be correct atm.
return dbs.HolderPath + sep + dbs.Index + ".index.txstores@@@" + sep + "store" + ty.FileSuffix() + "@" + sep + fmt.Sprintf("shard.%04v%v", dbs.Shard, ty.FileSuffix())
}
// if you don't know the shard, you have to use this.
// prefixForType and pathForType must be kept in sync!
func (per *DBPerShard) prefixForType(idx *Index, ty txtype) string {
// top level paths will end in "@@"
return per.HolderDir + sep + idx.name + ".index.txstores@@@" + sep + "store" + ty.FileSuffix() + "@" + sep
}
var ErrNoData = fmt.Errorf("no data")
func (per *DBPerShard) GetDBShard(index string, shard uint64, idx *Index) (dbs *DBShard, err error) {
per.Mu.Lock()
@ -398,12 +413,11 @@ func (per *DBPerShard) GetDBShard(index string, shard uint64, idx *Index) (dbs *
ParentDBIndex: dbi,
Index: index,
Shard: shard,
HolderPath: per.Dir,
//Path: per.Path(index, shard),
idx: idx,
per: per,
useOpenList: per.useOpenList,
hasRoaring: per.hasRoaring,
HolderPath: per.HolderDir,
idx: idx,
per: per,
useOpenList: per.useOpenList,
hasRoaring: per.hasRoaring,
}
dbi.Shard[shard] = dbs
}
@ -422,8 +436,8 @@ func (per *DBPerShard) GetDBShard(index string, shard uint64, idx *Index) (dbs *
default:
panic(fmt.Sprintf("unknown txtyp: '%v'", ty))
}
w, err := registry.OpenDBWrapper(dbs.pathForType(ty), DetectMemAccessPastTx)
path := dbs.pathForType(ty)
w, err := registry.OpenDBWrapper(path, DetectMemAccessPastTx)
panicOn(err)
h := idx.Holder()
w.SetHolder(h)
@ -466,20 +480,48 @@ func (per *DBPerShard) Close() (err error) {
return
}
// DBPerShardGetShardsForIndex returns the indexes from the B (green) database if
// blue-green comparison is in use, rather than from the A (blue) database.
func DBPerShardGetShardsForIndex(idx *Index, roaringViewPath string) (sliceOfShards []uint64, err error) {
// DBPerShardGetShardsForIndex returns the shards for idx.
func (f *TxFactory) GetShardsForIndex(idx *Index, roaringViewPath string) (sliceOfShards []uint64, err error) {
// follow the blueGreen convention of returning the answer for 'B' or
// the last wrapper type.
types := idx.holder.txf.Types()
ty := types[len(types)-1]
return TypedDBPerShardGetLocalShardsForIndex(ty, idx, roaringViewPath)
var shards [][]uint64
for _, ty := range f.types {
var slc []uint64
slc, err = f.dbPerShard.TypedDBPerShardGetShardsForIndex(ty, idx, roaringViewPath)
if err != nil {
return
}
shards = append(shards, slc)
}
n := len(f.types)
if n != 1 && n != 2 {
panic(fmt.Sprintf("internal error. only green or blue/green supported. we see types len %v", n))
}
if !f.blueGreenOff && n == 2 {
// this is a blue green check which cannot live inside Tx because we don't know the shard yet.
// Therefore it has to be above Tx, since we are getting all the shards to choose from here.
//
// But, we still want to check for blue-green consistency. In fact, this was written
// in response to an issue with balancing/re-balancing shards being different
// over the cluster of nodes between blue and green.
b := sliceToMap(shards[0])
g := sliceToMap(shards[1])
blueMinusGreenDiff := mapDiff(b, g)
greenMinusBlueDiff := mapDiff(g, b)
if len(blueMinusGreenDiff) == 0 && len(greenMinusBlueDiff) == 0 {
// ok
} else {
vv("blue[%v] and green[%v] have different shards for index '%v': blueMinusGreenDiff: %v, greenMinusBlueDiff: %v; blueShards='%v', greenShards='%v'; idx.path='%v'", f.types[0].String(), f.types[1].String(), idx.name, blueMinusGreenDiff, greenMinusBlueDiff, asInts(shards[0]), asInts(shards[1]), idx.path)
panic(fmt.Sprintf("blue[%v] and green[%v] have different shards for index '%v': blueMinusGreenDiff: %v, greenMinusBlueDiff: %v; blueShards='%v', greenShards='%v'; idx.path='%v'", f.types[0].String(), f.types[1].String(), idx.name, blueMinusGreenDiff, greenMinusBlueDiff, asInts(shards[0]), asInts(shards[1]), idx.path))
}
}
// If we are populating blue from green, it does matter that we return green.
return shards[n-1], nil
}
// if roaringViewPath is "" then for ty == roaringTxn we go to disk to discover
// all the view paths under idx for type ty.
func TypedDBPerShardGetLocalShardsForIndex(ty txtype, idx *Index, roaringViewPath string) (sliceOfShards []uint64, err error) {
func (per *DBPerShard) TypedDBPerShardGetShardsForIndex(ty txtype, idx *Index, roaringViewPath string) (sliceOfShards []uint64, err error) {
if ty == roaringTxn {
rx := &RoaringTx{
@ -503,7 +545,8 @@ func TypedDBPerShardGetLocalShardsForIndex(ty txtype, idx *Index, roaringViewPat
return rx.SliceOfShards("", "", "", roaringViewPath)
}
requiredSuffix := ty.FileSuffix()
path := idx.Path()
//path := idx.Path()
path := per.prefixForType(idx, ty)
ignoreEmpty := false
includeRoot := true
@ -517,8 +560,13 @@ func TypedDBPerShardGetLocalShardsForIndex(ty txtype, idx *Index, roaringViewPat
panic(fmt.Sprintf("should have 2 parts: nm='%v', base(nm)='%v'; requiredSuffix='%v'", nm, base, requiredSuffix))
}
prefix := splt[0]
const shardPrefix = "shard."
const lenOfShardPrefix = len(shardPrefix)
if !strings.HasPrefix(prefix, shardPrefix) {
continue
}
// Parse filename into integer.
shard, err := strconv.ParseUint(prefix, 10, 64)
shard, err := strconv.ParseUint(prefix[lenOfShardPrefix:], 10, 64)
if err != nil {
continue
}
@ -528,8 +576,11 @@ func TypedDBPerShardGetLocalShardsForIndex(ty txtype, idx *Index, roaringViewPat
}
func listDirUnderDir(root string, includeRoot bool, requiredSuffix string, ignoreEmpty bool) (files []string, err error) {
//vv("listDirUnderDir(root ='%v', suffix='%v')", root, requiredSuffix)
if !dirExists(root) {
return nil, fmt.Errorf("listFilesUnderDir error: root directory '%v' not found", root)
//vv("warning: listFilesUnderDir error: root directory '%v' not found", root)
//return nil, fmt.Errorf("listFilesUnderDir error: root directory '%v' not found", root)
return
}
n := len(root) + 1
if includeRoot {

View file

@ -64,9 +64,9 @@ func TestShardPerDB_SetBit(t *testing.T) {
}
}
// test that we find all shards
func Test_DBPerShard_GetShardsForIndex(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "TestDBPerShardGetShardsForIndex")
// test that we find all *local* shards
func Test_DBPerShard_GetShardsForIndex_LocalOnly(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "Test_DBPerShard_GetShardsForIndex_LocalOnly")
panicOn(err)
orig := os.Getenv("PILOSA_TXSRC")
@ -84,7 +84,7 @@ func Test_DBPerShard_GetShardsForIndex(t *testing.T) {
estd := "rick/_exists/views/standard"
std := "rick/f/views/standard"
sos, err := DBPerShardGetShardsForIndex(idx, tmpdir+sep+std)
sos, err := holder.txf.GetShardsForIndex(idx, tmpdir+sep+std)
panicOn(err)
for _, shard := range []uint64{93, 223, 221, 215, 219, 217} {
if !inSlice(sos, shard) {
@ -93,7 +93,7 @@ func Test_DBPerShard_GetShardsForIndex(t *testing.T) {
}
if src == "roaring" {
// check estd too
sos, err = DBPerShardGetShardsForIndex(idx, tmpdir+sep+estd)
sos, err = holder.txf.GetShardsForIndex(idx, tmpdir+sep+estd)
panicOn(err)
for _, shard := range []uint64{93, 223, 221, 215, 219, 217} {
if !inSlice(sos, shard) {
@ -137,58 +137,58 @@ rick/_exists/views/standard/fragments/219
rick/_exists/views/standard/fragments/223
`,
"lmdb": `
rick/0219-lmdb@/data.mdb
rick/0219-lmdb@/lock.mdb
rick/0093-lmdb@/data.mdb
rick/0093-lmdb@/lock.mdb
rick/0223-lmdb@/data.mdb
rick/0223-lmdb@/lock.mdb
rick/0215-lmdb@/data.mdb
rick/0215-lmdb@/lock.mdb
rick/0217-lmdb@/data.mdb
rick/0217-lmdb@/lock.mdb
rick/0221-lmdb@/data.mdb
rick/0221-lmdb@/lock.mdb
rick.index.txstores@@@/store-lmdb@@/shard.0219-lmdb@/data.mdb
rick.index.txstores@@@/store-lmdb@@/shard.0219-lmdb@/lock.mdb
rick.index.txstores@@@/store-lmdb@@/shard.0093-lmdb@/data.mdb
rick.index.txstores@@@/store-lmdb@@/shard.0093-lmdb@/lock.mdb
rick.index.txstores@@@/store-lmdb@@/shard.0223-lmdb@/data.mdb
rick.index.txstores@@@/store-lmdb@@/shard.0223-lmdb@/lock.mdb
rick.index.txstores@@@/store-lmdb@@/shard.0215-lmdb@/data.mdb
rick.index.txstores@@@/store-lmdb@@/shard.0215-lmdb@/lock.mdb
rick.index.txstores@@@/store-lmdb@@/shard.0217-lmdb@/data.mdb
rick.index.txstores@@@/store-lmdb@@/shard.0217-lmdb@/lock.mdb
rick.index.txstores@@@/store-lmdb@@/shard.0221-lmdb@/data.mdb
rick.index.txstores@@@/store-lmdb@@/shard.0221-lmdb@/lock.mdb
`,
"badger": `
rick/0219-badgerdb@/000000.vlog
rick/0219-badgerdb@/KEYREGISTRY
rick/0219-badgerdb@/MANIFEST
rick/0219-badgerdb@/LOCK
rick/0221-badgerdb@/000000.vlog
rick/0221-badgerdb@/KEYREGISTRY
rick/0221-badgerdb@/MANIFEST
rick/0221-badgerdb@/LOCK
rick/0223-badgerdb@/000000.vlog
rick/0223-badgerdb@/KEYREGISTRY
rick/0223-badgerdb@/MANIFEST
rick/0223-badgerdb@/LOCK
rick/0093-badgerdb@/000000.vlog
rick/0093-badgerdb@/KEYREGISTRY
rick/0093-badgerdb@/MANIFEST
rick/0093-badgerdb@/LOCK
rick/0217-badgerdb@/000000.vlog
rick/0217-badgerdb@/KEYREGISTRY
rick/0217-badgerdb@/MANIFEST
rick/0217-badgerdb@/LOCK
rick/0215-badgerdb@/000000.vlog
rick/0215-badgerdb@/KEYREGISTRY
rick/0215-badgerdb@/MANIFEST
rick/0215-badgerdb@/LOCK
rick.index.txstores@@@/store-badgerdb@@/shard.0219-badgerdb@/000000.vlog
rick.index.txstores@@@/store-badgerdb@@/shard.0219-badgerdb@/KEYREGISTRY
rick.index.txstores@@@/store-badgerdb@@/shard.0219-badgerdb@/MANIFEST
rick.index.txstores@@@/store-badgerdb@@/shard.0219-badgerdb@/LOCK
rick.index.txstores@@@/store-badgerdb@@/shard.0221-badgerdb@/000000.vlog
rick.index.txstores@@@/store-badgerdb@@/shard.0221-badgerdb@/KEYREGISTRY
rick.index.txstores@@@/store-badgerdb@@/shard.0221-badgerdb@/MANIFEST
rick.index.txstores@@@/store-badgerdb@@/shard.0221-badgerdb@/LOCK
rick.index.txstores@@@/store-badgerdb@@/shard.0223-badgerdb@/000000.vlog
rick.index.txstores@@@/store-badgerdb@@/shard.0223-badgerdb@/KEYREGISTRY
rick.index.txstores@@@/store-badgerdb@@/shard.0223-badgerdb@/MANIFEST
rick.index.txstores@@@/store-badgerdb@@/shard.0223-badgerdb@/LOCK
rick.index.txstores@@@/store-badgerdb@@/shard.0093-badgerdb@/000000.vlog
rick.index.txstores@@@/store-badgerdb@@/shard.0093-badgerdb@/KEYREGISTRY
rick.index.txstores@@@/store-badgerdb@@/shard.0093-badgerdb@/MANIFEST
rick.index.txstores@@@/store-badgerdb@@/shard.0093-badgerdb@/LOCK
rick.index.txstores@@@/store-badgerdb@@/shard.0217-badgerdb@/000000.vlog
rick.index.txstores@@@/store-badgerdb@@/shard.0217-badgerdb@/KEYREGISTRY
rick.index.txstores@@@/store-badgerdb@@/shard.0217-badgerdb@/MANIFEST
rick.index.txstores@@@/store-badgerdb@@/shard.0217-badgerdb@/LOCK
rick.index.txstores@@@/store-badgerdb@@/shard.0215-badgerdb@/000000.vlog
rick.index.txstores@@@/store-badgerdb@@/shard.0215-badgerdb@/KEYREGISTRY
rick.index.txstores@@@/store-badgerdb@@/shard.0215-badgerdb@/MANIFEST
rick.index.txstores@@@/store-badgerdb@@/shard.0215-badgerdb@/LOCK
`,
"rbf": `
rick/0223-rbfdb@/wal/0000000000000001.wal
rick/0223-rbfdb@/data
rick/0093-rbfdb@/wal/0000000000000001.wal
rick/0093-rbfdb@/data
rick/0217-rbfdb@/wal/0000000000000001.wal
rick/0217-rbfdb@/data
rick/0215-rbfdb@/wal/0000000000000001.wal
rick/0215-rbfdb@/data
rick/0221-rbfdb@/wal/0000000000000001.wal
rick/0221-rbfdb@/data
rick/0219-rbfdb@/wal/0000000000000001.wal
rick/0219-rbfdb@/data
rick.index.txstores@@@/store-rbfdb@@/shard.0223-rbfdb@/wal/0000000000000001.wal
rick.index.txstores@@@/store-rbfdb@@/shard.0223-rbfdb@/data
rick.index.txstores@@@/store-rbfdb@@/shard.0093-rbfdb@/wal/0000000000000001.wal
rick.index.txstores@@@/store-rbfdb@@/shard.0093-rbfdb@/data
rick.index.txstores@@@/store-rbfdb@@/shard.0217-rbfdb@/wal/0000000000000001.wal
rick.index.txstores@@@/store-rbfdb@@/shard.0217-rbfdb@/data
rick.index.txstores@@@/store-rbfdb@@/shard.0215-rbfdb@/wal/0000000000000001.wal
rick.index.txstores@@@/store-rbfdb@@/shard.0215-rbfdb@/data
rick.index.txstores@@@/store-rbfdb@@/shard.0221-rbfdb@/wal/0000000000000001.wal
rick.index.txstores@@@/store-rbfdb@@/shard.0221-rbfdb@/data
rick.index.txstores@@@/store-rbfdb@@/shard.0219-rbfdb@/wal/0000000000000001.wal
rick.index.txstores@@@/store-rbfdb@@/shard.0219-rbfdb@/data
`,
}

View file

@ -28,8 +28,6 @@ import (
"github.com/pilosa/pilosa/v2/test"
)
var sep = string(os.PathSeparator)
func skipForNonLMDB(t *testing.T) {
src := os.Getenv("PILOSA_TXSRC")
if src != "lmdb" {
@ -62,13 +60,15 @@ func Test_DBPerShard_multiple_shards_used(t *testing.T) {
hldr.SetBit(index, "general", 11, ShardWidth+2)
types := pilosa.MustTxsrcToTxtype("lmdb")
tx_suffix := types[0].FileSuffix()
root := hldr.Path() + sep + index
shards := []string{"0000", "0001", "0002"}
idx := hldr.Index(index)
shardsU := []uint64{0, 1, 2}
pathShard := []string{}
// check that 3 different shard databases/files were made
for i := 0; i < 2; i++ {
path := root + sep + shards[i] + tx_suffix
path, err := hldr.Txf().GetDBShardPath(index, shardsU[i], idx, types[0], !writable)
panicOn(err)
pathShard = append(pathShard, path)
if !DirExists(pathShard[i]) {

View file

@ -142,6 +142,7 @@ func (e *executor) Close() error {
// Execute executes a PQL query.
func (e *executor) Execute(ctx context.Context, index string, q *pql.Query, shards []uint64, opt *execOptions) (QueryResponse, error) {
span, ctx := tracing.StartSpanFromContext(ctx, "Executor.Execute")
span.LogKV("pql", q.String())
defer span.Finish()
@ -3834,15 +3835,15 @@ func (e *executor) executeClearBitField(ctx context.Context, qcx *Qcx, index str
shard := colID / ShardWidth
idx := e.Holder.Index(index)
tx, finisher := qcx.GetTx(Txo{Write: writable, Index: idx, Shard: shard})
defer finisher(&err)
ret := false
for _, node := range e.Cluster.shardNodes(index, shard) {
// Update locally if host matches.
if node.ID == e.Node.ID {
idx := e.Holder.Index(index)
tx, finisher := qcx.GetTx(Txo{Write: writable, Index: idx, Shard: shard})
defer finisher(&err)
val, err := f.ClearBit(tx, rowID, colID)
if err != nil {
return false, err
@ -4109,7 +4110,7 @@ func (e *executor) executeSetRowShard(ctx context.Context, qcx *Qcx, index strin
}
// executeSet executes a Set() call.
func (e *executor) executeSet(ctx context.Context, qcx *Qcx, index string, c *pql.Call, opt *execOptions) (_ bool, err error) {
func (e *executor) executeSet(ctx context.Context, qcx *Qcx, index string, c *pql.Call, opt *execOptions) (_ bool, err0 error) {
span, ctx := tracing.StartSpanFromContext(ctx, "Executor.executeSet")
defer span.Finish()
@ -4128,9 +4129,6 @@ func (e *executor) executeSet(ctx context.Context, qcx *Qcx, index string, c *pq
shard := colID / ShardWidth
tx, finisher := qcx.GetTx(Txo{Write: writable, Index: idx, Shard: shard})
defer finisher(&err)
// Read field name.
fieldName, err := c.FieldArg()
if err != nil {
@ -4145,11 +4143,15 @@ func (e *executor) executeSet(ctx context.Context, qcx *Qcx, index string, c *pq
// Set column on existence field.
if ef := idx.existenceField(); ef != nil {
// we create tx here, rather than just above, to avoid creating an extra empty shard.
tx, finisher := qcx.GetTx(Txo{Write: writable, Index: idx, Shard: shard})
defer finisher(&err0)
if _, err := ef.SetBit(tx, 0, colID, nil); err != nil {
return false, errors.Wrap(err, "setting existence column")
}
finisher(nil) // commit to free of the write lock needed inside executeSetBitField
}
finisher(nil) // commit to free of the write lock needed inside executeSetBitField
switch f.Type() {
case FieldTypeInt, FieldTypeDecimal:
@ -4206,13 +4208,14 @@ func (e *executor) executeSetBitField(ctx context.Context, qcx *Qcx, index strin
shard := colID / ShardWidth
ret := false
idx := e.Holder.Index(index)
tx, finisher := qcx.GetTx(Txo{Write: writable, Index: idx, Shard: shard})
defer finisher(&err0)
for _, node := range e.Cluster.shardNodes(index, shard) {
// Update locally if host matches.
if node.ID == e.Node.ID {
idx := e.Holder.Index(index)
tx, finisher := qcx.GetTx(Txo{Write: writable, Index: idx, Shard: shard})
defer finisher(&err0)
val, err := f.SetBit(tx, rowID, colID, timestamp)
if err != nil {
return false, err
@ -4245,13 +4248,14 @@ func (e *executor) executeSetValueField(ctx context.Context, qcx *Qcx, index str
shard := colID / ShardWidth
ret := false
idx := e.Holder.Index(index)
tx, finisher := qcx.GetTx(Txo{Write: writable, Index: idx, Shard: shard})
defer finisher(&err)
for _, node := range e.Cluster.shardNodes(index, shard) {
// Update locally if host matches.
if node.ID == e.Node.ID {
idx := e.Holder.Index(index)
tx, finisher := qcx.GetTx(Txo{Write: writable, Index: idx, Shard: shard})
defer finisher(&err)
val, err := f.SetValue(tx, colID, value)
if err != nil {
return false, err
@ -4284,13 +4288,14 @@ func (e *executor) executeClearValueField(ctx context.Context, qcx *Qcx, index s
shard := colID / ShardWidth
ret := false
idx := e.Holder.Index(index)
tx, finisher := qcx.GetTx(Txo{Write: writable, Index: idx, Shard: shard})
defer finisher(&err)
for _, node := range e.Cluster.shardNodes(index, shard) {
// Update locally if host matches.
if node.ID == e.Node.ID {
idx := e.Holder.Index(index)
tx, finisher := qcx.GetTx(Txo{Write: writable, Index: idx, Shard: shard})
defer finisher(&err)
val, err := f.ClearValue(tx, colID)
if err != nil {
return false, err

View file

@ -675,7 +675,7 @@ func (h *Holder) Open() error {
// under blue_green, we must sync blue from green before we turn on checking.
if err := h.txf.green2blue(h); err != nil {
return errors.Wrap(err, "Holder.Open h.txf.UpdateBlueFromGreen(h)")
return errors.Wrap(err, "Holder.Open h.txf.green2blue(h)")
}
h.txf.blueGreenOnIfRunningBlueGreen()

View file

@ -691,8 +691,8 @@ func FormatQualifiedIndexName(index string) string {
// Dump prints to stdout the contents of the roaring Containers
// stored in idx. Mostly for debugging.
func (idx *Index) Dump(label string) {
//fileline := FileLine(2)
fmt.Printf("\nDump: %v\n\n", label)
fileline := FileLine(2)
fmt.Printf("\n%v Dump: %v\n\n", fileline, label)
idx.holder.txf.dbPerShard.DumpAll()
}

10
lmdb.go
View file

@ -1575,7 +1575,7 @@ func (tx *LMDBTx) countBitsSet(bkey []byte) (n int) {
return
}
func (tx *LMDBTx) Dump(short bool) {
func (tx *LMDBTx) Dump(short bool, shard uint64) {
fmt.Printf("%v\n", stringifiedLMDBKeysTx(tx, short))
}
@ -1650,11 +1650,11 @@ func (w *LMDBWrapper) DeleteField(index, field, fieldPath string) (err error) {
// under blue-green roaring_lmdb, the directory will not be found, b/c roaring will have
// already done the os.RemoveAll(). BUT, RemoveAll returns nil error in this case. Docs:
// "If the path does not exist, RemoveAll returns nil (no error)"
//w.DeleteDBPath(&DBShard{Path: fieldPath})
//if err != nil {
//return errors.Wrap(err, "removing directory")
//}
err = os.RemoveAll(fieldPath)
if err != nil {
return errors.Wrap(err, "removing directory")
}
prefix := txkey.FieldPrefix(index, field)
return w.DeletePrefix(prefix)
}

View file

@ -404,7 +404,7 @@ func (tx *LMDBTx) IsDone() (done bool) {
panic("lmdb only available on 64-bit arch")
}
func (tx *LMDBTx) Dump(short bool) {
func (tx *LMDBTx) Dump(short bool, shard uint64) {
panic("lmdb only available on 64-bit arch")
}

4
rbf.go
View file

@ -426,8 +426,8 @@ func (tx *RBFTx) Pointer() string {
return fmt.Sprintf("%p", tx)
}
func (tx *RBFTx) Dump(short bool) {
tx.tx.Dump(short)
func (tx *RBFTx) Dump(short bool, shard uint64) {
tx.tx.Dump(short, shard)
}
// Readonly is true if the transaction is not read-and-write, but only doing reads.

View file

@ -1334,10 +1334,10 @@ func (si *emptyContainerIterator) Value() (uint64, *roaring.Container) {
panic("emptyContainerIterator never has any Values")
}
func (tx *Tx) Dump(short bool) {
fmt.Println(tx.DumpString(short))
func (tx *Tx) Dump(short bool, shard uint64) {
fmt.Println(tx.DumpString(short, shard))
}
func (tx *Tx) DumpString(short bool) (r string) {
func (tx *Tx) DumpString(short bool, shard uint64) (r string) {
r = "allkeys:[\n"

View file

@ -16,6 +16,7 @@ package rbf_test
import (
"fmt"
"math"
"math/rand"
"testing"
"time"
@ -483,7 +484,7 @@ func TestTx_Dump(t *testing.T) {
}
// test that we don't crash, and get *something* back
s := tx.DumpString(true)
s := tx.DumpString(true, math.MaxUint64)
if s == "" {
panic("should have had 3 containers!")
}

View file

@ -55,8 +55,10 @@ func (tx *RoaringTx) Type() string {
return RoaringTxn
}
func (tx *RoaringTx) Dump(short bool) {
fmt.Printf("%v\n", tx.Index.StringifiedRoaringKeys(short, false, tx.o))
func (tx *RoaringTx) Dump(short bool, shard uint64) {
o := tx.o
o.Shard = shard
fmt.Printf("%v\n", tx.Index.StringifiedRoaringKeys(short, false, o))
}
func (tx *RoaringTx) UseRowCache() bool {
@ -188,6 +190,7 @@ func (tx *RoaringTx) RemoveContainer(index, field, view string, shard uint64, ke
}
func (tx *RoaringTx) Add(index, field, view string, shard uint64, batched bool, a ...uint64) (changeCount int, err error) {
//vv("RoaringTx.Add(index='%v', shard='%v') stack=\n%v", index, shard, stack())
b, err := tx.bitmap(index, field, view, shard)
if err != nil {
return 0, err

View file

@ -310,8 +310,8 @@ func (c *statTx) ImportRoaringBits(index, field, view string, shard uint64, rit
return c.b.ImportRoaringBits(index, field, view, shard, rit, clear, log, rowSize, data)
}
func (c *statTx) Dump(short bool) {
c.b.Dump(short)
func (c *statTx) Dump(short bool, shard uint64) {
c.b.Dump(short, shard)
}
func (c *statTx) Readonly() bool {

2
tx.go
View file

@ -202,7 +202,7 @@ type Tx interface {
Group() *TxGroup
// Dump is for debugging, what does this Tx see as its database?
Dump(short bool)
Dump(short bool, shard uint64)
// Options returns the options used to create this Tx. This
// can be implementd by embedding Txo, and Txo provides the

View file

@ -827,6 +827,9 @@ func (idx *Index) StringifiedRoaringKeys(hashOnly, showOps bool, o Txo) (r strin
if err != nil {
continue // ignore .meta paths
}
if shard != o.Shard {
continue // only print the shard the Txo is on.
}
abspath := idx.path + sep + relpath
s, _, err := stringifiedRawRoaringFragment(abspath, index, field, view, shard, showOps, hashOnly, os.Stdout)
@ -839,7 +842,7 @@ func (idx *Index) StringifiedRoaringKeys(hashOnly, showOps bool, o Txo) (r strin
n++
}
if n == 0 {
return "" // new convention that empty database => empty string returned.
return "<empty roaring data>"
}
// note that we can have a bitmap present, but it can be empty
r += "]\n all-in-blake3:" + hash.Blake3sum16([]byte(r)) + "\n"
@ -1197,14 +1200,19 @@ func (f *TxFactory) green2blue(holder *Holder) (err error) {
for _, idx := range idxs {
blueShards, err := TypedDBPerShardGetLocalShardsForIndex(blueDest, idx, "")
// scan directories
blueShards, err := f.dbPerShard.TypedDBPerShardGetShardsForIndex(blueDest, idx, "")
if err != nil {
return errors.Wrap(err, fmt.Sprintf("GetDBShard(index='%v') error fetching blueShards", idx.name))
}
greenShards, err := TypedDBPerShardGetLocalShardsForIndex(greenSrc, idx, "")
//vv("from blueDest='%v', blueShards = '%#v'", blueDest, blueShards)
// scan directories
greenShards, err := f.dbPerShard.TypedDBPerShardGetShardsForIndex(greenSrc, idx, "")
if err != nil {
return errors.Wrap(err, fmt.Sprintf("GetDBShard(index='%v') error fetching greenShards", idx.name))
}
//vv("from greenSrc='%v', greenShards = '%#v'", greenSrc, greenShards)
diff := f.shardSliceDiff(blueShards, greenShards)
if diff != "" {
@ -1272,3 +1280,12 @@ func (f *TxFactory) shardSliceDiff(blueShards, greenShards []uint64) (diff strin
diff += fmt.Sprintf("shard diff: blueMinusGreen shards: '%#v'; greenMinusBlue shards: '%#v'", bmg, gmb)
return
}
func (f *TxFactory) GetDBShardPath(index string, shard uint64, idx *Index, ty txtype, write bool) (shardPath string, err error) {
dbs, err := f.dbPerShard.GetDBShard(index, shard, idx)
if err != nil {
return "", errors.Wrap(err, fmt.Sprintf("GetDBShardPath(index='%v', shard='%v', ty='%v')", index, shard, ty.String()))
}
shardPath = dbs.pathForType(ty)
return
}

View file

@ -112,7 +112,7 @@ func Test_TxFactory_Qcx_query_context(t *testing.T) {
// and b) we have an easy migration mechanism, to go from one storage format to another.
//
func Test_TxFactory_UpdateBlueFromGreen_OnStartup(t *testing.T) {
t.Skip("TODO(jea) bring this back in. broken by the local vs remote shard determination for a cluster")
//t.Skip("TODO(jea) bring this back in. broken by the local vs remote shard determination for a cluster")
orig := os.Getenv("PILOSA_TXSRC")
defer os.Setenv("PILOSA_TXSRC", orig) // must restore or will mess up other tests!
@ -218,6 +218,8 @@ func Test_TxFactory_UpdateBlueFromGreen_OnStartup(t *testing.T) {
// open a holder with path again, now looking at both blue and green.
// The Holder.Open should do the migration from green, populating blue.
h4 := NewHolder(path, nil)
//vv("about to h4.Open we should populate blue from green")
panicOn(h4.Open())
defer h4.Close()
@ -232,7 +234,7 @@ func Test_TxFactory_UpdateBlueFromGreen_OnStartup(t *testing.T) {
// go to verify it but blue has more data than green.
// That will also cause query divergence.
func Test_TxFactory_verifyBlueEqualsGreen(t *testing.T) {
t.Skip("TODO(jea) bring this back in. broken by the local vs remote shard determination for a cluster")
//t.Skip("TODO(jea) bring this back in. broken by the local vs remote shard determination for a cluster")
orig := os.Getenv("PILOSA_TXSRC")
defer os.Setenv("PILOSA_TXSRC", orig) // must restore or will mess up other tests!

View file

@ -173,7 +173,7 @@ var workQueue = make(chan struct{}, runtime.NumCPU()*2)
// replaces v.openFragments() with Tx generic code.
func (v *view) openFragmentsInTx() error {
shards, err := DBPerShardGetShardsForIndex(v.idx, v.path)
shards, err := v.holder.txf.GetShardsForIndex(v.idx, v.path)
if err != nil {
return errors.Wrap(err, "DBPerShardGetShardsForIndex()")
}