[HN Gopher] Elixir and Rust is a good mix
___________________________________________________________________
Elixir and Rust is a good mix
Author : rio517
Score : 196 points
Date : 2023-04-13 18:29 UTC (4 hours ago)
(HTM) web link (fly.io)
(TXT) w3m dump (fly.io)
| mrmincent wrote:
| I made the 'risky' decision to learn Elixir & Phoenix for a 6
| week University project that I delivered earlier this week. It
| could have turned out to be a terrible decision but honestly once
| I got my head around it, it's probably some of the most
| productive coding sessions I've ever had. Deploying to Fly.io was
| also amazing, I think it was 3-5 commands from signup to deployed
| with a database. Very happy with it all.
| alberth wrote:
| Doesn't the use of Rust have to be extremely minimal because
| ErlangVM has hard time limits on their preemptive scheduler and
| if your Rust code hasn't finished when preempted, that causes
| lots of problems.
|
| EDIT: thanks for pointing out where in the article this is talked
| about.
| peregrine wrote:
| I go into this in the article :) the rustler team has a made a
| DirtyNif that can work around that, or you can manually yield
| if you'd like.
| clessg wrote:
| Indeed, your (excellent) article addresses this, here's the
| gist for those following along:
|
| > Change `#[rustler::nif]` to `#[rustler::nif(schedule =
| "DirtyCpu")]`
|
| > This tells the Rustler and BEAM to automagically schedule
| this in a way that won't block the entire world while it
| works. Again amazing, this is called a DirtyNif and is way
| more difficult to work with when you are manually using this
| via C.
|
| Essentially, regular NIFs have to be extremely fast (< 1ms)
| because the VM can't preempt them - they run on the same
| scheduler threads the BEAM itself uses. Dirty NIFs solve this
| by running jobs in a completely separate thread pool ("dirty
| schedulers"). Rustler's docs explain it succinctly
| (https://docs.rs/rustler/latest/rustler/attr.nif.html):
|
| > For functions that may take some time to return - let's say
| more than 1 millisecond - it is recommended to use the
| `schedule` flag. This tells the BEAM to allocate that NIF
| call to a special scheduler. These special schedulers are
| called "dirty" schedulers.
|
| > We can have two types of "lengthy work" functions: those
| that are CPU intensive and those that are IO intensive. They
| should be flagged with "DirtyCpu" and "DirtyIo",
| respectively.
|
| (Somewhat OT, but since I'm here: excellent article @
| peregrine! I _really_ enjoyed the read. Elixir and Rust are
| such a perfect fit. Plus, some of the specifics will be
| helpful for certain image-related things I 'm actively
| working on, which is always nice. :) )
| Dowwie wrote:
| If you're extending Elixir with Rust, familiarize yourself with
| precompiled rustler: https://dashbit.co/blog/rustler-precompiled
| benatkin wrote:
| This makes me think of DotCloud, the precursor to Docker Inc. The
| idea that you can run anything has been around for a while. I
| don't see how Fly.io is especially good for Elixir. It seems to
| be about delivering reliable resources for each service, and it
| works no matter the web framework, as long as you have a PaaS
| that's geared towards running arbitrary OCI images. These posts
| seem a lot like the content on the DigitalOcean site, except
| they're written by staffers instead of freelancers.
| https://www.digitalocean.com/community/pages/write-for-digit...
| mrdoops wrote:
| Elixir does distribution and concurrency really well, so Fly
| making multi-region deployments easy makes it a good fit.
| benatkin wrote:
| So is Fly good for small sites or is it good for huge sites?
| What's a big customer? For small sites my idea of a multi-
| region deployment is a single-region deployment that works in
| multiple regions thanks to the magic of the Internet.
|
| I see this which mostly seems to be content sites.
| https://www.wappalyzer.com/technologies/paas/fly-io/ Same on
| the first forum result: https://community.fly.io/t/customer-
| success-stories/4882
| peregrine wrote:
| One of the interesting bits about Fly is we have our own
| servers distributed globally and connected via wireguard. Which
| makes it is trivial to setup Elixir/Erlang distribution
| globally, and most importantly, close to your customers. While
| its not a magic bullet it is pretty amazing to type a few
| commands have a globally deployed and directly distributed
| application.
|
| Further Fly builds its Dashboard internally with Phoenix
| LiveView. We want the Phoenix and Ruby and Laravel and more
| communities soon, to grow because we believe if they grow, we
| will too.
| benatkin wrote:
| That's just internal networking isn't it? VPC on the big
| clouds, DigitalOcean, and Vultr. Private Services on
| render.com.
|
| I remember when Fly.io used to tout Firecracker but that is
| just a KVM engine, along with QEMU used on a zillion hosts.
|
| What I'd like to see are customer success stories.
|
| Edit: looks like you have to set up your own cross-region
| links on DigitalOcean and Vultr. So that interests me
| somewhat. :)
| brightball wrote:
| This is essentially what has gotten me into Rust. Knowing how
| easy Elixir makes it to offload a piece of my system that would
| benefit from it without needing to write the entire thing in
| Rust.
| bglusman wrote:
| I admit for a long time this was my primary motivation to learn
| Rust, but, sadly, I haven't come across problems in years that
| were CPU bound/where I needed something like Rust... Rustler
| still looks like a great fit if needed, but, depending on the use
| case, if I were CPU bound and needed to write my own code/not
| just use a Rust library, I'd be as or more likely to look at
| using Zig and Zigler[0], for much faster learning curve, and from
| what I've read, easier tighter integration into elixir, including
| I think language server integration. Some discussion here[1]
| though I forget if I listened to this one or not.
|
| [0]https://github.com/ityonemo/zigler
| [1]https://podcast.thinkingelixir.com/83
| packetlost wrote:
| Efficient PLs are useful for latency sensitive applications
| too, not just CPU bound ones. Though, that doesn't widen the
| number of cases by much...
| jimbob45 wrote:
| _I haven 't come across problems in years that were CPU
| bound/where I needed something like Rust_
|
| This is where Rust falls short of C#: scaling to the issue at
| hand. C# can build you a beautiful app at a high-level but also
| lets you dick with pointers and assembly at a low level. Rust
| insists on defaulting to pass-by-move and an arcane trait
| system that hold it back from being usable in large projects.
| FpUser wrote:
| I remember MS reps coming to our offices in the 90s and
| "Teaching" us how to program and how it is cool to have one
| person writing GUI and the other "guru" writing high performance
| C++ to be used in critical parts.
|
| I showed them a piece of software that was mighty fast, Internet
| enabled and GUI intensive. They liked the software but asked
| where did you get this particular screen control from. You've got
| to see their faces when told that the whole software was written
| by a single person in Delphi.
| peregrine wrote:
| Author here. The rustler team has really made a great developer
| experience using Rust in Elixir and I just had to share.
| mtremsal wrote:
| This is amazing! The Gods have sent me the perfect excuse to
| finally read all the rust ebooks I've been collecting! I have a
| toy project where certain functions are CPU-bound and would make
| for a perfect learning project. Very timely read!
| maciejgryka wrote:
| Shameless plug & also a point of support for the article: I used
| that same stack to build https://regex.help/ (more details here
| https://maciej.gryka.net/building-regex-help)
| freedomben wrote:
| Neat! You should definitely add a link to the github repo to
| the main page of regex.help though, because I didn't realize it
| was open source and I'll use it now.
|
| Github link for others:
| https://github.com/maciejgryka/regex_help
| satvikpendem wrote:
| I used to use Elixir, but the lack of static types got to me
| (especially since I prefer the type-driven development
| methodology). Using Rust afterwards was great, plus it was faster
| than the BEAM. I guess, why not use Rust entirely instead of as a
| FFI into Elixir or other backend language? I've been using Axum
| and it works pretty well. The only time I had to do FFI with Rust
| was with Flutter via flutter_rust_bridge, for running a CRDT
| library (automerge) that was implemented in Rust, for offline app
| functionality.
| shiryel wrote:
| As it usually goes in programming, "it depends on your
| objectives", there are things that are easier accomplished with
| the ErlangVM than Rust. Also, if you want a language that uses
| the ErlangVM and has static types, maybe you should take a look
| at Glean[1].
|
| In my case I prefer to work with Elixir because of the
| community, as I find easier to work professionally with Elixir
| than some other mainstream languages, as mostly projects
| follows the same good practices, use the same tools and have
| good documentation.
|
| [1] - https://gleam.run/
| kerkeslager wrote:
| > I used to use Elixir, but the lack of static types got to me
| (especially since I prefer the type-driven development
| methodology).
|
| You might be interested in Gleam[1].
|
| [1] https://gleam.run/
| satvikpendem wrote:
| I used to use some Gleam back before the syntax change, when
| it looked more like Haskell. I found that rather than using a
| language with a comparatively smaller community, I'd rather
| just use something that's well supported, so I settled on
| Rust instead.
| whalesalad wrote:
| There is a great talk from the creator of the language on why
| static types are not necessary. Of course there is nothing
| wrong with wanting them and feeling that they are, but I think
| he (Jose) makes really great arguments for why Elixir is the
| way it is. https://www.youtube.com/watch?v=Jf5Hsa1KOc8
| hosh wrote:
| Everyone have different needs. For us, OTP on top of pre-
| emptive lightweight threads and a REPL in production is a huge
| winner for troubleshooting, finding performance bottlenecks,
| and reliability.
| callamdelaney wrote:
| Rust is faster, but that's like saying writing to memory is
| faster than writing to a database - sure, it is - but the BEAM
| is designed with something completely different in mind and is
| still 'quick enough' for handling those cases. That said
| dealing with elixir syntax is like rubbing sandpaper in your
| eyes for 8 hours a day.
| [deleted]
| malkosta wrote:
| > why not use Rust entirely instead of as a FFI into Elixir
|
| Because many times you value fault-tolerance and distribution
| more than performance.
| rapnie wrote:
| There's a couple of Rust libs and frameworks inspired on
| Erlang/OTP in 'best of both worlds' attempts, such as
| https://lunatic.solutions
|
| I found others like Lunatic before, but cannot remember right
| now.
| malkosta wrote:
| I guarantee your life will be simpler with Erlang.
| aeturnum wrote:
| I think the most attractive part of the Elixir / Erlang
| ecosystem is BEAM and its ability to manage code, processes and
| resources in an extremely parallel environment. Rust operates
| at a lower level and it feels deceptive to just compare the
| languages.
|
| That said - if you don't benefit from what BEAM has to offer -
| I agree Rust is a really attractive alternative.
| toast0 wrote:
| Rust doesn't have a lot of good runtime introspection tools (or
| they're very not obvious). If you're running a system with a
| lot of concurrency, it's nice to be able to attach a debugger
| and find out exactly what's going on with each of your tasks.
|
| I haven't seen hot loading for Rust (but a quick search shows
| there's some out there), and I'm not sure how amenable Rust is
| to dlopen and friends to force the issue.
|
| Erlang (and Elixir) have a constrained language that allows for
| BEAM to be effectively premptive in a way that a Rust
| concurrent runtime can't be. At every function call, BEAM
| checks if the process should be preempted, and because the only
| way to loop is recursion, a process _must_ call a function in a
| finite amount of time. A Rust runtime cannot preempt, if you
| need preemption, you 've got to use OS threads, which limits
| capacity, or you need to accept cooperative task switching.
|
| Also, some of us are as anti-typing as you are pro-typing. :)
| RhodesianHunter wrote:
| >Also, some of us are as anti-typing as you are pro-typing.
|
| Assuming ample experience with both, how does one reach this
| conclusion?
|
| I have yet to see a project of any size that needs to be
| worked on by multiple teams and is written in an untyped
| language not descend into dumpster fire.
| ReflectedImage wrote:
| Untyped languages work fine if you use them with
| microservices.
|
| The only thing you can't do is have both untyped and
| monolithic at the same time.
| freedomben wrote:
| I have yet to see a project of any size that needs to be
| worked on by multiple teams not descend into dumpster fire.
| Typing can definitely help, but it's just a small tool.
| Java is a pretty typed language and I'm seen some real
| doozy code bases in Java.
| toast0 wrote:
| I work on a lot of 'glue' issues, often with languages like
| Perl, PHP, and Erlang (and a bit of Javascript here and
| there). Specifying types all over the place in languages
| like C, C++, Java, and Rust feels like it gets in the way
| and limits more than it helps. (feelings more than data
| here, of course)
|
| Sure, at boundaries between teams, you need to specify the
| data in some way. That could be a type, but for me, often
| the other team is using a different language than me, so it
| needs to be a language agnostic type, and it can't include
| unsigned numbers because Java can't cope, and it can't
| include large integers because Javascript can't cope, etc.
| Protobufs are popular, json is too.
|
| I have a lot of unpopular opinions though, and that's fine.
| It's just tiresome that everyone wants to come in and add
| types to things that don't need them. Also, I agree with
| dllthomas, most developers and teams are capable of
| creating dumpster fires in all sorts of environments, with
| all sorts of tooling. :)
| cglong wrote:
| IMO, TypeScript strikes a great balance here. I loved the
| way I could cast something to `any` when hacking
| something out, then add proper type annotations once it's
| ready to be productized. It also a did a good job with
| type inference.
|
| Disclaimer: I work at Microsoft, but not in the Developer
| Division
| RhodesianHunter wrote:
| You can absolutely create a dumpster fire in any
| language.
|
| Putting the fire out in an untyped language is a
| Herculean effort.
| throwawaymaths wrote:
| have you tried doing it in elixir? It's not that bad.
| freedomben wrote:
| Test coverage is, in my experience, much more important
| factor than typing. A codebase with great testing is much
| easier to aggressively refactor/change whether typed or
| not.
|
| That said, a dumpster fire usually has no or little
| tests, so maybe we're arguing non-existent hypotheticals
| :|
| di4na wrote:
| In my experience it is even harder in a typed one because
| now you have to deal with the type system nightmare they
| built. So the compiler fight your refactoring.
| bombela wrote:
| Nobody code without a type system. The distinction is
| build time vs runtime type checking. At build time you
| catch bugs that would appear later at runtime. Which is
| more costly to fix later.
| toast0 wrote:
| > Which is more costly to fix later.
|
| This assumption is changed, IMHO, by Erlang. Hot loading
| makes the cost to make _small_ changes very low. So the
| question becomes, do you pay the definite cost of build
| time type checking (usually includes coding time type
| annotation), or do you accept the _possible_ future cost
| to making small fixes.
|
| Of course, if you work in an organization where even a
| small fix requires months to release, then do all the
| things you can to prevent making small mistakes.
| sdeframond wrote:
| The cost at runtime also includes loss of data, inferior
| user experience, direct financial loss or even loss of
| human life for some systems.
|
| It really depends on the domain but it definitely is more
| than pushing an update.
| ReflectedImage wrote:
| Well no since unit tests run faster than build time.
| [deleted]
| packetlost wrote:
| You never actually get away from types, they are a core
| requirement of using any data beyond raw bytes. The
| guarantees that a strong type system provide mean you can
| be certain about certain things before your program even
| runs. If that's a problem for you, you're likely just
| leaving bugs on the table to be discovered at runtime.
| [deleted]
| toast0 wrote:
| Conway's Law means you have to fix the organization
| before you can fix the software. That's the real
| Herculean effort.
| fiddlerwoaroof wrote:
| I tried Haskell for a while and switched to Common Lisp
| (although I still follow Haskell from a distance). My
| experience just doesn't match up with the claim that a
| project of any size written in an untyped inevitably
| descends into a dumpster fire. I've worked on largish
| systems in several dynamically-typed languages and several
| statically-typed and I personally haven't noticed any major
| difference in overall productivity suggesting that static
| types are better: they just have different friction points
| and different ways of working work better in each paradigm.
| RhodesianHunter wrote:
| The issue isn't productivity. As far as just slamming out
| code untyped languages are undeniably faster.
|
| The issue is working on projects once they've reached a
| certain size where you have no idea what the intent of
| the original author was and you maybe need to refactor,
| add-in major pieces, or change anything with the
| expectation that it continues to work.
| fiddlerwoaroof wrote:
| I'm including maintenance costs in "productivity". I've
| worked on large dynamically typed codebases and never
| experienced what you're talking about.
|
| I think it's a question of understanding how to work and
| think without explicit types rather than something that
| makes statically typed codebases easier to maintain.
| ReflectedImage wrote:
| Untyped code bases with microservices are the best code
| bases out there by far.
|
| They are exceptionally easy to refactor, add-in new
| parts, etc.
|
| The keyword is microservices, you need to know how to do
| proper microservices if you are using untyped code.
| throwawaymaths wrote:
| >I have yet to see a project of any size that needs to be
| worked on by multiple teams and is written in an untyped
| language not descend into dumpster fire.
|
| Github? Dropbox? I mean they both eventually went to type
| hints in their respective languages or migrated to a typed
| language but for a long time I'm certain it wasn't.
| RhodesianHunter wrote:
| And I can pretty much guarantee you that some form of
| dumpster, fire or other was the driving factor behind
| those moves.
| ollien wrote:
| It's worth noting of course that ~Github~ Dropbox was the
| driving force behind mypy
| throwawaymaths wrote:
| yes, that is exactly what I meant by "they both
| eventually went to type hints", but doesn't github use
| ruby? I think you mean sorbet? Dropbox was the driving
| force behind mypy (IIRC).
| ollien wrote:
| bleh, I meant to say Dropbox.
| dllthomas wrote:
| While I'm pretty solidly in the "pro-typing" camp, it seems
| worth acknowledging that such projects often turn into
| dumpster fires in typed languages as well, even
| expressively typed languages.
| jjnoakes wrote:
| Maybe, maybe not - but in my experience, a dumpster fire
| with static types is easier to read and understand and
| also easier to refactor.
| dllthomas wrote:
| That matches my experience as well. I just think we need
| to be resistant to reading too much into "I've seen bad
| code that X", because that can easily be true of almost
| any X.
| jibe wrote:
| You think that statically typed languages don't often
| turn into dumpster fires? Not my experience! Even though
| there are real advantages.
| throwawaymaths wrote:
| it's certainly possible to write a nif that cooperates with
| the VM so that its async yield points match up with the VM's
| expectation of yield points. Definitely tricky to do
| correctly in C (given that C doesn't have a yield statement,
| lol, you have to structure it as an awkward tail call where
| you pickle/unpickle whatever state you want to keep around or
| unmarshal it from a passed struct). I'm not sure if that's so
| easy to do in rust.
| toast0 wrote:
| Sure, you can do that for the code you write, but you can't
| for the code you call. Any Erlang you write or call will
| have this property, and most of the ERTS provided C code
| will as well; either it's trivially finite, it is designed
| to yield during the work, or it's neither but it hasn't
| triggered anyone to fix it, with occasional deviations of
| things that become known issues (like -- was for some time;
| although it got fixed twice and is now fairly ok).
| dxhdr wrote:
| > Using Rust afterwards was great, plus it was faster than the
| BEAM. I guess, why not use Rust entirely instead of as a FFI
| into Elixir or other backend language?
|
| Sure, you just need to reimplement light-weight threading with
| preemptive scheduling prioritizing latency over throughput,
| extremely robust fault tolerance with a supervision hierarchy,
| and runtime introspection with code hotloading capabilities.
| Maybe you could add frictionless distributed system support as
| well.
|
| No big deal, why not right?
| lucasyvas wrote:
| Is any of this needed though? For some use cases yes, but for
| many no. It's possible the parent has no practical use for
| these capabilities.
| [deleted]
| pdpi wrote:
| If you're building a project that actually benefits from
| Elixir/Erlang's features, you can have a constructive
| discussion about whether those features justify sacrificing
| static typing. If you don't actually benefit from those
| features, "I tried to use a language that's unfit for my
| purposes and found it unfit for my purposes" is a comment
| of very limited value.
| hexo wrote:
| "Is Rust needed at all? For some use cases yes, but for
| many no. It's possible the parent has no practical use for
| its capabilities."
|
| You hear yourself? Yes? Good.
| tines wrote:
| You may not realize this, but your comment comes off as
| being very snide and annoying, which I'm sure you didn't
| intend. Try rewording things in a way that assumes good
| faith, and besides making better relationships you'll
| persuade people more effectively as well.
| lucasyvas wrote:
| The author gave static types and performance as examples
| of things that matter to them that they clearly don't
| feel Elixir does as well. It's probable they weren't the
| target demographic for Elixir, like many aren't the
| target demographic for Rust.
|
| The initial response made an assumption that all these
| capabilities matter either way, and that's not true.
| tessierashpool wrote:
| This is not helpful. Please consider what if might be
| like if you were to edit your comment to improve it.
| dsiegel2275 wrote:
| I used to use Elixir. I still do, but I used to too.
| eikenberry wrote:
| Could also be nice as it means the Rust parts are smaller and
| so its slow compiler speed isn't as much of a bother. You get
| the safety and runtime speed of Rust with the fast iteration
| cycle of Elixir. Best of both worlds type of thing.
| satvikpendem wrote:
| I often find FFI more annoying than it should be (ie
| something always breaks somewhere), I try to avoid it where
| possible.
| zinclozenge wrote:
| I pretty much agree with this. As much as I liked the core
| language, I absolutely hated using anything else in the
| ecosystem, and in my opinion a lot of that was due to lack of
| static types. Using Ecto as the main way of interacting with
| the database was a miserable experience. Part of that was due
| to skill issue I'm sure.
| kerkeslager wrote:
| > As much as I liked the core language, I absolutely hated
| using anything else in the ecosystem, and in my opinion a lot
| of that was due to lack of static types.
|
| You might be interested in Gleam[1].
|
| [1] https://gleam.run/
| malkosta wrote:
| > Ecto as the main way of interacting with the database was a
| miserable experience
|
| That's very common when you don't know DBs. But DB savy
| developers usually claim the opposite, because the syntax is
| more familiar.
| zinclozenge wrote:
| > That's very common when you don't know DBs.
|
| I'm not a 20 year DBA greybeard veteran, but I'm
| comfortable enough to write schemas and queries by hand
| without any issue, and the entire time I wished I could do
| just that instead of using Ecto.
| sph wrote:
| No one stops you from writing raw SQL with Ecto.
|
| But good luck creating composable SQL with raw string
| interpolation, which Ecto excels at.
| sergiotapia wrote:
| On the flipside for me Ecto is the perfect balance between
| ORM and writing raw SQL queries. I've used many different
| ORMs like Entity Framework, ActiveRecord, Prisma, Ecto - Ecto
| stands alone.
| throwawaymaths wrote:
| I believe... technically Ecto is not an ORM but a query
| builder, which may be why you like it better than an ORM.
| parthdesai wrote:
| > Using Ecto as the main way of interacting with the database
| was a miserable experience.
|
| Ecto is prolly one of the best ways to interact with db.
| Genuinely curious, what other ORMs have you used?
| zinclozenge wrote:
| I usually just write the SQL, honestly. For the last 6ish
| years of my career I've mainly been in the Golang world, so
| the closest I've gotten to an ORM is a utility to scan rows
| into structs.
| freedomben wrote:
| Interesting, I usually just write the SQL too in most
| projects, but in Elixir I _love_ Ecto because it 's so
| close to the SQL but has an elegant and powerful way to
| handle interpolated values.
| vlunkr wrote:
| I'm in the same boat with Elixir. I love many aspects of the
| language, but it borrows the fast-and-loose type ecosystem of
| ruby.
|
| nil is an especially big problem. Any value could be nil, and
| this will absolutely bite you over and over. nil even allows
| you to use square brackets for some reason
| (some_nil_value[:some_key]) which is a great way to disguise
| the actual issue.
|
| There is optional type checking with Dialyzer, which is good
| but has some problems. The warning output can be really hard to
| read, and unless you're diligent in using it across most of
| your project, it's not very useful, because you'll end up with
| 'any' values all over.
| OkayPhysicist wrote:
| I disagree. Any value could be nil, but any value could be 5,
| too. In fact, nil is just like any other atom. Elixir shines
| when you lean on pattern matching as much as possible. Your
| functions should unpack as much as possible within the
| function definition, frankly the "if" macro is completely
| superfluous to case and in rare cases cond. You'll find that
| you match the data you want, and thus reject anything else.
|
| Square bracket dictionary accesses are a code smell, because
| you should be using %{^key = val} = dict or Map.fetch(map,
| key) or rarely Map.fetch!(map, key).
|
| If you do that, managing typing in Elixir just boils down to
| defining structs to differentiate cases where dictionary A
| and dictionary B contain similar keys but strictly are not
| interchangeable.
| throwawaymaths wrote:
| Elixir is incredibly consistent with how it uses nil. It does
| take a bit of work -- you have to really pay attention to the
| convention of fetch! vs fetch vs get -- and often times
| library maintainers are not... always the best at that... but
| for all the problems that dynamic languages has, nil in
| elixir is pretty low on the list. I think I get bitten by
| maybe one a year, and it causes the sporadic report on
| sentry, and then I go and fix it, and that's that.
|
| That access acts on nil is unfortunate, but it's necessary
| for things like get_in.
| pawelduda wrote:
| I used this in past, but there were a lot of people saying that
| NIFs/ports are dangerous to use. Did any of this change or are
| there tried and tested practices that can be applied? What I used
| this for wasn't critical app so I didn't mind it going down.
|
| IIRC one threat was Rust sharing memory with BEAM which could
| exhaust it and cause OOM crash?
| peregrine wrote:
| Nif's are _always_ a risk to deploy cause they run embedded in
| the Beam runtime. That said we all use many nif 's to do all
| kinds of stuff and its fine.
|
| I go into this in the article a little but but the ruslter team
| has made dirtycpu and dirtyio macro's to help reduce the risks.
| toast0 wrote:
| I wouldn't use the word dangerous. NIFs and (linked in) ports
| are a risk, yes. You're loading native code, and you don't have
| intrinsic protection against it doing naughty things that alter
| the underlying shared state in unapproved ways. Things like
| writing to improper addresses, closing improper filehandles,
| opening filehandles and leaking them, leaking memory, other
| improper use of syscalls, or just running for too long.
|
| The only proper solution is to audit and understand the code
| you're running, but hoping it's fine often works too; maybe
| formal methods, but to a first approximation, nobody uses
| those. Did you audit all of ERTS and/or OTP? I'm guessing
| probably not, but it's there to review if you run into a
| problem.
|
| IMHO, it's not worrying about if BEAM will crash; worry about
| it not crashing instead. If your Rust NIF ties up a scheduler
| with an infinite loop, that has the potentially to lock up the
| whole BEAM once another scheduler needs to do something that
| requires full cross scheduler coordination.
|
| BEAM can certainly crash on OOM; although I recommend setting a
| ulimit to ensure it will, because when it crashes, you can
| recover. I've also run into situations where instead of
| crashing or being killed by the OS OOM killer (which is close
| enough to crashing), the OS gets into some tricky to debug
| state where your application is neither functioning nor killed.
| Sometimes, you even get into a state where BEAM is making
| progress, but very slowly; that's a fate worse than death.
|
| If you follow the Erlang philosophy, you'll have a recovery
| strategy from crashes or other deaths. Heart can be used to
| turn completely blocked into death, although I never used it
| professionally. But you've still got to worry about working but
| not well.
| OkayPhysicist wrote:
| NIFs are dangerous, ports are not. Basically a NIF runs in the
| same memory space as the BEAM, so misbehavior of the NIF can
| crash the entire application. On the other hand, they have
| wildly better performance than ports, which have to operate
| through STDI/O.
|
| The Rust / BEAM memory sharing problem does exist, but it's not
| nearly as bad as in more traditional C NIFs, because almost all
| C programs leak memory due to bad manual memory management.
| Hence all the buzz about Elixir+Rust.
| throwawaymaths wrote:
| ports can be dangerous! One time my server lost performance
| because of a lot of zombie processes.
| toast0 wrote:
| I personally include port drivers in with ports, which gives
| you the same level of shared runtime environment as NIFs,
| with the benefits and drawbacks. Sometimes it makes more
| sense to interface as a port rather than as a function. Of
| course, sometimes it makes more sense to interface as a
| C-node rather than either; then you can be on a totally
| isolated machine, and the C-node can even crash or otherwise
| disable the whole OS and your Erlang node will be fine.
| OkayPhysicist wrote:
| What's the benefit of a port driver over a NIF?
| waynesonfire wrote:
| IO in erlang is slow, is Rustler a good candidate to improve IO
| intensive applications?
| throwawaymaths wrote:
| Where did you get this idea? Depending on your task, Erlang can
| be very fast with IO because it will aggressively use
| writev/readv instead of write. Obviously, you can do this if
| you "know about it" in low level langs, but it might be a pain.
|
| Erlang is generally considered to be compute-slow (which is
| generally the case without dropping to nifs).
| weatherlight wrote:
| I/O in beam languages is considered pretty fast, are you sure
| you aren't mistaken?
| toast0 wrote:
| Is IO in Erlang slow? I never got that impression; iolists are
| a good fit for scatter/gather I/O, which reduces copying, and
| Erlang supports kqueue, epoll, and I believe similar on
| Windows. I don't follow Erlang on Linux closely, so I don't
| know if there's any movement towards io_uring, which does seem
| promising to help with throughput.
|
| Either way, I wouldn't expect doing I/O in NIFs or ports to
| substantially improve throughput, unless you're also moving
| significant processing into that layer as well, or you're going
| to end up doing substantially the same level of marshaling work
| as ERTS does, just in a different language. Setting up a
| different set of kqueue/epoll descriptors sounds like a lot of
| work for not much gain too, IMHO; again, maybe io_uring would
| be useful, but I think you'd be better served to bite the
| bullet and integrate it with ERTS.
| sph wrote:
| IO in Erlang is faster than many other managed languages,
| thanks to stuff like IO Lists that are dealt with very
| efficiently through iovec syscalls (readv, writev).
|
| Then add efficient pattern matching for binary data, and
| networked servers on the BEAM are the most ergonomic than any
| other language.
|
| All this stuff that the BEAM offers you out of the box can be
| replicated in any native language with a lot of boilerplate and
| ceremony.
|
| Your "Hello, <name>" webapp in Rust will probably need two
| allocations and a string concatenation, while on the BEAM, if
| constructed as an iolist, it's a single writev syscall, using a
| static "Hello, " string and a shared "<name>" reference from
| the parsed HTTP data.
___________________________________________________________________
(page generated 2023-04-13 23:00 UTC)