I’m not familiar with Reverb and clearly you’ll get biased answers here, but I can draw some obvious and fair conclusions.
Let’s ignore scale entirely for arguments sake. With Phoenix there’s simply going to be less code to write, you’ll have less problems to think about, and less infrastructure to tinker with. No one offers out-of-the-box distributed pubsub with their built-in real-timer layer. In the case of Reverb, it either uses the pusher model of HTTP posts back and forth between client and server or uses redis for the same purpose. This means messages get load balanced to"¯_(ツ)_/¯" from the server that served the original client. This makes simple things difficult – caching, local lookup, files, etc. You immediately need to resort to some kind of IPC or async jobs to get anything to happen in the system. For example, Reverb’s page calls out the use of redis as a shining example of “scale”.
Built for Scale
Infinitely increase capacity by utilizing Reverb’s built-in support for horizontal scaling
using Redis, allowing you to manage connections and channels across multiple servers.
Not needing redis in the first place is going to scale better. Requiring you to serialize all events thru a datastore designed to be serial in nature, just to get IPC to work, even on the same instance the client is already connected to is a lot work.
The other thing to consider is Elixir’s process model itself. Processes are our unit of concurrency, unit of state, and unit of error isolation. We build all primitives from them like webservers, websockets, agents, etc. They are location transparent, which is a fancy way to say it doesn’t matter where a process lives in the cluster. Your code treats it the same if it was local or across the planet.
What location transparency means for your code is the code you write in your LiveView or channel to handle a message in the system, or broker a message from the client to other users, doesn’t need to consider “now how do I get this form this server to that server”. You just pubsub, or send/2
or GenServer.call
and carry on with life. The amount of layers this removes from your concerns is astonishing compared to other platforms.
Take the app I just published for measuring page speed from around the globe worldpagespeed.fly.dev. 350 lines of code including the markup and RPC calls. The RPC calls simply do :erpc.multicall
which is part of the erlang standard library to concurrently run a function across a cluster of nodes and get the results. Within the multicall we spawn headless chrome, fetch a web page, and report the results back to the caller pid. The caller pid in this case is just the LiveView process! send(pid, result)
makes it way to the handle_info/2
clause of the LiveView regardless of where the work happens. Doing this elsewhere would require some kind of persistent job queue, polling among members, or external dependencies/paid services with glue code to avoid doing those things. In Elixir we do them as a matter of course.
- If you had a small real-time app that doesn’t require the scalability of Elixir and purely want to build as rapidly as possible, would you consider Laravel Reverb?
The only reasonable argument for considering Laravel in this context is if you are already productive and heavily invested in Laravel. On any other merit it will fall short for real-time features.
- Do you think there’s anything that either Reverb or Phoenix Channels does better or worse than the other?
Most folks are going to come up short when compared to channels. Extra infra (redis), slower (redis), worse scale (php/redis), more code (no native ipc/rpc/pubsub so redis must be used)
- Are there specific use cases where you think Reverb or Channels would be more suited?
Same answer as # 1
Hope that helps!