HNHacker News
TopNewBestAskShowJobs

chrismccord

2,665 karma · joined December 9, 2011

chrismccord.com
submissionscomments
chrismccord··on Phoenix LiveView 1.0.0 is here
Thanks! This kind of thing was lacking for some time, and with the HEEx engine + function components released a couple years ago, we laid the groundwork for extensible component libs, and the community has put out some great options.

We list half a dozen in the readme, with https://fluxonui.com/ being the most recent, and fully accessible. There is also one inspired by shadcn

https://github.com/phoenixframework/phoenix_live_view?tab=re...

chrismccord··on Phoenix LiveView 1.0.0 is here
Will keep this in mind! My ElixirConfEU keynote just used limit/offset pagination, but shows how easy it is to do bidirectional infinite pagination. Streams allows you to keep a virtualized infinite list in the DOM without keeping the giant collection around on either client or server, example:

https://www.youtube.com/watch?v=FADQAnq0RpA&t=2322s

This demo doesn't update the URL to keep state (but push_patch would make that trivial). I'll think about more complex examples for cursor pagination, but the solution to massive collections (thousands of entries) is going to be stream + limit

chrismccord··on Phoenix LiveView 1.0.0 is here
I need to polish the code, but here's more advanced optimistic UI example added to an existing demo app (TodoTrek). It uses recent LiveView features that allows any client code to hook into the full optimistic UI/server sync primitives that are built into LV:

https://x.com/chris_mccord/status/1864160634394325038

chrismccord··on Phoenix LiveView 1.0.0 is here
Thanks! Love to hear it! Note that LiveView also fully works on the LongPoll transport (which is enabled by default), and we have automatic fallback to longpoll support if websocket fails for whatever reason. So even in the case of obscure websocket issues (like some "corporate proxies" doing weird things like allowing the 101 websocket upgrade then just dropping things into the ether), things should be good. In my experience 1-2% of traffic seems to have websocket issues today for the production apps I work on.

To your point though, LiveView indeed requires a semi reliable connection for reasonable UX, but there is a ton of nuance to this topic that is usually missed from the discussions. Apps should more or less degrade similarly to SPA's that are going to the server. For robust UX on unreliable connections you need offline/local-first SPAs, and in my experience the vast majority of SPAs do not handle this. Failing that, most SPA frameworks seem to place the optimistic UI/rollback concerns on the developer. In fact most degrade quite poorly on bad connections. It goes against folks intuition, but even with degraded connections LiveView does better than people imagine because we have the existing connection established and don't need to go through the overheard of reestablishing things, and our payloads are generally smaller.

Annecdata of me driving through the mountains with spotty cell tethering and browsing facebook vs a LiveView app: https://x.com/chris_mccord/status/1799100642654638543/video/...

chrismccord··on Phoenix LiveView 1.0.0 is here
Phoenix creator here – excited to finally have shipped this! Happy to answer any elixir/phoenix/liveview questions.

In case folks missed it, buried in the blog post is a new installer that lets folks try out elixir/phoenix in seconds. It installs elixir and generates a new phoenix project from a single command:

osx/linux:

    $ curl https://new.phoenixframework.org/yourappname | sh
windows powershell:

    > curl.exe -fsSO https://new.phoenixframework.org/app.bat; .\app.bat
You can visit those url's directly to see what the scripts do. It extends the official elixir prebuilt installers: https://elixir-lang.org/install.sh and https://elixir-lang.org/install.bat

edit: You can see it in action here: https://x.com/chris_mccord/status/1864067247255306332

Of course we also have non |sh installation guides if that's what you're after: https://hexdocs.pm/phoenix/installation.html

Now that this is out, I'm looking forward to put together a few new demos. What would folks like to see? Happy hacking!

chrismccord··on World Page Speed Test – planet-wide elastic scale with FLAME
Author of the post (and Phoenix/LiveView/FLAME) here! I'm happy to answer any questions about how this works. The distributed features of the Erlang VM makes this kind of application outrageously easy to implement. And with FLAME you can elastically scale it with a couple lines of code.

My goal with FLAME is to move us as an industry away from the silly places serverless has taken us.

The demo app linked in the post is here if you want to speed test your own applications: https://worldpagespeed.fly.dev/

chrismccord··on Rethinking serverless with FLAME
If you're talking about inflight work that is running on the runner, there is a Terminator process on the runner that will see the parent go away, then block on application shutdown for the configured `:shutdown_timeout` as long as active work is being done. So active processes/calls/casts are given a configurable amount of time to finish and no more work is accepted by the runner.

If you're talking about a FLAME.call at app shutdown that hasn't yet reached the runner, it will follow the same app shutdown flows of the rest of your code and eventually drop into the ether like any other code path you have. If you want durability you'd reach for your job queue (like Oban in Elixir) under the same considerations as regular app code. Make sense?

chrismccord··on Rethinking serverless with FLAME
> Or are Flame pools strictly children of each individual parent?

Confirm. Each parent node runs its own pool. There is no global coordination by design.

> Does it make a difference in pricing or anything else to run on hot workers instead of starting up per parent?

A lot would depend on what you are doing, the size of runner machines you decide to start in your pools (which can be different sizes from the app or other pools), etc. In general Elixir scales well enough that you aren't going to be running your app in every possible region. You'll be in a handful of regions servicing traffic in those regions and the load each region has. You could build in your own global coordination on top, ie try to find processes running on the cluster already (which could be running in a FLAME runner), but you're in distributed systems land and it All Depends™ what you're building the tradeoffs you want.

chrismccord··on Rethinking serverless with FLAME
They handle scaling at only highest level, similar to spinning up more dynos/workers/webservers like I talk about in the intro. FLAME is about elastically scaling individual hot operations of your app code. App Engine and such are about scaling at the level of your entire app/container. Splitting your operations into containers then breaks the monolith into microservice pieces and introduces all the downsides I talk about in the post. Also, while it's your code/language, you still need to interface with the mount of proprietary offerings to actual accomplish your needs.
chrismccord··on Rethinking serverless with FLAME
I talk about FLAME outside elixir in one one of the sections in the blog. The tldr; is it's a generally applicable pattern for languages with a reasonable concurrency model. You likely won't get all the ergonomics that we get for free like functions with captured variable serialization, but you can probably get 90% of the way there in something like js, where you can move your modular execution to a new file rather than wrapping it in a closure. Someone implementing a flame library will also need to write the pooling, monitoring, and remote communication bits. We get a lot for free in Elixir on the distributed messaging and monitoring side. The process placement stuff is also really only applicable to Elixir. Hope that helps!
chrismccord··on Rethinking serverless with FLAME
Good question. The pools themselves in your app will be per usecase, and you can reference the named pool you are a part of inside the runner, ie by looking in system env passed as pool options. That said, we should probably just encode the pool name along with the other parent info in the `%FLAME.Parent{}` for easier lookup
chrismccord··on Rethinking serverless with FLAME
Yes, more sophisticated pool growth options is something I want longer term. We can also provide knobs that will let you drive the pool growth logic yourself if needed.
chrismccord··on Rethinking serverless with FLAME
This is actually a feature. If you watch the screencast, I talk about Elixir supervision trees and how all Elixir programs carefully specify the order their services stop and stop in. So if your flame functions need DB access, you start your Ecto.Repo with a small or single DB connection pool. If not, you flip it off.

> It's probably a non-issue, the number of things done at initialization could be kept minimal, and FLAME could just have some checks to skip initialization code when in a flame context.

Exactly :)

chrismccord··on Rethinking serverless with FLAME
The FLAME.Pool discussed later in the post addresses this. Runners are pooled and remain configurable hot for whatever time you want before idling down. Under load you are rarely paying the cold start time because the pool is already hot. We are also adding more sophisticated pool growth techniques to the Elixir library next so you also avoid hitting an at capacity runner and cold starting one.

For hot runners, the only overhead is the latency between the parent and child, which should be the same datacenter so 1ms or sub 1ms.

chrismccord··on Rethinking serverless with FLAME
Ingest looks like an awesome service! I talk about job processors/durability/retries in the post. For Elixir specifically for durability, retries, and workflows we reach for Oban, which we'd continue to do here. The Oban job would call into FLAME to handle the elastic execution.
chrismccord··on Rethinking serverless with FLAME
Bring-your-own-container is certainly better than proprietary js runtimes, but as you said it carries every other negative I talk about in the post. You get to run your language of choice, but you're still doing all the nonsense. And you need to reach for the mound of proprietary services to actually ship features. This doesn't move the needle for me, but I would be happy to have it if forced to use FaaS.
chrismccord··on Rethinking serverless with FLAME
Thanks! I try to address this thought in the opening. The issue with this approach is you are scaling at the wrong level of operation. You're scaling your entire app, ie webserver, in order to service specific hot operations. Instead what we want (and often reach for FaaS for) is granular elastic scale. The idea here is we can do this kind of granular scale for our existing app code rather that smashing the webserver/workers scale buttons and hoping for the best. Make sense?
chrismccord··on Rethinking serverless with FLAME
Thanks for the thoughts – hopefully I can make this more clear:

> * Pretending that starting a fly machine doesn't cost the same as triggering via s3 seems disingenuous.

You're going to be paying for resources wherever you decide to run your code. I don't think this needs to be spelled out. The point about costs is rather than paying to run "my app", I'm paying at multiple layers to run a full solution to my problem. Lambda gateway requests, S3 put, SQS insert, each have their own separate costs. You pay a toll at every step instead of a single step on Fly or wherever you host your app.

> * I wouldn't do this at all. There's no reason the results need to be queued. Put them in a deterministically named s3 bucket where they'll live and be served from. Period. This is totally unnecessary. Your application should forget it dispatched work. That's the point of dispatching it. If you need subscribers to notice it or do some additional work I'd do it differently rather than chaining lambdas.

You still need to tell your app about the generated thumbnails if you want to persist the fact they exist where you placed them in S3, how many exist, where you left off, etc.

> * Your lambda really should be doing the DB work not your main application. If you've got subscribers waiting to be informed the lambda can fire an SNS notification and all subscribed applications will see "job 1234 complete"

This is exactly my point. You bolt on ever more Serverless offerings to accomplish any actual goal of your application. SNS notifications is exactly the kind of thing I don't want to think about, code around, and pay for. I have Phoenix.PubSub.broadcast and I continue shipping features. It's already running on all my nodes and I pay nothing for it because it's already baked into the price of what I'm running – my app.

chrismccord··on Rethinking serverless with FLAME
It starts your whole app, including the whole supervision tree, but you can turn on/off services based on whatever logic you want. I talk a bit about this in the screencast. For example, no need to start the phoenix endpoint (webserver) since we aren't serving web traffic. For the DB pool, you'd set a lower pool size or single connection in your runtime configuration based on the presence of FLAME parent or not.
chrismccord··on Rethinking serverless with FLAME
Author here. I’m excited to get this out and happy to answer any questions. Hopefully I sufficiently nerd sniped some folks to implement the FLAME pattern in js , go, and other langs :)
chrismccord··on JavaScript hydration is a workaround, not a solution
While we use JS and websockets, even with js disabled or curling the page will send all the HTML. So the initial render is strictly a regular html page with all your expected content.
chrismccord··on Ask HN: So, what's up with Phoenix (web framework)?
Phoenix is actually quite small if you look at the source and patterns. It would be interesting to compare to other frameworks. We get a lot for free from the platform, so even if you include Phoenix pubsub the feature set for size of the framework is going to be unmatched because of the process model and distributed runtime. Here’s an entire Phoenix app in 50 LOC https://gist.github.com/Gazler/b4e92e9ab7527c7e326f19856f8a9...
chrismccord··on Ask HN: So, what's up with Phoenix (web framework)?
Creator of Phoenix here. I was delightfully surprised to see Phoenix as most loved on the survey since I forgot SO survey was a thing this year. So we can rule out some coordinated voting effort. Truthfully, while we’re still a smaller community, the folks that find us tend to love us and stick around.
chrismccord··on Elixir Livebook now as a desktop app
> Write interactive & collaborative code notebooks in Elixir

Think Jupyter Notebooks (if you're familiar with the python world), but collaborative and for Elixir.

chrismccord··on My thoughts about Fly.io (so far) and other newish technology I'm getting into
To tame the snark from the quoted comment, I think it's worth breaking down.

Current SPA trends are about deploying your app separate from the backend, often CDN-style close to the user (because speed of light matters). Most apps at scale use caching for reads on hot-code paths, so now we have "eventual consistency" in the mix.

Elixir is distributed out of the box, so while "global distribution" sounds fanciful, it's literally baked into the Virtual Machine, Fly simply gives us a private ipv6 network across the globe. All Elixir sees is a cluster of hosts that it can connect to, and it's off to the races.

What I'm getting at is all this snark actually describes most application folks build today at any scale, and we build distributed apps with Elixir because it's a distributed platform.

> All of this tech sounds cool, but like the author, I'm unsure when it's called for.

Imagine if you could write your dynamic UI with realtime updates, and you didn't have to bootstrap JSON apis, or GraphQL schemas for it. Imagine doing `PubSub.broadcast(room, "new_message", ...)` and it gets sent globally to all your instances – with no external dependency. Want to show some activity on the page when something happens on the cluster? Broadcast the event, then write 3 lines of code to update your UI. Imagine writing "naive" template code that renders some markup, but what falls out is smaller payloads on the wire than your carefully typed and specified GraphQL schemas that require serialization rules for all your objects. Imagine doing away with all that and gaining all the benefits of payload size.

If that sounds interesting, Phoenix + LiveView would be called for any time you wanted a dynamic UI or realtime updates and bonus points if you care about writing less code and killing layers of abstraction. Fly would be called for for the same reason folks use CDN's today, to serve resources of the app close to the user, except we just serve the app there instead.

chrismccord··on My thoughts about Fly.io (so far) and other newish technology I'm getting into
Yeah we expose interfaces to ignore blocking on the LSN, but the way this works is by proxying the Ecto Repo interface with our own Repo. So you could call your underlying Repo directly if you wanted to perform a write without blocking on the LSN as well.
chrismccord··on My thoughts about Fly.io (so far) and other newish technology I'm getting into
One of the points about read replicas and read-your-own-writes is correct to call out, but on the Elixir side we have an answer to that:

> It seems like this would add a whole new class of bugs, like “I just submitted a form to change a setting and when the page reloaded, it still showed my previous value in the form” – since the write hadn’t propagated to the local read replica yet.

Elixir is distributed out of the box, so nodes can message each other. This allowed us to easily ship a `fly_postgres_elixir` library that guarantees read-your-own-writes: https://github.com/superfly/fly_postgres_elixir

It does this by sending writes to the primary region over RPC (via distributed elixir). The write is performed on a primary instance adjacent to the DB, then the result, and the postgres log-sequence-number, is sent back to the remote node. When the library gets a result of the RPC write, it blocks locally until its local read replica matches an LSN >= write LSN, then the result is returned to the caller

This gives us read-your-own-writes for the end-user, and the calling code remains unchanged for standard code paths. This doesn't solve all classes of race conditions – for example you may broadcast a message over Phoenix.PubSub that causes a read on the remote node for data that isn't yet replicated, but typically you'd avoid an N query problem from pubsub in general by populating the data in the message on the publisher beforehand.

There's no completely avoiding the fact you have a distributed system where the speed of light matters, but it's Fly's (and Phoenix's) goal to push those concerns back as far as possible. For read heavy apps, or apps that use caching layers for reads, developers already face these kinds of problems. If you think of your read-replicas as cache with a convenient SQL interface, you can avoid most foot guns.

I'm happy to answer other questions as it relates to Phoenix, Fly or what Phoenix + Fly enables from my perspective.

chrismccord··on Ask HN: Is Erlang an albatross to Elixir adoption?
As the creator of Phoenix, I've never written an Erlang program fwiw. That said, I frequent the Erlang docs and most seasoned Elixir developers will do the same because the standard library is a wealth of features, and the documentation while not 2022 polished, remains a remarkable resource.

> I don't know what a solution is, but perhaps a concerted effort to create a documentation and library ecosystem that never links back to Erlang would help. And where there are critical systems that use an Erlang library, perhaps rebuilding it in Elixir is in order?

This would not be helpful. Erlang has over three decades of features, heritage, and libraries. Elixir helped usher in first class documentation as a core language feature, but that doesn't make rock solid Erlang libraries somehow things that should be avoided. Rewriting for rewriting sake is also not a good idea. One of the benefits of bootstrapping off an already amazing platform is exactly because you don't have to invent the universe. See Clojure and Java.

In fact, Elixir has helped Erlang up its documentation game, and there is already work done to help the ecosystems share documentation tools. Both ecosystems work by rising together, not masking one or the other. We see this in tools like `telemetry`, documentation generators, and recently the Erlang Ecosystem Foundation: https://erlef.org

chrismccord··on Recognize Digits Using ML in Elixir
This is really exciting to see! LiveView / Livebook + NX / Axon is a neat way empower folks in the ML space. Everything is way more approachable if you can easily wire up your models and visualize and play with them in a UI.
chrismccord··on Back-end languages are coming to the front-end
The biggest difference (at least for Phoenix LiveView) is having a stateful backend represent the UI and we do away entirely with HTTP APIs/serializers/resolvers. So you get both interaction and push updates from the server at any time, without HTTP glue layers. And it actually scales. So think React's functional-reactive-templates on the server, pushing minimal diffs over the wire better than if you'd carefully written an efficient JSON API or GraphQL endpoint. Elixir is also a distributed programming language so you also get things for free like sending an update to everyone's UI across the entire cluster.
← PreviousPage 2 of 8Next →