3 Star 3 Fork 0

Gitee 极速下载/buntdb

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
此仓库是为了提升国内下载速度的镜像仓库,每日同步一次。 原始仓库: https://github.com/tidwall/buntdb
克隆/下载
buntdb_test.go 69.83 KB
一键复制 编辑 原始数据 按行查看 历史
tidwall 提交于 2024-05-16 20:04 . Ensure expired items do not load
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960
package buntdb
import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"math/rand"
"os"
"strconv"
"strings"
"sync"
"testing"
"time"
"github.com/tidwall/assert"
"github.com/tidwall/lotsa"
)
func testOpen(t testing.TB) *DB {
if err := os.RemoveAll("data.db"); err != nil {
t.Fatal(err)
}
return testReOpen(t, nil)
}
func testReOpen(t testing.TB, db *DB) *DB {
return testReOpenDelay(t, db, 0)
}
func testReOpenDelay(t testing.TB, db *DB, dur time.Duration) *DB {
if db != nil {
if err := db.Close(); err != nil {
t.Fatal(err)
}
}
time.Sleep(dur)
db, err := Open("data.db")
if err != nil {
t.Fatal(err)
}
return db
}
func testClose(db *DB) {
_ = db.Close()
_ = os.RemoveAll("data.db")
}
func TestBackgroudOperations(t *testing.T) {
db := testOpen(t)
defer testClose(db)
for i := 0; i < 1000; i++ {
if err := db.Update(func(tx *Tx) error {
for j := 0; j < 200; j++ {
if _, _, err := tx.Set(fmt.Sprintf("hello%d", j), "planet", nil); err != nil {
return err
}
}
if _, _, err := tx.Set("hi", "world", &SetOptions{Expires: true, TTL: time.Second / 2}); err != nil {
return err
}
return nil
}); err != nil {
t.Fatal(err)
}
}
n := 0
err := db.View(func(tx *Tx) error {
var err error
n, err = tx.Len()
return err
})
if err != nil {
t.Fatal(err)
}
if n != 201 {
t.Fatalf("expecting '%v', got '%v'", 201, n)
}
time.Sleep(time.Millisecond * 1500)
db = testReOpen(t, db)
defer testClose(db)
n = 0
err = db.View(func(tx *Tx) error {
var err error
n, err = tx.Len()
return err
})
if err != nil {
t.Fatal(err)
}
if n != 200 {
t.Fatalf("expecting '%v', got '%v'", 200, n)
}
}
func TestSaveLoad(t *testing.T) {
db, _ := Open(":memory:")
defer db.Close()
if err := db.Update(func(tx *Tx) error {
for i := 0; i < 20; i++ {
_, _, err := tx.Set(fmt.Sprintf("key:%d", i), fmt.Sprintf("planet:%d", i), nil)
if err != nil {
return err
}
}
return nil
}); err != nil {
t.Fatal(err)
}
f, err := os.Create("temp.db")
if err != nil {
t.Fatal(err)
}
defer func() {
f.Close()
os.RemoveAll("temp.db")
}()
if err := db.Save(f); err != nil {
t.Fatal(err)
}
if err := f.Close(); err != nil {
t.Fatal(err)
}
db.Close()
db, _ = Open(":memory:")
defer db.Close()
f, err = os.Open("temp.db")
if err != nil {
t.Fatal(err)
}
defer f.Close()
if err := db.Load(f); err != nil {
t.Fatal(err)
}
if err := db.View(func(tx *Tx) error {
for i := 0; i < 20; i++ {
ex := fmt.Sprintf("planet:%d", i)
val, err := tx.Get(fmt.Sprintf("key:%d", i))
if err != nil {
return err
}
if ex != val {
t.Fatalf("expected %s, got %s", ex, val)
}
}
return nil
}); err != nil {
t.Fatal(err)
}
}
func TestMutatingIterator(t *testing.T) {
db := testOpen(t)
defer testClose(db)
count := 1000
if err := db.CreateIndex("ages", "user:*:age", IndexInt); err != nil {
t.Fatal(err)
}
for i := 0; i < 10; i++ {
if err := db.Update(func(tx *Tx) error {
for j := 0; j < count; j++ {
key := fmt.Sprintf("user:%d:age", j)
val := fmt.Sprintf("%d", rand.Intn(100))
if _, _, err := tx.Set(key, val, nil); err != nil {
return err
}
}
return nil
}); err != nil {
t.Fatal(err)
}
if err := db.Update(func(tx *Tx) error {
return tx.Ascend("ages", func(key, val string) bool {
_, err := tx.Delete(key)
if err != ErrTxIterating {
t.Fatal("should not be able to call Delete while iterating.")
}
_, _, err = tx.Set(key, "", nil)
if err != ErrTxIterating {
t.Fatal("should not be able to call Set while iterating.")
}
return true
})
}); err != nil {
t.Fatal(err)
}
}
}
func TestCaseInsensitiveIndex(t *testing.T) {
db := testOpen(t)
defer testClose(db)
count := 1000
if err := db.Update(func(tx *Tx) error {
opts := &IndexOptions{
CaseInsensitiveKeyMatching: true,
}
return tx.CreateIndexOptions("ages", "User:*:age", opts, IndexInt)
}); err != nil {
t.Fatal(err)
}
if err := db.Update(func(tx *Tx) error {
for j := 0; j < count; j++ {
key := fmt.Sprintf("user:%d:age", j)
val := fmt.Sprintf("%d", rand.Intn(100))
if _, _, err := tx.Set(key, val, nil); err != nil {
return err
}
}
return nil
}); err != nil {
t.Fatal(err)
}
if err := db.View(func(tx *Tx) error {
var vals []string
err := tx.Ascend("ages", func(key, value string) bool {
vals = append(vals, value)
return true
})
if err != nil {
return err
}
if len(vals) != count {
return fmt.Errorf("expected '%v', got '%v'", count, len(vals))
}
return nil
}); err != nil {
t.Fatal(err)
}
}
func TestIndexTransaction(t *testing.T) {
db := testOpen(t)
defer testClose(db)
var errFine = errors.New("this is fine")
ascend := func(tx *Tx, index string) ([]string, error) {
var vals []string
if err := tx.Ascend(index, func(key, val string) bool {
vals = append(vals, key, val)
return true
}); err != nil {
return nil, err
}
return vals, nil
}
ascendEqual := func(tx *Tx, index string, vals []string) error {
vals2, err := ascend(tx, index)
if err != nil {
return err
}
if len(vals) != len(vals2) {
return errors.New("invalid size match")
}
for i := 0; i < len(vals); i++ {
if vals[i] != vals2[i] {
return errors.New("invalid order")
}
}
return nil
}
// test creating an index and adding items
if err := db.Update(func(tx *Tx) error {
tx.Set("1", "3", nil)
tx.Set("2", "2", nil)
tx.Set("3", "1", nil)
if err := tx.CreateIndex("idx1", "*", IndexInt); err != nil {
return err
}
if err := ascendEqual(tx, "idx1", []string{"3", "1", "2", "2", "1", "3"}); err != nil {
return err
}
return nil
}); err != nil {
t.Fatal(err)
}
// test to see if the items persisted from previous transaction
// test add item.
// test force rollback.
if err := db.Update(func(tx *Tx) error {
if err := ascendEqual(tx, "idx1", []string{"3", "1", "2", "2", "1", "3"}); err != nil {
return err
}
tx.Set("4", "0", nil)
if err := ascendEqual(tx, "idx1", []string{"4", "0", "3", "1", "2", "2", "1", "3"}); err != nil {
return err
}
return errFine
}); err != errFine {
t.Fatalf("expected '%v', got '%v'", errFine, err)
}
// test to see if the rollback happened
if err := db.View(func(tx *Tx) error {
if err := ascendEqual(tx, "idx1", []string{"3", "1", "2", "2", "1", "3"}); err != nil {
return err
}
return nil
}); err != nil {
t.Fatalf("expected '%v', got '%v'", nil, err)
}
// del item, drop index, rollback
if err := db.Update(func(tx *Tx) error {
if err := tx.DropIndex("idx1"); err != nil {
return err
}
return errFine
}); err != errFine {
t.Fatalf("expected '%v', got '%v'", errFine, err)
}
// test to see if the rollback happened
if err := db.View(func(tx *Tx) error {
if err := ascendEqual(tx, "idx1", []string{"3", "1", "2", "2", "1", "3"}); err != nil {
return err
}
return nil
}); err != nil {
t.Fatalf("expected '%v', got '%v'", nil, err)
}
various := func(reterr error) error {
// del item 3, add index 2, add item 4, test index 1 and 2.
// flushdb, test index 1 and 2.
// add item 1 and 2, add index 2 and 3, test index 2 and 3
return db.Update(func(tx *Tx) error {
tx.Delete("3")
tx.CreateIndex("idx2", "*", IndexInt)
tx.Set("4", "0", nil)
if err := ascendEqual(tx, "idx1", []string{"4", "0", "2", "2", "1", "3"}); err != nil {
return fmt.Errorf("err: %v", err)
}
if err := ascendEqual(tx, "idx2", []string{"4", "0", "2", "2", "1", "3"}); err != nil {
return fmt.Errorf("err: %v", err)
}
tx.DeleteAll()
if err := ascendEqual(tx, "idx1", []string{}); err != nil {
return fmt.Errorf("err: %v", err)
}
if err := ascendEqual(tx, "idx2", []string{}); err != nil {
return fmt.Errorf("err: %v", err)
}
tx.Set("1", "3", nil)
tx.Set("2", "2", nil)
tx.CreateIndex("idx1", "*", IndexInt)
tx.CreateIndex("idx2", "*", IndexInt)
if err := ascendEqual(tx, "idx1", []string{"2", "2", "1", "3"}); err != nil {
return fmt.Errorf("err: %v", err)
}
if err := ascendEqual(tx, "idx2", []string{"2", "2", "1", "3"}); err != nil {
return fmt.Errorf("err: %v", err)
}
return reterr
})
}
// various rollback
if err := various(errFine); err != errFine {
t.Fatalf("expected '%v', got '%v'", errFine, err)
}
// test to see if the rollback happened
if err := db.View(func(tx *Tx) error {
if err := ascendEqual(tx, "idx1", []string{"3", "1", "2", "2", "1", "3"}); err != nil {
return fmt.Errorf("err: %v", err)
}
if err := ascendEqual(tx, "idx2", []string{"3", "1", "2", "2", "1", "3"}); err != ErrNotFound {
return fmt.Errorf("err: %v", err)
}
return nil
}); err != nil {
t.Fatalf("expected '%v', got '%v'", nil, err)
}
// various commit
if err := various(nil); err != nil {
t.Fatalf("expected '%v', got '%v'", nil, err)
}
// test to see if the commit happened
if err := db.View(func(tx *Tx) error {
if err := ascendEqual(tx, "idx1", []string{"2", "2", "1", "3"}); err != nil {
return fmt.Errorf("err: %v", err)
}
if err := ascendEqual(tx, "idx2", []string{"2", "2", "1", "3"}); err != nil {
return fmt.Errorf("err: %v", err)
}
return nil
}); err != nil {
t.Fatalf("expected '%v', got '%v'", nil, err)
}
}
func TestDeleteAll(t *testing.T) {
db := testOpen(t)
defer testClose(db)
db.Update(func(tx *Tx) error {
tx.Set("hello1", "planet1", nil)
tx.Set("hello2", "planet2", nil)
tx.Set("hello3", "planet3", nil)
return nil
})
db.CreateIndex("all", "*", IndexString)
db.Update(func(tx *Tx) error {
tx.Set("hello1", "planet1.1", nil)
tx.DeleteAll()
tx.Set("bb", "11", nil)
tx.Set("aa", "**", nil)
tx.Delete("aa")
tx.Set("aa", "22", nil)
return nil
})
var res string
var res2 string
db.View(func(tx *Tx) error {
tx.Ascend("", func(key, val string) bool {
res += key + ":" + val + "\n"
return true
})
tx.Ascend("all", func(key, val string) bool {
res2 += key + ":" + val + "\n"
return true
})
return nil
})
if res != "aa:22\nbb:11\n" {
t.Fatal("fail")
}
if res2 != "bb:11\naa:22\n" {
t.Fatal("fail")
}
db = testReOpen(t, db)
defer testClose(db)
res = ""
res2 = ""
db.CreateIndex("all", "*", IndexString)
db.View(func(tx *Tx) error {
tx.Ascend("", func(key, val string) bool {
res += key + ":" + val + "\n"
return true
})
tx.Ascend("all", func(key, val string) bool {
res2 += key + ":" + val + "\n"
return true
})
return nil
})
if res != "aa:22\nbb:11\n" {
t.Fatal("fail")
}
if res2 != "bb:11\naa:22\n" {
t.Fatal("fail")
}
db.Update(func(tx *Tx) error {
tx.Set("1", "1", nil)
tx.Set("2", "2", nil)
tx.Set("3", "3", nil)
tx.Set("4", "4", nil)
return nil
})
err := db.Update(func(tx *Tx) error {
tx.Set("1", "a", nil)
tx.Set("5", "5", nil)
tx.Delete("2")
tx.Set("6", "6", nil)
tx.DeleteAll()
tx.Set("7", "7", nil)
tx.Set("8", "8", nil)
tx.Set("6", "c", nil)
return errors.New("please rollback")
})
if err == nil || err.Error() != "please rollback" {
t.Fatal("expecteding 'please rollback' error")
}
res = ""
res2 = ""
db.View(func(tx *Tx) error {
tx.Ascend("", func(key, val string) bool {
res += key + ":" + val + "\n"
return true
})
tx.Ascend("all", func(key, val string) bool {
res2 += key + ":" + val + "\n"
return true
})
return nil
})
if res != "1:1\n2:2\n3:3\n4:4\naa:22\nbb:11\n" {
t.Fatal("fail")
}
if res2 != "1:1\nbb:11\n2:2\naa:22\n3:3\n4:4\n" {
t.Fatal("fail")
}
}
func TestAscendEqual(t *testing.T) {
db := testOpen(t)
defer testClose(db)
if err := db.Update(func(tx *Tx) error {
for i := 0; i < 300; i++ {
_, _, err := tx.Set(fmt.Sprintf("key:%05dA", i), fmt.Sprintf("%d", i+1000), nil)
if err != nil {
return err
}
_, _, err = tx.Set(fmt.Sprintf("key:%05dB", i), fmt.Sprintf("%d", i+1000), nil)
if err != nil {
return err
}
}
return tx.CreateIndex("num", "*", IndexInt)
}); err != nil {
t.Fatal(err)
}
var res []string
if err := db.View(func(tx *Tx) error {
return tx.AscendEqual("", "key:00055A", func(key, value string) bool {
res = append(res, key)
return true
})
}); err != nil {
t.Fatal(err)
}
if len(res) != 1 {
t.Fatalf("expected %v, got %v", 1, len(res))
}
if res[0] != "key:00055A" {
t.Fatalf("expected %v, got %v", "key:00055A", res[0])
}
res = nil
if err := db.View(func(tx *Tx) error {
return tx.AscendEqual("num", "1125", func(key, value string) bool {
res = append(res, key)
return true
})
}); err != nil {
t.Fatal(err)
}
if len(res) != 2 {
t.Fatalf("expected %v, got %v", 2, len(res))
}
if res[0] != "key:00125A" {
t.Fatalf("expected %v, got %v", "key:00125A", res[0])
}
if res[1] != "key:00125B" {
t.Fatalf("expected %v, got %v", "key:00125B", res[1])
}
}
func TestDescendEqual(t *testing.T) {
db := testOpen(t)
defer testClose(db)
if err := db.Update(func(tx *Tx) error {
for i := 0; i < 300; i++ {
_, _, err := tx.Set(fmt.Sprintf("key:%05dA", i), fmt.Sprintf("%d", i+1000), nil)
if err != nil {
return err
}
_, _, err = tx.Set(fmt.Sprintf("key:%05dB", i), fmt.Sprintf("%d", i+1000), nil)
if err != nil {
return err
}
}
return tx.CreateIndex("num", "*", IndexInt)
}); err != nil {
t.Fatal(err)
}
var res []string
if err := db.View(func(tx *Tx) error {
return tx.DescendEqual("", "key:00055A", func(key, value string) bool {
res = append(res, key)
return true
})
}); err != nil {
t.Fatal(err)
}
if len(res) != 1 {
t.Fatalf("expected %v, got %v", 1, len(res))
}
if res[0] != "key:00055A" {
t.Fatalf("expected %v, got %v", "key:00055A", res[0])
}
res = nil
if err := db.View(func(tx *Tx) error {
return tx.DescendEqual("num", "1125", func(key, value string) bool {
res = append(res, key)
return true
})
}); err != nil {
t.Fatal(err)
}
if len(res) != 2 {
t.Fatalf("expected %v, got %v", 2, len(res))
}
if res[0] != "key:00125B" {
t.Fatalf("expected %v, got %v", "key:00125B", res[0])
}
if res[1] != "key:00125A" {
t.Fatalf("expected %v, got %v", "key:00125A", res[1])
}
}
func TestVariousTx(t *testing.T) {
db := testOpen(t)
defer testClose(db)
if err := db.Update(func(tx *Tx) error {
_, _, err := tx.Set("hello", "planet", nil)
return err
}); err != nil {
t.Fatal(err)
}
errBroken := errors.New("broken")
if err := db.Update(func(tx *Tx) error {
_, _, _ = tx.Set("hello", "world", nil)
return errBroken
}); err != errBroken {
t.Fatalf("did not correctly receive the user-defined transaction error.")
}
var val string
err := db.View(func(tx *Tx) error {
var err error
val, err = tx.Get("hello")
return err
})
if err != nil {
t.Fatal(err)
}
if val == "world" {
t.Fatal("a rollbacked transaction got through")
}
if val != "planet" {
t.Fatalf("expecting '%v', got '%v'", "planet", val)
}
if err := db.Update(func(tx *Tx) error {
tx.db = nil
if _, _, err := tx.Set("hello", "planet", nil); err != ErrTxClosed {
t.Fatal("expecting a tx closed error")
}
if _, err := tx.Delete("hello"); err != ErrTxClosed {
t.Fatal("expecting a tx closed error")
}
if _, err := tx.Get("hello"); err != ErrTxClosed {
t.Fatal("expecting a tx closed error")
}
tx.db = db
tx.writable = false
if _, _, err := tx.Set("hello", "planet", nil); err != ErrTxNotWritable {
t.Fatal("expecting a tx not writable error")
}
if _, err := tx.Delete("hello"); err != ErrTxNotWritable {
t.Fatal("expecting a tx not writable error")
}
tx.writable = true
if _, err := tx.Get("something"); err != ErrNotFound {
t.Fatalf("expecting not found error")
}
if _, err := tx.Delete("something"); err != ErrNotFound {
t.Fatalf("expecting not found error")
}
if _, _, err := tx.Set("var", "val", &SetOptions{Expires: true, TTL: 0}); err != nil {
t.Fatal(err)
}
if _, err := tx.Get("var"); err != ErrNotFound {
t.Fatalf("expecting not found error")
}
if _, err := tx.Delete("var"); err != ErrNotFound {
tx.unlock()
t.Fatalf("expecting not found error")
}
return nil
}); err != nil {
t.Fatal(err)
}
// test non-managed transactions
tx, err := db.Begin(true)
if err != nil {
t.Fatal(err)
}
defer tx.Rollback()
_, _, err = tx.Set("howdy", "world", nil)
if err != nil {
t.Fatal(err)
}
if err := tx.Commit(); err != nil {
t.Fatal(err)
}
tx, err = db.Begin(false)
if err != nil {
t.Fatal(err)
}
defer tx.Rollback()
v, err := tx.Get("howdy")
if err != nil {
t.Fatal(err)
}
if v != "world" {
t.Fatalf("expecting '%v', got '%v'", "world", v)
}
if err := tx.Rollback(); err != nil {
t.Fatal(err)
}
tx, err = db.Begin(true)
if err != nil {
t.Fatal(err)
}
defer tx.Rollback()
v, err = tx.Get("howdy")
if err != nil {
t.Fatal(err)
}
if v != "world" {
t.Fatalf("expecting '%v', got '%v'", "world", v)
}
_, err = tx.Delete("howdy")
if err != nil {
t.Fatal(err)
}
if err := tx.Commit(); err != nil {
t.Fatal(err)
}
// test for invalid commits
if err := db.Update(func(tx *Tx) error {
// we are going to do some hackery
defer func() {
if v := recover(); v != nil {
if v.(string) != "managed tx commit not allowed" {
t.Fatal(v.(string))
}
}
}()
return tx.Commit()
}); err != nil {
t.Fatal(err)
}
// test for invalid commits
if err := db.Update(func(tx *Tx) error {
// we are going to do some hackery
defer func() {
if v := recover(); v != nil {
if v.(string) != "managed tx rollback not allowed" {
t.Fatal(v.(string))
}
}
}()
return tx.Rollback()
}); err != nil {
t.Fatal(err)
}
// test for closed transactions
if err := db.Update(func(tx *Tx) error {
tx.db = nil
return nil
}); err != ErrTxClosed {
t.Fatal("expecting tx closed error")
}
db.mu.Unlock()
// test for invalid writes
if err := db.Update(func(tx *Tx) error {
tx.writable = false
return nil
}); err != ErrTxNotWritable {
t.Fatal("expecting tx not writable error")
}
db.mu.Unlock()
// test for closed transactions
if err := db.View(func(tx *Tx) error {
tx.db = nil
return nil
}); err != ErrTxClosed {
t.Fatal("expecting tx closed error")
}
db.mu.RUnlock()
// flush to unwritable file
if err := db.Update(func(tx *Tx) error {
_, _, err := tx.Set("var1", "val1", nil)
if err != nil {
t.Fatal(err)
}
return tx.db.file.Close()
}); err == nil {
t.Fatal("should not be able to commit when the file is closed")
}
db.file, err = os.OpenFile("data.db", os.O_CREATE|os.O_RDWR, 0666)
if err != nil {
t.Fatal(err)
}
if _, err := db.file.Seek(0, 2); err != nil {
t.Fatal(err)
}
db.buf = nil
if err := db.CreateIndex("blank", "*", nil); err != nil {
t.Fatal(err)
}
if err := db.CreateIndex("real", "*", IndexInt); err != nil {
t.Fatal(err)
}
// test scanning
if err := db.Update(func(tx *Tx) error {
less, err := tx.GetLess("junk")
if err != ErrNotFound {
t.Fatalf("expecting a not found, got %v", err)
}
if less != nil {
t.Fatal("expecting nil, got a less function")
}
less, err = tx.GetLess("blank")
if err != ErrNotFound {
t.Fatalf("expecting a not found, got %v", err)
}
if less != nil {
t.Fatal("expecting nil, got a less function")
}
less, err = tx.GetLess("real")
if err != nil {
return err
}
if less == nil {
t.Fatal("expecting a less function, got nil")
}
_, _, err = tx.Set("nothing", "here", nil)
return err
}); err != nil {
t.Fatal(err)
}
if err := db.View(func(tx *Tx) error {
s := ""
err := tx.Ascend("", func(key, val string) bool {
s += key + ":" + val + "\n"
return true
})
if err != nil {
return err
}
if s != "hello:planet\nnothing:here\n" {
t.Fatal("invalid scan")
}
tx.db = nil
err = tx.Ascend("", func(key, val string) bool { return true })
if err != ErrTxClosed {
tx.unlock()
t.Fatal("expecting tx closed error")
}
tx.db = db
err = tx.Ascend("na", func(key, val string) bool { return true })
if err != ErrNotFound {
t.Fatal("expecting not found error")
}
err = tx.Ascend("blank", func(key, val string) bool { return true })
if err != nil {
t.Fatal(err)
}
s = ""
err = tx.AscendLessThan("", "liger", func(key, val string) bool {
s += key + ":" + val + "\n"
return true
})
if err != nil {
return err
}
if s != "hello:planet\n" {
t.Fatal("invalid scan")
}
s = ""
err = tx.Descend("", func(key, val string) bool {
s += key + ":" + val + "\n"
return true
})
if err != nil {
return err
}
if s != "nothing:here\nhello:planet\n" {
t.Fatal("invalid scan")
}
s = ""
err = tx.DescendLessOrEqual("", "liger", func(key, val string) bool {
s += key + ":" + val + "\n"
return true
})
if err != nil {
return err
}
if s != "hello:planet\n" {
t.Fatal("invalid scan")
}
s = ""
err = tx.DescendGreaterThan("", "liger", func(key, val string) bool {
s += key + ":" + val + "\n"
return true
})
if err != nil {
return err
}
if s != "nothing:here\n" {
t.Fatal("invalid scan")
}
s = ""
err = tx.DescendRange("", "liger", "apple", func(key, val string) bool {
s += key + ":" + val + "\n"
return true
})
if err != nil {
return err
}
if s != "hello:planet\n" {
t.Fatal("invalid scan")
}
return nil
}); err != nil {
t.Fatal(err)
}
// test some spatial stuff
if err := db.CreateSpatialIndex("spat", "rect:*", IndexRect); err != nil {
t.Fatal(err)
}
if err := db.CreateSpatialIndex("junk", "rect:*", nil); err != nil {
t.Fatal(err)
}
err = db.Update(func(tx *Tx) error {
rect, err := tx.GetRect("spat")
if err != nil {
return err
}
if rect == nil {
t.Fatal("expecting a rect function, got nil")
}
rect, err = tx.GetRect("junk")
if err != ErrNotFound {
t.Fatalf("expecting a not found, got %v", err)
}
if rect != nil {
t.Fatal("expecting nil, got a rect function")
}
rect, err = tx.GetRect("na")
if err != ErrNotFound {
t.Fatalf("expecting a not found, got %v", err)
}
if rect != nil {
t.Fatal("expecting nil, got a rect function")
}
if _, _, err := tx.Set("rect:1", "[10 10],[20 20]", nil); err != nil {
return err
}
if _, _, err := tx.Set("rect:2", "[15 15],[25 25]", nil); err != nil {
return err
}
if _, _, err := tx.Set("shape:1", "[12 12],[25 25]", nil); err != nil {
return err
}
s := ""
err = tx.Intersects("spat", "[5 5],[13 13]", func(key, val string) bool {
s += key + ":" + val + "\n"
return true
})
if err != nil {
return err
}
if s != "rect:1:[10 10],[20 20]\n" {
t.Fatal("invalid scan")
}
tx.db = nil
err = tx.Intersects("spat", "[5 5],[13 13]", func(key, val string) bool {
return true
})
if err != ErrTxClosed {
t.Fatal("expecting tx closed error")
}
tx.db = db
err = tx.Intersects("", "[5 5],[13 13]", func(key, val string) bool {
return true
})
if err != nil {
t.Fatal(err)
}
err = tx.Intersects("na", "[5 5],[13 13]", func(key, val string) bool {
return true
})
if err != ErrNotFound {
t.Fatal("expecting not found error")
}
err = tx.Intersects("junk", "[5 5],[13 13]", func(key, val string) bool {
return true
})
if err != nil {
t.Fatal(err)
}
n, err := tx.Len()
if err != nil {
t.Fatal(err)
}
if n != 5 {
t.Fatalf("expecting %v, got %v", 5, n)
}
tx.db = nil
_, err = tx.Len()
if err != ErrTxClosed {
t.Fatal("expecting tx closed error")
}
tx.db = db
return nil
})
if err != nil {
t.Fatal(err)
}
// test after closing
if err := db.Close(); err != nil {
t.Fatal(err)
}
if err := db.Update(func(tx *Tx) error { return nil }); err != ErrDatabaseClosed {
t.Fatalf("should not be able to perform transactionso on a closed database.")
}
}
func TestNearby(t *testing.T) {
rand.Seed(time.Now().UnixNano())
N := 100000
db, _ := Open(":memory:")
db.CreateSpatialIndex("points", "*", IndexRect)
db.Update(func(tx *Tx) error {
for i := 0; i < N; i++ {
p := Point(
rand.Float64()*100,
rand.Float64()*100,
rand.Float64()*100,
rand.Float64()*100,
)
tx.Set(fmt.Sprintf("p:%d", i), p, nil)
}
return nil
})
var keys, values []string
var dists []float64
var pdist float64
var i int
db.View(func(tx *Tx) error {
tx.Nearby("points", Point(0, 0, 0, 0), func(key, value string, dist float64) bool {
if i != 0 && dist < pdist {
t.Fatal("out of order")
}
keys = append(keys, key)
values = append(values, value)
dists = append(dists, dist)
pdist = dist
i++
return true
})
return nil
})
if len(keys) != N {
t.Fatalf("expected '%v', got '%v'", N, len(keys))
}
}
func Example_descKeys() {
db, _ := Open(":memory:")
db.CreateIndex("name", "*", IndexString)
db.Update(func(tx *Tx) error {
tx.Set("user:100:first", "Tom", nil)
tx.Set("user:100:last", "Johnson", nil)
tx.Set("user:101:first", "Janet", nil)
tx.Set("user:101:last", "Prichard", nil)
tx.Set("user:102:first", "Alan", nil)
tx.Set("user:102:last", "Cooper", nil)
return nil
})
db.View(func(tx *Tx) error {
tx.AscendKeys("user:101:*",
func(key, value string) bool {
fmt.Printf("%s: %s\n", key, value)
return true
})
tx.AscendKeys("user:10?:*",
func(key, value string) bool {
fmt.Printf("%s: %s\n", key, value)
return true
})
tx.AscendKeys("*2*",
func(key, value string) bool {
fmt.Printf("%s: %s\n", key, value)
return true
})
tx.DescendKeys("user:101:*",
func(key, value string) bool {
fmt.Printf("%s: %s\n", key, value)
return true
})
tx.DescendKeys("*",
func(key, value string) bool {
fmt.Printf("%s: %s\n", key, value)
return true
})
return nil
})
// Output:
// user:101:first: Janet
// user:101:last: Prichard
// user:100:first: Tom
// user:100:last: Johnson
// user:101:first: Janet
// user:101:last: Prichard
// user:102:first: Alan
// user:102:last: Cooper
// user:102:first: Alan
// user:102:last: Cooper
// user:101:last: Prichard
// user:101:first: Janet
// user:102:last: Cooper
// user:102:first: Alan
// user:101:last: Prichard
// user:101:first: Janet
// user:100:last: Johnson
// user:100:first: Tom
}
func ExampleDesc() {
db, _ := Open(":memory:")
db.CreateIndex("last_name_age", "*", IndexJSON("name.last"), Desc(IndexJSON("age")))
db.Update(func(tx *Tx) error {
tx.Set("1", `{"name":{"first":"Tom","last":"Johnson"},"age":38}`, nil)
tx.Set("2", `{"name":{"first":"Janet","last":"Prichard"},"age":47}`, nil)
tx.Set("3", `{"name":{"first":"Carol","last":"Anderson"},"age":52}`, nil)
tx.Set("4", `{"name":{"first":"Alan","last":"Cooper"},"age":28}`, nil)
tx.Set("5", `{"name":{"first":"Sam","last":"Anderson"},"age":51}`, nil)
tx.Set("6", `{"name":{"first":"Melinda","last":"Prichard"},"age":44}`, nil)
return nil
})
db.View(func(tx *Tx) error {
tx.Ascend("last_name_age", func(key, value string) bool {
fmt.Printf("%s: %s\n", key, value)
return true
})
return nil
})
// Output:
//3: {"name":{"first":"Carol","last":"Anderson"},"age":52}
//5: {"name":{"first":"Sam","last":"Anderson"},"age":51}
//4: {"name":{"first":"Alan","last":"Cooper"},"age":28}
//1: {"name":{"first":"Tom","last":"Johnson"},"age":38}
//2: {"name":{"first":"Janet","last":"Prichard"},"age":47}
//6: {"name":{"first":"Melinda","last":"Prichard"},"age":44}
}
func ExampleDB_CreateIndex_jSON() {
db, _ := Open(":memory:")
db.CreateIndex("last_name", "*", IndexJSON("name.last"))
db.CreateIndex("age", "*", IndexJSON("age"))
db.Update(func(tx *Tx) error {
tx.Set("1", `{"name":{"first":"Tom","last":"Johnson"},"age":38}`, nil)
tx.Set("2", `{"name":{"first":"Janet","last":"Prichard"},"age":47}`, nil)
tx.Set("3", `{"name":{"first":"Carol","last":"Anderson"},"age":52}`, nil)
tx.Set("4", `{"name":{"first":"Alan","last":"Cooper"},"age":28}`, nil)
return nil
})
db.View(func(tx *Tx) error {
fmt.Println("Order by last name")
tx.Ascend("last_name", func(key, value string) bool {
fmt.Printf("%s: %s\n", key, value)
return true
})
fmt.Println("Order by age")
tx.Ascend("age", func(key, value string) bool {
fmt.Printf("%s: %s\n", key, value)
return true
})
fmt.Println("Order by age range 30-50")
tx.AscendRange("age", `{"age":30}`, `{"age":50}`, func(key, value string) bool {
fmt.Printf("%s: %s\n", key, value)
return true
})
return nil
})
// Output:
// Order by last name
// 3: {"name":{"first":"Carol","last":"Anderson"},"age":52}
// 4: {"name":{"first":"Alan","last":"Cooper"},"age":28}
// 1: {"name":{"first":"Tom","last":"Johnson"},"age":38}
// 2: {"name":{"first":"Janet","last":"Prichard"},"age":47}
// Order by age
// 4: {"name":{"first":"Alan","last":"Cooper"},"age":28}
// 1: {"name":{"first":"Tom","last":"Johnson"},"age":38}
// 2: {"name":{"first":"Janet","last":"Prichard"},"age":47}
// 3: {"name":{"first":"Carol","last":"Anderson"},"age":52}
// Order by age range 30-50
// 1: {"name":{"first":"Tom","last":"Johnson"},"age":38}
// 2: {"name":{"first":"Janet","last":"Prichard"},"age":47}
}
func ExampleDB_CreateIndex_strings() {
db, _ := Open(":memory:")
db.CreateIndex("name", "*", IndexString)
db.Update(func(tx *Tx) error {
tx.Set("1", "Tom", nil)
tx.Set("2", "Janet", nil)
tx.Set("3", "Carol", nil)
tx.Set("4", "Alan", nil)
tx.Set("5", "Sam", nil)
tx.Set("6", "Melinda", nil)
return nil
})
db.View(func(tx *Tx) error {
tx.Ascend("name", func(key, value string) bool {
fmt.Printf("%s: %s\n", key, value)
return true
})
return nil
})
// Output:
//4: Alan
//3: Carol
//2: Janet
//6: Melinda
//5: Sam
//1: Tom
}
func ExampleDB_CreateIndex_ints() {
db, _ := Open(":memory:")
db.CreateIndex("age", "*", IndexInt)
db.Update(func(tx *Tx) error {
tx.Set("1", "30", nil)
tx.Set("2", "51", nil)
tx.Set("3", "16", nil)
tx.Set("4", "76", nil)
tx.Set("5", "23", nil)
tx.Set("6", "43", nil)
return nil
})
db.View(func(tx *Tx) error {
tx.Ascend("age", func(key, value string) bool {
fmt.Printf("%s: %s\n", key, value)
return true
})
return nil
})
// Output:
//3: 16
//5: 23
//1: 30
//6: 43
//2: 51
//4: 76
}
func ExampleDB_CreateIndex_multipleFields() {
db, _ := Open(":memory:")
db.CreateIndex("last_name_age", "*", IndexJSON("name.last"), IndexJSON("age"))
db.Update(func(tx *Tx) error {
tx.Set("1", `{"name":{"first":"Tom","last":"Johnson"},"age":38}`, nil)
tx.Set("2", `{"name":{"first":"Janet","last":"Prichard"},"age":47}`, nil)
tx.Set("3", `{"name":{"first":"Carol","last":"Anderson"},"age":52}`, nil)
tx.Set("4", `{"name":{"first":"Alan","last":"Cooper"},"age":28}`, nil)
tx.Set("5", `{"name":{"first":"Sam","last":"Anderson"},"age":51}`, nil)
tx.Set("6", `{"name":{"first":"Melinda","last":"Prichard"},"age":44}`, nil)
return nil
})
db.View(func(tx *Tx) error {
tx.Ascend("last_name_age", func(key, value string) bool {
fmt.Printf("%s: %s\n", key, value)
return true
})
return nil
})
// Output:
//5: {"name":{"first":"Sam","last":"Anderson"},"age":51}
//3: {"name":{"first":"Carol","last":"Anderson"},"age":52}
//4: {"name":{"first":"Alan","last":"Cooper"},"age":28}
//1: {"name":{"first":"Tom","last":"Johnson"},"age":38}
//6: {"name":{"first":"Melinda","last":"Prichard"},"age":44}
//2: {"name":{"first":"Janet","last":"Prichard"},"age":47}
}
func TestNoExpiringItem(t *testing.T) {
item := &dbItem{key: "key", val: "val"}
if !item.expiresAt().Equal(maxTime) {
t.Fatal("item.expiresAt() != maxTime")
}
if min, max := item.Rect(nil); min != nil || max != nil {
t.Fatal("item min,max should both be nil")
}
}
func TestAutoShrink(t *testing.T) {
db := testOpen(t)
defer testClose(db)
for i := 0; i < 1000; i++ {
err := db.Update(func(tx *Tx) error {
for i := 0; i < 20; i++ {
if _, _, err := tx.Set(fmt.Sprintf("HELLO:%d", i), "WORLD", nil); err != nil {
return err
}
}
return nil
})
if err != nil {
t.Fatal(err)
}
}
db = testReOpen(t, db)
defer testClose(db)
db.config.AutoShrinkMinSize = 64 * 1024 // 64K
for i := 0; i < 2000; i++ {
err := db.Update(func(tx *Tx) error {
for i := 0; i < 20; i++ {
if _, _, err := tx.Set(fmt.Sprintf("HELLO:%d", i), "WORLD", nil); err != nil {
return err
}
}
return nil
})
if err != nil {
t.Fatal(err)
}
}
time.Sleep(time.Second * 3)
db = testReOpen(t, db)
defer testClose(db)
err := db.View(func(tx *Tx) error {
n, err := tx.Len()
if err != nil {
return err
}
if n != 20 {
t.Fatalf("expecting 20, got %v", n)
}
return nil
})
if err != nil {
t.Fatal(err)
}
}
// test database format loading
func TestDatabaseFormat(t *testing.T) {
// should succeed
func() {
resp := strings.Join([]string{
"*3\r\n$3\r\nset\r\n$4\r\nvar1\r\n$4\r\n1234\r\n",
"*3\r\n$3\r\nset\r\n$4\r\nvar2\r\n$4\r\n1234\r\n",
"*2\r\n$3\r\ndel\r\n$4\r\nvar1\r\n",
"*5\r\n$3\r\nset\r\n$3\r\nvar\r\n$3\r\nval\r\n$2\r\nex\r\n$2\r\n10\r\n",
}, "")
if err := os.RemoveAll("data.db"); err != nil {
t.Fatal(err)
}
if err := ioutil.WriteFile("data.db", []byte(resp), 0666); err != nil {
t.Fatal(err)
}
db := testOpen(t)
defer testClose(db)
}()
testFormat := func(t *testing.T, expectValid bool, resp string, do func(db *DB) error) {
t.Helper()
os.RemoveAll("data.db")
if err := ioutil.WriteFile("data.db", []byte(resp), 0666); err != nil {
t.Fatal(err)
}
defer os.RemoveAll("data.db")
db, err := Open("data.db")
if err == nil {
if do != nil {
if err := do(db); err != nil {
t.Fatal(err)
}
}
if err := db.Close(); err != nil {
t.Fatal(err)
}
}
if err == nil && !expectValid {
t.Fatalf("expected invalid database")
} else if err != nil && expectValid {
t.Fatalf("expected valid database, got '%s'", err)
}
}
// basic valid commands
testFormat(t, true, "*2\r\n$3\r\nDEL\r\n$5\r\nHELLO\r\n", nil)
testFormat(t, true, "*3\r\n$3\r\nSET\r\n$5\r\nHELLO\r\n$5\r\nWORLD\r\n", nil)
testFormat(t, true, "*1\r\n$7\r\nFLUSHDB\r\n", nil)
// commands with invalid names or arguments
testFormat(t, false, "*3\r\n$3\r\nDEL\r\n$5\r\nHELLO\r\n$5\r\nWORLD\r\n", nil)
testFormat(t, false, "*2\r\n$3\r\nSET\r\n$5\r\nHELLO\r\n", nil)
testFormat(t, false, "*1\r\n$6\r\nSET123\r\n", nil)
// partial tail commands should be ignored but allowed
pcmd := "*3\r\n$3\r\nSET\r\n$5\r\nHELLO\r\n$5\r\nWORLD\r\n"
for i := 1; i < len(pcmd); i++ {
cmd := "*3\r\n$3\r\nSET\r\n$5\r\nHELLO\r\n$5\r\nJELLO\r\n"
testFormat(t, true, cmd+pcmd[:len(pcmd)-i],
func(db *DB) error {
return db.View(func(tx *Tx) error {
val, err := tx.Get("HELLO")
if err != nil {
return err
}
if val != "JELLO" {
return fmt.Errorf("expected '%s', got '%s'", "JELLO", val)
}
return nil
})
})
}
// commands with invalid formatting
testFormat(t, false, "^3\r\n$3\r\nSET\r\n$5\r\nHELLO\r\n$5\r\nWORLD\r\n", nil)
testFormat(t, false, "*3\n$3\r\nSET\r\n$5\r\nHELLO\r\n$5\r\nWORLD\r\n", nil)
testFormat(t, false, "*\n$3\r\nSET\r\n$5\r\nHELLO\r\n$5\r\nWORLD\r\n", nil)
testFormat(t, false, "*3\r\n^3\r\nSET\r\n$5\r\nHELLO\r\n$5\r\nWORLD\r\n", nil)
testFormat(t, false, "*3\r\n$\r\nSET\r\n$5\r\nHELLO\r\n$5\r\nWORLD\r\n", nil)
testFormat(t, false, "*3\r\n$3\nSET\r\n$5\r\nHELLO\r\n$5\r\nWORLD\r\n", nil)
testFormat(t, false, "*3\r\n$3SET\r\n$5\r\nHELLO\r\n$5\r\nWORLD\r\n", nil)
testFormat(t, false, "*3\r\n$3\r\nSET\r\n$5\r\nHELLO\r\n$5\r\nWORLD\r\n123\n", nil)
// commands with nuls
testFormat(t, true, "\u0000*3\r\n$3\r\nSET\r\n$5\r\nHELLO\r\n$5\r\nWORLD\r\n"+
"\u0000\u0000*3\r\n$3\r\nSET\r\n$5\r\nHELLO\r\n$5\r\nJELLO\r\n\u0000", func(db *DB) error {
return db.View(func(tx *Tx) error {
val, err := tx.Get("HELLO")
if err != nil {
return err
}
if val != "JELLO" {
return fmt.Errorf("expected '%s', got '%s'", "JELLO", val)
}
return nil
})
})
}
func TestInsertsAndDeleted(t *testing.T) {
db := testOpen(t)
defer testClose(db)
if err := db.CreateIndex("any", "*", IndexString); err != nil {
t.Fatal(err)
}
if err := db.CreateSpatialIndex("rect", "*", IndexRect); err != nil {
t.Fatal(err)
}
if err := db.Update(func(tx *Tx) error {
if _, _, err := tx.Set("item1", "value1", &SetOptions{Expires: true, TTL: time.Second}); err != nil {
return err
}
if _, _, err := tx.Set("item2", "value2", nil); err != nil {
return err
}
if _, _, err := tx.Set("item3", "value3", &SetOptions{Expires: true, TTL: time.Second}); err != nil {
return err
}
return nil
}); err != nil {
t.Fatal(err)
}
// test replacing items in the database
if err := db.Update(func(tx *Tx) error {
if _, _, err := tx.Set("item1", "nvalue1", nil); err != nil {
return err
}
if _, _, err := tx.Set("item2", "nvalue2", nil); err != nil {
return err
}
if _, err := tx.Delete("item3"); err != nil {
return err
}
return nil
}); err != nil {
t.Fatal(err)
}
}
func TestInsertDoesNotMisuseIndex(t *testing.T) {
db := testOpen(t)
defer testClose(db)
fail := func(a, b string) bool { t.Fatal("Misused index"); return false }
if err := db.CreateIndex("some", "a*", fail); err != nil {
// Only one item is eligible for the index, so no comparison is necessary.
t.Fatal(err)
}
if err := db.Update(func(tx *Tx) error {
if _, _, err := tx.Set("a", "1", nil); err != nil {
return err
}
if _, _, err := tx.Set("b", "1", nil); err != nil {
return err
}
return nil
}); err != nil {
t.Fatal(err)
}
if err := db.Update(func(tx *Tx) error {
_, _, err := tx.Set("b", "2", nil)
return err
}); err != nil {
t.Fatal(err)
}
}
func TestDeleteDoesNotMisuseIndex(t *testing.T) {
db := testOpen(t)
defer testClose(db)
fail := func(a, b string) bool { t.Fatal("Misused index"); return false }
if err := db.CreateIndex("some", "a*", fail); err != nil {
// Only one item is eligible for the index, so no comparison is necessary.
t.Fatal(err)
}
if err := db.Update(func(tx *Tx) error {
if _, _, err := tx.Set("a", "1", nil); err != nil {
return err
}
if _, _, err := tx.Set("b", "1", nil); err != nil {
return err
}
return nil
}); err != nil {
t.Fatal(err)
}
if err := db.Update(func(tx *Tx) error {
_, err := tx.Delete("b")
return err
}); err != nil {
t.Fatal(err)
}
}
// test index compare functions
func TestIndexCompare(t *testing.T) {
if !IndexFloat("1.5", "1.6") {
t.Fatalf("expected true, got false")
}
if !IndexInt("-1", "2") {
t.Fatalf("expected true, got false")
}
if !IndexUint("10", "25") {
t.Fatalf("expected true, got false")
}
if !IndexBinary("Hello", "hello") {
t.Fatalf("expected true, got false")
}
if IndexString("hello", "hello") {
t.Fatalf("expected false, got true")
}
if IndexString("Hello", "hello") {
t.Fatalf("expected false, got true")
}
if IndexString("hello", "Hello") {
t.Fatalf("expected false, got true")
}
if !IndexString("gello", "Hello") {
t.Fatalf("expected true, got false")
}
if IndexString("Hello", "gello") {
t.Fatalf("expected false, got true")
}
if Rect(IndexRect("[1 2 3 4],[5 6 7 8]")) != "[1 2 3 4],[5 6 7 8]" {
t.Fatalf("expected '%v', got '%v'", "[1 2 3 4],[5 6 7 8]", Rect(IndexRect("[1 2 3 4],[5 6 7 8]")))
}
if Rect(IndexRect("[1 2 3 4]")) != "[1 2 3 4]" {
t.Fatalf("expected '%v', got '%v'", "[1 2 3 4]", Rect(IndexRect("[1 2 3 4]")))
}
if Rect(nil, nil) != "[]" {
t.Fatalf("expected '%v', got '%v'", "", Rect(nil, nil))
}
if Point(1, 2, 3) != "[1 2 3]" {
t.Fatalf("expected '%v', got '%v'", "[1 2 3]", Point(1, 2, 3))
}
}
// test opening a folder.
func TestOpeningAFolder(t *testing.T) {
if err := os.RemoveAll("dir.tmp"); err != nil {
t.Fatal(err)
}
if err := os.Mkdir("dir.tmp", 0700); err != nil {
t.Fatal(err)
}
defer func() { _ = os.RemoveAll("dir.tmp") }()
db, err := Open("dir.tmp")
if err == nil {
if err := db.Close(); err != nil {
t.Fatal(err)
}
t.Fatalf("opening a directory should not be allowed")
}
}
// test opening an invalid resp file.
func TestOpeningInvalidDatabaseFile(t *testing.T) {
if err := os.RemoveAll("data.db"); err != nil {
t.Fatal(err)
}
if err := ioutil.WriteFile("data.db", []byte("invalid\r\nfile"), 0666); err != nil {
t.Fatal(err)
}
defer func() { _ = os.RemoveAll("data.db") }()
db, err := Open("data.db")
if err == nil {
if err := db.Close(); err != nil {
t.Fatal(err)
}
t.Fatalf("invalid database should not be allowed")
}
}
// test closing a closed database.
func TestOpeningClosedDatabase(t *testing.T) {
if err := os.RemoveAll("data.db"); err != nil {
t.Fatal(err)
}
db, err := Open("data.db")
if err != nil {
t.Fatal(err)
}
defer func() { _ = os.RemoveAll("data.db") }()
if err := db.Close(); err != nil {
t.Fatal(err)
}
if err := db.Close(); err != ErrDatabaseClosed {
t.Fatal("should not be able to close a closed database")
}
db, err = Open(":memory:")
if err != nil {
t.Fatal(err)
}
if err := db.Close(); err != nil {
t.Fatal(err)
}
if err := db.Close(); err != ErrDatabaseClosed {
t.Fatal("should not be able to close a closed database")
}
}
// test shrinking a database.
func TestShrink(t *testing.T) {
db := testOpen(t)
defer testClose(db)
if err := db.Shrink(); err != nil {
t.Fatal(err)
}
fi, err := os.Stat("data.db")
if err != nil {
t.Fatal(err)
}
if fi.Size() != 0 {
t.Fatalf("expected %v, got %v", 0, fi.Size())
}
// add 10 items
err = db.Update(func(tx *Tx) error {
for i := 0; i < 10; i++ {
if _, _, err := tx.Set(fmt.Sprintf("key%d", i), fmt.Sprintf("val%d", i), nil); err != nil {
return err
}
}
return nil
})
if err != nil {
t.Fatal(err)
}
// add the same 10 items
// this will create 10 duplicate log entries
err = db.Update(func(tx *Tx) error {
for i := 0; i < 10; i++ {
if _, _, err := tx.Set(fmt.Sprintf("key%d", i), fmt.Sprintf("val%d", i), nil); err != nil {
return err
}
}
return nil
})
if err != nil {
t.Fatal(err)
}
fi, err = os.Stat("data.db")
if err != nil {
t.Fatal(err)
}
sz1 := fi.Size()
if sz1 == 0 {
t.Fatalf("expected > 0, got %v", sz1)
}
if err := db.Shrink(); err != nil {
t.Fatal(err)
}
fi, err = os.Stat("data.db")
if err != nil {
t.Fatal(err)
}
sz2 := fi.Size()
if sz2 >= sz1 {
t.Fatalf("expected < %v, got %v", sz1, sz2)
}
if err := db.Close(); err != nil {
t.Fatal(err)
}
if err := db.Shrink(); err != ErrDatabaseClosed {
t.Fatal("shrink on a closed databse should not be allowed")
}
// Now we will open a db that does not persist
db, err = Open(":memory:")
if err != nil {
t.Fatal(err)
}
defer func() { _ = db.Close() }()
// add 10 items
err = db.Update(func(tx *Tx) error {
for i := 0; i < 10; i++ {
if _, _, err := tx.Set(fmt.Sprintf("key%d", i), fmt.Sprintf("val%d", i), nil); err != nil {
return err
}
}
return nil
})
if err != nil {
t.Fatal(err)
}
// add the same 10 items
// this will create 10 duplicate log entries
err = db.Update(func(tx *Tx) error {
for i := 0; i < 10; i++ {
if _, _, err := tx.Set(fmt.Sprintf("key%d", i), fmt.Sprintf("val%d", i), nil); err != nil {
return err
}
}
return nil
})
if err != nil {
t.Fatal(err)
}
err = db.View(func(tx *Tx) error {
n, err := tx.Len()
if err != nil {
t.Fatal(err)
}
if n != 10 {
t.Fatalf("expecting %v, got %v", 10, n)
}
return nil
})
if err != nil {
t.Fatal(err)
}
// this should succeed even though it's basically a noop.
if err := db.Shrink(); err != nil {
t.Fatal(err)
}
}
func TestVariousIndexOperations(t *testing.T) {
db := testOpen(t)
defer testClose(db)
// test creating an index with no index name.
err := db.CreateIndex("", "", nil)
if err == nil {
t.Fatal("should not be able to create an index with no name")
}
// test creating an index with a name that has already been used.
err = db.CreateIndex("hello", "", nil)
if err != nil {
t.Fatal(err)
}
err = db.CreateIndex("hello", "", nil)
if err == nil {
t.Fatal("should not be able to create a duplicate index")
}
err = db.Update(func(tx *Tx) error {
if _, _, err := tx.Set("user:1", "tom", nil); err != nil {
return err
}
if _, _, err := tx.Set("user:2", "janet", nil); err != nil {
return err
}
if _, _, err := tx.Set("alt:1", "from", nil); err != nil {
return err
}
if _, _, err := tx.Set("alt:2", "there", nil); err != nil {
return err
}
if _, _, err := tx.Set("rect:1", "[1 2],[3 4]", nil); err != nil {
return err
}
if _, _, err := tx.Set("rect:2", "[5 6],[7 8]", nil); err != nil {
return err
}
return nil
})
if err != nil {
t.Fatal(err)
}
// test creating an index after adding items. use pattern matching. have some items in the match and some not.
if err := db.CreateIndex("string", "user:*", IndexString); err != nil {
t.Fatal(err)
}
// test creating a spatial index after adding items. use pattern matching. have some items in the match and some not.
if err := db.CreateSpatialIndex("rect", "rect:*", IndexRect); err != nil {
t.Fatal(err)
}
// test dropping an index
if err := db.DropIndex("hello"); err != nil {
t.Fatal(err)
}
// test dropping an index with no name
if err := db.DropIndex(""); err == nil {
t.Fatal("should not be allowed to drop an index with no name")
}
// test dropping an index with no name
if err := db.DropIndex("na"); err == nil {
t.Fatal("should not be allowed to drop an index that does not exist")
}
// test retrieving index names
names, err := db.Indexes()
if err != nil {
t.Fatal(err)
}
if strings.Join(names, ",") != "rect,string" {
t.Fatalf("expecting '%v', got '%v'", "rect,string", strings.Join(names, ","))
}
// test creating an index after closing database
if err := db.Close(); err != nil {
t.Fatal(err)
}
if err := db.CreateIndex("new-index", "", nil); err != ErrDatabaseClosed {
t.Fatal("should not be able to create an index on a closed database")
}
// test getting index names after closing database
if _, err := db.Indexes(); err != ErrDatabaseClosed {
t.Fatal("should not be able to get index names on a closed database")
}
// test dropping an index after closing database
if err := db.DropIndex("rect"); err != ErrDatabaseClosed {
t.Fatal("should not be able to drop an index on a closed database")
}
}
func test(t *testing.T, a, b bool) {
if a != b {
t.Fatal("failed, bummer...")
}
}
func TestBasic(t *testing.T) {
rand.Seed(time.Now().UnixNano())
db := testOpen(t)
defer testClose(db)
// create a simple index
if err := db.CreateIndex("users", "fun:user:*", IndexString); err != nil {
t.Fatal(err)
}
// create a spatial index
if err := db.CreateSpatialIndex("rects", "rect:*", IndexRect); err != nil {
t.Fatal(err)
}
if true {
err := db.Update(func(tx *Tx) error {
if _, _, err := tx.Set("fun:user:0", "tom", nil); err != nil {
return err
}
if _, _, err := tx.Set("fun:user:1", "Randi", nil); err != nil {
return err
}
if _, _, err := tx.Set("fun:user:2", "jane", nil); err != nil {
return err
}
if _, _, err := tx.Set("fun:user:4", "Janet", nil); err != nil {
return err
}
if _, _, err := tx.Set("fun:user:5", "Paula", nil); err != nil {
return err
}
if _, _, err := tx.Set("fun:user:6", "peter", nil); err != nil {
return err
}
if _, _, err := tx.Set("fun:user:7", "Terri", nil); err != nil {
return err
}
return nil
})
if err != nil {
t.Fatal(err)
}
// add some random items
start := time.Now()
if err := db.Update(func(tx *Tx) error {
for _, i := range rand.Perm(100) {
if _, _, err := tx.Set(fmt.Sprintf("tag:%d", i+100), fmt.Sprintf("val:%d", rand.Int()%100+100), nil); err != nil {
return err
}
}
return nil
}); err != nil {
t.Fatal(err)
}
if false {
println(time.Since(start).String(), db.keys.Len())
}
// add some random rects
if err := db.Update(func(tx *Tx) error {
if _, _, err := tx.Set("rect:1", Rect([]float64{10, 10}, []float64{20, 20}), nil); err != nil {
return err
}
if _, _, err := tx.Set("rect:2", Rect([]float64{15, 15}, []float64{24, 24}), nil); err != nil {
return err
}
if _, _, err := tx.Set("rect:3", Rect([]float64{17, 17}, []float64{27, 27}), nil); err != nil {
return err
}
return nil
}); err != nil {
t.Fatal(err)
}
}
// verify the data has been created
buf := &bytes.Buffer{}
err := db.View(func(tx *Tx) error {
err := tx.Ascend("users", func(key, val string) bool {
fmt.Fprintf(buf, "%s %s\n", key, val)
return true
})
if err != nil {
t.Fatal(err)
}
err = tx.AscendRange("", "tag:170", "tag:172", func(key, val string) bool {
fmt.Fprintf(buf, "%s\n", key)
return true
})
if err != nil {
t.Fatal(err)
}
err = tx.AscendGreaterOrEqual("", "tag:195", func(key, val string) bool {
fmt.Fprintf(buf, "%s\n", key)
return true
})
if err != nil {
t.Fatal(err)
}
err = tx.AscendGreaterOrEqual("", "rect:", func(key, val string) bool {
if !strings.HasPrefix(key, "rect:") {
return false
}
min, max := IndexRect(val)
fmt.Fprintf(buf, "%s: %v,%v\n", key, min, max)
return true
})
if err != nil {
t.Fatal(err)
}
expect := make([]string, 2)
n := 0
err = tx.Intersects("rects", "[0 0],[15 15]", func(key, val string) bool {
if n == 2 {
t.Fatalf("too many rects where received, expecting only two")
}
min, max := IndexRect(val)
s := fmt.Sprintf("%s: %v,%v\n", key, min, max)
if key == "rect:1" {
expect[0] = s
} else if key == "rect:2" {
expect[1] = s
}
n++
return true
})
if err != nil {
t.Fatal(err)
}
for _, s := range expect {
if _, err := buf.WriteString(s); err != nil {
return err
}
}
return nil
})
if err != nil {
t.Fatal(err)
}
res := `
fun:user:2 jane
fun:user:4 Janet
fun:user:5 Paula
fun:user:6 peter
fun:user:1 Randi
fun:user:7 Terri
fun:user:0 tom
tag:170
tag:171
tag:195
tag:196
tag:197
tag:198
tag:199
rect:1: [10 10],[20 20]
rect:2: [15 15],[24 24]
rect:3: [17 17],[27 27]
rect:1: [10 10],[20 20]
rect:2: [15 15],[24 24]
`
res = strings.Replace(res, "\r", "", -1)
if strings.TrimSpace(buf.String()) != strings.TrimSpace(res) {
t.Fatalf("expected [%v], got [%v]", strings.TrimSpace(res), strings.TrimSpace(buf.String()))
}
}
func TestIndexAscend(t *testing.T) {
rand.Seed(time.Now().UnixNano())
db := testOpen(t)
defer testClose(db)
// create a simple index
if err := db.CreateIndex("usr", "usr:*", IndexInt); err != nil {
t.Fatal(err)
}
if err := db.Update(func(tx *Tx) error {
for i := 10; i > 0; i-- {
tx.Set(fmt.Sprintf("usr:%d", i), fmt.Sprintf("%d", 10-i), nil)
}
return nil
}); err != nil {
t.Fatal(err)
}
buf := &bytes.Buffer{}
err := db.View(func(tx *Tx) error {
tx.Ascend("usr", func(key, value string) bool {
fmt.Fprintf(buf, "%s %s\n", key, value)
return true
})
fmt.Fprintln(buf)
tx.AscendGreaterOrEqual("usr", "8", func(key, value string) bool {
fmt.Fprintf(buf, "%s %s\n", key, value)
return true
})
fmt.Fprintln(buf)
tx.AscendLessThan("usr", "3", func(key, value string) bool {
fmt.Fprintf(buf, "%s %s\n", key, value)
return true
})
fmt.Fprintln(buf)
tx.AscendRange("usr", "4", "8", func(key, value string) bool {
fmt.Fprintf(buf, "%s %s\n", key, value)
return true
})
return nil
})
if err != nil {
t.Fatal(err)
}
res := `
usr:10 0
usr:9 1
usr:8 2
usr:7 3
usr:6 4
usr:5 5
usr:4 6
usr:3 7
usr:2 8
usr:1 9
usr:2 8
usr:1 9
usr:10 0
usr:9 1
usr:8 2
usr:6 4
usr:5 5
usr:4 6
usr:3 7
`
res = strings.Replace(res, "\r", "", -1)
s1 := strings.TrimSpace(buf.String())
s2 := strings.TrimSpace(res)
if s1 != s2 {
t.Fatalf("expected [%v], got [%v]", s1, s2)
}
}
func testRectStringer(min, max []float64) error {
nmin, nmax := IndexRect(Rect(min, max))
if len(nmin) != len(min) {
return fmt.Errorf("rect=%v,%v, expect=%v,%v", nmin, nmax, min, max)
}
for i := 0; i < len(min); i++ {
if min[i] != nmin[i] || max[i] != nmax[i] {
return fmt.Errorf("rect=%v,%v, expect=%v,%v", nmin, nmax, min, max)
}
}
return nil
}
func TestRectStrings(t *testing.T) {
test(t, Rect(IndexRect(Point(1))) == "[1]", true)
test(t, Rect(IndexRect(Point(1, 2, 3, 4))) == "[1 2 3 4]", true)
test(t, Rect(IndexRect(Rect(IndexRect("[1 2],[1 2]")))) == "[1 2]", true)
test(t, Rect(IndexRect(Rect(IndexRect("[1 2],[2 2]")))) == "[1 2],[2 2]", true)
test(t, Rect(IndexRect(Rect(IndexRect("[1 2],[2 2],[3]")))) == "[1 2],[2 2]", true)
test(t, Rect(IndexRect(Rect(IndexRect("[1 2]")))) == "[1 2]", true)
test(t, Rect(IndexRect(Rect(IndexRect("[1.5 2 4.5 5.6]")))) == "[1.5 2 4.5 5.6]", true)
test(t, Rect(IndexRect(Rect(IndexRect("[1.5 2 4.5 5.6 -1],[]")))) == "[1.5 2 4.5 5.6 -1]", true)
test(t, Rect(IndexRect(Rect(IndexRect("[]")))) == "[]", true)
test(t, Rect(IndexRect(Rect(IndexRect("")))) == "[]", true)
if err := testRectStringer(nil, nil); err != nil {
t.Fatal(err)
}
if err := testRectStringer([]float64{}, []float64{}); err != nil {
t.Fatal(err)
}
if err := testRectStringer([]float64{1}, []float64{2}); err != nil {
t.Fatal(err)
}
if err := testRectStringer([]float64{1, 2}, []float64{3, 4}); err != nil {
t.Fatal(err)
}
if err := testRectStringer([]float64{1, 2, 3}, []float64{4, 5, 6}); err != nil {
t.Fatal(err)
}
if err := testRectStringer([]float64{1, 2, 3, 4}, []float64{5, 6, 7, 8}); err != nil {
t.Fatal(err)
}
if err := testRectStringer([]float64{1, 2, 3, 4, 5}, []float64{6, 7, 8, 9, 10}); err != nil {
t.Fatal(err)
}
}
// TestTTLReOpen test setting a TTL and then immediately closing the database and
// then waiting the TTL before reopening. The key should not be accessible.
func TestTTLReOpen(t *testing.T) {
ttl := time.Second * 3
db := testOpen(t)
defer testClose(db)
err := db.Update(func(tx *Tx) error {
if _, _, err := tx.Set("key1", "val1", &SetOptions{Expires: true, TTL: ttl}); err != nil {
return err
}
return nil
})
if err != nil {
t.Fatal(err)
}
db = testReOpenDelay(t, db, ttl/4)
err = db.View(func(tx *Tx) error {
val, err := tx.Get("key1")
if err != nil {
return err
}
if val != "val1" {
t.Fatalf("expecting '%v', got '%v'", "val1", val)
}
return nil
})
if err != nil {
t.Fatal(err)
}
db = testReOpenDelay(t, db, ttl-ttl/4)
defer testClose(db)
err = db.View(func(tx *Tx) error {
val, err := tx.Get("key1")
if err == nil || err != ErrNotFound || val != "" {
t.Fatal("expecting not found")
}
return nil
})
if err != nil {
t.Fatal(err)
}
}
func TestTTL(t *testing.T) {
db := testOpen(t)
defer testClose(db)
err := db.Update(func(tx *Tx) error {
if _, _, err := tx.Set("key1", "val1", &SetOptions{Expires: true, TTL: time.Second}); err != nil {
return err
}
if _, _, err := tx.Set("key2", "val2", nil); err != nil {
return err
}
return nil
})
if err != nil {
t.Fatal(err)
}
err = db.View(func(tx *Tx) error {
dur1, err := tx.TTL("key1")
if err != nil {
t.Fatal(err)
}
if dur1 > time.Second || dur1 <= 0 {
t.Fatalf("expecting between zero and one second, got '%v'", dur1)
}
dur1, err = tx.TTL("key2")
if err != nil {
t.Fatal(err)
}
if dur1 >= 0 {
t.Fatalf("expecting a negative value, got '%v'", dur1)
}
return nil
})
if err != nil {
t.Fatal(err)
}
}
func TestConfig(t *testing.T) {
db := testOpen(t)
defer testClose(db)
err := db.SetConfig(Config{SyncPolicy: SyncPolicy(-1)})
if err == nil {
t.Fatal("expecting a config syncpolicy error")
}
err = db.SetConfig(Config{SyncPolicy: SyncPolicy(3)})
if err == nil {
t.Fatal("expecting a config syncpolicy error")
}
err = db.SetConfig(Config{SyncPolicy: Never})
if err != nil {
t.Fatal(err)
}
err = db.SetConfig(Config{SyncPolicy: EverySecond})
if err != nil {
t.Fatal(err)
}
err = db.SetConfig(Config{AutoShrinkMinSize: 100, AutoShrinkPercentage: 200, SyncPolicy: Always})
if err != nil {
t.Fatal(err)
}
var c Config
if err := db.ReadConfig(&c); err != nil {
t.Fatal(err)
}
if c.AutoShrinkMinSize != 100 || c.AutoShrinkPercentage != 200 && c.SyncPolicy != Always {
t.Fatalf("expecting %v, %v, and %v, got %v, %v, and %v", 100, 200, Always, c.AutoShrinkMinSize, c.AutoShrinkPercentage, c.SyncPolicy)
}
}
func testUint64Hex(n uint64) string {
s := strconv.FormatUint(n, 16)
s = "0000000000000000" + s
return s[len(s)-16:]
}
func textHexUint64(s string) uint64 {
n, _ := strconv.ParseUint(s, 16, 64)
return n
}
func benchClose(t *testing.B, persist bool, db *DB) {
if persist {
if err := os.RemoveAll("data.db"); err != nil {
t.Fatal(err)
}
}
if err := db.Close(); err != nil {
t.Fatal(err)
}
}
func benchOpenFillData(t *testing.B, N int,
set, persist, random bool,
geo bool,
batch int) (db *DB, keys, vals []string) {
///
t.StopTimer()
rand.Seed(time.Now().UnixNano())
var err error
if persist {
if err := os.RemoveAll("data.db"); err != nil {
t.Fatal(err)
}
db, err = Open("data.db")
} else {
db, err = Open(":memory:")
}
if err != nil {
t.Fatal(err)
}
keys = make([]string, N)
vals = make([]string, N)
perm := rand.Perm(N)
for i := 0; i < N; i++ {
if random && set {
keys[perm[i]] = testUint64Hex(uint64(i))
vals[perm[i]] = strconv.FormatInt(rand.Int63()%1000+1000, 10)
} else {
keys[i] = testUint64Hex(uint64(i))
vals[i] = strconv.FormatInt(rand.Int63()%1000+1000, 10)
}
}
if set {
t.StartTimer()
}
for i := 0; i < N; {
err := db.Update(func(tx *Tx) error {
var err error
for j := 0; j < batch && i < N; j++ {
_, _, err = tx.Set(keys[i], vals[i], nil)
i++
}
return err
})
if err != nil {
t.Fatal(err)
}
}
if set {
t.StopTimer()
}
var n uint64
err = db.View(func(tx *Tx) error {
err := tx.Ascend("", func(key, value string) bool {
n2 := textHexUint64(key)
if n2 != n {
t.Fatalf("expecting '%v', got '%v'", n2, n)
}
n++
return true
})
return err
})
if err != nil {
t.Fatal(err)
}
if n != uint64(N) {
t.Fatalf("expecting '%v', got '%v'", N, n)
}
t.StartTimer()
return db, keys, vals
}
func benchSetGet(t *testing.B, set, persist, random bool, batch int) {
N := t.N
for N > 0 {
n := 0
if N >= 100000 {
n = 100000
} else {
n = N
}
func() {
db, keys, _ := benchOpenFillData(t, n, set, persist, random, false, batch)
defer benchClose(t, persist, db)
if !set {
for i := 0; i < n; {
err := db.View(func(tx *Tx) error {
var err error
for j := 0; j < batch && i < n; j++ {
_, err = tx.Get(keys[i])
i++
}
return err
})
if err != nil {
t.Fatal(err)
}
}
}
}()
N -= n
}
}
// Set Persist
func Benchmark_Set_Persist_Random_1(t *testing.B) {
benchSetGet(t, true, true, true, 1)
}
func Benchmark_Set_Persist_Random_10(t *testing.B) {
benchSetGet(t, true, true, true, 10)
}
func Benchmark_Set_Persist_Random_100(t *testing.B) {
benchSetGet(t, true, true, true, 100)
}
func Benchmark_Set_Persist_Sequential_1(t *testing.B) {
benchSetGet(t, true, true, false, 1)
}
func Benchmark_Set_Persist_Sequential_10(t *testing.B) {
benchSetGet(t, true, true, false, 10)
}
func Benchmark_Set_Persist_Sequential_100(t *testing.B) {
benchSetGet(t, true, true, false, 100)
}
// Set NoPersist
func Benchmark_Set_NoPersist_Random_1(t *testing.B) {
benchSetGet(t, true, false, true, 1)
}
func Benchmark_Set_NoPersist_Random_10(t *testing.B) {
benchSetGet(t, true, false, true, 10)
}
func Benchmark_Set_NoPersist_Random_100(t *testing.B) {
benchSetGet(t, true, false, true, 100)
}
func Benchmark_Set_NoPersist_Sequential_1(t *testing.B) {
benchSetGet(t, true, false, false, 1)
}
func Benchmark_Set_NoPersist_Sequential_10(t *testing.B) {
benchSetGet(t, true, false, false, 10)
}
func Benchmark_Set_NoPersist_Sequential_100(t *testing.B) {
benchSetGet(t, true, false, false, 100)
}
// Get
func Benchmark_Get_1(t *testing.B) {
benchSetGet(t, false, false, false, 1)
}
func Benchmark_Get_10(t *testing.B) {
benchSetGet(t, false, false, false, 10)
}
func Benchmark_Get_100(t *testing.B) {
benchSetGet(t, false, false, false, 100)
}
func benchScan(t *testing.B, asc bool, count int) {
N := count
db, _, _ := benchOpenFillData(t, N, false, false, false, false, 100)
defer benchClose(t, false, db)
for i := 0; i < t.N; i++ {
count := 0
err := db.View(func(tx *Tx) error {
if asc {
return tx.Ascend("", func(key, val string) bool {
count++
return true
})
}
return tx.Descend("", func(key, val string) bool {
count++
return true
})
})
if err != nil {
t.Fatal(err)
}
if count != N {
t.Fatalf("expecting '%v', got '%v'", N, count)
}
}
}
func Benchmark_Ascend_1(t *testing.B) {
benchScan(t, true, 1)
}
func Benchmark_Ascend_10(t *testing.B) {
benchScan(t, true, 10)
}
func Benchmark_Ascend_100(t *testing.B) {
benchScan(t, true, 100)
}
func Benchmark_Ascend_1000(t *testing.B) {
benchScan(t, true, 1000)
}
func Benchmark_Ascend_10000(t *testing.B) {
benchScan(t, true, 10000)
}
func Benchmark_Descend_1(t *testing.B) {
benchScan(t, false, 1)
}
func Benchmark_Descend_10(t *testing.B) {
benchScan(t, false, 10)
}
func Benchmark_Descend_100(t *testing.B) {
benchScan(t, false, 100)
}
func Benchmark_Descend_1000(t *testing.B) {
benchScan(t, false, 1000)
}
func Benchmark_Descend_10000(t *testing.B) {
benchScan(t, false, 10000)
}
/*
func Benchmark_Spatial_2D(t *testing.B) {
N := 100000
db, _, _ := benchOpenFillData(t, N, true, true, false, true, 100)
defer benchClose(t, false, db)
}
*/
func TestCoverCloseAlreadyClosed(t *testing.T) {
db := testOpen(t)
defer testClose(db)
_ = db.file.Close()
if err := db.Close(); err == nil {
t.Fatal("expecting an error")
}
}
func TestCoverConfigClosed(t *testing.T) {
db := testOpen(t)
defer testClose(db)
_ = db.Close()
var config Config
if err := db.ReadConfig(&config); err != ErrDatabaseClosed {
t.Fatal("expecting database closed error")
}
if err := db.SetConfig(config); err != ErrDatabaseClosed {
t.Fatal("expecting database closed error")
}
}
func TestCoverShrinkShrink(t *testing.T) {
db := testOpen(t)
defer testClose(db)
if err := db.Update(func(tx *Tx) error {
for i := 0; i < 10000; i++ {
_, _, err := tx.Set(fmt.Sprintf("%d", i), fmt.Sprintf("%d", i), nil)
if err != nil {
return err
}
}
return nil
}); err != nil {
t.Fatal(err)
}
if err := db.Update(func(tx *Tx) error {
for i := 250; i < 250+100; i++ {
_, err := tx.Delete(fmt.Sprintf("%d", i))
if err != nil {
return err
}
}
return nil
}); err != nil {
t.Fatal(err)
}
var err1, err2 error
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
err1 = db.Shrink()
}()
go func() {
defer wg.Done()
err2 = db.Shrink()
}()
wg.Wait()
//println(123)
//fmt.Printf("%v\n%v\n", err1, err2)
if err1 != ErrShrinkInProcess && err2 != ErrShrinkInProcess {
t.Fatal("expecting a shrink in process error")
}
db = testReOpen(t, db)
defer testClose(db)
if err := db.View(func(tx *Tx) error {
n, err := tx.Len()
if err != nil {
return err
}
if n != 9900 {
t.Fatal("expecting 9900 items")
}
return nil
}); err != nil {
t.Fatal(err)
}
}
func TestPreviousItem(t *testing.T) {
db := testOpen(t)
defer testClose(db)
err := db.Update(func(tx *Tx) error {
_, _, err := tx.Set("hello", "world", nil)
if err != nil {
return err
}
prev, replaced, err := tx.Set("hello", "planet", nil)
if err != nil {
return err
}
if !replaced {
t.Fatal("should be replaced")
}
if prev != "world" {
t.Fatalf("expecting '%v', got '%v'", "world", prev)
}
return nil
})
if err != nil {
t.Fatal(err)
}
}
func TestJSONIndex(t *testing.T) {
db := testOpen(t)
defer testClose(db)
_ = db.CreateIndex("last_name", "*", IndexJSON("name.last"))
_ = db.CreateIndex("last_name_cs", "*", IndexJSONCaseSensitive("name.last"))
_ = db.CreateIndex("age", "*", IndexJSON("age"))
_ = db.CreateIndex("student", "*", IndexJSON("student"))
_ = db.Update(func(tx *Tx) error {
_, _, _ = tx.Set("1", `{"name":{"first":"Tom","last":"Johnson"},"age":38,"student":false}`, nil)
_, _, _ = tx.Set("2", `{"name":{"first":"Janet","last":"Prichard"},"age":47,"student":true}`, nil)
_, _, _ = tx.Set("3", `{"name":{"first":"Carol","last":"Anderson"},"age":52,"student":true}`, nil)
_, _, _ = tx.Set("4", `{"name":{"first":"Alan","last":"Cooper"},"age":28,"student":false}`, nil)
_, _, _ = tx.Set("5", `{"name":{"first":"bill","last":"frank"},"age":21,"student":true}`, nil)
_, _, _ = tx.Set("6", `{"name":{"first":"sally","last":"randall"},"age":68,"student":false}`, nil)
return nil
})
var keys []string
_ = db.View(func(tx *Tx) error {
_ = tx.Ascend("last_name_cs", func(key, value string) bool {
//fmt.Printf("%s: %s\n", key, value)
keys = append(keys, key)
return true
})
_ = tx.Ascend("last_name", func(key, value string) bool {
//fmt.Printf("%s: %s\n", key, value)
keys = append(keys, key)
return true
})
_ = tx.Ascend("age", func(key, value string) bool {
//fmt.Printf("%s: %s\n", key, value)
keys = append(keys, key)
return true
})
_ = tx.Ascend("student", func(key, value string) bool {
//fmt.Printf("%s: %s\n", key, value)
keys = append(keys, key)
return true
})
return nil
})
expect := "3,4,1,2,5,6,3,4,5,1,2,6,5,4,1,2,3,6,1,4,6,2,3,5"
if strings.Join(keys, ",") != expect {
t.Fatalf("expected %v, got %v", expect, strings.Join(keys, ","))
}
}
func TestOnExpiredSync(t *testing.T) {
db := testOpen(t)
defer testClose(db)
var config Config
if err := db.ReadConfig(&config); err != nil {
t.Fatal(err)
}
hits := make(chan int, 3)
config.OnExpiredSync = func(key, value string, tx *Tx) error {
n, err := strconv.Atoi(value)
if err != nil {
return err
}
defer func() { hits <- n }()
if n >= 2 {
_, err = tx.Delete(key)
if err != ErrNotFound {
return err
}
return nil
}
n++
_, _, err = tx.Set(key, strconv.Itoa(n), &SetOptions{Expires: true, TTL: time.Millisecond * 100})
return err
}
if err := db.SetConfig(config); err != nil {
t.Fatal(err)
}
err := db.Update(func(tx *Tx) error {
_, _, err := tx.Set("K", "0", &SetOptions{Expires: true, TTL: time.Millisecond * 100})
return err
})
if err != nil {
t.Fail()
}
done := make(chan struct{})
go func() {
ticks := time.NewTicker(time.Millisecond * 50)
defer ticks.Stop()
for {
select {
case <-done:
return
case <-ticks.C:
err := db.View(func(tx *Tx) error {
v, err := tx.Get("K", true)
if err != nil {
return err
}
n, err := strconv.Atoi(v)
if err != nil {
return err
}
if n < 0 || n > 2 {
t.Fail()
}
return nil
})
if err != nil {
t.Fail()
}
}
}
}()
OUTER1:
for {
select {
case <-time.After(time.Second * 2):
t.Fail()
case v := <-hits:
if v >= 2 {
break OUTER1
}
}
}
err = db.View(func(tx *Tx) error {
defer close(done)
v, err := tx.Get("K")
if err != nil {
t.Fail()
return err
}
if v != "2" {
t.Fail()
}
return nil
})
if err != nil {
t.Fail()
}
}
func TestTransactionLeak(t *testing.T) {
// This tests an bug identified in Issue #69. When inside a Update
// transaction, a Set after a Delete for a key that previously exists will
// remove the key when the transaction was rolledback.
buntDB, err := Open(":memory:")
if err != nil {
t.Fatal(err)
}
bd := buntDB
err = bd.Update(func(tx *Tx) error {
_, _, err := tx.Set("a", "a", nil)
return err
})
if err != nil {
t.Fatal(err)
}
bd.View(func(tx *Tx) error {
val, err := tx.Get("a")
if err != nil {
return err
}
if val != "a" {
return errors.New("mismatch")
}
return nil
})
if err != nil {
t.Fatal(err)
}
bd.Update(func(tx *Tx) error {
val, err := tx.Delete("a")
if err != nil {
return err
}
if val != "a" {
return errors.New("mismatch")
}
val, err = tx.Get("a")
if err != ErrNotFound {
return fmt.Errorf("expected NotFound, got %v", err)
}
if val != "" {
return errors.New("mismatch")
}
val, rep, err := tx.Set("a", "b", nil)
if err != nil {
return err
}
if rep {
return errors.New("replaced")
}
if val != "" {
return errors.New("mismatch")
}
val, err = tx.Get("a")
if err != nil {
return err
}
if val != "b" {
return errors.New("mismatch")
}
return errors.New("rollback")
})
if err != nil {
t.Fatal(err)
}
bd.View(func(tx *Tx) error {
val, err := tx.Get("a")
if err != nil {
return err
}
if val != "a" {
return errors.New("mismatch")
}
return nil
})
if err != nil {
t.Fatal(err)
}
}
func TestReloadNotInvalid(t *testing.T) {
rand.Seed(time.Now().UnixNano())
os.RemoveAll("data.db")
defer os.RemoveAll("data.db")
start := time.Now()
ii := 0
for time.Since(start) < time.Second*5 {
func() {
db, err := Open("data.db")
if err != nil {
t.Fatal(err)
}
defer func() {
if err := db.Close(); err != nil {
panic(err)
}
// truncate at a random point in the file
f, err := os.OpenFile("data.db", os.O_RDWR, 0666)
if err != nil {
panic(err)
}
defer f.Close()
sz, err := f.Seek(0, 2)
if err != nil {
panic(err)
}
n := sz/2 + int64(rand.Intn(int(sz/2)))
err = f.Truncate(n)
if err != nil {
panic(err)
}
}()
N := 500
lotsa.Ops(N, 16, func(i, t int) {
if i == N/2 && ii&7 == 0 {
if err := db.Shrink(); err != nil {
panic(err)
}
}
err := db.Update(func(tx *Tx) error {
_, _, err := tx.Set(fmt.Sprintf("key:%d", i), fmt.Sprintf("val:%d", i), &SetOptions{
Expires: true,
TTL: 30,
})
return err
})
if err != nil {
panic(err)
}
})
}()
ii++
}
}
func TestEstSize(t *testing.T) {
t.Run("estIntSize", func(t *testing.T) {
assert.Assert(estIntSize(0) == 1)
assert.Assert(estIntSize(1) == 1)
assert.Assert(estIntSize(9) == 1)
assert.Assert(estIntSize(10) == 2)
assert.Assert(estIntSize(11) == 2)
assert.Assert(estIntSize(19) == 2)
assert.Assert(estIntSize(20) == 2)
assert.Assert(estIntSize(113) == 3)
assert.Assert(estIntSize(3822) == 4)
assert.Assert(estIntSize(-1) == 2)
assert.Assert(estIntSize(-12) == 3)
assert.Assert(estIntSize(-124) == 4)
})
}
func TestWrappedError(t *testing.T) {
defer func() {
if err, ok := recover().(error); ok {
if strings.HasPrefix(err.Error(), "buntdb: ") {
err := errors.Unwrap(err)
if err.Error() != "my fake error" {
t.Fatal("!")
}
}
}
}()
panicErr(errors.New("my fake error"))
}
func TestIssue112(t *testing.T) {
defer os.RemoveAll("data.db")
db, err := Open("data.db")
if err != nil {
panicErr(err)
}
err = db.Update(func(tx *Tx) error {
_, _, err := tx.Set("key:112", "value:112-old", nil)
return err
})
if err != nil {
panicErr(err)
}
err = db.Update(func(tx *Tx) error {
_, _, err := tx.Set("key:112", "value:112", &SetOptions{Expires: true, TTL: time.Second})
return err
})
if err != nil {
panicErr(err)
}
db.Close()
time.Sleep(time.Second)
db, err = Open("data.db")
if err != nil {
panicErr(err)
}
err = db.View(func(tx *Tx) error {
_, err := tx.Get("key:112")
return err
})
assert.Assert(err == ErrNotFound)
db.View(func(tx *Tx) error {
err := tx.Ascend("", func(key, value string) bool {
t.Fatalf("key: %s, value: %s\n", key, value)
return true
})
return err
})
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/mirrors/buntdb.git
git@gitee.com:mirrors/buntdb.git
mirrors
buntdb
buntdb
master

搜索帮助