12 ms·
Two years of Elixir at The Outline
- rdtsc 8y ago> 90ms is a 90th percentile response time on The Outline. Our post page route is even faster! We got this performance out of the box That's one of the features you get when using BEAM VM -- your code will likely be scalable out of the box as it's already built out of isolated independent small processes. If you want to handle more requests just run on a bigger machine. > The Community is wonderful! I use Erlang mostly but one major thing I admire about Elixir is it's community. Jose and team did a great job there. So it really has both - the great technical stuff, built on BEAM VM which was battle tested for decades, and the great community and tooling. > As soon as you get data from the external world, cast it into a well known shape. Good advice. Have sane validation at the edges and then convert data to some internal format that's easy to handle. As opposed to defensively sprinkling validation conditionals everywhere in the core code. That's makes the code cleaner and easier to reason about.
- ilovecaching 8y agoAs a long time Erlang developer I was suprised when I first heard of Elixir, and even more surprised at how nice it was. That said I think Elixir is very unerlangish. It’s very much Ruby ported to a new platform, it’s heavy on meta programming with macros and using a giant solve all your problems web framework (Phoenix). Compared to Ruby on Rails I think it makes a lot of good arguments. It’s faster, mix is a better package manager, and the BEAM is reliable at small to medium scale. Of course I recently posted about how I was moving away from the BEAM to Rust. I think Elixir has reached peak adoption, and I think the days of large frameworks like Rails and Phoenix that ask you to program in a DSL are sunsetting in favor of client side frameworks and smaller web services built off of thinks like Go’s net/http. I also think the reliability of BEAM is happening at the level of orchestration with containers and serverless. The BEAM itself doesn’t fit well into the cattle model of reliability and it has scalability issues when you reach a certain cluster size. Erlang is also quite slow at processing data and performing computations. What it does provide is reliable latency and load as you scale, which lets you grow your build mess quickly without buying more hardware or performing software miracles. I’m more optimistic about Rust because it provides the reliability I’ve come to expect from Erlang. We’ve been able to scale Rust to handle the same load as our Erlang cluster with a more manageable code base, lower average client latency, and lower memory usage on the same number of machines. I also know that people like to treat dynamic vs static typing as a matter of taste, but in my experience dynamic typing only works until you reach a certain level of scale. I’ve watched several python and erlang codebases turn into incompressible messes because of this. This is why we’re seeing things like typescript and mypy become requirements rather than nice to have. So I’d rather just bite the bullet and get a really nice static type system up front.
- enraged_camel 8y ago>>I think Elixir has reached peak adoption, and I think the days of large frameworks like Rails and Phoenix that ask you to program in a DSL are sunsetting in favor of client side frameworks and smaller web services built off of thinks like Go’s net/http. Is this just a hunch or do you have numbers to back it up? edit: This is the second Elixir/Erlang thread you have attempted to hijack by spreading FUD about them and pitching Rust as an alternative. You come across as a Rust shill.
- dnautics 8y agoYou don't have to use Phoenix... I'm currently doing a plug-only deploy and it feels great. > I’m more optimistic about Rust because it provides the reliability I’ve come to expect from Erlang. Im very curious about rust myself, Out of curiosity, how does Rust protect you against wierd things happening like errors in drivers, or dropped packets, e.g.
- griffinmb 8y agoPhoenix is really a very small framework, and its codebase can be easily understood in just a couple of days. Phoenix being "large" is a common (but unfounded) criticism that seems to stem from Elixir's superficial resemblance to Ruby.
- ilovecaching 8y agoI’ve read the book on it, and I disagree. It’s certainly its own platform and you’ll have to deal with its ups and downs and learn its DSL.
- enraged_camel 8y agoThe only way Elixir resembles Ruby is the syntax. Everything else is totally different.
- zinclozenge 8y agoThe similarities are exclusively because of the do..end Beyond that there's barely any resemblance.
- tschellenbach 8y agoWhen working on V2 of our architecture we tried out both Elixir and Go extensively. For our workload Go was roughly 8 times faster than Elixir, while both handling concurrent workloads exceptionally well. (bit of background on the type of work we do: https://stackshare.io/stream/stream-and-go-news-feeds-for-over-300-million-end-users https://stackshare.io/stream/stream-and-go-news-feeds-for-ov...) That being said, I'd love to find an excuse to work on a little project with Elixir and Phoenix. Some really cool concepts. Especially the build in support for realtime is really nifty. With Go that's much more work to get up and running.
- dnautics 8y agoI can't say I'm an expert in go - I only wrote the cli for my program in go, but I feel like elixir optimized for developer productivity far more than go does. Most of the time you don't really need that level of performance.
- zinclozenge 8y agoYou're going to find that for workloads that require churning through requests, or something similar, that Go or Rust or something similar is simply better. Elixir/Erlang is mostly good for dealing with a lot of persistent state, staying up and running, and reliably getting data from point A to point B. But none of those it does particularly fast, unless you can do it without needing to mutate a data structure/variable, or you can take advantage of the fast binary pattern matching.
- Thaxll 8y ago> With Go that's much more work to get up and running. Can you expand on that? Go is very fast to get up and running, you DL go runtime, git clone a repo, go build . and have your single executable ready to go.
- tschellenbach 8y agoThis bit of Phoenix: https://phoenixframework.org/blog/the-road-to-2-million-websocket-connections https://phoenixframework.org/blog/the-road-to-2-million-webs... Takes substantially more work in Go.
- momania 8y ago> consider going old school and rendering your HTML on the server; you might be delighted One idea I'd wish more sites would consider.
- catacombs 8y agoYeah, writing about using Elixir at The Outline is cool and all, but let's not forget this place wiped out most of its staff, including developers.
- yawaramin 8y agoCare to point us to the story?
- latch 8y agoI'm bullish on Elixir and I agree that ExUnit deserves to be singled out. Whether you consider Elixir fast or slow probably depends on what you're coming from and what you're doing. Personally, I wish the performance story would significantly improve. But, the reason I think Elixir should be your goto language comes down to process isolation and the ecosystem around it (the synergy of the runtime, libraries and language). If you're doing a pure web app, you might not fully leverage it. But, for anything that involves long-lived in-process data (like a workflow/pipeline, or persistent connections), the way to approach this in Elixir (with supervisors, processes, message passing and pattern matching) tends to result in highly cohesive and maintainable code. Message passing is a pretty effective antidote to coupling. I just finished writing code that had to take a stream of unordered time-based data and dedupe it over a small window. I'm not overly pleased with the final result, but it's completely isolated from the much larger app it sits in, easy to refactor/rewrite, with no risk of someone taking a shortcut and just accessing some internal detail, and testable. I feel like I wrote a microservice, but without any of the challenges of a separate app.
- hderms 8y agoI can see the logic behind the statement 'message passing is a pretty effective antidote to coupling' but I personally subscribe to the idea that actors don't compose. In light of that I think use of actors and messages implies some level of coupling which may not be obvious. Actors can't be arbitrarily composed together without knowing which actors they will be interacting with, making their addresses available and understanding their protocols so they can communicate together. Functions have no such requirements so I don't totally understand how message passing implies less coupling than conventional function-based architectures
- blissofbeing 8y agoI would like to use Elixir more at work, I'm hoping that we'll be able to use it for some new projects. I love the language and the community.
- pg_bot 8y agoCan you print an example of the random test error? I run a decent sized phoenix project (150K lines) and likely can help you out if you give a bit more detail. With regard to associations, for simple belongs_to associations I will usually write separate changeset functions where the first argument is pattern matched against the record. I will give an example with Post/Comment defmodule MyApp.Blog.Comment do def changeset(%Post{} = record, params) do record |> build_assoc(:comments) |> changeset(params) end def changeset(%Comment{} = record, params) do record |> cast(params, [:body]) |> validate_required([:body]) # Continue with validations, constraints, etc. end end In this case the changeset function calls itself once it handles building the association. For more complex conditions, I will usually write a "Form" module that may handle that case usually with a Enum.reduce combining multiple calls to `put_assoc` so that I can make nested associations in one insert. Hope this helps a bit.
- enraged_camel 8y ago>>def changeset(%Comment = record, params) do Small typo: should be %Comment{}
- pg_bot 8y agothanks, fixed
- davidw 8y ago> 90ms is a 90th percentile response time on The Outline How much of that is the DB, and what DB are you using? I like the BEAM environment quite a bit, myself, and have used it successfully in several semi-embedded environments, where it really shines because of its robustness and reliability.
- davydog187 8y agoPostgres. Currently, our mean response time for the route with the most throughput is 16ms. Its to measure exactly how much of that time is being spent in the database, but the main query takes about ~6ms.
- conradfr 8y ago- Ecto is still a climbing hill for me. In fact I'm currently trying to warp my head around changesets and associations. - Same for GenStage. I wanted to use them for workers pulling from an episodic queue but the docs with the emphasis on consumers pulling multiple events at once from a never ending stream didn't really speak to me. Tailoring to my usage looked verbose: storing demand etc. I'll have to revisit when I'm better at Elixir. - I still think caching should be part of Ecto in some way, at least in the docs. - I would still like more straightforward template inheritance in Phoenix (think Jinja2, Twig). Excited for the future releases of Phoenix 1.4 and Ecto 3.
- pythonaut_16 8y agoI'm not sure exactly what made Ecto click for me, but once it did, it quickly became my favorite ORM/data mapper. Have you read some of the Plataformatec blog posts about Ecto?
- tortilla 8y agoI thought similar but once Ecto clicked for me, everything else felt clunky. I don't know what was the exact cause but using it for a real project helped. Also Programming Ecto is great. https://pragprog.com/book/wmecto/programming-ecto https://pragprog.com/book/wmecto/programming-ecto
- BenMorganIO 8y agoChangesets are great. I was hesitant when I first used them coming from Rails, but they're very useful. Combined with the contexts, they help remove the need for implementing ActiveRecord callbacks; which caused a lot of problems with Rails apps as they aged.
- dnautics 8y agoI had a hard time with those too! However, changesets are really awesome. Easiest, bastardized intro is to think of it as preventing a trip to the database/data sanitization before doing any sort of state modification. Then if you stay open minded later you will learn other things that it can do as you along.
- 8y ago
- joevandyk 8y agoDoes anyone miss or want type checking when working with Elixir? After working with Ruby to build large systems for so long, I'm missing a little bit of the safety that types provide. Typescript has been great.
- mmartinson 8y agoYes and no. I also come from a similar background with the pain of building a large system in Ruby, and having been doing Elixir for 2 years since then I find it that is has the right tools to let you go either way to a degree. I like that you have all sorts of optional tools for adding runtime type checks like guards and struct pattern matching if you want it. Using it extensively will inhibit generalizability, but it can be tremendously useful for isolating errors in problematic code paths by failing early and explicitly. Typespecs enforced by Diayzler aren't exactly a safe type system, but can help with some of the sanity checks, documentation, and linting that Typescript gives you (not really being a safe type system either). I would say the only thing I really feel is missing is being able to check for protocol implementations and perform runtime control flow based on these.
- Zalastax 8y agoGradualizer supports Elixir. It will move into beta soonish :)
- ninjakeyboard 8y agoI come from Scala to Elixir and I personally prefer static typing for a lot of reasons. I have come to very much enjoy the looser typing as I can write more code more quickly, serialization is less of a pain, etc. Elixir is still compiled (and you have dialyzer if you really want to go that way.) You can match on a struct type and the compiler will catch a typo if you try to access a member that doesn't exist in the struct so you can go a long way to lean on the compiler. All that said, I can attribute +50% of the bugs that hit production to things that a type system would have caught. I would prefer to go back to writing code with Scala/Akka and would choose those technologies in a blink for any system that I'm going to grow over time. development time _is_ slower with scala/akka (assuming you don't count time spent trying to refactor or fixing bugs a typesystem would catch) but it's probably an order of magnitude faster for computational tasks, not slower on concurrency related items, has a bigger/more mature eco system (which is huuuuge) and gives you OO beside FP which is highly valuable for design. The skill ceiling is higher with scala/akka so you can keep growing and learning. And OMG trying to refactor Scala vs trying to refactor elixir?? IntelliJ is a jet powered pogostick for applying refactorings (to quote Beck) - It's very had to make large structural changes to elixir code without a static type system and tools to help you rename, extract methods and classes, and move them around. I can do things in scala I would never dream attempting without aa type system which is probably my biggest gripe for long term maintenance. That being said, working with elixir has turned me into an Emacs junkie and I personally find a lot of value in that but it doesn't help me deliver better code faster, just gives me nerd points because I can check the weather and reply to email from my textt editor
- deleted 8y ago[deleted]
- helpme3 8y agoYou should definitely CDN your data. A majority of a client-side latency is spent round-tripping to the server. Using a CDN greatly reduces this time. I don't know your workload, but 60ms doesn't seem that fast. Don't get me wrong, it's pretty good, but for a read-heavy load it seems like this could be optimized to be sub-10 ms at the 99th percentile.
- davydog187 8y ago90ms is the mean across all routes. Our post route, with the highest throughput, has 16ms request/response time. We do use a CDN, and we spend a lot of time thinking about performance. Thanks for your insight.
- pdimitar 8y agoAn app I maintained that was doing anywhere from 3 to 50 DB queries per request, 98% of the time was spent in the DB roundtrips. The Elixir code itself took 0.2ms to 3ms. It's really lightweight. Can't be anywhere near Go or Rust of course but it's provably much faster than Ruby, Python and PHP, and very often outperforms JS on V8. Most of the overhead I observed in Elixir apps stems either from external network requests (DBs, K/V stores etc.) or CDNs / static assets. Most of the Elixir code is almost invisible in terms of footprint.
- jashmatthews 8y ago> it's provably much faster than Ruby, Python, PHP This is simply not true. Ruby + Sinatra/Roda/Hanami + Sequel even performs better than Elixir + Phoenix + Ecto in some cases. It's basically dead-even. For example: https://www.techempower.com/benchmarks/#section=data-r16&hw=ph&test=query&l=hos0z3&f=3y8-0-0-2zgg-0-0-0-2 https://www.techempower.com/benchmarks/#section=data-r16&hw=... BEAM has the benefit of being a register-based VM vs the stack based VM of CRuby but the immutable semantics of Erlang/Elixir negate a lot of the VM and GC advantages.
- pdimitar 8y agoYou should read a bit more on how the various popular benchmarks manage their different code bases and not just quickly believe the graphs. There's a lot of story behind them. Several languages, Elixir included, are misrepresented due to badly written and non-idiomatic code (caveat: this was true a while ago, I haven't checked lately though). And when its users try and open a PR they are shot down with "We don't allow cheats". No cheats though, just very normal language best practices. So I disagree with you here. I maintained a ton of Rails and several Sinatra apps and to say they are on the level of Elixir makes me wonder what kinds of hosting budgets we both had access to and were they really so hugely different. (Sinatra was actually very decent but it being Ruby it doesn't scale well and Erlang/Elixir work much more stably until they start having problems compared to it.) Again, don't just trust the popular benchmarking suites. You will quickly discover they usually either have an agenda (like the Crystal language creators always showing how quicker it is compared to Elixir, and refuses any Elixir PRs -- seriously no idea what the Elixir community ever did to them) or only pay lip service to representing all languages fairly (like TechEmpower does). (Edited because I wrote the first version after a nap and it came off sharper than I intended.)