Writing an Object .... and other stuff


#1

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


#2

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,


#3

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


#4

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.


#5

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.


#6

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.


#7
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.


#8

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).


#9

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


#10

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.