Writing an Object .... and other stuff

Hi,

i’m fairly new to Go in general and Aerospike as well. Coming from PHP (Symfony2) and MySQL i’m so far quite pleased how everything is working out, although i have to code a lot of “little helpers” myself, which i’m fine with, since i mostly care about the result.

Ive tested several approaches to writing an object, the two described here (Write Multiple Values, Write An Object) and a different approach i’ll describe in a few seconds.

type User struct {
Uid int64
Nickname string
Password []byte
Email string
}

With the first approach the functions takes ~10-12ms to complete, with the second one ~9-12. With my approach it takes ~6-9ms, but i think it could be improved a lot.

I basically have:

user := new(models.User)
go user.SetUid(someid,key)
go user.SetNickname(somenick,key)
go user.SetPassword(somepassword,key)
go user.SetEmail(someemail,key)

My problem is: in each of those custom functions i open a new client and close it again. So each of those goroutines is opening a new client … not the best approach i fear. Is there a way i can use 1 client for all functions?

Any help would be much appreciated.

Regards

What you’re doing is not recommended. You’re allocating a stack for each goroutine, and exhausting memory/CPU for each goroutine. Each of the first or second approaches should take well under a millisecond (a few µs at most) to complete per operation.

You should also allocate one client per app, and pass it to your methods (or better still, share it as a public var in a package). The Go client is designed to be thread-safe and goroutine friendly, so you should not worry about using the same client object for everything.

Cheers,

Hi,

yeah, that’s what i guessed. Thanks for the answer! I will take a look at how to pass the client as a global variable. Under a millisecond is what i thought … gonna have to take a good look why it’s taking so long. Running Ubuntu, namespace is persisted on SSD and memory. (I’m using Revel btw)

On a side-note since i find the query with filters stuff really annoying: is there a way i can query for a key with binvalue = X, if i know it’s only 1 key? So i don’t have to go with the for-loop.

Regards

If you mean the value can map to the key, then use that value as the key. Otherwise you can use secondary indexes and easily put the whole filtering logic in a function so that it takes the logic out of your code.

Yeah, i’m using http://www.aerospike.com/docs/client/go/usage/query/query.html. The only issue i have with this is: the for-loop takes 1.5-2.Xms and it’s only 1 record. Don’t know, if that’s normal or not. The queries themselves are fast, as you mentioned before.

Could you elaborate a bit further on the slow for-loop please? 2ms on a modern machine is an eternity, and should not happen for trivial loops.

stmt := as.NewStatement("users", "user", "Uid")
stmt.Addfilter(as.NewEqualFilter("Email", Email))

rs, _ := app.DB.Query(nil, stmt)
var uid int64
start := time.Now()
for res := range rs.Results() {
	uid = int64(res.Record.Bins["Uid"].(int))
}
fmt.Println(time.Since(start))

Thats the loop … and it’s only 1 record.

That’s typical, since Aerospike queries are designed to return more than just one record.

There’s some overhead on server-side to schedule a query, and run it while maintaining high key-value TPS (you may not have the high TPS, but the scheduling will still happen).

Fair enough, is there a better way to query for just 1 record based on a bin?

There isn’t, unfortunately. Key-Value stores behave differently from the RDBMS. The only workaround - other than querying - would be to have a different set with the key set to the field you want to find, and keeping these two sets in sync.