We switched to Node.js: the good and the not so good
blog.superfeedr.com
blog.superfeedr.com
That's not strictly true - a "while (true)" will lock up a Node process as far as I can tell. I think a more accurate way of stating it would be that "Javascript API's and libraries tend to be written with asynchronous use in mind", with lots of callbacks.
If you want something that's async at a deeper level, Erlang is worth a look.
It's not impossible to wedge Erlang, of course, it's just not quite so easy.
I'm running out of polite ways to say this, but Node advocates really need to learn about other ways of doing things before advocating so confidently that Node's way is better. Cooperative multithreading does not have the reasoning advantage, which is one of the reasons why it has been abandoned for so long at the OS level. It's incredibly harder to work with that preemptive multithreading sorts of things, combined with other techniques that have been developed over the decades.
(Also, I said "Node advocates" and not StavrosK or "you" specifically; I mean that more generally than just your post here.)
The "reasoning" I was referring to was more that I know that simple things like incrementing a global counter is guaranteed to be atomic. I agree with you that that's still a shared structure, and thus dirty, so I avoid it anyway.
Now that you mention it, I don't actually know why I said that before, since I never write production code like that. I guess I was referring to ad-hoc scripts, where I like knowing that I don't need a lock to guarantee atomic operations, and thus they're a bit easier to write.
To block a whole node'd have to perhaps write a native functions a (NIF) and do it there. Which is also a reason to be careful with NIFs, they can kill predictable latency under load, while initially in serial benchmarks they could show a performance improvement.
It is funny you'd ask though, I just read this post yesterday:
How Erlang Does Scheduling :
http://jlouisramblings.blogspot.dk/2013/01/how-erlang-does-s...
I believe the answer is no - the async nature is provided by V8 and the core Node libraries.
My experience with the Node community:
- Great people (Substack!)
- Great attitude #node.js/freenode
- However, many hours spent on solving bugs in existing libraries.
My experience with maintaining production Node code:
- It may be more maintainable than EventMachine, but it's absolutely not more maintainable than Ruby.
- V8 garbage collection is a real pain when you do work that needs it (this also includes memory held by open sockets).
- Was V8 built for the server? (rhetorical)
In the end, I prefer threaded Ruby code to evented Node code. I try to offset the inefficiencies of "threads vs evented" or "Ruby vs Node" by using the JVM and JRuby.
When I said 'Java' I really meant JVM. Specifically I'm using Scala.
Regardless, I have a different Clojure based project, with it as well, I prefer doing what clojure has to offer in terms of concurrency (pmap, etc).
I have dipped a toe in Go. Loved the fact that you get a compiled binary, ecosystem still feels skinny. Ended up concluding that in a year or two it'll be worth revisiting.
What does Node give you that a Scala async framework like Play or Scalatra doesn't?
Also what does Ruby give you that Scala doesn't?
Just wondering why do you choose to use 3 stacks (Ruby, Scala, Node) when one could be enough.
Like superfeeder, I have "backend" services, but I also have client-facing services.
I value how Node.js handles slow clients. It also services some more 'utility' use cases for me, such as reverse proxies, etc.
JRuby and Scala cover IO bound processing for me over the Web.
JRuby covers the majority of the backend services.
I use Scala coupled with Storm, and I could have used JRuby here too, but you can also use JRuby with Hadoop and you typically don't. Since this use case actually required the optimization (I wasn't prematurely optimizing), I went as bare metal as possible (ruled Java out, yes). Previously, this service was a Node.js service and got rewritten into Scala+Storm.
I don't use Akka because I didn't feel it was needed yet. Old school threaded workers with JRuby works fine so far.
I know that Scala is supposed to be a multi-layered solution and it can handle all of this.
However, Ruby and Node brings the ecosystem Scala doesn't have (I'm not moved by the "but Java has a million Jars out there" argument, already integrating with them with JRuby).
And in general Ruby makes me happy (SBT makes me very very angry and sad, for comparison - yet Scala is OK), that simple.
If you are using scala, which async framework are you using - I have heard it is basically scalatra vs spray.io
Simply because JRuby has real threads over MRI's GIL, and the JVM has a time-proven Server VM that has a very good JIT. I also leave open the option to drop to 'bare' Java. As the OP mentioned, it's a craft of balancing CPU and memory. In my case I don't mind the extra threads memory and context switching overhead.
> If you are using scala, which async framework are you using - I have heard it is basically scalatra vs spray.io
My Scala use case is with Storm, as mentioned in other reply. You can call that "backend" processing, instead of using it with a Web framework.
They rewrote the entire codebase and obtained for just a 25% gain? It doesn't sound like they are very happy to be coding in javascript now either.
Everyone: please don't rewrite your code, it's almost never worth it. The one exception is rewriting a core part of an algorithm in C for speed (the last 10x speedup).
Also, C is not the only language worth rewriting in.
I would really like to see someone write a node.js and Go back-end for the same front-end and do a head to head comparison.
As someone who spent a fair amount of time rewriting node.js prototypes in Go, I'm probably biased. I feel like javascript is a much less maintainable language. Perhaps is was the original node.js implementations (I don't think it was), but the Go versions were always faster, used less memory and IMHO were more readable.
I've got a small but growing node/socket.io app I figured I'd have to one day rewrite in go if I really wanted it to scale.
* Use Go tip; You can grab a snapshot review all the open issues for that snapshot (most are enhancements).
* Like any re-factor doing it sooner as opposed to later is less work :)
* Do some "from scratch" Go projects before doing re-factor projects to get your legs under you (if they are not already there)
* Write Go in Go, not C/Python/Java in Go. This is harder than you think when you get started, but, if you ask for help and people tell you you are fighting the system, carefully consider their advice.
* A lot of the Go community likes to use single letter variable names in contexts like receivers, struct state (just look at the stdlib), buck the system, don't do that, use short camel case names. The next guy / you in six months will be glad you did.
* If you have a Java/C++ background you might often write a single threaded version of a daemon and later multithread it later, this is generally an unnecessary step in Go.
* The Go versions really are not much larger (LOC)
* There is lots of useful Go code on github (don't be afraid to try them)
* If you are doing front-endy kind of stuff supplement "net/http" with Gorilla where needed whenever you can rather than rolling your own. ttp://www.gorillatoolkit.org/
* "go tool prof" is a great tool, know how to use it and its top20 / web commands. Even if you don't feel the pain, use it and you will learn what things you do are expensive and it will keep trouble from sneaking up on you.
* If you are using a SQL based store, use a driver that implements the interfaces in "database/sql" rather than providing its own interface. This will make your life very simple if you need to migrate between mySQL <-> Postgre, etc
* LiteIDE is a nice lean cross platform Go IDE that includes syntax highlighing, autocomplete (with gocode) and debugging support. The only think I had to do was write my own syntax highlighting theme, based on Solarized, because I thought the included ones were gross.
It sounds a bit like you guys rewrote it because node.js is hot and you then stuck with the rewrite because of sunk costs. Or am I imagining things?
Thanks for sharing your experiences with us. I'm just asking those questions because I want to learn more about them.
The article might as well be written as "we refactored our code and got a nice speed boost".
Most of node modules are still in active development. As I've stated in the blog post, that's a pro and con, but we estimated that the pro was greater than the con :)
Couldn't you rewrite for ease the maintenance in ruby?
What interested me most is the community part. I've always thought ruby/rails community is awesome. Maybe I should start learning some JavaScript now.
- js / node.js is very young, it might fade out of fashion, and in 10 years it is not impossible that good coders in js will be hard to find.
- Readability and simplicity are prominent in this context, and js has a syntax that is less than optimal in this regard.
- Maintenance tooling may be lacking.
If I were to choose a language for a project that I new will be big and will need care in 10 or 20 years, I'd hesitate, and maybe choose Java (which I hate) or Python (which I hate less). If in a risk-things mood, I'd give Go a try.
When you understand the zen of javascript and try to write in a consistent manner, JS is more readable than C. I equate these concerns with arguments about how lisp or scheme is unreadable.
The ecosystem for tooling is expanding, albeit slowly.
However, most of your code probably could be implemented in a way that can be run in browser (e.g. XLS parser: http://niggler.github.com/js-xls/) which is where I see the real value in node. Aligning the languages means fewer moving parts and potential points of failure (as opposed to having to worry about quirks in implementations of many languages and worrying about features supported in one context but not the other)
Also, I've gotta say, you completely undermined all 3 points of your argument by saying you'd choose Go.
"- Readability and simplicity are prominent in this context, and js has a syntax that is less than optimal in this regard."
Go code is almost always very simple and readable. What makes you think him choosing Go undermines the simplicity and readability arguments?
This is a common belief but I do not think it applies very well in most cases. If you write the last pic sharing thing, maybe you can dismiss worries, but if you build the next Google, entreprise or scientific software, or even something like Github, you should hope your baby to be well and alive in 10 years, and choose your tech stack accordingly. I guess.
Remember how Twitter was originally written in Rails, and then it collapsed under its own weight when they tried to scale it? Their business requirements changed, so they rewrote the parts that needed to be rewritten. On the flip side, if they had originally set out to try to build the system that now powers Twitter, not only would they likely have built the wrong thing entirely, they never would have launched.
Software services are evolutionary. You have to always be willing to burn pieces of it down and rewrite them as conditions change around you.
Sure, but changing the tech stack is much harder than rewriting some parts of it. And some setups are easier to adapt than others. Thus choosing the best available option when starting a new project is important, and the likeliness of the technology to be alive and well in ten years should be taken into consideration, among other parameters (your own experience, the current availability of good developers).
Moreover, the "trendiness" of a technology should be counted as a negative factor, because it would tend to be overestimated and cast shadows on potentially better (but less sexy) alternatives.
In that case it's a pretty good achievement, since the rewrite will have a lot more low-hanging fruit regarding performance improvements, than a polished, existing product. I'm used to see rewrites which are at least a little bit worse than previous versions due to all the work done to squeeze everything out of version N-1.
Another reason might be, that their workload is IO-bounded, so faster VM like V8 doesn't help.
Trying to make do with horrible code for long periods of time, suffering through bugs in every change... Until we finally rewrite it. I've never regretted a rewrite, and it has always ended up significantly better than the original (possibly because the horror threshold for rewriting is high, so that's not saying much).
A large, extremely messy code-base can make even trivial changes unsafe, let alone large changes.
The effort required not only to figure out what point B is, but also how to safely reach it from point A is immense. Much much harder than a rewrite.
However, it is a very good idea to meticulously read the bad old code and write down a list of things it handles -- to make sure none of it is missed in the rewrite.
So if you want speed, I'd go for those two out of your list. Go and node.js are still infants in the game, so I don't think there is enough serious software out there built with these to properly judge their effective speeds.
- Google is using it internally, where speed is an absolute requirement
- Vitess, recently open sourced (and used internally by youtube) would definitely have to be fast for the task youtube is using it for. (http://code.google.com/p/vitess/)
- Desktop window manager in Go that is very fast even on lower spec machines: https://github.com/BurntSushi/wingo
Just my two cents. I would absolutely stay away from node.js if you are in an environment where people touching the code aren't easily accessible, since it's very easy to write javascript that only you understand. The other languages seem to punish it a bit more, while at times it feels as if javascript embraces it.
There is serially fast. As in fast if you have a single client but that might not be "fast" when you have 10000 clients anymore. Erlang will make sure your system stays responsive under load.
Any of those languages will probably do that but I feel that Erlang is the one with most tooling and most practical experience behind its back.
Also, don't give up on Python. Python is an excellent language and when you don't need 5 nines or reliability or crazy scalability, a Python gevent or tornado based server might just do the job.
That, and Go seems to be close enough to python with those advantages built in that it becomes an easy choice for me. Combine that with the fact that my problems are mostly solved easily with the inbuilt libraries, and it's a clear solution.
I mean, look at the docs for gevent. In the docs one of the first things they teach is monkey patching: http://www.gevent.org/intro.html#monkey-patching
See I find a lot Twisted, and yield based concurrency frameworks not being Pythonic enough. Threads are just functions that run concurrently. In the case of green threads it is really just as simple as spawn(func) to start a new green thread running function func().
Take a look at these eventlet examples, they are pretty elegant:
http://eventlet.net/doc/examples.html
Yes there is monkey patching. However, that happens once per program at the very top. That is a small price to pay for the ability to use all the Python libraries out there.
Speaking of libraries. That is one good thing about Python. And probably the reason to stick to it -- the large library ecosystem. Of course it depends on the system you are designing but a large enough system will usually need some other libraries (parsing a protocol, using a work queue etc).
Go and Erlang also have plenty, but not nearly the level and breadth that Python has.
However, type checking is what ultimatley won out here along with Go's very C+Python love child feeling.
Haskell manages to combine the conciseness and elegance of good Python code with static safety not found in other mainstream languages. Speed-wise, it ranges from OK to great, depending on how skilled you are at optimizing Haskell. The tools for optimizing Haskell code are pretty great, though.
Go is actually fast, not just fast in theory or fast when speed isn't important, or whatever other weasel words you care to choose. Goroutines and channels give you the scalability advantages of async programming, without leaving behind readable code. Deployment is super simple because it produces a compiled binary.
Go has a solid foundation, including things like real support for integers, real support for threads, a runtime that was really developed for servers, and a well-thought-out type system. Some people have compared it to static duck typiing.
Not to mention, dead simple and clear to people who don't even know Go.
Most of the people writing in JavaScript are not programmers. They lack the training and discipline to write good programs. JavaScript has so much expressive power that they are able to do useful things in it, anyway. This has given JavaScript a reputation of being strictly for the amateurs, that it is not suitable for professional programming. This is simply not the case.
There's definitely a lot to like, and like they said it's still a very new world with a lot of exploration to be done.
* [1] http://wtfjs.com
* [2] https://github.com/jashkenas/coffee-script/wiki/List-of-languages-that-compile-to-JSBut, as much as I like CS and hate (really hate) JS, I have to agree with you wholeheartedly. You must know JavaScript well. Variable/function hoisting won't bite you in CS, but other JS idiocies will.
Read Douglas Crockford's "JavaScript: The Good Parts" at least twice (and take a lot of notes). It's the best no-bullshit book on JavaScript (the language, not "how to use JavaScript in Node/front-end web design") around.
People who only have a PHP background, for instance, have become accustomed to such stupidity. They think it's "normal", solely because they don't really know any better.
Those coming from C, C++, Java, C#, Ruby, Python or most other languages, on the other hand, know that the JavaScript way is not the right way. These people generally have a much harder time coming to terms with JavaScript's numerous issues.
I'm also coming from Obj-C, C and Ruby background and recently read "Javascript: The Good Parts". I'm still looking for the good parts promised in the title and introduction.
I agree; it's a shortcut for people who know JS, not for people who are learning it for the first time. As soon as you need to use or look into the internals of a library that isn't written in CoffeeScript, everything's going to get unstuck pretty quickly.
For many learning types, I'd guess it's preferable to leisurely learn JavaScript from the shores of CoffeeScript. Rather than having to deal with all of JavaScript's absurdity at once.
And when you need to read someone else's JavaScript code, you can usually get away with ignoring boilerplate.
My big beef with js is that there's no 'right' way to lay out your code. Trying to figure out how a js module works is always a unnecessarily massive pita.
In my mind, that alone makes JavaScript a bad language.
There are some trivial gotchas in Javascript, but same can be said for most languages. Most importantly weird type promotion system, variable & function hoisting and prototypal inheritance can cause some gray hairs. All those can be lived with, and prototypes are in some ways superior to "classical" inheritance.
C, C++, Java, Javascript, Lua(JIT), Perl and ObjC are languages I normally use.
I do look forward to switching to NodeJS and I am fairly early adopter... but it's still been too early for me.