[HN Gopher] A LiveView Is a Process
       ___________________________________________________________________
        
       A LiveView Is a Process
        
       Author : clessg
       Score  : 145 points
       Date   : 2023-06-15 13:06 UTC (1 days ago)
        
 (HTM) web link (fly.io)
 (TXT) w3m dump (fly.io)
        
       | jeremyjh wrote:
       | I suppose this is good basic information for people new to
       | Erlang/Elixir, but I don't feel like it does a good job of
       | explaining the significance of the fact that state is stored in a
       | process, compared to other frameworks which would store the state
       | in a dictionary and retrieve it for each event/request as its
       | dispatched in an event loop or whatever.
       | 
       | I'd think the dictionary approach is functionally equivalent in
       | most respects, and could even imitate how crashes are handled in
       | Elixir. The framework has to implement more code to do this (e.g.
       | messages between processes might be something some frameworks
       | don't bother to implement because its such a hassle, while its
       | almost free in Elixir), but that doesn't matter at all to users
       | of the framework if the features are there.
        
         | weatherlight wrote:
         | In essence, the dictionary approach you're referencing isn't
         | entirely dissimilar to Elixir's process-based state management,
         | and you're absolutely correct that, at a fundamental level,
         | they both accomplish a similar task: preserving state across
         | requests.
         | 
         | However, I'd argue that the key difference lies in the
         | granularity of control and the inherent characteristics of the
         | Erlang/Elixir process model, which is designed to excel in
         | concurrent, real-time, and distributed systems.
         | 
         | For instance, in Elixir, every LiveView session is an isolated
         | process that runs concurrently. This is native to the BEAM
         | (Erlang's virtual machine), not an add-on. It's this built-in
         | process model that allows LiveView to handle many users
         | simultaneously, manage state efficiently, and push real-time
         | updates, all while maintaining high performance.
         | 
         | Fault isolation, too, is a baked-in benefit of this process
         | model. A crash in one process doesn't affect others. While it's
         | certainly feasible to engineer fault isolation in a different
         | paradigm, it's a baseline guarantee in Elixir, reducing a
         | considerable amount of potential complexity from the get-go.
         | 
         | Elixir's supervision trees further provide a robust way of
         | handling failures, automatically restarting processes when they
         | crash. Implementing a similar mechanism outside of Elixir can
         | be quite intricate and might not offer the same level of
         | reliability.
         | 
         | Additionally, Elixir processes facilitate straightforward and
         | efficient inter-process communication, making it easier to
         | build distributed and concurrent systems. Other frameworks
         | could implement similar features, but it might not be as
         | effortless or as integral a part of the programming model as it
         | is in Elixir.
         | 
         | So, while the dictionary approach can mimic some aspects of
         | Elixir's processes, Elixir's model provides a more complete,
         | integrated solution for building highly concurrent, real-time
         | systems. The fact that many of these features are "almost free"
         | in Elixir does matter, as it can reduce the complexity of
         | application code, improve robustness, and increase developer
         | productivity.
        
           | jeremyjh wrote:
           | None of this is new information to me or contradicts anything
           | in my post. Yes, Elixir has a lot of features that are nice
           | and save framework developers time. That's why I've been
           | using Elixir for the last 10 years.
           | 
           | But most of us reading this article do not develop
           | frameworks, we develop applications. And when we choose a
           | framework to use for that, we look at the features it
           | provides, not the features it leverages from lower-level
           | frameworks.
        
             | HappMacDonald wrote:
             | Well in that case this boils down to: "LiveView does this
             | for you. In fact it does this because the underlying
             | language does this for you. Most other frameworks in other
             | languages do not do this for you. You might be able to roll
             | your own but it would be a huge headache to do". :)
        
           | throwawaymaths wrote:
           | > Fault isolation, too, is a baked-in benefit of this process
           | model
           | 
           | I wouldn't call it baked in, it's the _entire purpose_ of the
           | process model. The other benefits of the process model are
           | secondary.
        
             | manmal wrote:
             | Isn't it another quite important trait of this that one can
             | manage millions of ,,processes" on one machine which is
             | outright impossible with most other systems? It puts a one
             | machine setup for mid sized apps into the range of the
             | possible.
        
         | OkayPhysicist wrote:
         | The message passing is, IMO, the biggest win you get from the
         | process model. In the process model, you have the LiveView
         | process, which sits there, listening for messages, which it can
         | receive both from the browser client, or from independent
         | going-ons in the backend, quite symmetrically. To the
         | programmer, it looks just like any other process that you'd be
         | used to using everywhere in Elixir, which provides useful
         | interop with things like Task.async, which sends a message back
         | to it's linked process on completion, or other libraries that
         | are completely unaware of LiveView.
         | 
         | You could simulate the same thing with a dictionary lookup
         | scheme, but A) it would be far more complex, because of the
         | lack of native message passing support, and B) the you'd miss
         | out on the symmetries and interop, because the rest of your
         | program wouldn't be using message passing.
        
       | no_wizard wrote:
       | Legitimate question: is there anything Elixir / BEAM is
       | particularly bad at?
       | 
       | I've seen it start cropping up in some Fintech circles so I'm
       | wondering if this language has some serious juice for concurrent
       | data analysis or something.
        
         | ashton314 wrote:
         | Any algorithm that needs mutable data to be performant would be
         | a bad fit. Numerical number crunching isn't stellar either.
         | 
         | That said, there are ways to extend the BEAM with C or Rust
         | code to make those areas performant. I've written one such
         | extension (called a NIF; forget what it stands for exactly) and
         | it wasn't all that hard.
        
         | HalcyonicStorm wrote:
         | It is historically not great at number computing. This is being
         | addressed by a relatively new project called Nx.
         | https://github.com/elixir-nx/nx
         | 
         | It is not the right choice for CPU intensive tasks like
         | graphics, HFT, etc. Some companies have used Rust to write
         | native extensions for those kinds of problems.
         | https://discord.com/blog/using-rust-to-scale-elixir-for-11-m...
        
       | Etheryte wrote:
       | While the charts are nice for illustrating the flows, they could
       | use a little bit more love. The process flowchart doesn't need to
       | have any crossing lines, but it has multiple. While removing the
       | crossings is a small change, it reduces cognitive load
       | considerably.
        
         | cush wrote:
         | This is my gripe with mermaid, and is where excalidraw really
         | took over the space. Sure mermaid can be stuck in any text
         | file, but it's not worth the tradeoff of the diagram being
         | unreadable.
        
           | peregrine wrote:
           | Yea I should have used excalidraw instead of mermaid, thats
           | on me. I am trying to focus on writing good content and I try
           | to not get stuck on the stuff I'm bad at which is making
           | nicer charts.
        
             | ketzo wrote:
             | Hey, getting the work out there is always better than it
             | sitting in the drafts waiting to be perfected! Thanks for
             | the great article.
        
       | derekkraan wrote:
       | I'm glad someone has written about this. I actually wanted to
       | write this very blog post, and even made a crappier version of
       | this flow chart.
       | 
       | Why? Because people think they know what LiveView is, but they
       | don't, because the pretenders out there (LiveWire, StimulusReflex
       | and all the rest) are poor imitations.
       | 
       | Don't sleep on LiveView.
        
         | Scarbutt wrote:
         | OTOH, those other languages have decent ecosystems.
        
           | ashton314 wrote:
           | Have you, like, used Elixir seriously? I've worked as a
           | professional Elixir dev and we were never wanting for a
           | library to do what we needed. mix is excellent. It all feels
           | very mature given its age.
        
           | giraffe_lady wrote:
           | What do you need that isn't in OTP though? Integrating with
           | SaaS APIs is basically the only thing I've wanted that isn't
           | buried in there somewhere.
        
           | cultofmetatron wrote:
           | come join us and contribute!
           | 
           | built my startup on elixir/phoenix. while there could
           | certainly be a better selection of libraries, its not that
           | hard to roll your own and elixir has straight up features
           | that you can't replicate in most other ecosystems
        
             | parthdesai wrote:
             | Ecto on it's own should be a good enough reason to use
             | Elixir!
        
               | cultofmetatron wrote:
               | I concur.. once I wrapped my head around the way ecto
               | worked, I couldn't help but wonder why this wasnt the way
               | every orm works. its lightyears ahead of anything I tried
               | in node, rust or ruby.
        
           | mikl wrote:
           | I don't think it's fair to call the Elixir/Erlang ecosystem
           | _indecent_. Of course it's not on par with the vast
           | ecosystems of the top 10 most popular languages, but in my
           | experience, there's at least one solid package for most
           | things you might need.
        
             | OkayPhysicist wrote:
             | One of my favorite things about programming in Elixir is
             | the fact that for an absurd number of problems, there's not
             | just _a_ library that solves it, but an official one from
             | the OTP. It cuts down dramatically on time spent evaluating
             | different options.
        
         | omnimus wrote:
         | Why so much negativity? 37signals (with Stimulus) are the OGs
         | of this architecture. Livewire started at same time as LiveView
         | and because of PHPs limitations it has to use different
         | tactics/innovations (like using clientside JS more with
         | Alpinejs) that then LiveView benefits from.
         | 
         | Calling them poor imitations is disingenuous and spitting on
         | open work of many people.
        
           | regulation_d wrote:
           | "Poor imitations" is probably harsh, but I agree with the
           | idea that LiveView's architecture is substantially different
           | from Hotwire or LiveWire. To have experienced Hotwire is not
           | to have experienced LiveView, ironically enough, for reasons
           | outlined in the posted article.
        
             | omnimus wrote:
             | But all this sounds like Hotwire/Livewire are not good
             | architectures for many many types of projects.
             | 
             | Biggest Liveview app i know is fly.io dashboard and the
             | issues it has from ux standpoint are very similar to
             | hotwire apps.
             | 
             | Actually Basecamp and Hey.com are a lot bigger apps than
             | fly.io dashboard and they are doing just fine.
             | 
             | You don't have to jump to Elixir to experience similar
             | approach when you know rails/laravel already.
        
         | stanmancan wrote:
         | The best part about Livewire is that it was appealing enough to
         | try out but bad enough that I left PHP/Laravel and moved to
         | Elixir/Phoenix for the real deal.
        
         | Alifatisk wrote:
         | I would not call out StimulusReflex and the rest like that, it
         | all boils down to the language runtime they are built upon.
         | They are good at what they are trying to accomplish in my
         | opinion.
        
           | derekkraan wrote:
           | Yes, it does boil down to the language runtime.
           | 
           | But that doesn't change the fact that they don't reproduce
           | half of what LiveView can do.
           | 
           | They didn't have to call themselves "LiveView for Ruby!"
           | 
           | I meant it more as a PSA: if you've tried any of these other
           | projects, you still owe it to yourself to give LiveView a
           | try!
        
             | Alifatisk wrote:
             | You're right, the claim of an equivalent to LiveView is a
             | bit far fetched.
             | 
             | https://github.com/liveviews/liveviews
        
       | Alifatisk wrote:
       | You misspelled "Phoenix LivViews"
        
         | peregrine wrote:
         | Thanks fixed.
        
       | floodfx wrote:
       | I think it is more exciting and more innovative to think about
       | LiveView as a new architectural approach to building reactive
       | applications. Yes in Elixir land it is a Process and there are
       | some amazing things about the BEAM. But LiveViews have the
       | potential have an impact beyond just the Elixir ecosystem and
       | folks should embrace that.
       | 
       | I've been a part of porting the Phoenix LiveView Protocol to both
       | Javascript (https://LiveViewJS.com) and Go
       | (https://github.com/canopyclimate/golive) backends and supported
       | a friend that is porting it to Python.
       | 
       | Ironically I think taking LiveViews outside of Elixir could
       | actually make it easier for folks to adopt Elixir-based LiveViews
       | in the future.
        
         | jolux wrote:
         | Porting this is cool but why would people adopt Elixir if they
         | can have LiveViews in their preferred language?
        
           | floodfx wrote:
           | Understanding LiveViews as a concept could make it easier to
           | commit to learning a new language. I am not saying one should
           | but that one could and it would probably be easier since you
           | don't have the additional overhead of also learning what is a
           | LiveView.
        
           | weatherlight wrote:
           | Ergonomics matter.
           | 
           | Because outside of Go (and maybe Rust) it will be very
           | difficult to actually do what Elixir does, with the same
           | level of concurrency and fault tolerance, etc, with the same
           | ergonomics.
           | 
           | Ruby/Rails/StimulusReflex does something very similar, but
           | kind of falls over unless you replace ActionCable with
           | AnyCable which is written in Go.
           | 
           | So now, you have to support Go and Ruby runtimes. Some
           | developer who decided that the above was a nightmare to work
           | with, __might__ start a new project in Elixir, to get
           | something that actually does what Elixir does instead of just
           | mimicking it.
           | 
           | I'm not sure I buy this, personally, but I can understand the
           | argument.
        
             | troupo wrote:
             | > Because outside of Go (and maybe Rust) it will be very
             | difficult to actually do what Elixir does
             | 
             | It's just as difficult in Go and Rust. BEAM, the Erlang VM
             | that powers Elixir, is not just about lightweight
             | processes. It's also about _guarantees_ that neither Go nor
             | Rust provide. E.g. to do what Elixir does you need robust
             | process supervision trees. You can imitate, but not
             | replicate those in other languages.
        
               | thomasfortes wrote:
               | Virding's Law: Any sufficiently complicated concurrent
               | program in another language contains an ad hoc
               | informally-specified bug-ridden slow implementation of
               | half of Erlang.
        
               | floodfx wrote:
               | Comments like this push people away from Erlang rather
               | than draw them in.
        
               | thomasfortes wrote:
               | It's a play on
               | https://en.wikipedia.org/wiki/Greenspun%27s_tenth_rule
        
               | troupo wrote:
               | I find it quite the opposite: what is it that other
               | languages are trying to re-implement, and why they can't?
        
               | Rapzid wrote:
               | And yet it doesn't matter.
        
         | ziftface wrote:
         | Is your friend's python library open source by any chance? I
         | was thinking about doing the same.
        
           | floodfx wrote:
           | https://github.com/ogrodnek/pyview
        
       | whalesalad wrote:
       | The term 'assigns' has always rubbed me the wrong way. Context or
       | "view context" has always made more sense.
        
         | sodapopcan wrote:
         | Sure that's not a great reason not to use something. If you
         | want you can think of it as short for "assignments" just as
         | "conn" is short for "connection" or "attrs" is short for
         | "attributes". Just consider a coincidence that this shorthand
         | has the same name as a verb.
        
         | pavlov wrote:
         | "Assign" also rubs me the wrong way because it's a needless
         | nouning of a verb. The word is "assignment" and it's only four
         | letters longer.
        
           | edvinbesic wrote:
           | Not sure what you mean.                   assign
           | @-sin'         transitive verb         1. To select for a
           | duty or office; appoint: synonym: appoint.         2. To set
           | apart for a particular purpose or place in a particular
           | category; designate: synonym: allocate.         3. To give
           | out as a task; allot.
        
       | osener wrote:
       | Is there any widely used and public web or mobile app with its
       | entire UI built with LiveView?
        
         | a_bored_husky wrote:
         | You can find some here: https://github.com/caspg/made-with-
         | liveview
        
         | swlkr wrote:
         | cars dot com uses it
        
           | benzible wrote:
           | ElixirConf 2022: "The Launch of Elixir and LiveView at Scale
           | on the New Cars.com"
           | https://www.youtube.com/watch?v=XzAupUHiryg
        
         | suciptoid wrote:
         | fly.io dashboard use LiveView
        
       | mrcwinn wrote:
       | I absolutely loved working with Elixir and LiveView. I made a
       | decision to migrate to Python/Flask to simplify our
       | infrastructure and move application code into one language, but
       | I'll miss it nonetheless!
        
       | ashton314 wrote:
       | The real "aha!" moment for me with LiveView came when I was using
       | LaTeX to render a PDF in response to some user actions. The first
       | time I did something like this, I did some janky polling on the
       | client side. Other (bad) solutions involved blocking the client
       | process or just praying that the PDF would render in time before
       | the HTTP request timed out.
       | 
       | With LiveView, it works like this:
       | 
       | - user pushes a button to render the PDF
       | 
       | - the LV process calls `Task.async(fn -> do_work({data,
       | self.pid()}))` so the worker process has the PID _of the LiveView
       | process itself_
       | 
       | - when the job completes on a different thread, the worker sends
       | an _Elixir message_ using the standard and highly-flexible built-
       | in mechanism with `send()` to the PID of the LV
       | 
       | - the LV gets the message and updates the UI and/or initiates a
       | download of the rendered PDF automatically
       | 
       | It is so simple to get this workflow going. It's super efficient.
       | Perfect for low- to mid-range load on a web app. Perfect for one-
       | person development.
        
         | throwawaymaths wrote:
         | Task.async already implements a result message so you're
         | reduplicating the effort hete. You can listen for the
         | task.async callback from LiveView.handle_info directly, instead
         | of having the asynchronously run function send back the
         | message. You may want to store the ref and pid of the task in
         | the assigns.
         | 
         | From the docs: "Alternatively, if you spawn a task [via async]
         | inside a GenServer, then the GenServer will automatically await
         | for you and call GenServer.handle_info/2 with the task
         | response..."
         | 
         | Also if you prefer sequencing the response manually the way
         | you're doing, Task.start_link is probably more appropriate.
         | 
         | Note that the way you're doing it now your liveview's message
         | queue will leak with unhandled Task.async responses and down
         | messages (probably not a big deal given your problem domain).
         | If you do go the Task.start_link way, you do get two way
         | binding (if the task dies the liveview will restart itself).
        
           | ashton314 wrote:
           | Ok, that all sounds much better. Thanks for the reply! It's
           | been a hot minute since I've used LV--so much good stuff
           | happening.
           | 
           | I think it's a testament to the power of the platform that
           | the obvious dumb solution worked so well for me. Lol
        
             | throwawaymaths wrote:
             | Always do the obvious dumb solution first!!
        
         | [deleted]
        
       | diob wrote:
       | Is it a good experience for folks with high latency internet?
        
         | lewantmontreal wrote:
         | With higher latency the experience suffers. As an example
         | liveview page navigations work via websocket which is not
         | cacheable so navigating back/forward always needs to make a
         | request. You really need some edge setup like fly, or only
         | serve a geographically local audience.
        
         | gangstead wrote:
         | It can be pretty bad especially for UI updates that you are
         | used to being handled client side.
         | 
         | Example: You click on an upvote button and it changes color,
         | but there's 1 second of latency.
         | 
         | SPA: Color updates immediately, the "upvote pressed"
         | http/websocket call to the server arrives one second later.
         | 
         | LiveView: liveview.js sends "upvote pressed" over the web
         | socket, one second later the liveview process on the server
         | gets the message, patches the dom and replies, one more second
         | later the button changes color. Meanwhile the user has pressed
         | the button 2 more times wondering why the color isn't updating.
         | 
         | There are phx-hooks (https://hexdocs.pm/phoenix_live_view/js-
         | interop.html#client-...) to address this with small targeted
         | bits of js where you can add event listeners but it can get
         | messy quickly.
        
       | kgeist wrote:
       | Can a LiveView process migrate to a different machine/VM? For
       | example, there's a scheduled maintenance and I need to shut down
       | one of the machines/containers. The main advantage of stateless
       | architectures for me is that state is not bound to a specific
       | machine -- so you can easily add/remove machines, state is not
       | lost if application dies, etc.
        
         | jeremyjh wrote:
         | No, and while there is a capability to hot reload releases (so
         | that you could preserve state while deploying an update, for
         | example), in practice almost no uses it because it is
         | complicated.
         | 
         | In practice, if you care about this aspect of user experience
         | you may have to take some reasonably small steps to preserve
         | state information in the client. Form state gets preserved
         | automatically for most purposes, though there may be some forms
         | that need to implement a call-back to preserve state, as
         | explained in the docs:
         | https://hexdocs.pm/phoenix_live_view/form-bindings.html#reco...
        
           | throwawaymaths wrote:
           | I think liveview live reload uses hot reloading, so that's
           | definitely a very nice ergonomic improvement that basically
           | everyone uses all the time
        
             | jeremyjh wrote:
             | No, the dev tool works differently, it does not use the OTP
             | release feature I'm talking about. If you hot upgrade
             | stateful modules you have to supply migration functions
             | when the structure changes.
             | 
             | https://www.erlang.org/doc/design_principles/release_handli
             | n...
        
         | notpublic wrote:
         | This video may be of interest to you:
         | 
         | https://youtu.be/pQ0CvjAJXz4?t=1992
         | 
         | Bryan Hunter explains how they do it at HCA healthcare (not
         | specifically LiveView process, but processes in general).
        
         | sodapopcan wrote:
         | Not without doing that work yourself, no.
         | 
         | I take a more classic web approach to LiveView which is to say
         | I persist any important state right away. Like any multiple-
         | step forms I would never store in server state, I'd persist
         | each step. Of course that doesn't help if someone is halfway
         | through filling out a form. If the socket disconnects then re-
         | connects then you don't lose your work but not if you do a
         | restart. I do wish there was a built-in way to store state on
         | the client until it's submitted, we may get there, though!
         | 
         | The true value of LiveView for me is the simplicity of not
         | having to write yourself a web API to interact with your own
         | backend as well as the dead simple real-time features. If you
         | don't need the latter, that's fine, but having worked on
         | collaborative apps that was a giant pain is what led me to
         | LiveView.
        
         | throwawaymaths wrote:
         | Can a LiveView process migrate to a different machine/VM?
         | 
         | Liveview makes no assumptions about the client connection or
         | even stickiness of client to nodes in a cluster. If you store
         | critical data in the liveview you could be in for some trouble.
         | Always store important stuff into a recoverable, network aware
         | datastore. The nice thing is that liveview is _designed_ to be
         | tolerant to client or client connection failures, you should
         | design to be able to repopulate state sanely in the on_mount
         | call.
         | 
         | While this sounds bad, think of it as isolating the client
         | connection failure domain from the data failure domain, so you
         | should organize your code in those domains respectively
         | 
         | If you want some sort of ephermal network state that lives on
         | one node and not the database only (e.g. a game), the liveview
         | should connect into that. If you want to store that in a
         | genserver, a strategy like this is doable:
         | 
         | https://youtu.be/nLApFANtkHs
        
         | pythonaut_16 wrote:
         | My recollection of the details is fuzzy, but I think a common
         | solution to this is connection draining. You set up your deploy
         | to bring up the new instance and direct all new traffic to that
         | one and then hold the old one open until all
         | sessions/connections end.
         | 
         | Can't remember if/how that routing was accomplished in the BEAM
         | (Erlang/Elixir VM). It's definitely possible but you might have
         | to implement it yourself.
        
         | mikl wrote:
         | In general, LiveView applications are built so they can be
         | safely reloaded without significant data loss, according to the
         | Erlang/Elixir "Let it crash"-philosophy.
         | 
         | Besides scheduled maintenance, there's all sorts of reasons why
         | this might be needed: The user's log-in expires, network
         | issues, browser crashes, server hardware issues, deployment of
         | a new version of your app...
         | 
         | "Hot upgrades" can be implemented if you wish to, but often
         | this is just handled by asking the client to reload the page,
         | establishing a new connection, because any data or preferences
         | have been persisted.
        
       ___________________________________________________________________
       (page generated 2023-06-16 23:01 UTC)