11 ms·
Gotchas from Two Years with Node
- davidw 12y ago> At any given time there’s only a single running code block. > But here… there be dragons. That's one of the things Erlang solves pretty well. Sometimes I sort of envision it as that robot dog/mule thing that they keep kicking and it gets back up and keeps going. https://www.youtube.com/watch?v=cNZPRsrwumQ https://www.youtube.com/watch?v=cNZPRsrwumQ if you have never seen it before.
- digitalzombie 12y agoI'm sure once they finish migrating Node to Go, they'll migrate Go to Erlang.
- perishabledave 12y agoSegment.io seems like the type of challenge that would be right in Erlang's sweet spot. The talk that Whatsapp gave on scaling Erlang is a good proof point that it's built to handle these systems quite well.
- moonlighter 12y ago"To further avoid event loop problems entirely, we’ve started switching more of our data processing services to Go". This seems to become a common pattern... start with Node, hit some limits, rewrite microservices in Go.
- dreamdu5t 12y agoThe one problem they use as an example wasn't solved by switching to Go. Go is great but let's be real, parsing and concurrency problems don't just magically go away by switching languages.
- moonlighter 12y agoConcurrency issues don't magically go away by switching to another language, but they have a much better chance of getting cleanly addressed by switching to a language which has explicit/native features as part of its design to help address concurrency issues, via goroutines/channels in this case.
- mietek 12y ago> by switching to a language which has explicit/native features as part of its design to help address concurrency issues …such as Haskell or Erlang.
- eva1984 12y agoJust curious though, I have seem quite a number of posts in the past year mentioned the switch from Node to Go, is there a pattern here?Why Go in particular?
- mhogomchungu 12y ago> Why Go in particular? Its the new shiny thing.
- Kurtz79 12y agoI think go and node are more or less of the same "age" and "shininess"? EDIT: Very much so, they were actually released to the public in 2009.
- s_kilk 12y agoGo appears to have a similar kind of momentum behind it as Node had/has. It's a small and "familiar" language, so devs pick it up easily, and has a healthy open-source package ecosystem. It could also just be random chance. People and communities latch onto things for all sorts of reasons.
- abritishguy 12y agoIt is good at concurrency which makes it attractive to those coming from node.
- deleted 12y ago
- bontoJR 12y agoThe switch from Node to Go seems quite popular right now and it honestly makes me thing there's something wrong with the general perception of Node. We are currently in a world where we have a huge amount of traffic on almost every web app with just a discrete success, but we still make the mistake to pick a technology that seems "good enough", instead to pick a "great one" because looks slightly harder to manage/learn/deploy. I know that during the early stage, pace is very important and RAILS or Node are easier and faster to handle compare to Scala or Erlang, but sometimes another technology at the beginning would save a lot of headaches and night calls. We still fail at the very early stage to choice the right technology, but nobody is afraid to admit it and to switch, I find this amazing.
- galfarragem 12y ago> The switch from Node to Go seems quite popular right now TJ Holowaychuk desertion was a strong hit on Node troops' morale. https://news.ycombinator.com/item?id=7987146 https://news.ycombinator.com/item?id=7987146
- k__ 12y agoWhen I read your post I thought "What? I'm using Koa right now and it's maintained by TJ" but the post says it's the only one haha. Did anything of this change after IO.js was forked?
- Touche 12y agoTJ had already written every module possible in Node so he moved on to rewrite them all again in Go. He'll do the same once he's exhausted all Go modules there are to write. Suffice to say his exit hasn't changed anything, his efforts have been taken over by others and NPM is still growing at a fast rate.
- hayksaakian 12y agoI think it makes sense, early on you're still "figuring it out" so a flexible framework thats easy to dive into is advantageous. I don't see anything wrong with planning a rewrite X months into a product, since 90% of things don't make it to month X.
- klodolph 12y ago> Plenty of times, there will be an uncaught exception which–through no fault of your own–bubbles up and kills the whole process. Really? I'm just a beginner with node.js, and I've been deeply frustrated by error handling, but if this is true, that's pretty damning. In just about every other web framework under the sun, you can go wild with exceptions and the worst you'll get is a 500 response for that request. (Yes, worse behavior is possible but very uncommon.)
- toxicFork 12y agoSame happens with Java, C++, and many other languages. There are web server packages on node that do catch errors that happen in synchronous code. For async, you can use promises, which take errors into consideration. I'm not sure what happens on unhandled async errors on other languages, I guess in Java you could have a dangling request (happened to me before) or it could crash the app (also happened to me before). C++? I haven't really done much webserver work on that one so someone else may be able to write more about it. Also obviously PHP, you just get a white page or actual full error stacks sent to the client, the joy! (edit: of course, if you don't handle it properly)
- deleted 12y ago[deleted]
- onion2k 12y agoThere's nothing in PHP that stops it sending correct HTTP error codes. Incompetent developers might write things that throw blank pages or stack traces, but that's true of any language.
- hearwa 12y agoI've run into issues with php where it always outputs the warning as a table. Recently I was working on an in house project that used the mysqli extension. It's being deprecated for PDO and outputs a warning of this as an html table. I didn't have time to port this to the new library. I had to disable deprecated warnings to get rid of this, but I'm left fearing there might be other warnings somewhere else in the project that will print these tables as well, which naturally fucks with the json deserialization stage client side. A scary thing about this is it disables ALL deprecated warnings, and the warning is no longer even printed in the error log. The fact that there is no way for me to say "don't print errors and warnings in the http response, but instead just put it in the log" is concerning to me. I know this is beating a dead horse but the language really needs standardized errors. Give me exceptions or return values, I don't care. Anything is better than printing the fucking warning in the http response.
- drapper 12y agoSome good stuff there, but I thought "don't do any heavy processing in consumer-facing node.js instance" was part of Node's 101.
- swang 12y agoI wonder why they don't use a stream JSON parser like oboe.js?
- jonpress 12y agoThe root of the eventloop issue is not Node-specific. The real underlying issue here is that one CPU core is given too much work while others are more or less idle. Offloading some of the work to another process naturally solves this issue - It doesn't really matter that this other process is a Go program or a Node.js one - Both approaches would have solved the problem. Attributing credit to Go itself for solving the issue is disingenuous. If you ran Go as a single thread, you would also run into similar issues. The main advantage of Go is that it makes it easier to parallelize your code thanks to goroutines and channels (a single source file can encapsulate the logic of multiple concurrent processes). That said, I find that this 'ease of concurrency' makes Go code less readable. In Node.js, it's really easy to identify process boundaries since the child_process module forces you to put code into different files and communicate via loosely coupled IPC channels. Most of the Node.js vs Go arguments are weak. It's surprising that Node.js is still outpacing Go in popularity in spite of all this slander.
- mjburgess 12y ago> If you ran Go as a single thread, you would also run into similar issues. Yes, if you hobbled yourself, you would be hobbled. The inability to do concurrency properly is not a feature, it's a missing feature. The whole point of node was to be a node.
- Rapzid 12y agoI'm also unsure about this comment. It reads to me as "The main advantage of Go is.. that it makes it really easy to not have this problem." The OP talks about single core being an issue. It is not for Go due to the cooperative goroutine scheduling and function call pre-empting. You can still shoot yourself in the foot, but it's really easy to create an escape hatch by just making a function call or occasionally yielding to the scheduler...
- wpietri 12y ago> really easy to create an escape hatch by just making a function call or occasionally yielding to the scheduler... Having dealt with cooperative multitasking back in the dark ages, I definitely don't believe it is easy. With proper threads, you just write your code in a straightforward manner. With cooperative multitasking, you now have to be continuously imagining performance and sprinkling in otherwise useless calls every time you think something might take a while. When you get that wrong, which will be a fair bit, you have to go back and re-sprinkle. And then when the character of your input changes, you get to re-sprinkle again. I was ok with it in the dark ages; there wasn't an alternative given the hardware of the time. But now? Even watches are multi-core. I want to use languages that make parallelization easy.
- ExpiredLink 12y agoWhy not Java EE? Everything out of the box. Tried, proven, standardized. Well suited for startups: http://www.adam-bien.com/roller/abien/entry/a_java_ee_startup_getting http://www.adam-bien.com/roller/abien/entry/a_java_ee_startu...
- pjmlp 12y agoBecause apparently it is not cool stuff that works instead of shinny toys.
- smegel 12y ago> Everything out of the box. Everything except that special sauce called green threads (or goroutines). They are quite popular these days for good reason - callbacks suck.
- anewhnaccount 12y agoThis comment is quite ironic considering Java originally only had green threads.
- pron 12y agoBlatant self-promotion here, but Java's got fibers (aka lightweight threads, aka goroutines), too: https://github.com/puniverse/quasar https://github.com/puniverse/quasar BTW, I don't like the term green threads because they have a connotation of being scheduled onto a single OS threads, while fibers/lightweight threads employ parallelism.
- zerr 12y agoOr C#/F#/.NET
- deleted 12y ago[deleted]
- fennecfoxen 12y agoAh, yes, Java... the language that doesn't have niceties like hash literals, so the ecosystem adopted XML instead, and you'd use complex difficult-to-debug XML documents to metaprogram your frameworks because it was actually the least-painful way of doing things. (It's moved on slightly: annotations are the new XML. cough Jersey cough) I mean, there's plenty of good stuff too, but... let's just not pretend that it's a choice without downsides, shall we?
- eeZi 12y agoPyPy (a JITed Python interpreter) is experimenting with STM to solve the issue: http://morepypy.blogspot.de/2014/11/tornado-without-gil-on-pypy-stm.html http://morepypy.blogspot.de/2014/11/tornado-without-gil-on-p...
- TazeTSchnitzel 12y agoArbitrarily limiting JSON size/nesting because it blocks your event loop seems silly. What if a customer needs to give you something taking a while to parse? Do the parsing in a web worker.
- vans 12y ago"Simple exceptions should be caught using a linter. There’s no reason to have bugs for undefined vars when they could be caught with some basic automation [...} It catches unhandled errors and undefined variables before they even get pushed to production. " And there are people still thinking untyped languages are good for the server... "Node is good when you're using a linter, when you're writing your back end in go, when you write MASSIVE unit tests..." May be it could be much simpler to use a more suitable langage, no ? Just saying...
- failedstartup2 12y agoNode, like Rails before it, was an enabler tech - it enabled non-developers to build stuff. Those with experience would have gone direct to something like Erlang in the first place, carefully sidestepping the dog that is Go.
- vkjv 12y agoI've had some similar experiences as we've scaled from internal only applications a couple years ago, to handling thousands of requests a second at peak. 1. Event loop. Yep, always be careful not to block it. But, I think this is more of a tuning thing. If you're using Java, you figure out how many requests you can reasonably handle and make that your water mark. Do the same thing in node, if you take too many connections, start refusing them fast and not queuing them forever. 2. Exceptions. Honestly this has never been an issue. We've used Promises for async flow from the beginning. This means that there is nearly zero code that is not inside of a promise chain and hence inside of a try {} catch {}. We just don't have this problem and we don't crash. 3 / 4. Oddly enough, I found streams to be a solution to this problem and not a cause, if using back pressure properly. I highly recommend highland.js.
- vosper 12y agoTheir first problem was sending customer requests directly into processing. They're collecting customer metrics - they should never be unable to handle a request because another request has consumed all the resources. A much better way to do this would be to have a very lightweight API putting events into a stream (Kafka or, since they seems to be on AWS, Kinesis [1]). Let the stream absorb that crazy customer data, and let your data processing run full-speed from the stream. You'll get blips and slow downs, but it won't affect your ability to receive more data. Log out any errors or malformed data so that customers can see the problems. Do your profiling and optimisation, but avoid losing data. [1] We're using Kinesis. Very easy to provision, does what it says on the box, and can easily handle thousands of requests per second.
- 15155 12y agoYep. This technology is too cheap/readily available to "do it wrong." Let the Kafka project handle the nuances of durable, performant queueing - that's not your business model.
- spion 12y agoAll these problems are solvable (streams by ditching built-in streams and replacing them with something decent, errors with promises + typescript, event loop blocking with a streaming JSON parser). But that still means that out of the box node is a pretty unsatisfying experience all around. It took us a year to arrive at the solutions above, and many prominent members of the community scoffed at a number of them. Some are still scoffed at. For example, everyone still thinks that promises should not catch errors, as if somehow throwing on typos is useful. Its not. The real solution here is a type system, the sensible error capturing model of promises that doesn't destroy all assumptions about code (unlike domains), and a sensible library like bluebird that reports rather than swallows up unhandled errors. People also swore that streams are the best thing ever but the reality is that the built in streams are an organically grown design that accreted many flaws along the way. Most of them can be mended though and with streams 3 things are finally starting to be acceptable.
- astrodust 12y agoAfter using Q for some time and recently discovering Bluebird, I've got to say Bluebird is the way it should have been done. Great library and the API offers a lot of conveniences without being overwhelming.
- xhrpost 12y ago> "ditching built-in streams and replacing them with something decent" What have you found sufficient for this?
- glibgil 12y agoThey just told you: Bluebird and streams 3. Did you get bored and stop reading?
- juliangregorian 12y agoOoh, swing and a miss. Bluebird is not a streams replacement, streams 3 IS the built-in streams. Maybe save the snark for situations where you actually have a clue.
- Kiro 12y agoOT but can someone explain this? PHP: Starts reading from database, blocks everything else, returns data. Node: Starts reading from database, lets it go and continues with other stuff until the data is ready and then returns it. I understand it in theory but since Node is single-threaded, doesn't it need to use that thread for the database operations? Which means it's blocking the program until it's done anyway?
- taternuts 12y agoI believe what happens is it registers the functions callback on the event-loop which it'll check the next time around to see if it's done, then pushes the work to be handled in a separate pool of threads that libuv manages. When it's done, the next event-loop tick or the next time around it will execute the callback with the results. This is how asynchronous functions work anyways, but some functions are actually synchronous functions such as JSON.stringify, so if you are doing a JSON.stringify on a huge JSON object, you're literally grinding everything to a halt until you're done doing that
- Kiro 12y agoThanks. So Node is actually using multiple threads behind the scenes or did I read that wrong? How do I know which kind of operations are pushed there by libuv?
- taternuts 12y agoYeah, there's a pool of non-blocking C++ threads that do the work behind the scenes. Any async operation gets pushed out, and honestly most functions will behave asynchronously. JSON parsing is a sync operation that usually doesn't block for very long because it will be a small operation 99.9% of the time, so it's kind of a pitfall because you almost never see this blocking issue unless your parsing huge JSON objects. In nodeland things are kind of expected to be asynchronous, and if they are not they should be labeled as such so that everyone using it knows. Checkout the FileSystem (fs) nodejs lib, you'll see calls explicitly labeled as "sync" to denote that they are blocking.
- cpprototypes 12y agoNode has a lot of flaws today. But, even with those flaws, it's very useful for quick prototyping (discipline is required here since it must be a true prototype which means throwing away the code) and command line scripting (for me, it has mostly replaced python for small scripting tasks). Using it in a production service is possible, but should be limited to simple services (not too much business logic and it should be IO heavy). But there is a lot of potential in the future. ES6 greatly improves the Javascript language and it's possible ES7 or 8 will add types. If something like TypeScript becomes standard Javascript and async/await is added, the language will become a "serious" language for many developers. I think the Node of the future will look nothing like the Node of today. When Javascript/Node gains these features, it has the potential to become a true server side language.
- mark_l_watson 12y agoI agree that JavaScript will get better with ES7, etc. that said, I am half way throw the edX Typescript class and it offers a lot of what ES6 and future releases will offer today. I also really like ClojureScript but TypeScript is an incredibly well designed language.
- rvirding 12y agoWhy am I getting this strong deja vu feeling? Hmm. Oh, now I know! These were exactly the type of problems we were attacking, and solving, 25 years ago when we were creating Erlang. How you build concurrent, fault-tolerant and non-blocking systems with low-latency. And about 10 years ago there came implementations of Erlang which handled multi-core transparently and provided things like load balancing automatically. I would say you would have to have really extreme requirements to handle those type of things yourself.
- aioprisan 12y agoEvent loop debugging is key and I'm glad that there are some interesting offering profiling services on this level, especially for frameworks like Meteor: https://kadira.io/ https://kadira.io/
- lobster_johnson 12y agoAnyone else thinking this sort of blog post is actually really bad PR? There are some very obvious architectural issues here, issues that aren't tied to the choice of Node.js. I wouldn't want to use a provider whose stack was this immature, to be perfectly blunt. For example, an API shouldn't do any of the processing they describe. The API's role is to handle submitted requests as fast as possible, so it must off-load the actual work to a queue that can be processed elsewhere. I'm also skeptical of solutions like YAL, at least the way it seems to be used here. IMHO you'll want local spooling via something like rsyslog, so that you're impervious to network failures. Logging directly to a remote server means you become completely reliant on that server.