Beispiel #1
0
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
}
Beispiel #2
0
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.")
			}
		}
	}
}
Beispiel #3
0
// 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)}
}
Beispiel #4
0
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
}
Beispiel #5
0
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
}
Beispiel #6
0
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
}
Beispiel #7
0
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()
}
Beispiel #8
0
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}
}
Beispiel #9
0
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
}
Beispiel #10
0
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
}
Beispiel #11
0
func (s subspace) Pack(t tuple.Tuple) fdb.Key {
	return fdb.Key(concat(s.b, t.Pack()...))
}
Beispiel #12
0
func (s subspace) FDBRangeKeys() (fdb.KeyConvertible, fdb.KeyConvertible) {
	return fdb.Key(concat(s.b, 0x00)), fdb.Key(concat(s.b, 0xFF))
}
Beispiel #13
0
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)
	}
}
Beispiel #14
0
func (sm *StackMachine) popKeyRange() fdb.KeyRange {
	kr := fdb.KeyRange{fdb.Key(sm.waitAndPop().item.([]byte)), fdb.Key(sm.waitAndPop().item.([]byte))}
	return kr
}
Beispiel #15
0
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
}
Beispiel #16
0
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()
}
Beispiel #17
0
func (s subspace) FDBKey() fdb.Key {
	return fdb.Key(s.b)
}
Beispiel #18
0
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()
	}
}
Beispiel #19
0
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()
}