I want to increment and decrement int counter value using atomic operations. I am confused on how to use the add method in GO Lang. Can someone share an example? I am looking for something similar to Cassandra counter.
This was asked before, but the answer unfortunately ended up on on a github issue instead of on the forums, so let me reproduce it here for search ability:
But to answer your question, the short answer is to use “encoding/binary” to encode a -1 (in little endian two’s compliment). Adding a negative 1 is then the same as a 1. In two’s complement, -1 is entirely 0xff bytes, so you can hard code that in, or you could do something like this:
func incrKey(tor fdb.Transactor, k fdb.Key) error { _, e := tor.Transact(func (tr fdb.Transaction) (interface{}, error) { buf := new(bytes.Buffer) err := binary.Write(buf, binary.LittleEndian, int64(1)) if err != nil { return nil, err } one := buf.Bytes() tr.Add(k, one) return nil, nil }) return e } func decrKey(tor fdb.Transactor, k fdb.Key) error { _, e := tor.Transact(func (tr fdb.Transaction) (interface{}, error) { buf := new(bytes.Buffer) err := binary.Write(buf, binary.LittleEndian, int64(-1)) if err != nil { return nil, err } negativeOne := buf.Bytes() tr.Add(k, negativeOne) return nil, nil }) return e }
Those two samples are very similar, but in the first, we increment the key by 1, and in the second we decrement by 1 (i.e., increment by -1). If we looked at the bytes, we would see that the first is a 1 byte followed by 7 0 bytes, and then the second is 8 0xff bytes.
Here’s a sample that reads the given key, as well as a little tester than tries to read the key and do some atomic updates:
func getKey(tor fdb.Transactor, k fdb.Key) (int64, error) { val, e := tor.Transact(func (tr fdb.Transaction) (interface{}, error) { return tr.Get(k).Get() }) if e != nil { return 0, e } if val == nil { return 0, nil } byteVal := val.([]byte) var numVal int64 readE := binary.Read(bytes.NewReader(byteVal), binary.LittleEndian, &numVal) if readE != nil { return 0, readE } else { return numVal, nil } } func run(db fdb.Database) { var t tuple.Tuple t = []tuple.TupleElement{"foo"} var key fdb.Key key = t.Pack() db.Transact(func (tr fdb.Transaction) (interface{}, error) { tr.Clear(key) return nil, nil }) v1, _ := getKey(db, key) fmt.Printf("v1 = %d\n", v1) incrKey(db, key) v2, _ := getKey(db, key) fmt.Printf("v2 = %d\n", v2) decrKey(db, key) v3, _ := getKey(db, key) fmt.Printf("v3 = %d\n", v3) incrKey(db, key) v4, _ := getKey(db, key) fmt.Printf("v1 = %d\n", v4) incrKey(db, key) v5, _ := getKey(db, key) fmt.Printf("v2 = %d\n", v5) decrKey(db, key) }
If I run this (passing a database for my local instance), I get:
v1 = 0 v2 = 1 v3 = 0 v1 = 1 v2 = 2
which is the desired behavior.
Thank you. I highly appreciate. Wow. For someone (me) new to Go and FoundationDB this looks super complicated. In Cassandra I have to call update with +1 or -1.
@alexmiller The more I learn about Go
, more respect and appreciation I have for your answer. Thank you _/\_