func ExampleRangeIterator() { fdb.MustAPIVersion(200) db := fdb.MustOpenDefault() tr, e := db.CreateTransaction() if e != nil { fmt.Printf("Unable to create transaction: %v\n", e) return } // Clear and initialize data in this transaction. In examples we do not // commit transactions to avoid mutating a real database. tr.ClearRange(fdb.KeyRange{fdb.Key(""), fdb.Key{0xFF}}) tr.Set(fdb.Key("apple"), []byte("foo")) tr.Set(fdb.Key("cherry"), []byte("baz")) tr.Set(fdb.Key("banana"), []byte("bar")) rr := tr.GetRange(fdb.KeyRange{fdb.Key(""), fdb.Key{0xFF}}, fdb.RangeOptions{}) ri := rr.Iterator() // Advance will return true until the iterator is exhausted for ri.Advance() { kv, e := ri.Get() if e != nil { fmt.Printf("Unable to read next value: %v\n", e) return } fmt.Printf("%s is %s\n", kv.Key, kv.Value) } // Output: // apple is foo // banana is bar // cherry is baz }
func (sm *StackMachine) testLocality() { tr, e := db.CreateTransaction() if e != nil { panic(e) } tr.Options().SetReadSystemKeys() boundaryKeys, e := db.LocalityGetBoundaryKeys(fdb.KeyRange{fdb.Key(""), fdb.Key("\xff\xff")}, 0, 0) if e != nil { panic(e) } for i := 0; i < len(boundaryKeys)-1; i++ { start := boundaryKeys[i] end := tr.GetKey(fdb.LastLessThan(boundaryKeys[i+1])).MustGet() startAddresses := tr.LocalityGetAddressesForKey(start).MustGet() endAddresses := tr.LocalityGetAddressesForKey(end).MustGet() for _, address1 := range startAddresses { found := false for _, address2 := range endAddresses { if address1 == address2 { found = true break } } if !found { panic("Locality not internally consistent.") } } } }
// Range returns the KeyRange that describes the keys encoding tuples that // strictly begin with t (that is, all tuples of greater length than t of which // t is a prefix). Range will panic if the tuple contains an element of any type // other than []byte, string, int64, int, or nil. func (t Tuple) Range() fdb.KeyRange { p := t.Pack() begin := make([]byte, len(p)+1) copy(begin, p) end := make([]byte, len(p)+1) copy(end, p) end[len(p)] = 0xFF return fdb.KeyRange{fdb.Key(begin), fdb.Key(end)} }
func ExampleReadTransactor() { fdb.MustAPIVersion(200) db := fdb.MustOpenDefault() getOne := func(rt fdb.ReadTransactor, key fdb.Key) ([]byte, error) { fmt.Printf("getOne called with: %T\n", rt) ret, e := rt.ReadTransact(func(rtr fdb.ReadTransaction) (interface{}, error) { return rtr.Get(key).MustGet(), nil }) if e != nil { return nil, e } return ret.([]byte), nil } getTwo := func(rt fdb.ReadTransactor, key1, key2 fdb.Key) ([][]byte, error) { fmt.Printf("getTwo called with: %T\n", rt) ret, e := rt.ReadTransact(func(rtr fdb.ReadTransaction) (interface{}, error) { r1, _ := getOne(rtr, key1) r2, _ := getOne(rtr.Snapshot(), key2) return [][]byte{r1, r2}, nil }) if e != nil { return nil, e } return ret.([][]byte), nil } var e error fmt.Println("Calling getOne with a database:") _, e = getOne(db, fdb.Key("foo")) if e != nil { fmt.Println(e) return } fmt.Println("\nCalling getTwo with a database:") _, e = getTwo(db, fdb.Key("foo"), fdb.Key("bar")) if e != nil { fmt.Println(e) return } // Output: // Calling getOne with a database: // getOne called with: fdb.Database // // Calling getTwo with a database: // getTwo called with: fdb.Database // getOne called with: fdb.Transaction // getOne called with: fdb.Snapshot }
func ExampleTransactor() { fdb.MustAPIVersion(200) db := fdb.MustOpenDefault() setOne := func(t fdb.Transactor, key fdb.Key, value []byte) error { fmt.Printf("setOne called with: %T\n", t) _, e := t.Transact(func(tr fdb.Transaction) (interface{}, error) { // We don't actually call tr.Set here to avoid mutating a real database. // tr.Set(key, value) return nil, nil }) return e } setMany := func(t fdb.Transactor, value []byte, keys ...fdb.Key) error { fmt.Printf("setMany called with: %T\n", t) _, e := t.Transact(func(tr fdb.Transaction) (interface{}, error) { for _, key := range keys { setOne(tr, key, value) } return nil, nil }) return e } var e error fmt.Println("Calling setOne with a database:") e = setOne(db, []byte("foo"), []byte("bar")) if e != nil { fmt.Println(e) return } fmt.Println("\nCalling setMany with a database:") e = setMany(db, []byte("bar"), fdb.Key("foo1"), fdb.Key("foo2"), fdb.Key("foo3")) if e != nil { fmt.Println(e) return } // Output: // Calling setOne with a database: // setOne called with: fdb.Database // // Calling setMany with a database: // setMany called with: fdb.Database // setOne called with: fdb.Transaction // setOne called with: fdb.Transaction // setOne called with: fdb.Transaction }
func ExamplePrefixRange() { _ = fdb.APIVersion(100) db, _ := fdb.OpenDefault() tr, _ := db.CreateTransaction() // Clear and initialize data in this transaction. In examples we do not // commit transactions to avoid mutating a real database. tr.ClearRange(fdb.KeyRange{fdb.Key(""), fdb.Key{0xFF}}) tr.Set(fdb.Key("alpha"), []byte("1")) tr.Set(fdb.Key("alphabetA"), []byte("2")) tr.Set(fdb.Key("alphabetB"), []byte("3")) tr.Set(fdb.Key("alphabetize"), []byte("4")) tr.Set(fdb.Key("beta"), []byte("5")) // Construct the range of all keys beginning with "alphabet" pr, _ := fdb.PrefixRange([]byte("alphabet")) // Read and process the range kvs, _ := tr.GetRange(pr, fdb.RangeOptions{}).GetSliceWithError() for _, kv := range kvs { fmt.Printf("%s: %s\n", string(kv.Key), string(kv.Value)) } // Output: // alphabetA: 2 // alphabetB: 3 // alphabetize: 4 }
func (sm *StackMachine) Run() { r, e := db.Transact(func(tr fdb.Transaction) (interface{}, error) { return tr.GetRange(tuple.Tuple{sm.prefix}, fdb.RangeOptions{}).GetSliceOrPanic(), nil }) if e != nil { panic(e) } instructions := r.([]fdb.KeyValue) for i, kv := range instructions { inst, _ := tuple.Unpack(fdb.Key(kv.Value)) if sm.verbose { fmt.Printf("Instruction %d\n", i) } sm.processInst(i, inst) } sm.threads.Wait() }
func (es *fdbStore) ReadAll(last []byte, limit int) *GlobalSlice { globalSpace := es.space.Sub(globalPrefix) start, end := globalSpace.FDBRangeKeys() r, err := es.db.ReadTransact(func(tr fdb.ReadTransaction) (interface{}, error) { var scan fdb.KeyRange if nil == last { scan = fdb.KeyRange{start, end} } else { next := tr.Snapshot().GetKey(fdb.FirstGreaterThan(fdb.Key(last))).MustGet() scan = fdb.KeyRange{next, end} } rr := tr.Snapshot().GetRange(scan, fdb.RangeOptions{Limit: limit}) return rr.GetSliceOrPanic(), nil }) if err != nil { panic("Failed to read all events") } kvs := r.([]fdb.KeyValue) result := make([]GlobalRecord, len(kvs)) for i, kv := range kvs { if t, err := globalSpace.Unpack(kv.Key); err != nil { panic("Failed to unpack key") } else { result[i].Contract = t[1].(string) result[i].Data = kv.Value last = []byte(kv.Key) } } return &GlobalSlice{result, last} }
func (dl directoryLayer) nodeContainingKey(rtr fdb.ReadTransaction, key []byte) (subspace.Subspace, error) { if bytes.HasPrefix(key, dl.nodeSS.Bytes()) { return dl.rootNode, nil } bk, _ := dl.nodeSS.FDBRangeKeys() kr := fdb.KeyRange{bk, fdb.Key(append(dl.nodeSS.Pack(tuple.Tuple{key}), 0x00))} kvs := rtr.GetRange(kr, fdb.RangeOptions{Reverse: true, Limit: 1}).GetSliceOrPanic() if len(kvs) == 1 { pp, e := dl.nodeSS.Unpack(kvs[0].Key) if e != nil { return nil, e } prevPrefix := pp[0].([]byte) if bytes.HasPrefix(key, prevPrefix) { return dl.nodeWithPrefix(prevPrefix), nil } } return nil, nil }
func ExamplePrefixRange() { fdb.MustAPIVersion(200) db := fdb.MustOpenDefault() tr, e := db.CreateTransaction() if e != nil { fmt.Printf("Unable to create transaction: %v\n", e) return } // Clear and initialize data in this transaction. In examples we do not // commit transactions to avoid mutating a real database. tr.ClearRange(fdb.KeyRange{fdb.Key(""), fdb.Key{0xFF}}) tr.Set(fdb.Key("alpha"), []byte("1")) tr.Set(fdb.Key("alphabetA"), []byte("2")) tr.Set(fdb.Key("alphabetB"), []byte("3")) tr.Set(fdb.Key("alphabetize"), []byte("4")) tr.Set(fdb.Key("beta"), []byte("5")) // Construct the range of all keys beginning with "alphabet". It is safe to // ignore the error return from PrefixRange unless the provided prefix might // consist entirely of zero or more 0xFF bytes. pr, _ := fdb.PrefixRange([]byte("alphabet")) // Read and process the range kvs, e := tr.GetRange(pr, fdb.RangeOptions{}).GetSliceWithError() if e != nil { fmt.Printf("Unable to read range: %v\n", e) } for _, kv := range kvs { fmt.Printf("%s: %s\n", string(kv.Key), string(kv.Value)) } // Output: // alphabetA: 2 // alphabetB: 3 // alphabetize: 4 }
func (s subspace) Pack(t tuple.Tuple) fdb.Key { return fdb.Key(concat(s.b, t.Pack()...)) }
func (s subspace) FDBRangeKeys() (fdb.KeyConvertible, fdb.KeyConvertible) { return fdb.Key(concat(s.b, 0x00)), fdb.Key(concat(s.b, 0xFF)) }
func (de *DirectoryExtension) processOp(sm *StackMachine, op string, isDB bool, idx int, t fdb.Transactor, rt fdb.ReadTransactor) { defer func() { if r := recover(); r != nil { sm.store(idx, []byte("DIRECTORY_ERROR")) if createOps[op] { de.store(nil) } } }() var e error switch { case op == "CREATE_SUBSPACE": tuples := sm.popTuples(1) rp := sm.waitAndPop().item.([]byte) s := subspace.FromBytes(rp).Sub(tuples[0]...) de.store(s) case op == "CREATE_LAYER": idx1 := sm.waitAndPop().item.(int64) idx2 := sm.waitAndPop().item.(int64) amp := sm.waitAndPop().item.(int64) nodeSS := de.list[idx1] contentSS := de.list[idx2] if nodeSS == nil || contentSS == nil { de.store(nil) } else { de.store(directory.NewDirectoryLayer(nodeSS.(subspace.Subspace), contentSS.(subspace.Subspace), (amp == int64(1)))) } case op == "CREATE_OR_OPEN": tuples := sm.popTuples(1) l := sm.waitAndPop().item var layer []byte if l != nil { layer = l.([]byte) } d, e := de.cwd().CreateOrOpen(t, tupleToPath(tuples[0]), layer) if e != nil { panic(e) } de.store(d) case op == "CREATE": tuples := sm.popTuples(1) l := sm.waitAndPop().item var layer []byte if l != nil { layer = l.([]byte) } p := sm.waitAndPop().item var d directory.Directory if p == nil { d, e = de.cwd().Create(t, tupleToPath(tuples[0]), layer) } else { // p.([]byte) itself may be nil, but CreatePrefix handles that appropriately d, e = de.cwd().CreatePrefix(t, tupleToPath(tuples[0]), layer, p.([]byte)) } if e != nil { panic(e) } de.store(d) case op == "OPEN": tuples := sm.popTuples(1) l := sm.waitAndPop().item var layer []byte if l != nil { layer = l.([]byte) } d, e := de.cwd().Open(rt, tupleToPath(tuples[0]), layer) if e != nil { panic(e) } de.store(d) case op == "CHANGE": i := sm.waitAndPop().item.(int64) if de.list[i] == nil { i = de.errorIndex } de.index = i case op == "SET_ERROR_INDEX": de.errorIndex = sm.waitAndPop().item.(int64) case op == "MOVE": tuples := sm.popTuples(2) d, e := de.cwd().Move(t, tupleToPath(tuples[0]), tupleToPath(tuples[1])) if e != nil { panic(e) } de.store(d) case op == "MOVE_TO": tuples := sm.popTuples(1) d, e := de.cwd().MoveTo(t, tupleToPath(tuples[0])) if e != nil { panic(e) } de.store(d) case strings.HasPrefix(op, "REMOVE"): path := sm.maybePath() // This ***HAS*** to call Transact to ensure that any directory version // key set in the process of trying to remove this potentially // non-existent directory, in the REMOVE but not REMOVE_IF_EXISTS case, // doesn't end up committing the version key. (Other languages have // separate remove() and remove_if_exists() so don't have this tricky // issue). _, e := t.Transact(func(tr fdb.Transaction) (interface{}, error) { ok, e := de.cwd().Remove(tr, path) if e != nil { panic(e) } switch op[6:] { case "": if !ok { panic("directory does not exist") } case "_IF_EXISTS": } return nil, nil }) if e != nil { panic(e) } case op == "LIST": subs, e := de.cwd().List(rt, sm.maybePath()) if e != nil { panic(e) } t := make(tuple.Tuple, len(subs)) for i, s := range subs { t[i] = s } sm.store(idx, t.Pack()) case op == "EXISTS": b, e := de.cwd().Exists(rt, sm.maybePath()) if e != nil { panic(e) } if b { sm.store(idx, int64(1)) } else { sm.store(idx, int64(0)) } case op == "PACK_KEY": tuples := sm.popTuples(1) sm.store(idx, de.css().Pack(tuples[0])) case op == "UNPACK_KEY": t, e := de.css().Unpack(fdb.Key(sm.waitAndPop().item.([]byte))) if e != nil { panic(e) } for _, el := range t { sm.store(idx, el) } case op == "RANGE": ss := de.css().Sub(sm.popTuples(1)[0]...) bk, ek := ss.FDBRangeKeys() sm.store(idx, bk) sm.store(idx, ek) case op == "CONTAINS": k := sm.waitAndPop().item.([]byte) b := de.css().Contains(fdb.Key(k)) if b { sm.store(idx, int64(1)) } else { sm.store(idx, int64(0)) } case op == "OPEN_SUBSPACE": de.store(de.css().Sub(sm.popTuples(1)[0]...)) case op == "LOG_SUBSPACE": k := sm.waitAndPop().item.([]byte) k = append(k, tuple.Tuple{de.index}.Pack()...) v := de.css().Bytes() t.Transact(func(tr fdb.Transaction) (interface{}, error) { tr.Set(fdb.Key(k), v) return nil, nil }) case op == "LOG_DIRECTORY": rp := sm.waitAndPop().item.([]byte) ss := subspace.FromBytes(rp).Sub(de.index) k1 := ss.Pack(tuple.Tuple{"path"}) v1 := tuplePackStrings(de.cwd().GetPath()) k2 := ss.Pack(tuple.Tuple{"layer"}) v2 := tuple.Tuple{de.cwd().GetLayer()}.Pack() k3 := ss.Pack(tuple.Tuple{"exists"}) var v3 []byte exists, e := de.cwd().Exists(rt, nil) if e != nil { panic(e) } if exists { v3 = tuple.Tuple{1}.Pack() } else { v3 = tuple.Tuple{0}.Pack() } k4 := ss.Pack(tuple.Tuple{"children"}) var subs []string if exists { subs, e = de.cwd().List(rt, nil) if e != nil { panic(e) } } v4 := tuplePackStrings(subs) t.Transact(func(tr fdb.Transaction) (interface{}, error) { tr.Set(k1, v1) tr.Set(k2, v2) tr.Set(k3, v3) tr.Set(k4, v4) return nil, nil }) case op == "STRIP_PREFIX": ba := sm.waitAndPop().item.([]byte) ssb := de.css().Bytes() if !bytes.HasPrefix(ba, ssb) { panic("prefix mismatch") } ba = ba[len(ssb):] sm.store(idx, ba) } }
func (sm *StackMachine) popKeyRange() fdb.KeyRange { kr := fdb.KeyRange{fdb.Key(sm.waitAndPop().item.([]byte)), fdb.Key(sm.waitAndPop().item.([]byte))} return kr }
func (sm *StackMachine) popSelector() fdb.KeySelector { sel := fdb.KeySelector{fdb.Key(sm.waitAndPop().item.([]byte)), int64ToBool(sm.waitAndPop().item.(int64)), int(sm.waitAndPop().item.(int64))} return sel }
func (sm *StackMachine) processInst(idx int, inst tuple.Tuple) { defer func() { if r := recover(); r != nil { switch r := r.(type) { case fdb.Error: sm.store(idx, []byte(tuple.Tuple{[]byte("ERROR"), []byte(fmt.Sprintf("%d", r.Code))}.Pack())) default: panic(r) } } }() var e error op := inst[0].(string) if sm.verbose { fmt.Printf("%d. Instruction is %s (%v)\n", idx, op, sm.prefix) fmt.Printf("Stack from [") sm.dumpStack() fmt.Printf(" ]\n") } var t fdb.Transactor var rt fdb.ReadTransactor var isDB bool switch { case strings.HasSuffix(op, "_SNAPSHOT"): rt = sm.tr.Snapshot() op = op[:len(op)-9] case strings.HasSuffix(op, "_DATABASE"): t = db rt = db op = op[:len(op)-9] isDB = true default: t = sm.tr rt = sm.tr } switch { case op == "PUSH": sm.store(idx, inst[1]) case op == "DUP": entry := sm.stack[len(sm.stack)-1] sm.store(entry.idx, entry.item) case op == "EMPTY_STACK": sm.stack = []stackEntry{} sm.stack = make([]stackEntry, 0) case op == "SWAP": idx := sm.waitAndPop().item.(int64) sm.stack[len(sm.stack)-1], sm.stack[len(sm.stack)-1-int(idx)] = sm.stack[len(sm.stack)-1-int(idx)], sm.stack[len(sm.stack)-1] case op == "POP": sm.stack = sm.stack[:len(sm.stack)-1] case op == "SUB": sm.store(idx, sm.waitAndPop().item.(int64)-sm.waitAndPop().item.(int64)) case op == "NEW_TRANSACTION": sm.tr, e = db.CreateTransaction() if e != nil { panic(e) } case op == "ON_ERROR": sm.store(idx, sm.tr.OnError(fdb.Error{int(sm.waitAndPop().item.(int64))})) case op == "GET_READ_VERSION": _, e = rt.ReadTransact(func(rtr fdb.ReadTransaction) (interface{}, error) { sm.lastVersion = rtr.GetReadVersion().MustGet() sm.store(idx, []byte("GOT_READ_VERSION")) return nil, nil }) if e != nil { panic(e) } case op == "SET": sm.executeMutation(t, func(tr fdb.Transaction) (interface{}, error) { tr.Set(fdb.Key(sm.waitAndPop().item.([]byte)), sm.waitAndPop().item.([]byte)) return nil, nil }, isDB, idx) case op == "LOG_STACK": prefix := sm.waitAndPop().item.([]byte) for i := len(sm.stack) - 1; i >= 0; i-- { if i%100 == 0 { sm.tr.Commit().MustGet() } el := sm.waitAndPop() var keyt tuple.Tuple keyt = append(keyt, int64(i)) keyt = append(keyt, int64(el.idx)) pk := append(prefix, keyt.Pack()...) var valt tuple.Tuple valt = append(valt, el.item) pv := valt.Pack() vl := 40000 if len(pv) < vl { vl = len(pv) } sm.tr.Set(fdb.Key(pk), pv[:vl]) } sm.tr.Commit().MustGet() case op == "GET": _, e = rt.ReadTransact(func(rtr fdb.ReadTransaction) (interface{}, error) { sm.store(idx, rtr.Get(fdb.Key(sm.waitAndPop().item.([]byte)))) return nil, nil }) if e != nil { panic(e) } case op == "COMMIT": sm.store(idx, sm.tr.Commit()) case op == "RESET": sm.tr.Reset() case op == "CLEAR": sm.executeMutation(t, func(tr fdb.Transaction) (interface{}, error) { tr.Clear(fdb.Key(sm.waitAndPop().item.([]byte))) return nil, nil }, isDB, idx) case op == "SET_READ_VERSION": sm.tr.SetReadVersion(sm.lastVersion) case op == "WAIT_FUTURE": entry := sm.waitAndPop() sm.store(entry.idx, entry.item) case op == "GET_COMMITTED_VERSION": sm.lastVersion, e = sm.tr.GetCommittedVersion() if e != nil { panic(e) } sm.store(idx, []byte("GOT_COMMITTED_VERSION")) case op == "GET_KEY": sel := sm.popSelector() _, e = rt.ReadTransact(func(rtr fdb.ReadTransaction) (interface{}, error) { sm.store(idx, rtr.GetKey(sel)) return nil, nil }) if e != nil { panic(e) } case strings.HasPrefix(op, "GET_RANGE"): var r fdb.Range switch op[9:] { case "_STARTS_WITH": r = sm.popPrefixRange() case "_SELECTOR": r = fdb.SelectorRange{sm.popSelector(), sm.popSelector()} case "": r = sm.popKeyRange() } ro := sm.popRangeOptions() _, e = rt.ReadTransact(func(rtr fdb.ReadTransaction) (interface{}, error) { sm.pushRange(idx, rtr.GetRange(r, ro).GetSliceOrPanic()) return nil, nil }) if e != nil { panic(e) } case strings.HasPrefix(op, "CLEAR_RANGE"): var er fdb.ExactRange switch op[11:] { case "_STARTS_WITH": er = sm.popPrefixRange() case "": er = sm.popKeyRange() } sm.executeMutation(t, func(tr fdb.Transaction) (interface{}, error) { tr.ClearRange(er) return nil, nil }, isDB, idx) case op == "TUPLE_PACK": var t tuple.Tuple count := sm.waitAndPop().item.(int64) for i := 0; i < int(count); i++ { t = append(t, sm.waitAndPop().item) } sm.store(idx, []byte(t.Pack())) case op == "TUPLE_UNPACK": t, e := tuple.Unpack(fdb.Key(sm.waitAndPop().item.([]byte))) if e != nil { panic(e) } for _, el := range t { sm.store(idx, []byte(tuple.Tuple{el}.Pack())) } case op == "TUPLE_RANGE": var t tuple.Tuple count := sm.waitAndPop().item.(int64) for i := 0; i < int(count); i++ { t = append(t, sm.waitAndPop().item) } bk, ek := t.FDBRangeKeys() sm.store(idx, []byte(bk.FDBKey())) sm.store(idx, []byte(ek.FDBKey())) case op == "START_THREAD": newsm := newStackMachine(sm.waitAndPop().item.([]byte), verbose, sm.de) sm.threads.Add(1) go func() { newsm.Run() sm.threads.Done() }() case op == "WAIT_EMPTY": prefix := sm.waitAndPop().item.([]byte) er, e := fdb.PrefixRange(prefix) if e != nil { panic(e) } db.Transact(func(tr fdb.Transaction) (interface{}, error) { v := tr.GetRange(er, fdb.RangeOptions{}).GetSliceOrPanic() if len(v) != 0 { panic(fdb.Error{1020}) } return nil, nil }) sm.store(idx, []byte("WAITED_FOR_EMPTY")) case op == "READ_CONFLICT_RANGE": e = sm.tr.AddReadConflictRange(fdb.KeyRange{fdb.Key(sm.waitAndPop().item.([]byte)), fdb.Key(sm.waitAndPop().item.([]byte))}) if e != nil { panic(e) } sm.store(idx, []byte("SET_CONFLICT_RANGE")) case op == "WRITE_CONFLICT_RANGE": e = sm.tr.AddWriteConflictRange(fdb.KeyRange{fdb.Key(sm.waitAndPop().item.([]byte)), fdb.Key(sm.waitAndPop().item.([]byte))}) if e != nil { panic(e) } sm.store(idx, []byte("SET_CONFLICT_RANGE")) case op == "READ_CONFLICT_KEY": e = sm.tr.AddReadConflictKey(fdb.Key(sm.waitAndPop().item.([]byte))) if e != nil { panic(e) } sm.store(idx, []byte("SET_CONFLICT_KEY")) case op == "WRITE_CONFLICT_KEY": e = sm.tr.AddWriteConflictKey(fdb.Key(sm.waitAndPop().item.([]byte))) if e != nil { panic(e) } sm.store(idx, []byte("SET_CONFLICT_KEY")) case op == "ATOMIC_OP": opname := strings.Replace(strings.Title(strings.Replace(strings.ToLower(sm.waitAndPop().item.(string)), "_", " ", -1)), " ", "", -1) key := fdb.Key(sm.waitAndPop().item.([]byte)) value := sm.waitAndPop().item.([]byte) sm.executeMutation(t, func(tr fdb.Transaction) (interface{}, error) { reflect.ValueOf(tr).MethodByName(opname).Call([]reflect.Value{reflect.ValueOf(key), reflect.ValueOf(value)}) return nil, nil }, isDB, idx) case op == "DISABLE_WRITE_CONFLICT": sm.tr.Options().SetNextWriteNoWriteConflictRange() case op == "CANCEL": sm.tr.Cancel() case op == "UNIT_TESTS": db.Options().SetLocationCacheSize(100001) db.Options().SetMaxWatches(10001) tr, e := db.CreateTransaction() if e != nil { panic(e) } tr.Options().SetPrioritySystemImmediate() tr.Options().SetPriorityBatch() tr.Options().SetCausalReadRisky() tr.Options().SetCausalWriteRisky() tr.Options().SetReadYourWritesDisable() tr.Options().SetReadAheadDisable() tr.Options().SetReadSystemKeys() tr.Options().SetAccessSystemKeys() tr.Options().SetDurabilityDevNullIsWebScale() tr.Options().SetTimeout(1000) tr.Options().SetRetryLimit(5) tr.Options().SetMaxRetryDelay(100) tr.Get(fdb.Key("\xff")).MustGet() tr.Commit().MustGet() sm.testWatches() sm.testLocality() case strings.HasPrefix(op, "DIRECTORY_"): sm.de.processOp(sm, op[10:], isDB, idx, t, rt) default: log.Fatalf("Unhandled operation %s\n", string(inst[0].([]byte))) } if sm.verbose { fmt.Printf(" to [") sm.dumpStack() fmt.Printf(" ]\n\n") } runtime.Gosched() }
func (s subspace) FDBKey() fdb.Key { return fdb.Key(s.b) }
func (sm *StackMachine) testWatches() { tr, e := db.CreateTransaction() if e != nil { panic(e) } tr.Set(fdb.Key("w0"), []byte("0")) tr.Set(fdb.Key("w2"), []byte("2")) tr.Set(fdb.Key("w3"), []byte("3")) tr.Commit().MustGet() var watches [4]fdb.FutureNil watches[0] = tr.Watch(fdb.Key("w0")) watches[1] = tr.Watch(fdb.Key("w1")) watches[2] = tr.Watch(fdb.Key("w2")) watches[3] = tr.Watch(fdb.Key("w3")) time.Sleep(1 * time.Second) for _, watch := range watches { if watch.IsReady() { panic("Watch should not be ready (1)") } } tr.Set(fdb.Key("w0"), []byte("0")) tr.Clear(fdb.Key("w1")) tr.Commit().MustGet() time.Sleep(5 * time.Second) for _, watch := range watches { if watch.IsReady() { panic("Watch should not be ready (2)") } } tr.Set(fdb.Key("w0"), []byte("a")) tr.Set(fdb.Key("w1"), []byte("b")) tr.Clear(fdb.Key("w2")) tr.BitXor(fdb.Key("w3"), []byte("\xff\xff")) tr.Commit().MustGet() for _, watch := range watches { watch.MustGet() } }
func (sm *StackMachine) processInst(idx int, inst tuple.Tuple) { defer func() { if r := recover(); r != nil { switch r := r.(type) { case fdb.Error: sm.store(idx, tuple.Tuple{[]byte("ERROR"), []byte(fmt.Sprintf("%d", int(r)))}.Pack()) default: panic(r) } } }() var e error op := string(inst[0].([]byte)) if sm.verbose { fmt.Printf("Instruction is %s (%v)\n", op, sm.prefix) fmt.Printf("Stack from [") sm.dumpStack() fmt.Printf(" ]\n") } var obj interface{} switch { case strings.HasSuffix(op, "_SNAPSHOT"): obj = sm.tr.Snapshot() op = op[:len(op)-9] case strings.HasSuffix(op, "_DATABASE"): obj = db op = op[:len(op)-9] default: obj = sm.tr } switch string(op) { case "PUSH": sm.store(idx, inst[1]) case "DUP": entry := sm.stack[len(sm.stack)-1] sm.store(entry.idx, entry.item) case "EMPTY_STACK": sm.stack = []stackEntry{} sm.stack = make([]stackEntry, 0) case "SWAP": idx := sm.waitAndPop().item.(int64) sm.stack[len(sm.stack)-1], sm.stack[len(sm.stack)-1-int(idx)] = sm.stack[len(sm.stack)-1-int(idx)], sm.stack[len(sm.stack)-1] case "POP": sm.stack = sm.stack[:len(sm.stack)-1] case "SUB": sm.store(idx, sm.waitAndPop().item.(int64)-sm.waitAndPop().item.(int64)) case "NEW_TRANSACTION": sm.tr, e = db.CreateTransaction() if e != nil { panic(e) } case "ON_ERROR": sm.store(idx, sm.tr.OnError(fdb.Error(int(sm.waitAndPop().item.(int64))))) case "GET_READ_VERSION": sm.lastVersion = obj.(fdb.ReadTransaction).GetReadVersion().GetOrPanic() sm.store(idx, []byte("GOT_READ_VERSION")) case "SET": switch o := obj.(type) { case fdb.Database: e = o.Set(fdb.Key(sm.waitAndPop().item.([]byte)), sm.waitAndPop().item.([]byte)) if e != nil { panic(e) } sm.store(idx, []byte("RESULT_NOT_PRESENT")) case fdb.Transaction: o.Set(fdb.Key(sm.waitAndPop().item.([]byte)), sm.waitAndPop().item.([]byte)) } case "LOG_STACK": prefix := sm.waitAndPop().item.([]byte) for i := len(sm.stack) - 1; i >= 0; i-- { if i%100 == 0 { sm.tr.Commit().GetOrPanic() } el := sm.waitAndPop() var keyt tuple.Tuple keyt = append(keyt, int64(i)) keyt = append(keyt, int64(el.idx)) pk := append(prefix, keyt.Pack()...) var valt tuple.Tuple valt = append(valt, el.item) pv := valt.Pack() vl := 40000 if len(pv) < vl { vl = len(pv) } sm.tr.Set(fdb.Key(pk), pv[:vl]) } sm.tr.Commit().GetOrPanic() case "GET": switch o := obj.(type) { case fdb.Database: v, e := db.Get(fdb.Key(sm.waitAndPop().item.([]byte))) if e != nil { panic(e) } if v != nil { sm.store(idx, v) } else { sm.store(idx, []byte("RESULT_NOT_PRESENT")) } case fdb.ReadTransaction: sm.store(idx, o.Get(fdb.Key(sm.waitAndPop().item.([]byte)))) } case "COMMIT": sm.store(idx, sm.tr.Commit()) case "RESET": sm.tr.Reset() case "CLEAR": switch o := obj.(type) { case fdb.Database: e := db.Clear(fdb.Key(sm.waitAndPop().item.([]byte))) if e != nil { panic(e) } sm.store(idx, []byte("RESULT_NOT_PRESENT")) case fdb.Transaction: o.Clear(fdb.Key(sm.waitAndPop().item.([]byte))) } case "SET_READ_VERSION": sm.tr.SetReadVersion(sm.lastVersion) case "WAIT_FUTURE": entry := sm.waitAndPop() sm.store(entry.idx, entry.item) case "GET_COMMITTED_VERSION": sm.lastVersion, e = sm.tr.GetCommittedVersion() if e != nil { panic(e) } sm.store(idx, []byte("GOT_COMMITTED_VERSION")) case "GET_KEY": sel := fdb.KeySelector{fdb.Key(sm.waitAndPop().item.([]byte)), int64ToBool(sm.waitAndPop().item.(int64)), int(sm.waitAndPop().item.(int64))} switch o := obj.(type) { case fdb.Database: v, e := o.GetKey(sel) if e != nil { panic(e) } sm.store(idx, []byte(v)) case fdb.ReadTransaction: sm.store(idx, o.GetKey(sel)) } case "GET_RANGE": begin := fdb.Key(sm.waitAndPop().item.([]byte)) end := fdb.Key(sm.waitAndPop().item.([]byte)) var limit int switch l := sm.waitAndPop().item.(type) { case int64: limit = int(l) } reverse := int64ToBool(sm.waitAndPop().item.(int64)) mode := sm.waitAndPop().item.(int64) switch o := obj.(type) { case fdb.Database: v, e := db.GetRange(fdb.KeyRange{begin, end}, fdb.RangeOptions{Limit: int(limit), Reverse: reverse, Mode: fdb.StreamingMode(mode + 1)}) if e != nil { panic(e) } sm.pushRange(idx, v) case fdb.ReadTransaction: sm.pushRange(idx, o.GetRange(fdb.KeyRange{begin, end}, fdb.RangeOptions{Limit: int(limit), Reverse: reverse, Mode: fdb.StreamingMode(mode + 1)}).GetSliceOrPanic()) } case "CLEAR_RANGE": switch o := obj.(type) { case fdb.Database: e := o.ClearRange(fdb.KeyRange{fdb.Key(sm.waitAndPop().item.([]byte)), fdb.Key(sm.waitAndPop().item.([]byte))}) if e != nil { panic(e) } sm.store(idx, []byte("RESULT_NOT_PRESENT")) case fdb.Transaction: o.ClearRange(fdb.KeyRange{fdb.Key(sm.waitAndPop().item.([]byte)), fdb.Key(sm.waitAndPop().item.([]byte))}) } case "GET_RANGE_STARTS_WITH": prefix := sm.waitAndPop().item.([]byte) var limit int switch l := sm.waitAndPop().item.(type) { case int64: limit = int(l) } reverse := int64ToBool(sm.waitAndPop().item.(int64)) mode := sm.waitAndPop().item.(int64) er, e := fdb.PrefixRange(prefix) if e != nil { panic(e) } switch o := obj.(type) { case fdb.Database: v, e := db.GetRange(er, fdb.RangeOptions{Limit: int(limit), Reverse: reverse, Mode: fdb.StreamingMode(mode + 1)}) if e != nil { panic(e) } sm.pushRange(idx, v) case fdb.ReadTransaction: sm.pushRange(idx, o.GetRange(er, fdb.RangeOptions{Limit: int(limit), Reverse: reverse, Mode: fdb.StreamingMode(mode + 1)}).GetSliceOrPanic()) } case "GET_RANGE_SELECTOR": begin := fdb.KeySelector{Key: fdb.Key(sm.waitAndPop().item.([]byte)), OrEqual: int64ToBool(sm.waitAndPop().item.(int64)), Offset: int(sm.waitAndPop().item.(int64))} end := fdb.KeySelector{Key: fdb.Key(sm.waitAndPop().item.([]byte)), OrEqual: int64ToBool(sm.waitAndPop().item.(int64)), Offset: int(sm.waitAndPop().item.(int64))} var limit int switch l := sm.waitAndPop().item.(type) { case int64: limit = int(l) } reverse := int64ToBool(sm.waitAndPop().item.(int64)) mode := sm.waitAndPop().item.(int64) switch o := obj.(type) { case fdb.Database: v, e := db.GetRange(fdb.SelectorRange{begin, end}, fdb.RangeOptions{Limit: int(limit), Reverse: reverse, Mode: fdb.StreamingMode(mode + 1)}) if e != nil { panic(e) } sm.pushRange(idx, v) case fdb.ReadTransaction: sm.pushRange(idx, o.GetRange(fdb.SelectorRange{begin, end}, fdb.RangeOptions{Limit: int(limit), Reverse: reverse, Mode: fdb.StreamingMode(mode + 1)}).GetSliceOrPanic()) } case "CLEAR_RANGE_STARTS_WITH": prefix := sm.waitAndPop().item.([]byte) er, e := fdb.PrefixRange(prefix) if e != nil { panic(e) } switch o := obj.(type) { case fdb.Database: e := o.ClearRange(er) if e != nil { panic(e) } sm.store(idx, []byte("RESULT_NOT_PRESENT")) case fdb.Transaction: o.ClearRange(er) } case "TUPLE_PACK": var t tuple.Tuple count := sm.waitAndPop().item.(int64) for i := 0; i < int(count); i++ { t = append(t, sm.waitAndPop().item) } sm.store(idx, t.Pack()) case "TUPLE_UNPACK": t, e := tuple.Unpack(sm.waitAndPop().item.([]byte)) if e != nil { panic(e) } for _, el := range t { sm.store(idx, tuple.Tuple{el}.Pack()) } case "TUPLE_RANGE": var t tuple.Tuple count := sm.waitAndPop().item.(int64) for i := 0; i < int(count); i++ { t = append(t, sm.waitAndPop().item) } kr := t.Range() sm.store(idx, kr.Begin) sm.store(idx, kr.End) case "START_THREAD": newsm := newStackMachine(sm.waitAndPop().item.([]byte), verbose) sm.threads.Add(1) go func() { newsm.Run() sm.threads.Done() }() case "WAIT_EMPTY": prefix := sm.waitAndPop().item.([]byte) er, e := fdb.PrefixRange(prefix) if e != nil { panic(e) } db.Transact(func(tr fdb.Transaction) (interface{}, error) { v := tr.GetRange(er, fdb.RangeOptions{}).GetSliceOrPanic() if len(v) != 0 { panic(fdb.Error(1020)) } return nil, nil }) sm.store(idx, []byte("WAITED_FOR_EMPTY")) case "READ_CONFLICT_RANGE": e = sm.tr.AddReadConflictRange(fdb.KeyRange{fdb.Key(sm.waitAndPop().item.([]byte)), fdb.Key(sm.waitAndPop().item.([]byte))}) if e != nil { panic(e) } sm.store(idx, []byte("SET_CONFLICT_RANGE")) case "WRITE_CONFLICT_RANGE": e = sm.tr.AddWriteConflictRange(fdb.KeyRange{fdb.Key(sm.waitAndPop().item.([]byte)), fdb.Key(sm.waitAndPop().item.([]byte))}) if e != nil { panic(e) } sm.store(idx, []byte("SET_CONFLICT_RANGE")) case "READ_CONFLICT_KEY": e = sm.tr.AddReadConflictKey(fdb.Key(sm.waitAndPop().item.([]byte))) if e != nil { panic(e) } sm.store(idx, []byte("SET_CONFLICT_KEY")) case "WRITE_CONFLICT_KEY": e = sm.tr.AddWriteConflictKey(fdb.Key(sm.waitAndPop().item.([]byte))) if e != nil { panic(e) } sm.store(idx, []byte("SET_CONFLICT_KEY")) case "ATOMIC_OP": opname := strings.Title(strings.ToLower(string(sm.waitAndPop().item.([]byte)))) key := fdb.Key(sm.waitAndPop().item.([]byte)) value := sm.waitAndPop().item.([]byte) reflect.ValueOf(obj).MethodByName(opname).Call([]reflect.Value{reflect.ValueOf(key), reflect.ValueOf(value)}) switch obj.(type) { case fdb.Database: sm.store(idx, []byte("RESULT_NOT_PRESENT")) } case "DISABLE_WRITE_CONFLICT": sm.tr.Options().SetNextWriteNoWriteConflictRange() case "CANCEL": sm.tr.Cancel() case "UNIT_TESTS": default: log.Fatalf("Unhandled operation %s\n", string(inst[0].([]byte))) } if sm.verbose { fmt.Printf(" to [") sm.dumpStack() fmt.Printf(" ]\n\n") } runtime.Gosched() }