mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-09-07 00:55:55 +00:00
1447 lines
40 KiB
Go
1447 lines
40 KiB
Go
// Copyright 2020 Pilosa Corp.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
// badgerdb builds but won't run in 32-bit 386 world, as of 2020 July 20.
|
|
// See https://github.com/dgraph-io/badger/issues/1384 for any progress.
|
|
// What we see is that the value-log allocations immediately run out of
|
|
// memory. So we turn off 386 with a build tag to keep the .circleci happy.
|
|
|
|
// +build !386
|
|
|
|
package pilosa
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"math"
|
|
"os"
|
|
"strconv"
|
|
"testing"
|
|
|
|
"github.com/dgraph-io/badger/v2"
|
|
"github.com/pilosa/pilosa/v2/roaring"
|
|
)
|
|
|
|
var _ = &roaring.Bitmap{}
|
|
|
|
// helpers, each runs their own new txn, and commits if a change/delete
|
|
// was made. The txn is rolled back if it is just viewing the data.
|
|
|
|
func badgerDBMustHaveBitvalue(dbwrap *BadgerDBWrapper, index, field, view string, shard uint64, bitvalue uint64) {
|
|
|
|
tx := dbwrap.NewBadgerTx(!writable)
|
|
defer tx.Rollback()
|
|
exists, err := tx.Contains(index, field, view, shard, bitvalue)
|
|
panicOn(err)
|
|
if !exists {
|
|
panic(fmt.Sprintf("ARG bitvalue '%v' was NOT SET!!!", bitvalue))
|
|
}
|
|
|
|
tx.Rollback()
|
|
}
|
|
|
|
func badgerDBMustNotHaveBitvalue(dbwrap *BadgerDBWrapper, index, field, view string, shard uint64, bitvalue uint64) {
|
|
|
|
tx := dbwrap.NewBadgerTx(!writable)
|
|
defer tx.Rollback()
|
|
exists, err := tx.Contains(index, field, view, shard, bitvalue)
|
|
panicOn(err)
|
|
if exists {
|
|
panic(fmt.Sprintf("ARG bitvalue '%v' WAS SET but should not have been.!!!", bitvalue))
|
|
}
|
|
tx.Rollback()
|
|
}
|
|
|
|
func badgerDBMustSetBitvalue(dbwrap *BadgerDBWrapper, index, field, view string, shard uint64, putme uint64) {
|
|
tx := dbwrap.NewBadgerTx(writable)
|
|
|
|
// add a bit
|
|
changed, err := tx.Add(index, field, view, shard, doBatched, putme)
|
|
if changed != 1 {
|
|
panic("should have 1 bit changed")
|
|
}
|
|
panicOn(err)
|
|
|
|
exists, err := tx.Contains(index, field, view, shard, putme)
|
|
panicOn(err)
|
|
if !exists {
|
|
panic("ARG putme was NOT SET!!!")
|
|
}
|
|
panicOn(tx.Commit())
|
|
}
|
|
|
|
func badgerDBMustDeleteBitvalueContainer(dbwrap *BadgerDBWrapper, index, field, view string, shard uint64, putme uint64) {
|
|
tx := dbwrap.NewBadgerTx(writable)
|
|
hi := highbits(putme)
|
|
panicOn(tx.RemoveContainer(index, field, view, shard, hi))
|
|
panicOn(tx.Commit())
|
|
}
|
|
|
|
func badgerDBMustDeleteBitvalue(dbwrap *BadgerDBWrapper, index, field, view string, shard uint64, putme uint64) {
|
|
tx := dbwrap.NewBadgerTx(writable)
|
|
_, err := tx.Remove(index, field, view, shard, putme)
|
|
panicOn(err)
|
|
panicOn(tx.Commit())
|
|
}
|
|
|
|
func mustOpenEmptyBadgerWrapper(path string) (w *BadgerDBWrapper, cleaner func()) {
|
|
var err error
|
|
fn := badgerPath(path)
|
|
panicOn(os.RemoveAll(fn))
|
|
w, err = newBadgerDBWrapper(path)
|
|
panicOn(err)
|
|
|
|
// verify it is empty
|
|
allkeys := w.StringifiedBadgerKeys(nil)
|
|
if allkeys != "" {
|
|
panic(fmt.Sprintf("freshly created database was not empty! had keys:'%v'", allkeys))
|
|
}
|
|
|
|
return w, func() {
|
|
os.RemoveAll(fn)
|
|
}
|
|
}
|
|
|
|
//
|
|
func TestBadger_SetBitmap(t *testing.T) {
|
|
dbwrap, clean := mustOpenEmptyBadgerWrapper("TestBadger_SetBitmap")
|
|
defer clean()
|
|
defer dbwrap.Close()
|
|
tx := dbwrap.NewBadgerTx(writable)
|
|
index, field, view, shard := "i", "f", "v", uint64(0)
|
|
bitvalue := uint64(0)
|
|
changed, err := tx.Add(index, field, view, shard, doBatched, bitvalue)
|
|
if changed <= 0 {
|
|
panic("should have changed")
|
|
}
|
|
panicOn(err)
|
|
|
|
exists, err := tx.Contains(index, field, view, shard, bitvalue)
|
|
panicOn(err)
|
|
if !exists {
|
|
panic("ARG bitvalue was NOT SET!!!")
|
|
}
|
|
|
|
err = tx.Commit()
|
|
panicOn(err)
|
|
|
|
//
|
|
// commited, so should be visible outside the txn
|
|
//
|
|
|
|
tx2 := dbwrap.NewBadgerTx(!writable)
|
|
exists, err = tx2.Contains(index, field, view, shard, bitvalue)
|
|
panicOn(err)
|
|
if !exists {
|
|
panic("ARG bitvalue was NOT SET!!! on tx2")
|
|
}
|
|
|
|
n, err := tx2.Count(index, field, view, shard)
|
|
panicOn(err)
|
|
if n != 1 {
|
|
panic(fmt.Sprintf("should have Count 1; instead n = %v", n))
|
|
}
|
|
tx2.Rollback()
|
|
}
|
|
|
|
func TestBadger_OffsetRange(t *testing.T) {
|
|
dbwrap, clean := mustOpenEmptyBadgerWrapper("TestBadger_SetBitmap")
|
|
defer clean()
|
|
defer dbwrap.Close()
|
|
tx := dbwrap.NewBadgerTx(writable)
|
|
|
|
index, field, view, shard := "i", "f", "v", uint64(0)
|
|
bitvalue := uint64(1 << 20)
|
|
changed, err := tx.Add(index, field, view, shard, doBatched, bitvalue)
|
|
if changed <= 0 {
|
|
panic("should have changed")
|
|
}
|
|
panicOn(err)
|
|
|
|
bitvalue2 := uint64(1<<20 + 1)
|
|
changed, err = tx.Add(index, field, view, shard, doBatched, bitvalue2)
|
|
if changed <= 0 {
|
|
panic("should have changed")
|
|
}
|
|
panicOn(err)
|
|
|
|
exists, err := tx.Contains(index, field, view, shard, bitvalue)
|
|
panicOn(err)
|
|
if !exists {
|
|
panic("ARG bitvalue was NOT SET!!!")
|
|
}
|
|
exists, err = tx.Contains(index, field, view, shard, bitvalue2)
|
|
panicOn(err)
|
|
if !exists {
|
|
panic("ARG bitvalue2 was NOT SET!!!")
|
|
}
|
|
|
|
err = tx.Commit()
|
|
panicOn(err)
|
|
|
|
offset := uint64(0 << 20)
|
|
start := uint64(0 << 16)
|
|
endx := bitvalue + 1<<16
|
|
|
|
tx2 := dbwrap.NewBadgerTx(!writable)
|
|
rbm2, err := tx2.OffsetRange(index, field, view, shard, offset, start, endx)
|
|
panicOn(err)
|
|
tx2.Rollback()
|
|
|
|
// should see our 1M value
|
|
s2 := bitmapAsString(rbm2)
|
|
expect2 := "c(1048576, 1048577)"
|
|
if s2 != expect2 {
|
|
panic(fmt.Sprintf("s2='%v', but expected '%v'", s2, expect2))
|
|
}
|
|
|
|
// now offset by 2M
|
|
offset = uint64(2 << 20)
|
|
tx3 := dbwrap.NewBadgerTx(!writable)
|
|
rbm3, err := tx3.OffsetRange(index, field, view, shard, offset, start, endx)
|
|
panicOn(err)
|
|
tx3.Rollback()
|
|
|
|
//expect to see 3M == 3145728
|
|
s3 := bitmapAsString(rbm3)
|
|
expect3 := "c(3145728, 3145729)"
|
|
|
|
if s3 != expect3 {
|
|
panic(fmt.Sprintf("s3='%v', but expected '%v'", s3, expect3))
|
|
}
|
|
}
|
|
|
|
func TestBadger_Count_on_many_containers(t *testing.T) {
|
|
dbwrap, clean := mustOpenEmptyBadgerWrapper("TestBadger_Count_on_many_containers")
|
|
defer clean()
|
|
defer dbwrap.Close()
|
|
index, field, view, shard := "i", "f", "v", uint64(0)
|
|
|
|
putmeValues := []uint64{0, 2 << 16, 4 << 16}
|
|
|
|
for _, putme := range putmeValues {
|
|
badgerDBMustNotHaveBitvalue(dbwrap, index, field, view, shard, putme)
|
|
badgerDBMustSetBitvalue(dbwrap, index, field, view, shard, putme)
|
|
badgerDBMustHaveBitvalue(dbwrap, index, field, view, shard, putme)
|
|
}
|
|
|
|
tx := dbwrap.NewBadgerTx(writable)
|
|
defer tx.Rollback()
|
|
|
|
n, err := tx.Count(index, field, view, shard)
|
|
panicOn(err)
|
|
if int(n) != len(putmeValues) {
|
|
panic(fmt.Sprintf("expected Count of %v but got n=%v", len(putmeValues), n))
|
|
}
|
|
}
|
|
|
|
func TestBadger_Count_dense_containers(t *testing.T) {
|
|
dbwrap, clean := mustOpenEmptyBadgerWrapper("TestBadger_Count_dense_containers")
|
|
defer clean()
|
|
defer dbwrap.Close()
|
|
index, field, view, shard := "i", "f", "v", uint64(0)
|
|
|
|
tx := dbwrap.NewBadgerTx(writable)
|
|
|
|
expected := 0
|
|
// can't do more than about 100k writes per badger txn by default, so
|
|
// have to keep this kind of small.
|
|
// (See maxBatchCount:104857, maxBatchSize:10066329).
|
|
for i := uint64(0); i < (1<<16)+2; i += 2 {
|
|
changed, err := tx.Add(index, field, view, shard, doBatched, i)
|
|
panicOn(err)
|
|
if changed <= 0 {
|
|
panic("wat? should have changed")
|
|
}
|
|
expected++
|
|
}
|
|
defer tx.Rollback()
|
|
|
|
n, err := tx.Count(index, field, view, shard)
|
|
panicOn(err)
|
|
if int(n) != expected {
|
|
panic(fmt.Sprintf("expected Count of %v but got n=%v", expected, n))
|
|
}
|
|
}
|
|
|
|
func TestBadger_ContainerIterator_on_empty(t *testing.T) {
|
|
// iterate on empty container, should not find anything.
|
|
dbwrap, clean := mustOpenEmptyBadgerWrapper("TestBadger_ContainerIterator")
|
|
defer clean()
|
|
defer dbwrap.Close()
|
|
tx := dbwrap.NewBadgerTx(!writable)
|
|
defer tx.Rollback()
|
|
index, field, view, shard := "i", "f", "v", uint64(0)
|
|
bitvalue := uint64(0)
|
|
citer, found, err := tx.ContainerIterator(index, field, view, shard, bitvalue)
|
|
panicOn(err)
|
|
defer citer.Close()
|
|
if found {
|
|
panic("should not have found anything")
|
|
}
|
|
panicOn(err)
|
|
}
|
|
|
|
func TestBadger_ContainerIterator_on_one_bit(t *testing.T) {
|
|
// set one bit, iterate.
|
|
dbwrap, clean := mustOpenEmptyBadgerWrapper("TestBadger_ContainerIterator_on_one_bit")
|
|
defer clean()
|
|
defer dbwrap.Close()
|
|
tx := dbwrap.NewBadgerTx(writable)
|
|
defer tx.Rollback()
|
|
index, field, view, shard := "i", "f", "v", uint64(0)
|
|
|
|
bitvalue := uint64(42)
|
|
|
|
// add a bit
|
|
changed, err := tx.Add(index, field, view, shard, doBatched, bitvalue)
|
|
if changed <= 0 {
|
|
panic("should have changed")
|
|
}
|
|
panicOn(err)
|
|
|
|
exists, err := tx.Contains(index, field, view, shard, bitvalue)
|
|
panicOn(err)
|
|
if !exists {
|
|
panic("ARG bitvalue was NOT SET!!!")
|
|
}
|
|
|
|
// same Tx, continues in use.
|
|
|
|
citer, found, err := tx.ContainerIterator(index, field, view, shard, highbits(bitvalue))
|
|
if !found {
|
|
panic("ContainerIterator did not find the 42 bit")
|
|
}
|
|
panicOn(err)
|
|
defer citer.Close()
|
|
|
|
loopCount := 0
|
|
for citer.Next() {
|
|
key, container := citer.Value()
|
|
if key != 0 {
|
|
panic("42 should have had key 0")
|
|
}
|
|
if container == nil {
|
|
panic("container was nil")
|
|
}
|
|
if container.N() != 1 {
|
|
panic("put a bit in, but size of container was not 1")
|
|
}
|
|
if !container.Contains(lowbits(bitvalue)) {
|
|
panic("container did not have our bitvalue!")
|
|
}
|
|
loopCount++
|
|
if loopCount > 0 { // happier linter
|
|
break
|
|
}
|
|
}
|
|
if loopCount != 1 {
|
|
panic("ContainerIterator did not return a citer that scanned our set bit")
|
|
}
|
|
}
|
|
|
|
func TestBadger_badgerKey_badgerPrefix(t *testing.T) {
|
|
|
|
// badgerPrefix() must agree with badgerKey(), but not have the key at the end.
|
|
// This is important for iteration over containers.
|
|
|
|
index, field, view, shard := "i", "f", "v", uint64(0)
|
|
|
|
// needle examples with the container-key extremes:
|
|
// "index:'i';field:'f';view:'v';shard:'0';key@00000000000000000000" // smallest
|
|
// "index:'i';field:'f';view:'v';shard:'0';key@18446744073709551615" // largest
|
|
needle := badgerKey(index, field, view, shard, 0)
|
|
|
|
// prefix example: "index:'i';field:'f';view:'v';shard:'0';key@"
|
|
prefix := badgerPrefix(index, field, view, shard)
|
|
|
|
if !bytes.HasPrefix(needle, prefix) {
|
|
panic(fmt.Sprintf("badgerPrefix() output '%v'was not a prefix of badgerKey() '%v'", string(needle), string(prefix)))
|
|
}
|
|
if len(prefix)+20 != len(needle) {
|
|
panic(fmt.Sprintf("badgerPrefix() output '%v'was 20 characters shorter than badgerKey() '%v'", string(needle), string(prefix)))
|
|
}
|
|
|
|
// validate assumption that badgerKeyExtractContainerKey() makes about strconv.ParseUint() error reporting;
|
|
// for distinguishing prefixes from full keys. Even if the shard number is so large that the prefix
|
|
// starts with a legitimate decimal number.
|
|
shouldNotParse := "12345123451234';key@"
|
|
containerKey, err := strconv.ParseUint(shouldNotParse, 10, 64)
|
|
if err == nil {
|
|
panic(fmt.Sprintf("strconv.ParseUint should have returned an error parsing this string '%v'; instead we got '%v'", shouldNotParse, containerKey))
|
|
}
|
|
|
|
// verify panic on submitting a prefix
|
|
func() {
|
|
defer func() {
|
|
r := recover()
|
|
if r == nil {
|
|
panic(fmt.Sprintf("should have seen panic on call to badgerKeyExtractContainerKey(prefix='%v')", prefix))
|
|
}
|
|
}()
|
|
badgerKeyExtractContainerKey(prefix) // should panic.
|
|
}()
|
|
}
|
|
|
|
func TestBadger_ContainerIterator_on_one_bit_fail_to_find(t *testing.T) {
|
|
dbwrap, clean := mustOpenEmptyBadgerWrapper("TestBadger_ContainerIterator_on_one_bit")
|
|
defer clean()
|
|
defer dbwrap.Close()
|
|
tx := dbwrap.NewBadgerTx(writable)
|
|
defer tx.Rollback()
|
|
index, field, view, shard := "i", "f", "v", uint64(0)
|
|
|
|
putme := uint64(1<<16) + 3 // in the key:1 container
|
|
searchme := putme + 1
|
|
|
|
// add a bit
|
|
changed, err := tx.Add(index, field, view, shard, doBatched, putme)
|
|
if changed <= 0 {
|
|
panic("should have changed")
|
|
}
|
|
panicOn(err)
|
|
|
|
exists, err := tx.Contains(index, field, view, shard, putme)
|
|
panicOn(err)
|
|
if !exists {
|
|
panic("ARG putme was NOT SET!!!")
|
|
}
|
|
|
|
// same Tx, continues in use.
|
|
|
|
citer, found, err := tx.ContainerIterator(index, field, view, shard, highbits(searchme))
|
|
if !found {
|
|
panic("ContainerIterator did not find the searchme")
|
|
}
|
|
defer citer.Close()
|
|
loopCount := 0
|
|
for citer.Next() {
|
|
key, container := citer.Value()
|
|
if key != 1 {
|
|
panic("Containeriterator searching for highbits(searchme) should not have had a bit")
|
|
}
|
|
if container == nil {
|
|
panic("container was nil")
|
|
}
|
|
if container.N() != 1 {
|
|
panic("put a bit in, but size of container was not 1")
|
|
}
|
|
if container.Contains(lowbits(searchme)) {
|
|
panic("container should have putme but not our searchme!")
|
|
}
|
|
loopCount++
|
|
// only want first pass. keep linter happy by avoiding raw break
|
|
if loopCount > 0 {
|
|
break
|
|
}
|
|
}
|
|
panicOn(err)
|
|
}
|
|
|
|
func TestBadger_ContainerIterator_empty_iteration_loop(t *testing.T) {
|
|
dbwrap, clean := mustOpenEmptyBadgerWrapper("TestBadger_ContainerIterator_empty_iteration_loop")
|
|
defer clean()
|
|
defer dbwrap.Close()
|
|
tx := dbwrap.NewBadgerTx(writable)
|
|
defer tx.Rollback()
|
|
index, field, view, shard := "i", "f", "v", uint64(0)
|
|
|
|
putme := uint64(1<<16) + 3 // in the key:1 container
|
|
searchme := uint64(1 << 17) // in the next container, key:2
|
|
|
|
// add a bit
|
|
changed, err := tx.Add(index, field, view, shard, doBatched, putme)
|
|
if changed <= 0 {
|
|
panic("should have changed")
|
|
}
|
|
panicOn(err)
|
|
|
|
exists, err := tx.Contains(index, field, view, shard, putme)
|
|
panicOn(err)
|
|
if !exists {
|
|
panic("ARG putme was NOT SET!!!")
|
|
}
|
|
|
|
// same Tx, continues in use.
|
|
|
|
citer, found, err := tx.ContainerIterator(index, field, view, shard, highbits(searchme))
|
|
panicOn(err)
|
|
if found {
|
|
panic("ContainerIterator found the searchme, when it should not have")
|
|
}
|
|
defer citer.Close()
|
|
if citer.Next() {
|
|
panic("expected no looping, 0 iterations, b/c started searchme past our data in putme")
|
|
}
|
|
|
|
// expect to see a blow up from the citer.Value() call, verify that we do.
|
|
func() {
|
|
defer func() {
|
|
r := recover()
|
|
if r == nil {
|
|
panic("expected a panic from citer.Value() in this case")
|
|
}
|
|
}()
|
|
citer.Value() // should panic
|
|
}()
|
|
|
|
}
|
|
|
|
func TestBadger_ForEach_on_one_bit(t *testing.T) {
|
|
|
|
dbwrap, clean := mustOpenEmptyBadgerWrapper("TestBadger_ContainerIterator_on_one_bit")
|
|
defer clean()
|
|
defer dbwrap.Close()
|
|
tx := dbwrap.NewBadgerTx(writable)
|
|
defer tx.Rollback()
|
|
index, field, view, shard := "i", "f", "v", uint64(0)
|
|
|
|
bitvalue := uint64(42)
|
|
|
|
// add a bit
|
|
changed, err := tx.Add(index, field, view, shard, doBatched, bitvalue)
|
|
if changed <= 0 {
|
|
panic("should have changed")
|
|
}
|
|
panicOn(err)
|
|
|
|
exists, err := tx.Contains(index, field, view, shard, bitvalue)
|
|
panicOn(err)
|
|
if !exists {
|
|
panic("ARG bitvalue was NOT SET!!!")
|
|
}
|
|
|
|
// same Tx, continues in use.
|
|
count := 0
|
|
err = tx.ForEach(index, field, view, shard, func(v uint64) error {
|
|
if v != bitvalue {
|
|
panic(fmt.Sprintf("bitvalue corrupt got %v want %v", v, bitvalue))
|
|
}
|
|
count += 1
|
|
return nil
|
|
})
|
|
panicOn(err)
|
|
if count != 1 {
|
|
panic(fmt.Sprintf("Expected single iteration got %v ", count))
|
|
}
|
|
}
|
|
|
|
func TestBadger_RemoveContainer_one_bit_test(t *testing.T) {
|
|
|
|
dbwrap, clean := mustOpenEmptyBadgerWrapper("TestBadger_RemoveContainer_one_bit_test")
|
|
defer clean()
|
|
defer dbwrap.Close()
|
|
|
|
index, field, view, shard := "i", "f", "v", uint64(0)
|
|
|
|
putmeValues := []uint64{0, 13, 77, 1511}
|
|
|
|
for _, putme := range putmeValues {
|
|
|
|
// a) delete of whole container in a seperate txn. Commit should establish the deletion.
|
|
|
|
badgerDBMustNotHaveBitvalue(dbwrap, index, field, view, shard, putme)
|
|
badgerDBMustSetBitvalue(dbwrap, index, field, view, shard, putme)
|
|
badgerDBMustHaveBitvalue(dbwrap, index, field, view, shard, putme)
|
|
badgerDBMustDeleteBitvalueContainer(dbwrap, index, field, view, shard, putme)
|
|
badgerDBMustNotHaveBitvalue(dbwrap, index, field, view, shard, putme)
|
|
|
|
// b) deletion + rollback on the txn should restore the deleted bit
|
|
|
|
badgerDBMustSetBitvalue(dbwrap, index, field, view, shard, putme)
|
|
badgerDBMustHaveBitvalue(dbwrap, index, field, view, shard, putme)
|
|
|
|
// delete, but rollback instead of commit
|
|
tx := dbwrap.NewBadgerTx(writable)
|
|
hi := highbits(putme)
|
|
panicOn(tx.RemoveContainer(index, field, view, shard, hi))
|
|
tx.Rollback()
|
|
|
|
// verify that the rollback undid the deletion.
|
|
badgerDBMustHaveBitvalue(dbwrap, index, field, view, shard, putme)
|
|
|
|
// c) within one Tx, after delete it should be gone as viewed within the txn.
|
|
tx = dbwrap.NewBadgerTx(writable)
|
|
hi = highbits(putme)
|
|
|
|
exists, err := tx.Contains(index, field, view, shard, putme)
|
|
panicOn(err)
|
|
if !exists {
|
|
panic(fmt.Sprintf("ARG putme '%v' was NOT SET!!!", putme))
|
|
}
|
|
|
|
panicOn(tx.RemoveContainer(index, field, view, shard, hi))
|
|
|
|
exists, err = tx.Contains(index, field, view, shard, putme)
|
|
panicOn(err)
|
|
if exists {
|
|
panic(fmt.Sprintf("ARG putme '%v' was SET even after RemoveContiner in this txn.", putme))
|
|
}
|
|
|
|
tx.Rollback()
|
|
|
|
// verify that the rollback undid the deletion.
|
|
badgerDBMustHaveBitvalue(dbwrap, index, field, view, shard, putme)
|
|
// leave with clean slate
|
|
badgerDBMustDeleteBitvalueContainer(dbwrap, index, field, view, shard, putme)
|
|
}
|
|
}
|
|
|
|
func TestBadger_Remove_one_bit_test(t *testing.T) {
|
|
|
|
dbwrap, clean := mustOpenEmptyBadgerWrapper("TestBadger_Remove_one_bit_test")
|
|
defer clean()
|
|
defer dbwrap.Close()
|
|
|
|
index, field, view, shard := "i", "f", "v", uint64(0)
|
|
|
|
putmeValues := []uint64{0, 13, 77, 1511}
|
|
|
|
for _, putme := range putmeValues {
|
|
|
|
// a) delete of whole container in a seperate txn. Commit should establish the deletion.
|
|
|
|
badgerDBMustNotHaveBitvalue(dbwrap, index, field, view, shard, putme)
|
|
badgerDBMustSetBitvalue(dbwrap, index, field, view, shard, putme)
|
|
badgerDBMustHaveBitvalue(dbwrap, index, field, view, shard, putme)
|
|
badgerDBMustDeleteBitvalue(dbwrap, index, field, view, shard, putme)
|
|
badgerDBMustNotHaveBitvalue(dbwrap, index, field, view, shard, putme)
|
|
|
|
// b) deletion + rollback on the txn should restore the deleted bit
|
|
|
|
badgerDBMustSetBitvalue(dbwrap, index, field, view, shard, putme)
|
|
badgerDBMustHaveBitvalue(dbwrap, index, field, view, shard, putme)
|
|
|
|
// delete, but rollback instead of commit
|
|
tx := dbwrap.NewBadgerTx(writable)
|
|
hi, lo := highbits(putme), lowbits(putme)
|
|
_, _ = hi, lo
|
|
_, err := tx.Remove(index, field, view, shard, hi)
|
|
panicOn(err)
|
|
tx.Rollback()
|
|
|
|
// verify that the rollback undid the deletion.
|
|
badgerDBMustHaveBitvalue(dbwrap, index, field, view, shard, putme)
|
|
|
|
// c) within one Tx, after delete it should be gone as viewed within the txn.
|
|
tx = dbwrap.NewBadgerTx(writable)
|
|
|
|
exists, err := tx.Contains(index, field, view, shard, putme)
|
|
panicOn(err)
|
|
if !exists {
|
|
panic(fmt.Sprintf("ARG putme '%v' was NOT SET!!!", putme))
|
|
}
|
|
|
|
mustRemove(tx.Remove(index, field, view, shard, putme))
|
|
|
|
exists, err = tx.Contains(index, field, view, shard, putme)
|
|
panicOn(err)
|
|
if exists {
|
|
panic(fmt.Sprintf("ARG putme '%v' was SET even after Remove in this txn.", putme))
|
|
}
|
|
|
|
tx.Rollback()
|
|
|
|
// verify that the rollback undid the deletion.
|
|
badgerDBMustHaveBitvalue(dbwrap, index, field, view, shard, putme)
|
|
// leave with clean slate
|
|
badgerDBMustDeleteBitvalueContainer(dbwrap, index, field, view, shard, putme)
|
|
}
|
|
}
|
|
|
|
func TestBadger_reverse_badger_iterator(t *testing.T) {
|
|
|
|
// sanity check our understanding of Seek()-ing on reverse iterators.
|
|
dbwrap, clean := mustOpenEmptyBadgerWrapper("TestBadger_reverse_badger_iterator")
|
|
defer clean()
|
|
defer dbwrap.Close()
|
|
|
|
// add 0, 1, 2 to badgerdb as keys (and same value).
|
|
err := dbwrap.db.Update(func(txn *badger.Txn) error {
|
|
for i := 0; i < 3; i++ {
|
|
kv := []byte(fmt.Sprintf("a:%v", i))
|
|
err := txn.Set(kv, kv)
|
|
panicOn(err)
|
|
}
|
|
return nil
|
|
})
|
|
panicOn(err)
|
|
|
|
tx := dbwrap.db.NewTransaction(!writable)
|
|
|
|
opts := badger.DefaultIteratorOptions
|
|
opts.PrefetchValues = false // else by default, pre-fetches the 1st 100 values, which would be slow.
|
|
opts.Reverse = true
|
|
it := tx.NewIterator(opts)
|
|
it.Rewind()
|
|
if !it.Valid() {
|
|
panic("invalid reversed iterator?")
|
|
}
|
|
a := []byte("a:3")
|
|
it.Seek(a)
|
|
if !it.Valid() {
|
|
panic("invalid reversed iterator after seek")
|
|
}
|
|
|
|
it.Next()
|
|
if !it.Valid() {
|
|
panic("invalid reversed iterator after seek and next")
|
|
}
|
|
item := it.Item()
|
|
|
|
err = item.Value(func(val []byte) error {
|
|
// This func with val would only be called if item.Value encounters no error.
|
|
if string(val) != "a:1" {
|
|
panic(fmt.Sprintf("we are in trouble, should have gotten 'a:1' but instead got '%v'", string(val)))
|
|
}
|
|
return nil
|
|
})
|
|
panicOn(err)
|
|
}
|
|
|
|
func TestBadger_Max_on_many_containers(t *testing.T) {
|
|
dbwrap, clean := mustOpenEmptyBadgerWrapper("TestBadger_Max_on_many_containers")
|
|
defer clean()
|
|
defer dbwrap.Close()
|
|
index, field, view, shard := "i", "f", "v", uint64(0)
|
|
|
|
putmeValues := []uint64{0, 2 << 16, 4 << 16}
|
|
|
|
for _, putme := range putmeValues {
|
|
badgerDBMustNotHaveBitvalue(dbwrap, index, field, view, shard, putme)
|
|
badgerDBMustSetBitvalue(dbwrap, index, field, view, shard, putme)
|
|
badgerDBMustHaveBitvalue(dbwrap, index, field, view, shard, putme)
|
|
}
|
|
|
|
tx := dbwrap.NewBadgerTx(!writable)
|
|
defer tx.Rollback()
|
|
|
|
max, err := tx.Max(index, field, view, shard)
|
|
panicOn(err)
|
|
expected := putmeValues[len(putmeValues)-1]
|
|
if max != expected {
|
|
panic(fmt.Sprintf("expected Max() of %v but got max=%v", expected, max))
|
|
}
|
|
}
|
|
|
|
func TestBadger_Min_on_many_containers(t *testing.T) {
|
|
dbwrap, clean := mustOpenEmptyBadgerWrapper("TestBadger_Min_on_many_containers")
|
|
defer clean()
|
|
defer dbwrap.Close()
|
|
index, field, view, shard := "i", "f", "v", uint64(0)
|
|
|
|
// verify no containers flag works
|
|
tx := dbwrap.NewBadgerTx(!writable)
|
|
min, containersExist, err := tx.Min(index, field, view, shard)
|
|
_ = min
|
|
panicOn(err)
|
|
if containersExist {
|
|
panic("no containers should exist")
|
|
}
|
|
tx.Rollback()
|
|
|
|
putmeValues := []uint64{3, 2 << 16, 4 << 16}
|
|
|
|
for _, putme := range putmeValues {
|
|
badgerDBMustNotHaveBitvalue(dbwrap, index, field, view, shard, putme)
|
|
badgerDBMustSetBitvalue(dbwrap, index, field, view, shard, putme)
|
|
badgerDBMustHaveBitvalue(dbwrap, index, field, view, shard, putme)
|
|
}
|
|
|
|
tx = dbwrap.NewBadgerTx(!writable)
|
|
defer tx.Rollback()
|
|
|
|
min, containersExist, err = tx.Min(index, field, view, shard)
|
|
panicOn(err)
|
|
if !containersExist {
|
|
panic("containers should exist")
|
|
}
|
|
expected := putmeValues[0]
|
|
if min != expected {
|
|
panic(fmt.Sprintf("expected Min() of %v but got min=%v", expected, min))
|
|
}
|
|
}
|
|
|
|
func TestBadger_CountRange_on_many_containers(t *testing.T) {
|
|
dbwrap, clean := mustOpenEmptyBadgerWrapper("TestBadger_CountRange_on_many_containers")
|
|
defer clean()
|
|
defer dbwrap.Close()
|
|
index, field, view, shard := "i", "f", "v", uint64(0)
|
|
|
|
// verify no containers flag works
|
|
tx := dbwrap.NewBadgerTx(!writable)
|
|
n, err := tx.CountRange(index, field, view, shard, 0, math.MaxUint64)
|
|
panicOn(err)
|
|
if n != 0 {
|
|
panic("no containers should exist")
|
|
}
|
|
tx.Rollback()
|
|
|
|
putmeValues := []uint64{3, 2 << 16, 4 << 16}
|
|
|
|
for _, putme := range putmeValues {
|
|
badgerDBMustNotHaveBitvalue(dbwrap, index, field, view, shard, putme)
|
|
badgerDBMustSetBitvalue(dbwrap, index, field, view, shard, putme)
|
|
badgerDBMustHaveBitvalue(dbwrap, index, field, view, shard, putme)
|
|
}
|
|
|
|
tx = dbwrap.NewBadgerTx(!writable)
|
|
defer tx.Rollback()
|
|
|
|
n, err = tx.CountRange(index, field, view, shard, 0, math.MaxUint64)
|
|
panicOn(err)
|
|
if n == 0 {
|
|
panic("containers should exist")
|
|
}
|
|
expected := uint64(len(putmeValues))
|
|
if n != expected {
|
|
panic(fmt.Sprintf("expected CountRange() of %v but got n=%v", expected, n))
|
|
}
|
|
}
|
|
|
|
func TestBadger_CountRange_middle_container(t *testing.T) {
|
|
dbwrap, clean := mustOpenEmptyBadgerWrapper("TestBadger_CountRange_middle_container")
|
|
defer clean()
|
|
defer dbwrap.Close()
|
|
index, field, view, shard := "i", "f", "v", uint64(0)
|
|
|
|
putmeValues := []uint64{3, 2 << 16, 4 << 16}
|
|
|
|
for _, putme := range putmeValues {
|
|
badgerDBMustNotHaveBitvalue(dbwrap, index, field, view, shard, putme)
|
|
badgerDBMustSetBitvalue(dbwrap, index, field, view, shard, putme)
|
|
badgerDBMustHaveBitvalue(dbwrap, index, field, view, shard, putme)
|
|
}
|
|
|
|
tx := dbwrap.NewBadgerTx(!writable)
|
|
defer tx.Rollback()
|
|
|
|
// pick out just the middle container with the 1 bit set on it.
|
|
n, err := tx.CountRange(index, field, view, shard, 4, (2<<16)+1)
|
|
panicOn(err)
|
|
if n != 1 {
|
|
panic("middle 1 bit container should exist")
|
|
}
|
|
}
|
|
|
|
func TestBadger_CountRange_many_middle_container(t *testing.T) {
|
|
dbwrap, clean := mustOpenEmptyBadgerWrapper("TestBadger_CountRange_many_middle_container")
|
|
defer clean()
|
|
defer dbwrap.Close()
|
|
index, field, view, shard := "i", "f", "v", uint64(0)
|
|
|
|
putmeValues := []uint64{3, 2 << 16, 4 << 16}
|
|
|
|
for _, putme := range putmeValues {
|
|
badgerDBMustNotHaveBitvalue(dbwrap, index, field, view, shard, putme)
|
|
badgerDBMustSetBitvalue(dbwrap, index, field, view, shard, putme)
|
|
badgerDBMustHaveBitvalue(dbwrap, index, field, view, shard, putme)
|
|
}
|
|
|
|
tx := dbwrap.NewBadgerTx(!writable)
|
|
defer tx.Rollback()
|
|
|
|
// get them all
|
|
n, err := tx.CountRange(index, field, view, shard, 0, (4<<16)+1)
|
|
panicOn(err)
|
|
if n != 3 {
|
|
panic("count should have been all 3 bits")
|
|
}
|
|
}
|
|
|
|
func TestBadger_UnionInPlace(t *testing.T) {
|
|
|
|
dbwrap, clean := mustOpenEmptyBadgerWrapper("TestBadger_UnionInPlace")
|
|
defer clean()
|
|
defer dbwrap.Close()
|
|
index, field, view, shard := "i", "f", "v", uint64(0)
|
|
|
|
putmeValues := []uint64{3, 2 << 16}
|
|
|
|
others := roaring.NewBitmap()
|
|
others2 := roaring.NewBitmap()
|
|
others3 := roaring.NewBitmap()
|
|
// populate others with putmeValues +1 into others
|
|
|
|
for _, putme := range putmeValues {
|
|
badgerDBMustNotHaveBitvalue(dbwrap, index, field, view, shard, putme)
|
|
badgerDBMustSetBitvalue(dbwrap, index, field, view, shard, putme)
|
|
badgerDBMustHaveBitvalue(dbwrap, index, field, view, shard, putme)
|
|
}
|
|
|
|
tx2 := dbwrap.NewBadgerTx(!writable)
|
|
n, err := tx2.Count(index, field, view, shard)
|
|
panicOn(err)
|
|
if n != 2 {
|
|
panic("should have 2 bits set")
|
|
}
|
|
tx2.Rollback()
|
|
|
|
for _, putme := range putmeValues {
|
|
mustAddR(others.Add(putme)) // should not change count, b/c putme already in the rbm
|
|
mustAddR(others.Add(putme + 1))
|
|
mustAddR(others2.Add(putme + 2))
|
|
}
|
|
mustAddR(others3.Add(4 << 16)) // outside the 2<<16 container
|
|
|
|
tx := dbwrap.NewBadgerTx(writable)
|
|
defer tx.Rollback()
|
|
err = tx.UnionInPlace(index, field, view, shard, others, others2, others3)
|
|
panicOn(err)
|
|
|
|
// end game, check we got the union.
|
|
rbm, err := tx.RoaringBitmap(index, field, view, shard)
|
|
panicOn(err)
|
|
n = rbm.Count()
|
|
if n != 7 {
|
|
panic("should have a total 3 + 3 +1 = 7 bits set on the containers")
|
|
}
|
|
}
|
|
|
|
func TestBadger_RoaringBitmap(t *testing.T) {
|
|
|
|
dbwrap, clean := mustOpenEmptyBadgerWrapper("TestBadger_RoaringBitmap")
|
|
defer clean()
|
|
defer dbwrap.Close()
|
|
index, field, view, shard := "i", "f", "v", uint64(0)
|
|
|
|
expected := uint64(3)
|
|
putme := expected
|
|
badgerDBMustSetBitvalue(dbwrap, index, field, view, shard, putme)
|
|
|
|
tx := dbwrap.NewBadgerTx(!writable)
|
|
defer tx.Rollback()
|
|
|
|
rbm, err := tx.RoaringBitmap(index, field, view, shard)
|
|
panicOn(err)
|
|
|
|
slc := rbm.Slice()
|
|
if slc[0] != uint64(expected) {
|
|
panic(fmt.Sprintf("should have gotten %v back", expected))
|
|
}
|
|
}
|
|
|
|
func TestBadger_reverse_badger_iterator_and_prefix_valid(t *testing.T) {
|
|
|
|
// does a reverse iterator and ValidForPrefix behave like we expect it too?
|
|
dbwrap, clean := mustOpenEmptyBadgerWrapper("TestBadger_reverse_badger_iterator_and_prefix_valid")
|
|
defer clean()
|
|
defer dbwrap.Close()
|
|
|
|
// add 0, 1, 2 to badgerdb as keys (and same value).
|
|
err := dbwrap.db.Update(func(txn *badger.Txn) error {
|
|
for _, prefix := range []string{"a", "b", "c"} {
|
|
for i := 0; i < 3; i++ {
|
|
kv := []byte(fmt.Sprintf("%v:%v", prefix, i))
|
|
err := txn.Set(kv, kv)
|
|
panicOn(err)
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
panicOn(err)
|
|
//vv("stringifiedBadgerKeys(db) = '%v'", stringifiedBadgerKeys(dbwrap.db))
|
|
// allkeys:["a:0", "a:1", "a:2", "b:0", "b:1", "b:2", "c:0", "c:1", "c:2", ]'
|
|
|
|
tx := dbwrap.NewBadgerTx(!writable)
|
|
|
|
prefix := []byte("b:")
|
|
it := NewBadgerIterator(tx, prefix)
|
|
|
|
if !it.it.Valid() {
|
|
panic("why is underlying badger it not valid here?")
|
|
}
|
|
results := ""
|
|
for it.Next() {
|
|
item := it.it.Item()
|
|
sk := string(item.Key())
|
|
results += sk + ", "
|
|
}
|
|
expected := `b:0, b:1, b:2, `
|
|
if results != expected {
|
|
panic(fmt.Sprintf("observed: '%v' but expected: '%v'", results, expected))
|
|
}
|
|
it.Close()
|
|
|
|
// now reversed
|
|
seekto := []byte("c:")
|
|
rit := NewBadgerReverseIterator(tx, prefix, seekto) // Seeks("b:") goes to b:0
|
|
defer rit.Close()
|
|
|
|
if !rit.it.Valid() {
|
|
panic("why is underlying badger it not valid here?")
|
|
}
|
|
results = ""
|
|
for rit.Next() {
|
|
item := rit.it.Item()
|
|
sk := string(item.Key())
|
|
results += sk + ", "
|
|
}
|
|
expected = `b:2, b:1, b:0, `
|
|
if results != expected {
|
|
panic(fmt.Sprintf("observed: '%v' but expected: '%v'", results, expected))
|
|
}
|
|
rit.Close()
|
|
}
|
|
|
|
func TestBadger_just_reverse_badger_iterator_and_prefix_valid(t *testing.T) {
|
|
|
|
// does a reverse iterator and ValidForPrefix behave like we expect it too?
|
|
dbwrap, clean := mustOpenEmptyBadgerWrapper("TestBadger_reverse_badger_iterator_and_prefix_valid")
|
|
defer clean()
|
|
defer dbwrap.Close()
|
|
|
|
// add 0, 1, 2 to badgerdb as keys (and same value).
|
|
err := dbwrap.db.Update(func(txn *badger.Txn) error {
|
|
for _, prefix := range []string{"a", "b", "c"} {
|
|
for i := 0; i < 3; i++ {
|
|
kv := []byte(fmt.Sprintf("%v:%v", prefix, i))
|
|
err := txn.Set(kv, kv)
|
|
panicOn(err)
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
panicOn(err)
|
|
//vv("stringifiedBadgerKeys(db) = '%v'", stringifiedBadgerKeys(dbwrap.db))
|
|
// allkeys:["a:0", "a:1", "a:2", "b:0", "b:1", "b:2", "c:0", "c:1", "c:2", ]'
|
|
|
|
tx := dbwrap.NewBadgerTx(!writable)
|
|
|
|
seekto := []byte("c:")
|
|
prefix := []byte("b:")
|
|
// now reversed
|
|
rit := NewBadgerReverseIterator(tx, prefix, seekto) // Seeks("b:") goes to b:0
|
|
defer rit.Close()
|
|
|
|
if !rit.it.Valid() {
|
|
panic("why is underlying badger rit not valid here?")
|
|
}
|
|
results := ""
|
|
for rit.Next() {
|
|
item := rit.it.Item()
|
|
sk := string(item.Key())
|
|
results += sk + ", "
|
|
}
|
|
expected := `b:2, b:1, b:0, `
|
|
if results != expected {
|
|
panic(fmt.Sprintf("observed: '%v' but expected: '%v'", results, expected))
|
|
}
|
|
rit.Close()
|
|
}
|
|
|
|
func TestBadger_ImportRoaringBits(t *testing.T) {
|
|
|
|
dbwrap, clean := mustOpenEmptyBadgerWrapper("TestBadger_ImportRoaringBits")
|
|
defer clean()
|
|
defer dbwrap.Close()
|
|
tx := dbwrap.NewBadgerTx(writable)
|
|
defer tx.Rollback()
|
|
index, field, view, shard := "i", "f", "v", uint64(0)
|
|
|
|
//bitvalue := uint64(42)
|
|
|
|
// get some roaring bits, get an itr RoaringIterator from them
|
|
rowSize := uint64(0)
|
|
//bits := []uint64{0}
|
|
bits := []uint64{0, 2, 5, 1<<16 + 1, 2 << 16}
|
|
data := getTestBitmapAsRawRoaring(bits...)
|
|
itr, err := roaring.NewRoaringIterator(data)
|
|
panicOn(err)
|
|
clear := false
|
|
logme := false
|
|
|
|
changed, rowSet, err := tx.ImportRoaringBits(index, field, view, shard, itr, clear, logme, rowSize)
|
|
_ = rowSet
|
|
if changed != len(bits) {
|
|
panic(fmt.Sprintf("should have changed %v bits: changed='%v', rowSet='%#v', err='%v'", len(bits), changed, rowSet, err))
|
|
}
|
|
panicOn(err)
|
|
|
|
for _, v := range bits {
|
|
exists, err := tx.Contains(index, field, view, shard, v)
|
|
panicOn(err)
|
|
if !exists {
|
|
panic(fmt.Sprintf("ARG bitvalue was NOT SET!!! '%v'", v))
|
|
}
|
|
}
|
|
|
|
// now test the union in place with the same set gives no change.
|
|
|
|
changed, rowSet, err = tx.ImportRoaringBits(index, field, view, shard, itr, clear, logme, rowSize)
|
|
_ = rowSet
|
|
if changed != 0 {
|
|
panic(fmt.Sprintf("should have not changed any bits on the second import, but we see changed='%v', rowSet='%#v', err='%v'", changed, rowSet, err))
|
|
}
|
|
panicOn(err)
|
|
|
|
for _, v := range bits {
|
|
exists, err := tx.Contains(index, field, view, shard, v)
|
|
panicOn(err)
|
|
if !exists {
|
|
panic(fmt.Sprintf("ARG bitvalue was NOT SET!!! '%v'", v))
|
|
}
|
|
}
|
|
|
|
// now test the clear path
|
|
clear = true
|
|
|
|
for _, v := range bits {
|
|
// clear 1 bit at a time
|
|
data := getTestBitmapAsRawRoaring(v)
|
|
itr, err := roaring.NewRoaringIterator(data)
|
|
panicOn(err)
|
|
|
|
changed, rowSet, err := tx.ImportRoaringBits(index, field, view, shard, itr, clear, logme, rowSize)
|
|
_ = rowSet
|
|
if changed != 1 {
|
|
panic(fmt.Sprintf("should have changed 1 bit: '%v', rowSet='%#v', err='%v'", changed, rowSet, err))
|
|
}
|
|
panicOn(err)
|
|
}
|
|
n, err := tx.Count(index, field, view, shard)
|
|
panicOn(err)
|
|
if n != 0 {
|
|
panic(fmt.Sprintf("n = %v not zero so the clearbits didn't happen!", n))
|
|
}
|
|
allkeys := stringifiedBadgerKeysTx(tx)
|
|
|
|
// should have no keys
|
|
if allkeys != "" {
|
|
panic("badger should have no keys now")
|
|
}
|
|
}
|
|
|
|
func TestBadger_ImportRoaringBits_set_nonoverlapping_bits(t *testing.T) {
|
|
|
|
dbwrap, clean := mustOpenEmptyBadgerWrapper("TestBadger_ImportRoaringBits_set_nonoverlapping_bits")
|
|
defer clean()
|
|
defer dbwrap.Close()
|
|
tx := dbwrap.NewBadgerTx(writable)
|
|
defer tx.Rollback()
|
|
index, field, view, shard := "i", "f", "v", uint64(0)
|
|
|
|
// get some roaring bits, get an itr RoaringIterator from them
|
|
rowSize := uint64(0)
|
|
//bits := []uint64{0}
|
|
bits := []uint64{0, 2, 1 << 16, 1<<16 + 2}
|
|
data := getTestBitmapAsRawRoaring(bits...)
|
|
itr, err := roaring.NewRoaringIterator(data)
|
|
panicOn(err)
|
|
|
|
bits2 := []uint64{1, 2, 3, 1<<16 + 1, 1<<16 + 2, 1<<16 + 3} //, 5, 1<<16 + 1, 2 << 16}
|
|
data2 := getTestBitmapAsRawRoaring(bits2...)
|
|
itr2, err := roaring.NewRoaringIterator(data2)
|
|
panicOn(err)
|
|
|
|
clear := false
|
|
logme := false
|
|
|
|
changed, rowSet, err := tx.ImportRoaringBits(index, field, view, shard, itr, clear, logme, rowSize)
|
|
_ = rowSet
|
|
if changed != len(bits) {
|
|
panic(fmt.Sprintf("should have changed %v bits: changed='%v', rowSet='%#v', err='%v'", len(bits), changed, rowSet, err))
|
|
}
|
|
panicOn(err)
|
|
|
|
for _, v := range bits {
|
|
exists, err := tx.Contains(index, field, view, shard, v)
|
|
panicOn(err)
|
|
if !exists {
|
|
panic(fmt.Sprintf("ARG bitvalue was NOT SET!!! '%v'", v))
|
|
}
|
|
}
|
|
|
|
// now import the 2nd, overlapping set and set them.
|
|
|
|
changed, rowSet, err = tx.ImportRoaringBits(index, field, view, shard, itr2, clear, logme, rowSize)
|
|
_ = rowSet
|
|
if changed != 4 {
|
|
panic(fmt.Sprintf("should have changed 2 bits: the 1 and the 3, but we see changed='%v', rowSet='%#v', err='%v'", changed, rowSet, err))
|
|
}
|
|
panicOn(err)
|
|
}
|
|
|
|
func TestBadger_ImportRoaringBits_clear_nonoverlapping_bits(t *testing.T) {
|
|
|
|
dbwrap, clean := mustOpenEmptyBadgerWrapper("TestBadger_ImportRoaringBits_clear_nonoverlapping_bits")
|
|
defer clean()
|
|
defer dbwrap.Close()
|
|
tx := dbwrap.NewBadgerTx(writable)
|
|
defer tx.Rollback()
|
|
index, field, view, shard := "i", "f", "v", uint64(0)
|
|
|
|
// get some roaring bits, get an itr RoaringIterator from them
|
|
rowSize := uint64(0)
|
|
//bits := []uint64{0}
|
|
bits := []uint64{0, 2, 1 << 16, 1<<16 + 2} //, 5, 1<<16 + 1, 2 << 16}
|
|
data := getTestBitmapAsRawRoaring(bits...)
|
|
itr, err := roaring.NewRoaringIterator(data)
|
|
panicOn(err)
|
|
|
|
bits2 := []uint64{1, 2, 3, 1<<16 + 1, 1<<16 + 2, 1<<16 + 3} //, 5, 1<<16 + 1, 2 << 16}
|
|
data2 := getTestBitmapAsRawRoaring(bits2...)
|
|
itr2, err := roaring.NewRoaringIterator(data2)
|
|
panicOn(err)
|
|
|
|
clear := false
|
|
logme := false
|
|
|
|
changed, rowSet, err := tx.ImportRoaringBits(index, field, view, shard, itr, clear, logme, rowSize)
|
|
_ = rowSet
|
|
if changed != len(bits) {
|
|
panic(fmt.Sprintf("should have changed %v bits: changed='%v', rowSet='%#v', err='%v'", len(bits), changed, rowSet, err))
|
|
}
|
|
panicOn(err)
|
|
|
|
for _, v := range bits {
|
|
exists, err := tx.Contains(index, field, view, shard, v)
|
|
panicOn(err)
|
|
if !exists {
|
|
panic(fmt.Sprintf("ARG bitvalue was NOT SET!!! '%v'", v))
|
|
}
|
|
}
|
|
|
|
// now import the 2nd overlapping set and clear them.
|
|
clear = true
|
|
|
|
changed, rowSet, err = tx.ImportRoaringBits(index, field, view, shard, itr2, clear, logme, rowSize)
|
|
_ = rowSet
|
|
if changed != 2 {
|
|
panic(fmt.Sprintf("should have changed 1 bit: the 2, but we see changed='%v', rowSet='%#v', err='%v'", changed, rowSet, err))
|
|
}
|
|
panicOn(err)
|
|
|
|
n, err := tx.Count(index, field, view, shard)
|
|
panicOn(err)
|
|
if n != 2 { // just the 0 and the 1<<16 bits should be left set.
|
|
panic(fmt.Sprintf("n = %v not 2 so the clearbits didn't happen!", n))
|
|
}
|
|
|
|
}
|
|
|
|
func getTestBitmapAsRawRoaring(bitsToSet ...uint64) []byte {
|
|
b := roaring.NewBitmap()
|
|
changed := b.DirectAddN(bitsToSet...)
|
|
n := len(bitsToSet)
|
|
if changed != n {
|
|
panic(fmt.Sprintf("changed=%v but bitsToSet len = %v", changed, n))
|
|
}
|
|
buf := bytes.NewBuffer(make([]byte, 0, 100000))
|
|
_, err := b.WriteTo(buf)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return buf.Bytes()
|
|
}
|
|
|
|
func TestBadger_DeleteIndex(t *testing.T) {
|
|
|
|
// setup
|
|
dbwrap, clean := mustOpenEmptyBadgerWrapper("TestBadger_DeleteIndex")
|
|
defer clean()
|
|
defer dbwrap.Close()
|
|
tx := dbwrap.NewBadgerTx(writable)
|
|
bitvalue := uint64(777)
|
|
index, field, view, shard := "i", "f", "v", uint64(0)
|
|
bits := []uint64{0, 3, 1 << 16, 1<<16 + 3, 8 << 16}
|
|
for _, v := range bits {
|
|
changed, err := tx.Add(index, field, view, shard, doBatched, v)
|
|
if changed <= 0 {
|
|
panic("should have changed")
|
|
}
|
|
panicOn(err)
|
|
}
|
|
|
|
index2 := "i2" // should not be deleted, even though it shares a prefix with 'i'
|
|
changed, err := tx.Add(index2, field, view, shard, doBatched, bitvalue)
|
|
if changed <= 0 {
|
|
panic("should have changed")
|
|
}
|
|
panicOn(err)
|
|
|
|
for _, v := range bits {
|
|
exists, err := tx.Contains(index, field, view, shard, v)
|
|
panicOn(err)
|
|
if !exists {
|
|
panic("ARG bitvalue was NOT SET!!!")
|
|
}
|
|
}
|
|
exists, err := tx.Contains(index2, field, view, shard, bitvalue)
|
|
panicOn(err)
|
|
if !exists {
|
|
panic("ARG bitvalue was NOT SET!!! on index2")
|
|
}
|
|
err = tx.Commit()
|
|
panicOn(err)
|
|
|
|
// end of setup
|
|
err = dbwrap.DeleteIndex(index)
|
|
panicOn(err)
|
|
|
|
tx = dbwrap.NewBadgerTx(!writable)
|
|
defer tx.Rollback()
|
|
exists, err = tx.Contains(index2, field, view, shard, bitvalue)
|
|
panicOn(err)
|
|
if !exists {
|
|
panic(fmt.Sprintf("after delete of '%v', another index '%v' was gone too?!?", index, index2))
|
|
}
|
|
|
|
for _, v := range bits {
|
|
exists, err = tx.Contains(index, field, view, shard, v)
|
|
panicOn(err)
|
|
if exists {
|
|
allkeys := stringifiedBadgerKeysTx(tx)
|
|
panic(fmt.Sprintf("after delete of index '%v', bit v=%v was not gone?!?; allkeys='%v'", index, v, allkeys))
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestBadger_DeleteIndex_over100k(t *testing.T) {
|
|
|
|
// setup
|
|
dbwrap, clean := mustOpenEmptyBadgerWrapper("TestBadger_DeleteIndex_over100k")
|
|
defer clean()
|
|
defer dbwrap.Close()
|
|
tx := dbwrap.NewBadgerTx(writable)
|
|
bitvalue := uint64(777)
|
|
index, field, view, shard := "i", "f", "v", uint64(0)
|
|
limit := uint64(100002) // default batch size in DeleteIndex is 100k keys per delete transaction.
|
|
//limit := uint64(101)
|
|
for v := uint64(1); v < limit; v++ {
|
|
// shift by << 16 to get into a different shard
|
|
changed, err := tx.Add(index, field, view, shard, doBatched, v<<16)
|
|
if changed <= 0 {
|
|
panic("should have changed")
|
|
}
|
|
panicOn(err)
|
|
if v%100000 == 0 {
|
|
panicOn(tx.Commit())
|
|
tx = dbwrap.NewBadgerTx(writable)
|
|
}
|
|
}
|
|
|
|
index2 := "i2" // should not be deleted, even though it shares a prefix with 'i'
|
|
changed, err := tx.Add(index2, field, view, shard, doBatched, bitvalue)
|
|
if changed <= 0 {
|
|
panic("should have changed")
|
|
}
|
|
panicOn(err)
|
|
err = tx.Commit()
|
|
panicOn(err)
|
|
|
|
// end of setup
|
|
err = dbwrap.DeleteIndex(index)
|
|
panicOn(err)
|
|
|
|
tx = dbwrap.NewBadgerTx(!writable)
|
|
defer tx.Rollback()
|
|
exists, err := tx.Contains(index2, field, view, shard, bitvalue)
|
|
panicOn(err)
|
|
if !exists {
|
|
panic(fmt.Sprintf("after delete of '%v', another index '%v' was gone too?!?", index, index2))
|
|
}
|
|
|
|
for v := uint64(0); v < limit; v++ {
|
|
exists, err = tx.Contains(index, field, view, shard, v<<16)
|
|
panicOn(err)
|
|
if exists {
|
|
allkeys := stringifiedBadgerKeysTx(tx)
|
|
panic(fmt.Sprintf("after delete of index '%v', bit v=%v was not gone?!?; allkeys='%v'", index, v, allkeys))
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestBitmapDiff(t *testing.T) {
|
|
a := roaring.NewBitmap()
|
|
b := roaring.NewBitmap()
|
|
err := roaringBitmapDiff(a, b)
|
|
panicOn(err)
|
|
err = roaringBitmapDiff(b, a)
|
|
panicOn(err)
|
|
|
|
a = roaring.NewBitmap(0)
|
|
err = roaringBitmapDiff(a, b)
|
|
if err == nil {
|
|
panic("diff should have been noticed")
|
|
}
|
|
err = roaringBitmapDiff(b, a)
|
|
if err == nil {
|
|
panic("diff should have been noticed")
|
|
}
|
|
|
|
b = roaring.NewBitmap(0)
|
|
err = roaringBitmapDiff(a, b)
|
|
panicOn(err)
|
|
err = roaringBitmapDiff(b, a)
|
|
panicOn(err)
|
|
|
|
a = roaring.NewBitmap()
|
|
|
|
err = roaringBitmapDiff(a, b)
|
|
if err == nil {
|
|
panic("diff should have been noticed")
|
|
}
|
|
err = roaringBitmapDiff(b, a)
|
|
if err == nil {
|
|
panic("diff should have been noticed")
|
|
}
|
|
|
|
a = roaring.NewBitmap(1)
|
|
|
|
err = roaringBitmapDiff(a, b)
|
|
if err == nil {
|
|
panic("diff should have been noticed")
|
|
}
|
|
err = roaringBitmapDiff(b, a)
|
|
if err == nil {
|
|
panic("diff should have been noticed")
|
|
}
|
|
|
|
b = roaring.NewBitmap(1, 2)
|
|
a = roaring.NewBitmap(0, 1)
|
|
|
|
err = roaringBitmapDiff(a, b)
|
|
if err == nil {
|
|
panic("diff should have been noticed")
|
|
}
|
|
err = roaringBitmapDiff(b, a)
|
|
if err == nil {
|
|
panic("diff should have been noticed")
|
|
}
|
|
|
|
b = roaring.NewBitmap(1, 2, 3)
|
|
a = roaring.NewBitmap(1, 2)
|
|
|
|
err = roaringBitmapDiff(a, b)
|
|
if err == nil {
|
|
panic("diff should have been noticed")
|
|
}
|
|
err = roaringBitmapDiff(b, a)
|
|
if err == nil {
|
|
panic("diff should have been noticed")
|
|
}
|
|
}
|
|
|
|
// mustAddR is a helper for calling roaring.Container.Add() in tests to
|
|
// keep the linter happy that we are checking the error.
|
|
func mustAddR(changed bool, err error) {
|
|
panicOn(err)
|
|
}
|
|
|
|
// mustRemove is a helper for calling Tx.Remove() in tests to
|
|
// keep the linter happy that we are checking the error.
|
|
func mustRemove(changeCount int, err error) {
|
|
panicOn(err)
|
|
}
|