Elixir just really nice. I gave it a shot again and it is super smooth experience nowadays. Distillery, mix, iex <3 Also most of the libraries I care about are 2.0+ and now this:
I am talking about raw HTTP request handling performance. Elixir/Cowboy/Plug is in the middle range of web servers. Again, it is good enough for most use cases.
Ah, fair enough, apologies for misconstruing. Definitely good enough for a very wide range of use cases; it's not a language built for speed and power anyway.
Not really, you can't have a microsecond latency talking over a network. I understand localhost microbenchmarks are nice but real life scenarios are much better.
The setup for the test:
- provision node A for being the server
- provision node B for being the client
- open X (16..16000) connections from node A and use http pipelining start to send requests to node B
I use wrk2 as the test client it is pretty amazing and looking at the latency distribution graphs.
I am not sure what you are talking about. wrk2 can put out 7M req/s on a 16 core box. That is way beyond Phoenix's performance on the same HW type. wrk2 is widely used and accepted performance measurement tool. Again, you mentioned microseconds latency which means you are talking about localhost microbenchmarking. That is irrelevant from the production workload point of view. I have saturated network links successfully with wrk2 which is the definition of fast enough.
Interestingly there was a thread on HN previously which tools are used for HTTP perf testing:
>>> - Wrk: https://github.com/wg/wrk - Fastest tool in the universe. About 25x faster than Locust. 3x faster than Jmeter. Scriptable in Lua. Drawbacks are limited output options/reporting and a scripting API that is callback-based, so painful to use for scripting user scenario flows.
alright. this looks interesting. I'll have to dive back into this space and see what's changed, I'm probably out of the loop. What's the diff between "wrk" and "wrk2"?
I am building a quite involved video learning platform as we speak with Elixir and Phoenix. No regrets so far, and if anything as time goes on, I'm becoming more and more happy with the decision.
The community is really great and there's a lot of quality libraries available. Not just libraries, but entire production systems too. For example https://changelog.com/ is written with Elixir / Phoenix and their platform is open source'd at https://github.com/thechangelog/changelog.com. There's so much good stuff in that repo to learn from.
Also the Elixir Slack channel has 20,000+ people in it and the official forums at https://elixirforum.com/ are very active.
Yep. Would bet on it again. It's proven itself to me and my employer, and has increased developer productivity and joy dramatically. Bad code looks bad, good design emerges organically, and macros let you hide the plumbing where needed, and optimize things away at compile time. Not even mentioning the wonderful world of OTP.
Could someone give a more concise reason for using Elixir? I have a "rule of 3" for checking out things - the third time I hear it mentioned and it seems interesting, then I'll go check it out.
Elixir is past 3 times - so I will check it out for sure! - but this article didn't seem to actually say anything (seemed more like a PR piece that was trying not to be technical, and the main argument appeared to be "well, it's not javascript!").
The part that actually talked about Elixir listed some Pros that didn't seem that unique. What's the "killer feature" of Elixir - or is it just a combination of "good features"?
It is an ergonomic language built on top of a stable, performant, proven runtime with a lot of features built-in.
It's a nice ecosystem with a good culture in a language that promotes pretty good programming practices.
The only major downsides (if they even are for you) are that it's dynamic--not good for number crunching, though you can connect to compiled binaries--and is not strongly typed--which can lead to runtime bugs.
That said, part of the philosophy is to enable fast failure without taking down the whole application. In communications, it's not considered the end of the world to drop or fail on one connection, so it works well for web services, chat, etc.
I would say it's about developer happiness while coding and that all the decisions made by Jose and the core team tend to have been the correct decision. So taking Phoenix as an example you look at the framework and every time they find a problem i.e. Presence instead of saying that's a difficult problem and moving on they fix said problem in a really scalable way [1].
The same could be said for things like data processing with Flow [2] or even things like Ecto (semi official database wrapper) or even third party libraries like say ex_money [3].
Then you start looking at the packages and language and see that there are rarely thousands of bugs or that the infrastructure (mix, hex, docs etc.) is really nice to use and that the language is really stable, yet still provides you with useful but clear abstractions. Or that you can spin off processes and tasks inline without too much worry, or that you can use 20+ years of Erlang libs transparently, or that it's immutable and has the best concurrency primitives of any system available, or that it allow you to supervise processes and let them crash if needed without bringing down your app, or that you can transparently get multi machine out of the box, or that message passing is build in as the default way to scale the system. Or pattern matching or |> or the amazing community.
Actually Ecto is the most unhappy part of the Elixir ecosystem to me. It's unnecessarily complicated for almost any software project. I'd even take Django's ORM instead of it, but what I want is something close to ActiveRecord. There are some Elixir modules similar to AR, none popular.
It is tragically funny how many people like and how many people dislike Ecto. :) I guess that's "ORM"s in a nutshell? Which is totally fine, of course!
Ecto is not an ORM, and an ORM like ActiveRecord would never fit a functional, immutable language anyway. Ecto is a query builder and DSL.
But I may be biased, I _love_ Ecto and have been using it every day for the past year, and have never come across something as powerful yet lightweight (perhaps SQLAlchemy, ignoring its ORM features).
The real "killer feature" is the Erlang VM (the BEAM). Thanks to a combination of exceptionally lightweight "processes" (not true OS processes, a construct of the VM), which share nothing, can only interact through message passing, and which can be supervised by other processes, it's possible to build incredibly robust systems.
Elixir makes the Erlang VM much more pleasant to use in my opinion (plenty of Erlangers will disagree with this, ymmv). It provides a developer-friendly, modern ecosystem, with doc support, testing support, a great macro language for building DSLs, package management, etc, but underlying it all is a battle-tested VM, that has been under active development for 30 years.
I think the real killer feature of Elixir is that it is a friendly, Ruby-like language that gives you access to the underlying power of Erlang/OTP on the Beam VM.
OTP (Open Telecom Platform) is a set of tools, libraries, and middleware that was built around Erlang. This has been in development since the 90s, and was originally developed by Ericsson for the telecom industry to handle massive numbers of concurrent connections without a single point of failure (I may be butchering this story, but this was the end result). OTP includes things like extremely lightweight processes, process supervision to quickly restart based on your desired behavior, multiple native real-time datastore options (both in-memory and/or persistent), hot deployments with no downtime, an extensive standard library, and other cool things that have stood the test of time. All of this just comes with the base Erlang/OTP installation.
Elixir essentially introduces a modern ecosystem around the Erlang/OTP system.
True in a lot of cases. However, Phoenix has major similarities to Ruby on Rails, and Elixir is often the functional language of choice for those moving away from RoR.
It's the synergy between the language, the runtime and the standard library to deliver the actor model.
Elixir "threads" are called "processes". It's a bit confusing at first, but there's a good reason for it. So from hereon in, when I say "process" think "thread".
Elixir processes, like OS processes, are fully isolated from each other. If Process A has data than Process B wants, Process A has to ask it (send it a message) and can only get a copy of the data (like real OS processes, hence why the name makes sense). The advantage to this is that data ownership is explicit, race conditions are eliminated, and code is less coupled (A can't have a reference to an object that points to B that points to C..., that anyone anywhere in the code can mutate).
At a high level, this allows you to get many of the benefits of microservices, without the high cost (deployment complexity, service discovery, lower performance).
We run an analytics on live sports which does various transformation on a stream of play by play data. There are very distinct features: boxscores, lineups, scores, time alignment, broadcast information...For each live game we start a process for each of these features. When a play by play event comes it, we pass that to each worker of the appropriate game, and each worker does its own thing.
The workers are isolated. Not just the physical code, but at runtime. This makes them easier to test, refactor and grasp.
There's some interaction between the workers. For example our boxscore worker needs to know the score at a given time. So it sends a message to the score worker for that game: {:get, time}. The score worker replies with the score. There's no need for explicit locking. A process handles a message at a time. There's no chance that the boxscore worker changes the score worker's data (it can change the copy of the data that it got, but that change is only on the copy).
Really, it's most of the benefits of microservices (and I mean, being able to have true MICROservices, not just rebranded SOA) with few of the downsides.
The JavaScript Fatigue argument is not good. There's simply no data that backs it and nobody is forced to use new libraries only because they use JavaScript.
I've seen third party dependencies churn on Elixir as well (packages that are no longer maintained or alternatives that are better) - I think it's an inherent problem with using dependencies and has nothing to do with the programming language in which those dependencies are written.
> As a developer I just want to get on with my work, not have to read another Hackernoon post on how everything from last week is obsolete because XYZ framework
My recommendation is that you don't read Hackernoon. This seems like a very ineffective way to level up your developer skills.
Edit: I agree that Elixir is very nice and would pick it over JavaScript for backend heavy applications without thinking. I just don't think this argument makes any sense in that context.
There is no connection, at all, between a language being interpreted and it being error-prone to write or run. You either mean something else or are mistaken.
> nobody is forced to use new libraries only because they use JavaScript.
It's not completely true IMO for 2 reasons:
1- the nodejs standard lib is quite poor compared to say, Java's, Scala's or python's, so you generally need quite a lot of modules to do anything
2- the npm ecosystem is much more amateur. To do anything you have a ton of poorly supported by hobbyists or not supported at all modules. This can force you to change modules/libs regularly. This is to be compared to the Java ecosystem for example, were more people are working together to build well supported/high quality libs (Apache libraries for example)
Exactly, it is impossible to use a stable long term Linux distribution and node, most packages force you to get the latest or before latest version of node.
Other issue is that things move fast and break, you are not sure that 3 months old tutorial will work in present.
Edit: I know I can and I did grabbed node and npm outside the repositories, but you do not see this issue with other languages where I must install latest stuff to get most libraries working.
Yeah but on Node.js, Express has been the de facto framework of choice for building REST APIs for over 6 years. The JS fatigue phenomenon was mostly on the front end, and even that has basically settled down as people have rallied around React, Angular and Vue.
I’m not calling out JS or elixir communities by any means here. I just want to mention that a big part of the “JavaScript Fatigue” is the amount of overlapping libraries that all do the same thing and it isn’t always straightforward to figure out which one is better - and this happens with so many packages it would be impossible to do it with all of them. Just look up “isArray” (which I wish where just not showing up in libs at this point. It’s a built in in both node and the browser now) and you get 102 packages that are very similar but will clearly vary in implementation and quality.
Where as something like the Python community or Rust community (where I have had more experience) I have always found that even if there are packages that do the same thing, there usually aren’t nearly as many duplicates and often times the community has done a better job communicating the value of many of the packages. There is just less confusion around the whole thing
I have also found there to be relatively little overlap between the big packages, in my experience
It's no more difficult than choosing an app in the app store. You go on NPM, you look at the download count, you look at the feature set, you install and then move on with your day. JS is a vast and very open ecosystem so duplication is inevitable.
I keep wanting to be hyped about elixer but the performance benchmarks confuse me. If you look at the tech emperor benchmarks[1] phoenix makes the list at #46 registering 16% of the performance of the top framework. I'm willing to sacrifice performance for readable maintainable code but it just surprises me that its that slow.
I don't know the exact specifics here but I know a lot of communities that rank high invest time contributing into the open source Tech Empower tests for their framework. This way they can tweak & make sure their stack runs at optimal speeds.
At the same time, Elixir (and Erlang) are not meant for raw speed. It is best used for real time communication, lots of users & handling errors. At least that is what I have read.
It's worth reading this very long thread about exactly this, from 2016. Look for Sasa Juric and Chris McCord's comments in particular. [1]
tl;dr benchmarks are hard, not all benchmarks are implemented well, Elixir folks haven't heard back from Tech Empower re: details of errors rates etc. It's an unfair analysis, and not just for Elixir.
Simply looking at its position in that list will tell you nothing without looking at what is above and below. I don't use Phoenix or Elixir but from what I understand Phoenix tries to be for Elixir what Rails is for Ruby. That is, it is a full web framework. The number one position on that list is a "Asynchronous PostgreSQL driver".
- better configuration (tweaked to this benchmark / hardware). Bigger communities will try harder to tweak their benchmark config. The Phoenix one is probably just the default one or slightly tweaked. Is the SQL connection pool size ideal? Are all default Phoenix-added Plugs ("middlewares") needed here? Is the BEAM virtual machine config / flags ideal? Would using OTP releases be beneficial?
- Elixir ecosystem is constantly improving the performance. The VM, language and packages versions should be updated. Elixir 1.6.5, Phoenix 1.3.2, and Ecto 2.1 are not recent.
- Abstractions used (and amount of them, so amount of work done at run-time). Compare the Fortunes "handler" implementation in vertx-postgres [1] to the Phoenix one [2]. Raw SQL vs generated query, almost bare DB connection pool vs Repository abstraction, and it's just the "handler". But you wouldn't want to maintain a lot of low level code.
- Typical functional language overhead. Copying (copying the conn when modifying it, Enum.sort on a list etc) is more expensive than modifying in-place. Again, functional code will be easier to maintain.
But overall I think in the particular benchmark you linked, Phoenix isn't that bad. Latency is pretty low and consistent (0.5 ms min and 9.9 ms max (!)) thanks to the BEAM pre-emptive scheduler. And BEAM will scale well vertically.
I use Elixir daily for the past 8 months, and I love it. For my personal projects, I use a great Heroku-like service called Gigalixir (https://gigalixir.com/). No restarts or connection limits for the free tier, and it runs on your choice of Google Cloud or AWS behind the scenes. Elixir doesn't have as many easy cloud deployment options as, say, JS, so this service is really helpful.
But what about not so computation-heavy tasks like parsing HTTP requests, interacting with databases, generating and serving responses based on the input, templates, reasonably simple logic and the data? And if it's not fast then why even consider it when there are more well-established alternatives like Ruby for those who like the syntax, ASP.Net/Core, Python/Django, Node/Express, Scala/Play for the FP lovers etc? I've been previously told it's key features are it's super fast, functional and Ruby-like.
Super fast, no. At least not in the C++ way. Faster than Ruby or Python, yes.
Functional, yes.
Ruby-like, yes, as German is English like (mostly guessable vocabulary.) Then you discover that the two languages work in totally different ways and your Ruby skills don't really matter anything when working in Elixir.
Elixir is pretty fast, but far from the fastest or efficient. IMO if all you need is CRUD server that operates efficiently then elixir is probably the wrong choice unless you already know it. It does have some nice features within distribution and real-time stuff, but if you don't need that and don't know erlang or elixir I wouldn't use it.
I've been using Elixir to build applications for production use for 3 years now my summary is I and my whole team can (in contrast to languages I've used in the past):
- Ship faster
- Write simple, readable, reliable and fast code
- Scale easier and with less resources
- Onboard and train new hires into the code base quicker
I know I'm making it out to be a panacea which to be clear it isn't as the deployment story still has some final pieces for the core team to work through but I will say I'll continue to use it to build in the future
I’ve been programming in elixir for about 2 years now. I have to say it’s hard to go back to something like Ruby or JavaScript.
In elixir you really get the full power of multi core and support for distributed computing out of the box.
Code that would have been beyond my pay grade or wouldn’t even imagine to write in Ruby or JavaScript is now easily reasoned about and maintained in projects. I can write succinct code that is easy to read, is fast, able to take advantage of multiple cores, less error prone, which I can scale to multiple machines easily.
The erlang scheduler is so damn powerful and it feels amazing to be able to execute your code on multiple machines with a simple distributed task which is built in as a standard functionality of the language.
I’ll end this note saying that, look at the problem you are trying to solve. If you need multi core and distributed features (which is generally more common than you think) elixir is truly your friend.
I can say without a shadow of a doubt the project I’m building right now would not be progressing as fast as it is if I picked anything other than Elixir. You get a lot of bang for your buck when it comes to productivity in the domain that elixir solves for.
> If you need multi core and distributed features (which is generally more common than you think) elixir is truly your friend.
Is it though? At least in my line of work I don't think I've ever run into this. I feel like I've always been able to distribute just fine with workers/queues. If I even suspected it would I'd look into it more, but generally I find distributing across systems to be a software architecture-level and not language-level work; perhaps I'm missing something, however.
Until I used Elixir, I thought workers/queues were enough. But after the last nearly-three-years, I've actually fallen into a place where workers/queues are almost always strictly inferior.
Workers/queues in languages like Ruby have problems like,
* Require very specific ergonomics(for example, don't hand the model over, hand over the ID so you can pull over the freshest version and not overwrite)
* They require a separate storage system, like your DB, Redis, etc. This doesn't sound big, but when doing complex things it can turn into hell.
* They have to be run in a separate process, which makes deployment more difficult.
* They're slow. Almost all of them work on polling the receiving tables for work, which means you've got a lag time of 1-5 seconds per job. Furthermore, the worse your system load, the slower they go.
* You can't reliably "resume" from going multi-process. Lets say you're fine with the user waiting 2-3 seconds to have a request finish. With workers/queues, you either have to poll to figure out when something finished(which is not only very slow, but error prone), or you have to just go slow and not multi-process, making it into a 8-10 second request even though you've got the processing power to go faster.
So, you've got all that. Or in Elixir, for a simple case, you replace `Enum`(your generic collection functions) with `Flow` and suddenly the whole thing is parallel. I mean that pretty literally too- when I need free performance on collections, that's usually what I do. Works 95% of the time, and that other 5% is where you need really specific functionality anyway, and for those, Elixir still has the best solution to it I've ever seen.
If you need a request/response model (ex: query some data) and not simply queue an operation to execute later without waiting for it. I agree (worker/queue) are the wrong solution. But you should use a multi-language RPC framework like GRPC instead of building a distributed monolith.
With kubernetes you have an endpoint per service that route and load balance to the correct machine.
It seem to me you already got all the benefits from using actor but with less lock-in to a language.
That's even more work? Most developers already have redis/Postgres running, the issue there is the added complexity in complex operations.
Not to mention, microservices are not always the answer. And even if they were, they're still an insane amount of more work than literally changing what functions you call.
I'm not saying you should never use an RPC, but I've significantly reduced the times I'd want to use one. The only reason I even advocate for one now, is because I prefer to empower developers to use whatever language makes them happy, even if I personally greatly prefer Elixir.
Also, building Elixir apps aren’t really the same as traditional monoliths.
It’s really not a distributed monolith as each node can deploy with different code. More importantly actors and supervision trees really help keep projects organized. It’s easy to use PubSub mechanisms or just named actors to communicate between services.
As an example I recently took a project that ran on a single IoT device and moved a chunk of it that managed a piece of hardware to another IoT device connected by Ethernet. It only took moving a handful of files and renaming a few modules. It took longer to figure out why multicast wasn’t working than to refactor the app. There are some limitations with type specs not working as well as I’d like with PubSub style messages (most type checking is done via api functions, not on individual messages).
Yes, web developers. Elixir is primarily a network systems language, and generally, the Ruby, Javascript, Python, etc communities use Postgres/Redis. Obviously it isn't universal, but there are obvious analogies(MySQL, etc).
Calling a remote function is exactly the same as calling a service in both case you are doing an RPC. While doing a Rest service is more work, using something like GRPC or java RMI is not and also support pub-sub mechanism and give you have a clear interface that define which functions can be called remotely which make understanding the cost of the function call and the security implications a lot easier.
Erlang/Elixir has some really great advantages in concurrency and parallelism but what you're describing are just badly designed systems.
Shopify, for example, use Resque (Ruby + Redis) to process thousands of background jobs per second.
> * Require very specific ergonomics(for example, don't hand the model over, hand over the ID so you can pull over the freshest version and not overwrite)
This is good practice but certainly not a requirement. You can pass objects in a serialized format like JSON or use Protobuf etc.
> * They require a separate storage system, like your DB, Redis, etc. This doesn't sound big, but when doing complex things it can turn into hell.
> * They have to be run in a separate process, which makes deployment more difficult.
Background tasks have different requirements so this is a good idea regardless.
> * They're slow. Almost all of them work on polling the receiving tables for work, which means you've got a lag time of 1-5 seconds per job. Furthermore, the worse your system load, the slower they go.
Redis queues have millisecond latency and there's no polling. Resque and Sidekiq use the BRPOP to wait for jobs. BRPOP is O(1), so it doesn't slow down as the queue backs up.
PG has LISTEN/NOTIFY to announce new jobs or the state change of an existing job so there's no need to poll. SKIP LOCKED also prevents performance degrading under load.
> * You can't reliably "resume" from going multi-process. Lets say you're fine with the user waiting 2-3 seconds to have a request finish. With workers/queues, you either have to poll to figure out when something finished(which is not only very slow, but error prone), or you have to just go slow and not multi-process, making it into a 8-10 second request even though you've got the processing power to go faster.
There are multiple other options here which are better:
Threads - GIL allows parallel IO anyway and JRuby has no GIL
Pub/Sub - Both Redis and PG have a great basic implementation usable from the Ruby clients
Websockets - Respond early and notify directly from the background jobs
I didn't mention ETS or Mnesia? The OP was talking specifically about using job queues to get concurrency/parallelism, in which case you absolutely don't need job queues. If you need a job queue, you need a job queue.
> Background tasks have different requirements so this is a good idea regardless.
Why? You're just stating this like it's obviously true, and honestly I can't think of a time I significantly wanted a different system doing my jobs than the one handling requests.
> Redis queues have millisecond latency and there's no polling. Resque and Sidekiq use the BRPOP to wait for jobs. BRPOP is O(1), so it doesn't slow down as the queue backs up.
Redis queues have millisecond latency, Ruby using Redis queues does not. That's the part that polls when there's nothing else going on. If you're never running out of jobs to do then your latency is fast, but you're also not accomplishing things as fast as possible(since it's waiting on whatever is in front of it).
If this isn't true anymore, then alright, but last I used Sidekiq(early 2018), the latency to start processing a job was often greater than a second.
> Threads - GIL allows parallel IO anyway and JRuby has no GIL
And are incredibly difficult to use and pass information back and forth(hence why Elixir exists at all- Jose Valim was the person implementing this on the Rails core team).
> Pub/Sub - Both Redis and PG have a great basic implementation usable from the Ruby clients
Can certainly work, to be honest I never tried this because of the complexity of initial setup and how green I was when I needed it.
> Websockets - Respond early and notify directly from the background jobs
Which Ruby has a lot of trouble maintaining performantly. When my original team went to use Rails5 sockets, we found we could barely support 50 sockets per machine.
---
It's worth saying, I'm not saying one shouldn't use Ruby- the place I work right now is a primarily Ruby shop, and my Elixir work is for event processing and systems needing microsecond response times. But, we've also built things in Elixir that normally I would use Ruby or JS for, and not only does it do well, but often it's write it and forget it, with deployment being literally "run a container and set the address in connected apps".
> ie, requiring very specific ergonomics. If you have to change what you're doing, it's a new domain to learn.
Resque & Sidekiq build this in by converting job arguments to JSON. There's nothing extra to learn.
> I didn't mention ETS or Mnesia? The OP was talking specifically about using job queues to get concurrency/parallelism, in which case you absolutely don't need job queues. If you need a job queue, you need a job queue.
Sorry, I thought you were talking about building a background job system in Erlang using out of the box OTP but it sounds like you're actually talking about trying to get parallelism in Ruby by doing RPC over Sidekiq? That's always a bad idea!
> Redis queues have millisecond latency, Ruby using Redis queues does not. That's the part that polls when there's nothing else going on. If you're never running out of jobs to do then your latency is fast, but you're also not accomplishing things as fast as possible(since it's waiting on whatever is in front of it).
Ahhh! When Mike Perham says "Sidekiq Pro cannot reliably handle multiple queues without polling" what this really means is a Redis client can only block on and immediately process from the highest priority queue. The lower priority queues are only checked when blocking timeout expires. There's no "check all queues and sleep" polling loop which adds artificial latency.
> And are incredibly difficult to use and pass information back and forth(hence why Elixir exists at all- Jose Valim was the person implementing this on the Rails core team).
Jose Valim didn't join Rails core until a couple years after Josh Peek (now working for GitHub) made Rails thread-safe.
> And are incredibly difficult to use and pass information back and forth(hence why Elixir exists at all- Jose Valim was the person implementing this on the Rails core team).
2012-2013 onwards Ruby got great libraries like concurrent-ruby and parallel that make things a lot easier.
> Which Ruby has a lot of trouble maintaining performantly. When my original team went to use Rails5 sockets, we found we could barely support 50 sockets per machine.
Workers and queues fail. SQS was down for us for almost two weeks while AWS fixed a bug. We had no choice but to wait or rewrite our implementation... Again! We've already had to rewrite once due to poor visibility and rare occasional problems processing data. Debugging such distributed systems is legendary hell. And that's just for simple async processing so that we can return a response quickly to the user and finish the task in a few seconds. There is simply no comparison between such a complex, failure prone distributed system and the simplicity, reliability, and ease of use of having support built into the language for this, IMO.
I am sorry but I disagree. You are trying to make it sound that your cloud provider downtime has something to do how you manage your workload in your code.
Debugging __any__ distributed system is difficult, this is why monitoring and tracing should be first class citizens in your deployments. It seems they are not for you.
Yeah, monitoring told us it was down and eventually we figured it was an AWS issue we could do nothing about until they patched it. My main point there is actually that for many use cases, this doesn't have to be a distributed computing problem and thus the non-distributed version is superior to the distributed version.
I'm a big fan of Erlang, but I think you can acheive similar things in other languages with queues and workers. Erlang's advantage here is that you can do easily do a worker per client connection, for almost any number of client connections; for data processing queues, the lack of data sharing between processes (threads) strongly pushes you towards writing things in a way that easily scales to multiple queue workers -- you can of course write scalable code in other languages, but it's easier to write code with locking on shared state when shared state is easier.
As for distribution, again, this isn't necessarily exclusive, but the right primatives are there and work well to start with. You could have good reasons for a bigger separation between nodes as well.
Erlang has some warts too, of course. For me, the warts are usually about scale, oddly enough. BEAM itself scales very well, but some parts of the OTP don't, often because of the difference in expectations between a telcom environment and a large scale internet service. Two examples:
A) the (OTP) tls session cache is serviced by a single process and in earlier versions, the schema and queries were poorly designed so you could store multiple entries for a single destination, and the query would retrieve all and then discard all but the first. When you were making many connections to a host that issued sessions but didn't resume them, all of the extra data could overwhelm that one process, and resulting in timeouts attempting to connect to any tls host. This was fixed in a release after r18, I believe, to store only one session per cache key, and the cache was plugable before then, but it wasn't fun to find this out in production.
B) reloading /etc/hosts and querying the table it loads into weren't done in an atomic way. I believe this is fixed in upstream as well, but queries satisfied by /etc/hosts were actually two queries on the same table, and reloading the table was done by clearing and then loading, so the second query could fail unexpectedly. This led to the bundled http client getting stuck, despite timeouts.
Because those features are accessible, you end-up using them a lot more frequently and find new and exciting ways to use them. For example, I am really happy that Elixir and its tooling does pretty much everything using all cores: compiling code, running tests, generating documentation, etc. and all of this has a direct impact on the developer experience.
The other part is that you can build more efficient systems by relying on this. If you have a machine with 8 cores, it is more efficient to start a single process that can leverage all 8 cores and multiplex on both IO and CPU accordingly. This impacts everything from database utilization, to metrics, third-party APIs, and so on.
The Phoenix web framework also has great examples of using distribution to provide features like distributed pubsub for messaging and presence without external dependencies.
However, when it comes building systems, then I agree with you and I would probably use a queue, because you get other properties from queues such as persistence and making the systems language agnostic.
Exactly right, just the pure fact that it's at your disposal makes you think about problems in a whole new way. For example, I recently just abstracted my solutions away from Redis. I have nothing against redis however removing a dependency makes things more simple, which is important for my setup.
For caching you have SO many options which are already built in, ets, agent, ets + gen_server, or even reaching out for a library like nebulex.
Another example is recurring jobs, you can create a gen_server that will run a job every x hours in roughly 50-100 lines of code depending on how complex your problem is.
I rarely feel the need to reach out for external dependencies. Not that there is anything wrong with that, it's just that you now have a wider array of tools to work with and some problems are just solvable with a couple of lines of code instead of having to reach out for external tooling.
I recently built a distributed work queue, generally in Ruby I would use something like sidekiq, however elixir made me feel like "hey you can write your own" which generally is not recommended since you don't want to re-invent the wheel, but if you are doing something that breaks away from the existing solution, having the ability to craft a custom solution that works for your specific set of problems is extremely powerful, you can get much more creative, and the important thing is it got done fast (I wrote a distributed job scheduler in 2 weeks + 1 week to clean it up and work out the kinks, it's already running stably in production)
There are plenty of great, battle-tested frameworks and libraries in Node.js which help to leverage multiple cores but they just don't get as much hype.
> In elixir you really get the full power of multi core and support for distributed computing out of the box. Code that would have been beyond my pay grade or wouldn’t even imagine to write in Ruby or JavaScript is now easily reasoned about and maintained in projects.
One of the alternative languages you mention is single-threaded, and the other has a global interpreter lock (in its most common implementation). That Elixir is superior to them for parallel programming doesn't really say much.
Tangent: It's proposed that Elixir has better error handling than Rust... but this doesn't sit well with me, and I know the Rust community is in flux here as well. I personally like not having exceptions. It's very easy to trace where an error is coming from when it's a value like anything else. Yea it might be a bit more typing... this is the conflict. Rust does have an issue with the boilerplate involved with writing error types, but there are already attempts at fixing this as a crate: https://github.com/rust-lang-nursery/failure
?
OTP and Supervisers give you powerful tools for building fault tolerant apps but at the lang level it would be hard to argue that Elixir has better error handling
Different tools for different jobs. Rust is more of a systems language for close to metal performance and memory safety. Elixir is memory safe from a functional perspective utilizing the actor model. Elixir is good for real time concurrency and higher level systems.
I've been fortunate to work with a CTO that sees the value in Elixir and also letting us push forward with it. It has been excellent. At this point we have about 20 engineers who have chosen to work in it close to full time for their services.
It's hard to pick one big draw, but I'd say the biggest for me is that everything I wanted to do in rails has been possible in Elixir and then additional functionality not easily possible in rails is trivial in Elixir. I often consider the distribution techniques as "enhancers" as you could work around them with global locks and data stores, but you don't need to.
I'm very bullish on Elixir and I'm curious to see where it will go. Looking forward to giving my talk about bringing Elixir into production (from a human and technical standpoint) at Lonestar Elixir conference.
I guess I feel like the annoying formatting is indicative of the community, immature.
Web developers seem to follow trends; Perl -> DJango|RoR -> Node.JS -> Scala -> GoLang -> Elixir -> Something. Or, something like that. To me, it's like buying a $500 pencil and expecting that you should be capable of writing a better book.
If you get in bed with that crowd, don't expect that your program and 3rd party dependencies are going to be stable in 2 years.
A lot of individuals I've seen in the community so far haven't been the type to quickly jump onto a trend. People have been thoughtful with their application design and have chosen Elixir instead of alternatives. Given how drastically different the BEAM is from these other languages, I have a hard time seeing some of the people I've met (and myself) jump to something else. My guess is that if it does happen to others, it is because they switched jobs and cannot get buy in.
Pretty unfounded comments regarding stability of packages long term. As with any community that makes it easy to publish packages, there will certainly be package churn over time. However, core libraries show 0 sign of this and Phoenix in particular has taken a very mature stance on new features.
Yes. And in 3 years (or so) when the hype-train moves on, and all that is left is the core users. It will be a much more viable choice for development, in my opinion.
I don't think anything negative of the language or core libraries.
Elixir is a niche (that's the truth). Also in the article it is written 'Relatively difficult to "recruit" developers with existing experience in Elixir'.
Why would a SW company invest in niche languages where the resources (software developers) are really expensive and really hard to get? Technologically it's all great but economically that's a nightmare.
If you have a solid background in software development, it's not too hard to become proficient in Elixir in a few weeks. If you recruit good talent with a demonstrated proficiency and willingness to learn, you will be fine. If you need to switch languages or systems in the future, these devs will still be of great use to you.
Ok, that sounds good. My overall experience is only for most companies (I'm working as a contractor for many years) that they don't really want to invest in you. Either you fit for the job or not (and competition is not easy sometimes).
I can’t argue with this point in general, but your first question was from the perspective of a company. There certainly are companies, that are willing to use things like Elixir (like my company for example) because we believe that an experienced developer should be able to come up to speed quickly with Elixir. What really takes the time is learning our business domain and our existing codebase. Or looking at it another way, I wouldn’t want to hire someone that was a Rails dev, I would want to hire someone that was a strong dev in general and may have happened to be doing Rails most recently.
I have been involved with bringing it to my company as the primary proponent. The truth is it hasn't been hard to teach people it and they can produce decent code pretty quickly and good code a bit longer than that but still acceptable.
We have found a few people who knew it already and were looking for a job, but that is fairly rare. Instead, we know we can bring people up to speed on it quickly and also it signals to people that we're willing to give them some language options (Ruby or Elixir) within some boundaries. Having these options is good for ownership of an area.
The only times I see this argued is from suits looking to treat programmers as a fungible resource. This is never a problem for companies looking to retain people.
If your status quo is working programmers as long as you can without a raise until they switch jobs, a niche language is a threat. If you re-evaluate your staff based on their experience gained, you can head off the churn.
I think this is too harsh. A company can establish an enjoyable and empowering culture, that attracts and supports talented developers — and can still try to avoid hiring obstacles at the same time. It might not have been what therealmarv meant in his post, but your comment makes it sound mutually exclusive.
As a counter-argument: I've experienced several times that "niche tech" companies offered non-competitive salary packages and perks, because they offered the cool tech instead; "sure, we can't match that other offer, but we built our stack on that language/tech that is so hot right now".
Not arguing about the quality of Elixir, just about the gatekeeping that happens in this thread.
Something I'd also mention is, with Elixir, you'll get your MVP done maybe 10% slower than you would in Ruby.
The difference is, in Ruby, after 2-3 months, you'll be rewriting large portions of what you did to get there. And in another year or two, you'll find yourself being blocked by earlier designs not for a day or two, but by a month of work or better.
In Elixir, you simply will not experience that. Even if written poorly, it's easy to rework/change, and you probably won't need to anyway because it's just easier to get things done without painting yourself into a corner.
Doesn't that depend a lot on the project? Only one datapoint, of course, but we have an elixir project at work and it's in dire need of a refractor. The dynamic nature of the language doesn't help.
Thing is, it's not 100% dynamic. It's compiled, so most common refactor errors will be revealed by that alone(no function named, not the right number of arguments, etc). The harder to find things would be harder to find in most typed languages too, unless you had tests to point them out to you.
That's one of the things you lose when going from Erlang to Elixir. Sure the macros are useful and convenient, but they also make the code less explicit and harder to refactor. There's probably a reason why Erlang for the past 30 years had no macros, despite being a homoiconic language.
TLDR: try working with Erlang if you find your Elixir code-base in need of a refactor - chances are you'll learn conventions which will help you write more maintainable code in Elixir, too.
I don't think it is that black and white. I have heard from some companies adopting Elixir that hiring became easier because the demand for their previous technology was really high and they could differentiate themselves with Elixir.
There are also companies that we really successful in hiring by reaching out to functional communities in general. And of course, there are also companies struggling to hire Elixir developers compared to other techs. YMMV.
Depends on the project. I have a project that heavily relies on headless chrome for scraping dynamic pages I'd rather stick to Node and Puppeteer for this particular project.
In general Elixir is a joy to use but some of the trade-offs BEAM(Erlang VM) makes might not match your requirements e.g. if you don't need live code upgrades but project could benefit from static typing you might want to consider something else.
It's the first language I genuinely enjoy reading and writing even in my private life.
If I wonder about the internals of a library I use, I can just look into the code and kind of understand what's happening. Never had that with JS or anything.
I'm just a genuine fanboy.
Only drawback I feel is: Some libraries that would have been quite developed in JS are not that well developed in Elixir. Some libraries are quite dead and it's hard to find alternatives (mostly obscure stuff)
But on the other hand, it often seems manageable to just write it yourself, or fork it and move on.
From a technology perspective, a thousand times yes. And the same for Ember JS (my other go-to).
But from a talent and recruiting perspective, I'm less enthusiastic. Elixir, yes, there's growing talent. But Ember, boy it seems like nobody is doing it, and I've had to convince potential candidates that it'll be worth their time for future employability to learn Ember.
245 comments
[ 3.9 ms ] story [ 238 ms ] threadBut every other word being emphasised in this article was tiring to read.
https://github.com/aws-samples/aws-lambda-elixir-runtime
The only downside is that the out of the box performance is subpar for http services but it is still acceptable.
The setup for the test:
- provision node A for being the server
- provision node B for being the client
- open X (16..16000) connections from node A and use http pipelining start to send requests to node B
I use wrk2 as the test client it is pretty amazing and looking at the latency distribution graphs.
Tools that clear winners of performance:
- https://github.com/valyala/fasthttp
- https://www.rapidoid.org
Elixir/Cowboy/Plug is in the middle range, kind of like what Techempower[1] guys saw during their tests.
[1] https://www.techempower.com/blog/2018/10/30/framework-benchm...
The only measuring tool that is fast enough to accurately measure Phoenix performance is something like Tsung (which is also an Erlang app...)
Interestingly there was a thread on HN previously which tools are used for HTTP perf testing:
>>> - Wrk: https://github.com/wg/wrk - Fastest tool in the universe. About 25x faster than Locust. 3x faster than Jmeter. Scriptable in Lua. Drawbacks are limited output options/reporting and a scripting API that is callback-based, so painful to use for scripting user scenario flows.
https://news.ycombinator.com/item?id=15738967
https://news.ycombinator.com/item?id=15733910
I never had any debugging issues in particular, but the dependency hell drives me nuts, too.
I am building a quite involved video learning platform as we speak with Elixir and Phoenix. No regrets so far, and if anything as time goes on, I'm becoming more and more happy with the decision.
The community is really great and there's a lot of quality libraries available. Not just libraries, but entire production systems too. For example https://changelog.com/ is written with Elixir / Phoenix and their platform is open source'd at https://github.com/thechangelog/changelog.com. There's so much good stuff in that repo to learn from.
Also the Elixir Slack channel has 20,000+ people in it and the official forums at https://elixirforum.com/ are very active.
Elixir is past 3 times - so I will check it out for sure! - but this article didn't seem to actually say anything (seemed more like a PR piece that was trying not to be technical, and the main argument appeared to be "well, it's not javascript!").
The part that actually talked about Elixir listed some Pros that didn't seem that unique. What's the "killer feature" of Elixir - or is it just a combination of "good features"?
It's a nice ecosystem with a good culture in a language that promotes pretty good programming practices.
The only major downsides (if they even are for you) are that it's dynamic--not good for number crunching, though you can connect to compiled binaries--and is not strongly typed--which can lead to runtime bugs.
That said, part of the philosophy is to enable fast failure without taking down the whole application. In communications, it's not considered the end of the world to drop or fail on one connection, so it works well for web services, chat, etc.
The same could be said for things like data processing with Flow [2] or even things like Ecto (semi official database wrapper) or even third party libraries like say ex_money [3].
Then you start looking at the packages and language and see that there are rarely thousands of bugs or that the infrastructure (mix, hex, docs etc.) is really nice to use and that the language is really stable, yet still provides you with useful but clear abstractions. Or that you can spin off processes and tasks inline without too much worry, or that you can use 20+ years of Erlang libs transparently, or that it's immutable and has the best concurrency primitives of any system available, or that it allow you to supervise processes and let them crash if needed without bringing down your app, or that you can transparently get multi machine out of the box, or that message passing is build in as the default way to scale the system. Or pattern matching or |> or the amazing community.
[1] https://phoenixframework.org/blog/the-road-to-2-million-webs... and https://dockyard.com/blog/2016/03/25/what-makes-phoenix-pres...
[2] https://www.youtube.com/watch?v=XPlXNUXmcgE
[3] https://github.com/kipcole9/money
But I may be biased, I _love_ Ecto and have been using it every day for the past year, and have never come across something as powerful yet lightweight (perhaps SQLAlchemy, ignoring its ORM features).
Elixir makes the Erlang VM much more pleasant to use in my opinion (plenty of Erlangers will disagree with this, ymmv). It provides a developer-friendly, modern ecosystem, with doc support, testing support, a great macro language for building DSLs, package management, etc, but underlying it all is a battle-tested VM, that has been under active development for 30 years.
OTP (Open Telecom Platform) is a set of tools, libraries, and middleware that was built around Erlang. This has been in development since the 90s, and was originally developed by Ericsson for the telecom industry to handle massive numbers of concurrent connections without a single point of failure (I may be butchering this story, but this was the end result). OTP includes things like extremely lightweight processes, process supervision to quickly restart based on your desired behavior, multiple native real-time datastore options (both in-memory and/or persistent), hot deployments with no downtime, an extensive standard library, and other cool things that have stood the test of time. All of this just comes with the base Erlang/OTP installation.
Elixir essentially introduces a modern ecosystem around the Erlang/OTP system.
Elixir looks a little like Ruby, but that's where I'd end the comparison. There's almost know similarity in how can be used well.
Elixir "threads" are called "processes". It's a bit confusing at first, but there's a good reason for it. So from hereon in, when I say "process" think "thread".
Elixir processes, like OS processes, are fully isolated from each other. If Process A has data than Process B wants, Process A has to ask it (send it a message) and can only get a copy of the data (like real OS processes, hence why the name makes sense). The advantage to this is that data ownership is explicit, race conditions are eliminated, and code is less coupled (A can't have a reference to an object that points to B that points to C..., that anyone anywhere in the code can mutate).
At a high level, this allows you to get many of the benefits of microservices, without the high cost (deployment complexity, service discovery, lower performance).
We run an analytics on live sports which does various transformation on a stream of play by play data. There are very distinct features: boxscores, lineups, scores, time alignment, broadcast information...For each live game we start a process for each of these features. When a play by play event comes it, we pass that to each worker of the appropriate game, and each worker does its own thing.
The workers are isolated. Not just the physical code, but at runtime. This makes them easier to test, refactor and grasp.
There's some interaction between the workers. For example our boxscore worker needs to know the score at a given time. So it sends a message to the score worker for that game: {:get, time}. The score worker replies with the score. There's no need for explicit locking. A process handles a message at a time. There's no chance that the boxscore worker changes the score worker's data (it can change the copy of the data that it got, but that change is only on the copy).
Really, it's most of the benefits of microservices (and I mean, being able to have true MICROservices, not just rebranded SOA) with few of the downsides.
Bonus: parallelism almost for free.
If only GenServers had a sensible interface instead of semi random handle_* functions that obfuscate what a given GenServer is implementing.
I've seen third party dependencies churn on Elixir as well (packages that are no longer maintained or alternatives that are better) - I think it's an inherent problem with using dependencies and has nothing to do with the programming language in which those dependencies are written.
> As a developer I just want to get on with my work, not have to read another Hackernoon post on how everything from last week is obsolete because XYZ framework
My recommendation is that you don't read Hackernoon. This seems like a very ineffective way to level up your developer skills.
Edit: I agree that Elixir is very nice and would pick it over JavaScript for backend heavy applications without thinking. I just don't think this argument makes any sense in that context.
I would never work on backend with JavaScript or any other interpreted language, due to error proness.
There is no connection, at all, between a language being interpreted and it being error-prone to write or run. You either mean something else or are mistaken.
It's not completely true IMO for 2 reasons: 1- the nodejs standard lib is quite poor compared to say, Java's, Scala's or python's, so you generally need quite a lot of modules to do anything 2- the npm ecosystem is much more amateur. To do anything you have a ton of poorly supported by hobbyists or not supported at all modules. This can force you to change modules/libs regularly. This is to be compared to the Java ecosystem for example, were more people are working together to build well supported/high quality libs (Apache libraries for example)
Other issue is that things move fast and break, you are not sure that 3 months old tutorial will work in present.
Edit: I know I can and I did grabbed node and npm outside the repositories, but you do not see this issue with other languages where I must install latest stuff to get most libraries working.
It might've become the defacto standard for consuming them, but definitely not for creating them.
Almost no service I've ever administered used a nodejs backend.
Where as something like the Python community or Rust community (where I have had more experience) I have always found that even if there are packages that do the same thing, there usually aren’t nearly as many duplicates and often times the community has done a better job communicating the value of many of the packages. There is just less confusion around the whole thing
I have also found there to be relatively little overlap between the big packages, in my experience
Anyone know whats going on there?
1. https://www.techempower.com/benchmarks/#section=data-r17&hw=...
At the same time, Elixir (and Erlang) are not meant for raw speed. It is best used for real time communication, lots of users & handling errors. At least that is what I have read.
Yup, many things.
It's worth reading this very long thread about exactly this, from 2016. Look for Sasa Juric and Chris McCord's comments in particular. [1]
tl;dr benchmarks are hard, not all benchmarks are implemented well, Elixir folks haven't heard back from Tech Empower re: details of errors rates etc. It's an unfair analysis, and not just for Elixir.
[1] https://elixirforum.com/t/techempower-benchmarks/171/44
This would be a more apt comparison:
https://www.techempower.com/benchmarks/#section=data-r17&hw=...
- better configuration (tweaked to this benchmark / hardware). Bigger communities will try harder to tweak their benchmark config. The Phoenix one is probably just the default one or slightly tweaked. Is the SQL connection pool size ideal? Are all default Phoenix-added Plugs ("middlewares") needed here? Is the BEAM virtual machine config / flags ideal? Would using OTP releases be beneficial?
- Elixir ecosystem is constantly improving the performance. The VM, language and packages versions should be updated. Elixir 1.6.5, Phoenix 1.3.2, and Ecto 2.1 are not recent.
- Abstractions used (and amount of them, so amount of work done at run-time). Compare the Fortunes "handler" implementation in vertx-postgres [1] to the Phoenix one [2]. Raw SQL vs generated query, almost bare DB connection pool vs Repository abstraction, and it's just the "handler". But you wouldn't want to maintain a lot of low level code.
- Typical functional language overhead. Copying (copying the conn when modifying it, Enum.sort on a list etc) is more expensive than modifying in-place. Again, functional code will be easier to maintain.
But overall I think in the particular benchmark you linked, Phoenix isn't that bad. Latency is pretty low and consistent (0.5 ms min and 9.9 ms max (!)) thanks to the BEAM pre-emptive scheduler. And BEAM will scale well vertically.
1. https://github.com/TechEmpower/FrameworkBenchmarks/blob/mast... 2. https://github.com/TechEmpower/FrameworkBenchmarks/blob/mast...
Functional, yes.
Ruby-like, yes, as German is English like (mostly guessable vocabulary.) Then you discover that the two languages work in totally different ways and your Ruby skills don't really matter anything when working in Elixir.
- Ship faster
- Write simple, readable, reliable and fast code
- Scale easier and with less resources
- Onboard and train new hires into the code base quicker
I know I'm making it out to be a panacea which to be clear it isn't as the deployment story still has some final pieces for the core team to work through but I will say I'll continue to use it to build in the future
In elixir you really get the full power of multi core and support for distributed computing out of the box.
Code that would have been beyond my pay grade or wouldn’t even imagine to write in Ruby or JavaScript is now easily reasoned about and maintained in projects. I can write succinct code that is easy to read, is fast, able to take advantage of multiple cores, less error prone, which I can scale to multiple machines easily.
The erlang scheduler is so damn powerful and it feels amazing to be able to execute your code on multiple machines with a simple distributed task which is built in as a standard functionality of the language.
I’ll end this note saying that, look at the problem you are trying to solve. If you need multi core and distributed features (which is generally more common than you think) elixir is truly your friend.
I can say without a shadow of a doubt the project I’m building right now would not be progressing as fast as it is if I picked anything other than Elixir. You get a lot of bang for your buck when it comes to productivity in the domain that elixir solves for.
Is it though? At least in my line of work I don't think I've ever run into this. I feel like I've always been able to distribute just fine with workers/queues. If I even suspected it would I'd look into it more, but generally I find distributing across systems to be a software architecture-level and not language-level work; perhaps I'm missing something, however.
Workers/queues in languages like Ruby have problems like,
* Require very specific ergonomics(for example, don't hand the model over, hand over the ID so you can pull over the freshest version and not overwrite)
* They require a separate storage system, like your DB, Redis, etc. This doesn't sound big, but when doing complex things it can turn into hell.
* They have to be run in a separate process, which makes deployment more difficult.
* They're slow. Almost all of them work on polling the receiving tables for work, which means you've got a lag time of 1-5 seconds per job. Furthermore, the worse your system load, the slower they go.
* You can't reliably "resume" from going multi-process. Lets say you're fine with the user waiting 2-3 seconds to have a request finish. With workers/queues, you either have to poll to figure out when something finished(which is not only very slow, but error prone), or you have to just go slow and not multi-process, making it into a 8-10 second request even though you've got the processing power to go faster.
So, you've got all that. Or in Elixir, for a simple case, you replace `Enum`(your generic collection functions) with `Flow` and suddenly the whole thing is parallel. I mean that pretty literally too- when I need free performance on collections, that's usually what I do. Works 95% of the time, and that other 5% is where you need really specific functionality anyway, and for those, Elixir still has the best solution to it I've ever seen.
With kubernetes you have an endpoint per service that route and load balance to the correct machine.
It seem to me you already got all the benefits from using actor but with less lock-in to a language.
Not to mention, microservices are not always the answer. And even if they were, they're still an insane amount of more work than literally changing what functions you call.
I'm not saying you should never use an RPC, but I've significantly reduced the times I'd want to use one. The only reason I even advocate for one now, is because I prefer to empower developers to use whatever language makes them happy, even if I personally greatly prefer Elixir.
It’s really not a distributed monolith as each node can deploy with different code. More importantly actors and supervision trees really help keep projects organized. It’s easy to use PubSub mechanisms or just named actors to communicate between services.
As an example I recently took a project that ran on a single IoT device and moved a chunk of it that managed a piece of hardware to another IoT device connected by Ethernet. It only took moving a handful of files and renaming a few modules. It took longer to figure out why multicast wasn’t working than to refactor the app. There are some limitations with type specs not working as well as I’d like with PubSub style messages (most type checking is done via api functions, not on individual messages).
You are joking right? You mean web developers?
Shopify, for example, use Resque (Ruby + Redis) to process thousands of background jobs per second.
> * Require very specific ergonomics(for example, don't hand the model over, hand over the ID so you can pull over the freshest version and not overwrite)
This is good practice but certainly not a requirement. You can pass objects in a serialized format like JSON or use Protobuf etc.
> * They require a separate storage system, like your DB, Redis, etc. This doesn't sound big, but when doing complex things it can turn into hell.
ETS and Mnesia aren't production ready job queues, unfortunately: https://news.ycombinator.com/item?id=9828608
> * They have to be run in a separate process, which makes deployment more difficult.
Background tasks have different requirements so this is a good idea regardless.
> * They're slow. Almost all of them work on polling the receiving tables for work, which means you've got a lag time of 1-5 seconds per job. Furthermore, the worse your system load, the slower they go.
Redis queues have millisecond latency and there's no polling. Resque and Sidekiq use the BRPOP to wait for jobs. BRPOP is O(1), so it doesn't slow down as the queue backs up.
PG has LISTEN/NOTIFY to announce new jobs or the state change of an existing job so there's no need to poll. SKIP LOCKED also prevents performance degrading under load.
> * You can't reliably "resume" from going multi-process. Lets say you're fine with the user waiting 2-3 seconds to have a request finish. With workers/queues, you either have to poll to figure out when something finished(which is not only very slow, but error prone), or you have to just go slow and not multi-process, making it into a 8-10 second request even though you've got the processing power to go faster.
There are multiple other options here which are better:
Threads - GIL allows parallel IO anyway and JRuby has no GIL
Pub/Sub - Both Redis and PG have a great basic implementation usable from the Ruby clients
Websockets - Respond early and notify directly from the background jobs
ie, requiring very specific ergonomics. If you have to change what you're doing, it's a new domain to learn.
> ETS and Mnesia aren't production ready job queues, unfortunately: https://news.ycombinator.com/item?id=9828608
I didn't mention ETS or Mnesia? The OP was talking specifically about using job queues to get concurrency/parallelism, in which case you absolutely don't need job queues. If you need a job queue, you need a job queue.
> Background tasks have different requirements so this is a good idea regardless.
Why? You're just stating this like it's obviously true, and honestly I can't think of a time I significantly wanted a different system doing my jobs than the one handling requests.
> Redis queues have millisecond latency and there's no polling. Resque and Sidekiq use the BRPOP to wait for jobs. BRPOP is O(1), so it doesn't slow down as the queue backs up.
Redis queues have millisecond latency, Ruby using Redis queues does not. That's the part that polls when there's nothing else going on. If you're never running out of jobs to do then your latency is fast, but you're also not accomplishing things as fast as possible(since it's waiting on whatever is in front of it).
If this isn't true anymore, then alright, but last I used Sidekiq(early 2018), the latency to start processing a job was often greater than a second.
> Threads - GIL allows parallel IO anyway and JRuby has no GIL
And are incredibly difficult to use and pass information back and forth(hence why Elixir exists at all- Jose Valim was the person implementing this on the Rails core team).
> Pub/Sub - Both Redis and PG have a great basic implementation usable from the Ruby clients
Can certainly work, to be honest I never tried this because of the complexity of initial setup and how green I was when I needed it.
> Websockets - Respond early and notify directly from the background jobs
Which Ruby has a lot of trouble maintaining performantly. When my original team went to use Rails5 sockets, we found we could barely support 50 sockets per machine.
---
It's worth saying, I'm not saying one shouldn't use Ruby- the place I work right now is a primarily Ruby shop, and my Elixir work is for event processing and systems needing microsecond response times. But, we've also built things in Elixir that normally I would use Ruby or JS for, and not only does it do well, but often it's write it and forget it, with deployment being literally "run a container and set the address in connected apps".
Resque & Sidekiq build this in by converting job arguments to JSON. There's nothing extra to learn.
> I didn't mention ETS or Mnesia? The OP was talking specifically about using job queues to get concurrency/parallelism, in which case you absolutely don't need job queues. If you need a job queue, you need a job queue.
Sorry, I thought you were talking about building a background job system in Erlang using out of the box OTP but it sounds like you're actually talking about trying to get parallelism in Ruby by doing RPC over Sidekiq? That's always a bad idea!
> Redis queues have millisecond latency, Ruby using Redis queues does not. That's the part that polls when there's nothing else going on. If you're never running out of jobs to do then your latency is fast, but you're also not accomplishing things as fast as possible(since it's waiting on whatever is in front of it).
Ahhh! When Mike Perham says "Sidekiq Pro cannot reliably handle multiple queues without polling" what this really means is a Redis client can only block on and immediately process from the highest priority queue. The lower priority queues are only checked when blocking timeout expires. There's no "check all queues and sleep" polling loop which adds artificial latency.
> And are incredibly difficult to use and pass information back and forth(hence why Elixir exists at all- Jose Valim was the person implementing this on the Rails core team).
Jose Valim didn't join Rails core until a couple years after Josh Peek (now working for GitHub) made Rails thread-safe.
> And are incredibly difficult to use and pass information back and forth(hence why Elixir exists at all- Jose Valim was the person implementing this on the Rails core team).
It's really not that hard anymore!
results = ['url1','url2'].parallel_map{|url| HTTParty.get(url) }
2012-2013 onwards Ruby got great libraries like concurrent-ruby and parallel that make things a lot easier.
> Which Ruby has a lot of trouble maintaining performantly. When my original team went to use Rails5 sockets, we found we could barely support 50 sockets per machine.
ActionCable is designed for convenience not performance. https://github.com/websocket-rails/websocket-rails will handle thousands of connections per process.
Debugging __any__ distributed system is difficult, this is why monitoring and tracing should be first class citizens in your deployments. It seems they are not for you.
As for distribution, again, this isn't necessarily exclusive, but the right primatives are there and work well to start with. You could have good reasons for a bigger separation between nodes as well.
Erlang has some warts too, of course. For me, the warts are usually about scale, oddly enough. BEAM itself scales very well, but some parts of the OTP don't, often because of the difference in expectations between a telcom environment and a large scale internet service. Two examples:
A) the (OTP) tls session cache is serviced by a single process and in earlier versions, the schema and queries were poorly designed so you could store multiple entries for a single destination, and the query would retrieve all and then discard all but the first. When you were making many connections to a host that issued sessions but didn't resume them, all of the extra data could overwhelm that one process, and resulting in timeouts attempting to connect to any tls host. This was fixed in a release after r18, I believe, to store only one session per cache key, and the cache was plugable before then, but it wasn't fun to find this out in production.
B) reloading /etc/hosts and querying the table it loads into weren't done in an atomic way. I believe this is fixed in upstream as well, but queries satisfied by /etc/hosts were actually two queries on the same table, and reloading the table was done by clearing and then loading, so the second query could fail unexpectedly. This led to the bundled http client getting stuck, despite timeouts.
The other part is that you can build more efficient systems by relying on this. If you have a machine with 8 cores, it is more efficient to start a single process that can leverage all 8 cores and multiplex on both IO and CPU accordingly. This impacts everything from database utilization, to metrics, third-party APIs, and so on.
The Phoenix web framework also has great examples of using distribution to provide features like distributed pubsub for messaging and presence without external dependencies.
However, when it comes building systems, then I agree with you and I would probably use a queue, because you get other properties from queues such as persistence and making the systems language agnostic.
I hope this clarifies it a bit!
For caching you have SO many options which are already built in, ets, agent, ets + gen_server, or even reaching out for a library like nebulex.
Another example is recurring jobs, you can create a gen_server that will run a job every x hours in roughly 50-100 lines of code depending on how complex your problem is.
I rarely feel the need to reach out for external dependencies. Not that there is anything wrong with that, it's just that you now have a wider array of tools to work with and some problems are just solvable with a couple of lines of code instead of having to reach out for external tooling.
I recently built a distributed work queue, generally in Ruby I would use something like sidekiq, however elixir made me feel like "hey you can write your own" which generally is not recommended since you don't want to re-invent the wheel, but if you are doing something that breaks away from the existing solution, having the ability to craft a custom solution that works for your specific set of problems is extremely powerful, you can get much more creative, and the important thing is it got done fast (I wrote a distributed job scheduler in 2 weeks + 1 week to clean it up and work out the kinks, it's already running stably in production)
One of the alternative languages you mention is single-threaded, and the other has a global interpreter lock (in its most common implementation). That Elixir is superior to them for parallel programming doesn't really say much.
It's hard to pick one big draw, but I'd say the biggest for me is that everything I wanted to do in rails has been possible in Elixir and then additional functionality not easily possible in rails is trivial in Elixir. I often consider the distribution techniques as "enhancers" as you could work around them with global locks and data stores, but you don't need to.
I'm very bullish on Elixir and I'm curious to see where it will go. Looking forward to giving my talk about bringing Elixir into production (from a human and technical standpoint) at Lonestar Elixir conference.
I also noticed that every functionality I write in both Ruby and Elixir is both more concise (less code) in Elixir as well as 5-10x faster :)
Web developers seem to follow trends; Perl -> DJango|RoR -> Node.JS -> Scala -> GoLang -> Elixir -> Something. Or, something like that. To me, it's like buying a $500 pencil and expecting that you should be capable of writing a better book.
If you get in bed with that crowd, don't expect that your program and 3rd party dependencies are going to be stable in 2 years.
Pretty unfounded comments regarding stability of packages long term. As with any community that makes it easy to publish packages, there will certainly be package churn over time. However, core libraries show 0 sign of this and Phoenix in particular has taken a very mature stance on new features.
I don't think anything negative of the language or core libraries.
Why would a SW company invest in niche languages where the resources (software developers) are really expensive and really hard to get? Technologically it's all great but economically that's a nightmare.
We have found a few people who knew it already and were looking for a job, but that is fairly rare. Instead, we know we can bring people up to speed on it quickly and also it signals to people that we're willing to give them some language options (Ruby or Elixir) within some boundaries. Having these options is good for ownership of an area.
If your status quo is working programmers as long as you can without a raise until they switch jobs, a niche language is a threat. If you re-evaluate your staff based on their experience gained, you can head off the churn.
As a counter-argument: I've experienced several times that "niche tech" companies offered non-competitive salary packages and perks, because they offered the cool tech instead; "sure, we can't match that other offer, but we built our stack on that language/tech that is so hot right now".
Not arguing about the quality of Elixir, just about the gatekeeping that happens in this thread.
The difference is, in Ruby, after 2-3 months, you'll be rewriting large portions of what you did to get there. And in another year or two, you'll find yourself being blocked by earlier designs not for a day or two, but by a month of work or better.
In Elixir, you simply will not experience that. Even if written poorly, it's easy to rework/change, and you probably won't need to anyway because it's just easier to get things done without painting yourself into a corner.
TLDR: try working with Erlang if you find your Elixir code-base in need of a refactor - chances are you'll learn conventions which will help you write more maintainable code in Elixir, too.
See http://www.paulgraham.com/avg.html for one answer.
There are also companies that we really successful in hiring by reaching out to functional communities in general. And of course, there are also companies struggling to hire Elixir developers compared to other techs. YMMV.
It's the first language I genuinely enjoy reading and writing even in my private life.
If I wonder about the internals of a library I use, I can just look into the code and kind of understand what's happening. Never had that with JS or anything.
I'm just a genuine fanboy.
Only drawback I feel is: Some libraries that would have been quite developed in JS are not that well developed in Elixir. Some libraries are quite dead and it's hard to find alternatives (mostly obscure stuff)
But on the other hand, it often seems manageable to just write it yourself, or fork it and move on.
But from a talent and recruiting perspective, I'm less enthusiastic. Elixir, yes, there's growing talent. But Ember, boy it seems like nobody is doing it, and I've had to convince potential candidates that it'll be worth their time for future employability to learn Ember.