Sorry, I stopped reading at 10K rps and p50 of 5ms. In this day and age these numbers are pretty bad, especially for a cache where presumably all accesses are constant time. Every single caching solution listed out-performs this handily.
Writing a very fast cache service with millions of entries in Go
41–50 of 92 posts
Re: Writing a very fast cache service with millions of entries in Go
#42"basically everything in Go is built on pointers: structs, slices, even fixed arrays" As I understand it while that does apply to pointer-to-struct and slices and probably strings, that isn't true for naked structs and naked arrays. Those both behave as value types like int.
Re: Writing a very fast cache service with millions of entries in Go
#43So essentially, to meet their requirements, they had to work around the Go garbage collector and use a non-standard HTTP server and JSON parser. Why not just write it in C++?
Correction: they thought they had to. I suspect that an in-process solution would avoid the HTTP and JSON issues, and better implementation of the store itself would avoid GC issues.
Re: Writing a very fast cache service with millions of entries in Go
#44Earlier quoted context omitted.
Maybe this type of program is better suited for a language like Rust. However, while not having a GC, you have to take care of all memory issues in a way that you convince the compiler that your won't ever blow up. It would be very interesting to have such a comparison, so we could see whether it's easier to work around the GC, or easier to write bullet-proof code with manual memory management. I'd expect the Rust to…
I'm not sure how the current implementation of Go handles it, but its spiritual relatives Modula-3/Oberon handled this quite well, with a GC for most occasions and ways to bypass this with "unsafe" modules that allowed for untracked allocations/deallocations and pointer arithmetic. It's not really an either/or situation by (language) definition...
Although from all mainstream languages, C# is probably the closest in spirit to Modula-3.
Re: Writing a very fast cache service with millions of entries in Go
#45Earlier quoted context omitted.
Correction: they thought they had to. I suspect that an in-process solution would avoid the HTTP and JSON issues, and better implementation of the store itself would avoid GC issues.
Can you expand? Not knowing anything about Go, what's an 'in-process solution'?
The reason why Go doesn't (currently?) have a super-performant HTTP server and a super-performant JSON library is that the designers probably didn't envision someone using the language like this.
Re: Writing a very fast cache service with millions of entries in Go
#46I wonder if I'm missing the point here, but why are Redis or Memcached—implementations of exactly this service which are battle-tested and well-used—not suitable due to "additional time needed on the network", but this service is suitable? Is it just down to the requirement for a HTTP API? One thing I've noticed is an extreme demand for making internal services available over HTTP. It has it's benefits, but the obvio…
I had to read the relevant sections three times to sort this out. I believe what they are saying is that this service they made had to speak HTTP in some specific way, and since Redis doesn't speak that way directly they would have needed to proxy requests in their service to Redis which would mean one network hop to reach their HTTP service plus one hop to reach Redis. Of course, Redis and Memcached support Unix dom…
https://github.com/openresty/memc-nginx-module
https://github.com/openresty/redis2-nginx-module
This has the added benefit of being able to take advantage of other nginx features. For example, you could set different permissions on `set` and `get` routes.
Re: Writing a very fast cache service with millions of entries in Go
#47Since you obviously ran some quick benchmarks and concluded that running it locally over a unix socket (confused why you would mention "time needed on the network"... you tested with local sockets, right?) was too slow, you should at least let Antirez know you've run into a new mysterious performance bug ;) Writing a cache service can be a fun side project, but I doubt you gained anything by doing so except another homegrown part to maintain.
Re: Writing a very fast cache service with millions of entries in Go
#48I still don’t get why people try writing their own data store, especially in a language that's simply not very well suited to that task (and we're an almost 100% Golang shop here). Seems to be a rite of passage. The requirements are literally Public service announcement: Don't write your own data store. Repeat after me: Don't write your own data store, except if you want to experimentally find out how to build data s…
Re: Writing a very fast cache service with millions of entries in Go
#49Earlier quoted context omitted.
Can you expand? Not knowing anything about Go, what's an 'in-process solution'?
One of the points of Go seems to have been the idea of making a Go process a lot like a mini-Unix, with many small programs plumbed together and communicating through pipes, only strong-typed ones (and often by passing ownership of data using pointers, so that you don't have to actually copy stuff). Which uses zero HTTP and JSON traffic, which is faster than even the fastest HTTP and JSON library could ever be. The r…
In this case, while that is a microbenchmark, it's a relevant one; both things are basically measuring "what's the minimal cost for a web request"? I could still quibble around the question of routing, but generally "within 2x of nginx" is good enough for most uses, and generally, for any non-trivial use of either nginx or Go's web server, you're going to dominate the cost of the HTTP request with your processing. (Considering how nasty the inside of nginx is and how nice the Go HTTP server looks, that's actually surprisingly good performance. And I don't mean that "nasty" as a criticism; it is what it is for good reason.)
(That said, if my back was against the wall performance-wise, I'd seriously consider looking at my incoming requests, seeing if there's a strong enough pattern in what's going on, and writing myself a psuedo-HTTP server that isn't actually an HTTP server, but just looks like one, skipping as much parsing as I can on the way in, and emitting a lot more hard-coded stuff as headers on the way out. I've never had to do this yet, but it's an option I'm keeping in my pocket.)
As for JSON, well, people generally conflate "parsing" and "marshalling" with JSON. JSON parsing is so drop-dead easy that one skilled in the art can write a decent parser in just a day or two; it's a good format that way. However, the task of converting a parsed JSON representation into local data types is actually surprisingly subtle, and any mature language will almost certainly have at least two if not more JSON marshallers that work in fundamentally different ways.
There will generally be at least one built for raw parsing speed, but will always hand you back a very generic data structure that has none of your application-specific types in it. There will be one built for really, really convenient marshalling and unmarshalling of your application-specific types, but it'll probably be significantly slower, and make certain decisions that will mean it can't be used safely on arbitrary JSON... i.e., if there's something that may be a string but may be an object, this library will range from inconvenient to impossible to use. And there are other valid cost/benefit points; the JSON marshaller that loads the JSON into memory and deparses it with nearly-0 additional overhead by re-using the original byte buffer intelligently, the JSON marshaller that can build up specialized parsers with code generation at compile time even if you're in a dynamic language, the JSON parser that for better or worse permits a certain amount of sloppiness to deal with sloppy emitters, etc.
Go's default encoding/json is the one built for convenience of application-specific types that can't handle or emit arbitrary JSON easily. As a sideline it can also do the generic raw parsing if you pass it the correct type to start with, but I believe it's paying some overhead vs. something custom written for that. I'm pretty sure they know they made this choice; all that stuff I described was pretty clear by the time Go was being written, that it is basically impossible to write the JSON library, so you might as well choose which one you're looking to ship. I think for a standard library it was the right choice, because it's a solid middle-of-the-ground choice... most JSON can be marshalled by it, because most JSON is still well-enough behaved for that to work. It's mostly good enough for most uses. But if you need the ultimate speed, or the ultimate flexibility, or the ultimate anything-else, you'll need to pick something else.
Re: Writing a very fast cache service with millions of entries in Go
#50Earlier quoted context omitted.
Can you expand? Not knowing anything about Go, what's an 'in-process solution'?
One of the points of Go seems to have been the idea of making a Go process a lot like a mini-Unix, with many small programs plumbed together and communicating through pipes, only strong-typed ones (and often by passing ownership of data using pointers, so that you don't have to actually copy stuff). Which uses zero HTTP and JSON traffic, which is faster than even the fastest HTTP and JSON library could ever be. The r…
"is that the designers probably didn't envision someone using the language like this"
They didn't envisage that the HTTP server would be used to, you know, serve HTTP requests?