17 ms·
When Node.js is the wrong tool for the job
- tannhaeuser 10y agoI'd also add that node.js might not be the right choice for complex backend business logic with lots of service calls because of. Node.js' always-async execution model tends which tends to make things more complicated than need be.
- PCaponetti 10y agoasync / await allows you to choose how async you want your code to be/look, and I for one like to have a choice as opposed to say other languages that don't always have an async option.
- ldev 10y agoC# has this same async await. The only difference - I can choose to use sync workflow by appending .Result to async function, ie `var response = asyncFunction().Result;` ..... Node.js is garbage (well, good for simple crud apps) and so is JS.
- Klathmon 10y agoBut with async/await in JS, you just prepend an `await` and you get the sync workflow. That being said, I do like how C# handles it more than the way JS has handled it, but both are fantastic. You can say JS is "garbage" all you want, but it's close to the most popular language on the planet and many many people are writing great things because of it.
- tps5 10y agoI think "always async" is the main advantage of node. My general (perhaps wrong) impression is that other languages commonly used in backends are moving toward async io, usually through maturing libraries.
- ionrock 10y agoMost services that might be considered "backends" (ie databases, queues, cloud services) end up being in written in languages that can safely use async techniques for I/O and still use real threading or some other method for managing CPU bound problems. Many of the applications people think of for node.js end up being "glue", much like python, and can live within this constraint for a very long time, where the I/O optimization is a nice benefit.
- tannhaeuser 10y agoIt's not that I don't like async (I think it's a defensible choice for JavaScript), but that in my experience backend logic for e.g. e-commerce apps doesn't benefit from using it. Projects which expose services to web frontends are often implemented in an architecture where only one-shot, aggregated service calls (and often times REST-y services) are exposed to Node.js, with the actual business processing and granular service calls being implemented in eg. Java web services in a synchronous programming style. I actually like that architecture because it gives front end devs leaway to define their browser-facing head server backend, rather than enshrine a dogmatic frontend/backend architecture upfront. It's true that other languages add async models (or emphasize those that they already have), but eg. in the case of Java you're sitting on 20 years of synchronous library and custom code, and it's not clear moving to async is worth it at this pont.
- brlewis 10y ago"With lots of service calls" describes problems for which async is generally the most performant solution.
- utnick 10y agoIt could be, but if the service calls generally depend on the response from the previous service call. Or if you need to rate limit your calls to these services, I think its easier to code it not async
- brlewis 10y agoFor batches of transactions where each transaction has a service call that depends on the response from a previous service call, async especially outperforms. Rate limiting is not hard either way, but single-threaded async is slightly easier because your counter is automatically thread safe. The larger and more complex your system gets the harder it gets to keep it thread-safe. For me this is the big advantage of single-threaded async, and I like node because this single-threaded async is idiomatic in JavaScript.
- vkjv 10y agoI strongly disagree. Making many async service calls concurrently and awaiting the result is the text book use case for node.js (well, event based programming in general).
- klodolph 10y agoIt seems like a lot of JavaScript developers are repeating things like "more people know JavaScript so you don't have to learn a new language, which saves you time". I don't get it. In my experience, if you find a good developer, they can pick up C#, Swift, or Go pretty quickly, and if you can't find a good developer, the fact that they already know JavaScript is not much of an advantage. Even if the developer you hire already knows your language, they're going to be spending time learning your code base and how your organization works (shared repo? PRs? Feature branches? Code review? Coding standards?) That, and nose.js developers seem to repeat the claim that node.js makes delivery faster… but is it really any faster than ASP.NET, Rails, Django, or Go stdlib? Those frameworks are so fast for prototyping and delivering bread-and-butter apps as it is (and some of them let you do multithreading to boot). I'm also really not interested in how things work for "typical CRUD apps" because those are so trivial to write in any decent environment. I'm worried that node.js articles are the same kind of echo chamber that Rails articles were 10 ago.
- zMiller 10y agoDepends who you ask really. If you are a developer then you should invest your time and be able to learn and use the best tool for the job. If however you are something like a Product manager then learning JS is much more valuable time investment. It is the one language that will, currently, run anywhere ... Backend, Frontend, Side end, whatever. You can quickly get good at JS to the point of being able to prototype the full app stack and come up with something that works, goto market and fail. Should you find your self in the unlikely position that your product meets its success, then you can go ahead and hire a Go/Elxir/WebASM whatever guy to optimize this and needs larger throughput. You have 24 hours in day, 6 to sleep, 3 to breath, 1 to eat. How do you want to spend the remaining 14 ? You get good at what you practice :)
- rb808 10y agoI've worked on projects in which both the front and end back end were written in the same language & those that are different. The former were more productive imho with developers working on both and with shared libraries & shared test platforms. The latter often end up getting silo'd with stuff passed over the fence. Yes many developers can do both, but its difficult to do both at the same time & even harder to hire for.
- dlojudice 10y agoThey could have fixed part of the architecture by having a "cache service" process (4 cpus: 3 for proxies, 1 for the cache service). With that they'd have a single point consuming their limited resources (memory, cpu and socket for redis connections), using IPC to communicate between process.
- gaastonsr 10y agoI thought the same or even the same 4 cpus for proxies and a shared 1 for the cache service.
- deleted 10y ago[deleted]
- neebz 10y agoJSON.parse() is one issue we faced regularly. Any large amount of data fetching could block the event loop and the whole server slows down. It's very unforgiving. We go great length to figure out which attributes to fetch and add limits to all our sql queries. These are best practices but with node they are must.
- beejiu 10y agoNode.js isn't great for CPU bound tasks in general.
- danenania 10y agoParsing json is sneaky because it can show up in apps that are otherwise IO bound (where Node shines) and don't seem like they should be CPU intensive on the surface.
- reaktivo 10y agoThere's always streaming json parsers[1] that will help you when parsing large json data sets. [1]: https://github.com/dominictarr/JSONStream https://github.com/dominictarr/JSONStream
- smokeyj 10y ago> As node.js is not multi-threaded, we spin up 4 instances of node.js per server, 1 instance per CPU core. Thus, we cache in-memory 4 times per server. And why not use a shared memory server? > Operations started adding rules with 100,000s of domains, which caused a single set of rules to be about 10mb large ... If we weren’t using node.js, we could cut this bandwidth by 4 as there would only be one connection to the Redis cluster retrieving rule sets. Maybe a 10mb json string isn't the best design decision..... Or you know, you could have one node process connect to the Redis server, and have the local processes read from a shared memory server.. Or you could not store your rules as a 10mb friggin JSON string.. > When rule sets were 10mb JSON strings, each node.js process would need to JSON.parse() the string every 30 seconds. We found that this actually blocked the event loop quite drastically Well then do it in another thread and save it to shared memory. Maybe, just maybe, JSON strings aren't the tool for the job here.
- ionrock 10y agoWhile I don't know node.js very well and can't speak to the "shared memory server", I do think it is valid to point out that this sort of redesign to fit the platform is frustrating. I've hit similar situations in Python where an async i/o framework is used and at some point there becomes a CPU bound task. The application was redesigned and new microservices were started in order to cope and the architecture became convoluted. This sort of question always seems to always show up in Python where limitations of the GIL influences the design of the software, the libraries used and deployment. It seems node.js, not surprisingly, has the same problems. As the above comment clearly points out, there are workarounds to make things work. Still, when I think about the amount of time and effort that goes into debugging systems when they hit limits like the author mentioned, I imagine most of the the benefits gained by using a language like Python or JavaScript go away.
- gaastonsr 10y agoIt's not a Node.js limitation either. The design was not thought out from the beginning. It has happened to me before. I design something, then I run it in multiple CPUS and it doesn't make sense anymore... Or realize that if try to scale the current design doesn't hold. Node.js running in a single thread is a good thing if you know how to work with it. Or if you really need multiple threads, use another PL. Or you could write it in C++ and bind it to Node.js. And run it as a background process. Or spin up a worker? there seems to be multiple solutions here...
- yahyaheee 10y agoI debated between learning Node and Go for my latest project. I took a couple days doing beginner tutorials on each, and Go was actually a lot easier for me to learn. Could just be my background, but I know a couple other people who picked it up in about a week too, it's surprisingly simple.
- Slackwise 10y ago> it's surprisingly simple. Actually, that's its fundamental design. They reduced everything down to a very small core with very little features, so things would be obvious and you don't have to learn or remember much. It's refreshingly simple! With that said, I'd say they reduced it too far down. There's no generics so you end up using `interface{}` everywhere which often leads to issues due to its late binding. Or you end up just using codegen tools, IIRC. Also since there are no exceptions, you end up with constant checks for error code returns, which end up usually just being strings and not much else. Not saying exceptions are the best way to approach error handling, but they do allow you to reverse through an entire function call stack and clean up any state along the way, along with adding more granular error information you can write handlers to react to. Go reminds me of the pain that was C error handling and juggling error codes.
- yahyaheee 10y agoYeah, I totally agree. I think they did some smart rework of the fundamentals, but there are a lot of other parts that are needlessly painful. I would like to see things like Go channels and Go's build system brought up with some modern features. If only the Go community wasn't so ridiculously stubborn!
- donatj 10y ago"Usually" /snark
- tyingq 10y ago"Operations started adding rules with 100,000s of domains, which caused a single set of rules to be about 10mb large" There's not enough detail to be sure, but this sounds more like "when a relational database would be a better idea than redis." Edit: That is, pushing the evaluation of the rules down...rather than pulling a kv and walking 10MB (of JSON?) to get to the small number of rules that apply for the transaction.
- binocarlos 10y agoThis is an excellent article which really highlights the underlying trade-offs when you choose node for your service (i/o bound work vs cpu). Unless you know for sure what limits you will hit - it makes sense to iterate quickly and find out. Then, if the service is actually hitting limits (and probably not the ones you thought) - re-write it in a multi-threaded concurrent language like go, elixr etc - or a language designed to solve the actual problems the service is hitting (which might be disk i/o or other infrastructure level things not language choice)
- rodp 10y agoWhile I agree Node.js isn't the right tool for any job -- just like anything else, really -- after reading his description of the problem, I can't shake off this feeling that the main issues he has with performance in this case have very little to do with Node itself. Parsing a huge JSON string in any language would block CPU for a while. This JSON then becomes a huge hash table in memory, so no wonder each process uses up a lot of RAM. I don't know how these rules are then used but it seems to me he might be better off trying to rethink how to do shared memory in this case before he simply blames Node for blocking CPU and wasting memory. That said, I can imagine other languages (like Java or Go) could still end up being more efficient than Node.
- jonas21 10y agoThe issue isn't that it takes a long time to parse the JSON. It's that the server can't do anything else while it's parsing. In Java, for example, you could parse the JSON on a background thread without affecting your ability to serve requests. Similarly, the memory issue isn't so much that a single copy of the table takes a lot of space, but rather that they need to store 4 copies of the table -- because they're running 4 different processes in order to utilize multiple cores. Both of these issues are specific to nodejs.
- wehadfun 10y agoJavaScript is my first non-mathematical programming language and I haven’t found the need to expand my programming skills to more -Having a hard time taking anything this guy says seriously
- leshow 10y agoI think it's a lot closer to 10x as fast for Rust and 6-7x for Go.
- stevebmark 10y agore: multiple processes duplicating memory, would a single menmcache instance or similar solve this problem? I don't have any perspective on how that would perform at scale vs individual programs reading from application state. Although thinking about it, each process would probably have to store all that data in app memory anyway...
- cbem 10y agoIt was an very unfortunate decision for Node devs to deep six multithreaded web workers. A pull request implementing it was ready to go with an optional flag to enable it but they did not want to support it. So node will be forever more compute bound to a single thread blocking all I/O.
- gaastonsr 10y ago> So node will be forever more compute bound to a single thread blocking all I/O. I don't think I understood your point. But is not Node.js supposed to not block long lasting tasks?
- BuuQu9hu 10y agoWhen WebAssembly comes, what will that mean for the node.js ecosystem?
- kowdermeister 10y agoNothing, WebAssembly targets the browser. Devs who already understand JS could just easily pick up Node.
- suzzer99 10y ago> On each server, rules are retrieved from Redis and cached in-memory using an LRU-cache. As node.js is not multi-threaded, we spin up 4 instances of node.js per server, 1 instance per CPU core. Thus, we cache in-memory 4 times per server. This is a waste of memory! This is completely standard and the only way to do node in-memory caching. Think of each worker as a completely independent node process, which is only bound to the cluster by a master process which has the ability spawn and kill child cluster processes.
- mi100hael 10y agoSeeing as Redis is already in-memory and has a LRU-cache feature, and he's already caching data from a database in Redis, the whole Node LRU seems awfully redundant and unnecessary.
- vkjv 10y ago> This is completely standard and the only way to do node in-memory caching. This isn't accurate you can use shared memory. There are a few modules that implement this. In addition, you can offload the JSON.parse to the dedicated "caching" process that updates the shared memory.
- suzzer99 10y agoDo you have a link that describes an example of this? Ok nevermind, google is my friend: https://github.com/PaquitoSoft/memored https://github.com/PaquitoSoft/memored I can see where this would come in handy. But at 240MB total resident memory per CPU across 4 node workers that OP describes, I wouldn't hassle with it.
- chrshawkes 10y agoSo this article was a big gripe about certain limitations to node. Maybe I missed something but if the author is saying they don't want to use node, well than what is an alternative? Would any Python framework be more suited, Ruby, Rust? Some of the limitations mentioned with sockets had more to do with hosting on Azure.
- deleted 10y ago[deleted]
- hitgeek 10y agogood detailed write up. There were probably opportunities for the author to architect the system in ways that were better suited to node (given that was the chosen platform), but the architecture choices were not unreasonable by design. These are some good things to consider when architecting a system, and considering node as the platform. I'm not sure I agree that node is the "perfect for simple CRUD apps" though
- deleted 10y ago[deleted]
- hmans 10y agoAlways.