LiveView handling large, frequently changed socket assigns

I was playing around with LiveView, making a sort of Google Docs replica. This led me to wonder about how LiveView deals with large socket assigns that are changed inside the client often (e.g. triggered by a phx-change).

Say you have a large text file that is stored in the view’s socket as one assign. If I understand correctly, in that scenario that whole text would be sent to the server, whenever a message is sent to the server. I know that some rich text editors, like Quill, make it possible to only send the changes to the text to the server, not the whole text. For my Google Docs replica, that works fine. But I was wondering whether that would be a nice native feature for Phoenix. Would that increase performance even further?

You could, of course, also avoid using large socket assigns or lower the frequency of checking for changes. And extra client-side features would increase the client-side JavaScript package size. But I was too curious not to ask.

If you are talking about textarea contents, those are generally not that big. I send everything back and forth between the client and the server with only a 100ms phx-debounce for the textarea and it works beautifully in normal network conditions.

I think it depends on what exactly “google docs like” is supposed to mean. If you’re looking at real time collaboration features I’d expect it to be simpler to skip LV and let the JS of the editor communicate with the server over plain channels.

Without collaborative features the suggestion of @derek-zhou should be fine.

My first instinct is to debounce it. If you’re typing a document of appreciable length then a very long debounce (~1000ms) saves processing time and still saves frequently enough.

It sounds like you’re saving the text as a string, not a file.

I am making the replica real-time collaborative. Curious to your reasons to skip LV and just use plain channels? For my learning purposes, as I am still in the process of getting a full grasp of Phoenix.

Liveview is build to work with markup and less so with other forms of data. If you‘re not working with markup, which I expect you to do, then it doesn‘t make sense to use it. Phoenix channels is the more generic tool LiveView is built on top of, which will allow you to build up the necessary communication patterns you need.

1 Like

FWIW this is a deep topic. Pushing the full text back and forth on every update doesn’t get you super far if you’re going for a full multi-page editor. How you model the text inside the page is a complex topic, you can read about VSCode’s implementation here Text Buffer Reimplementation, a Visual Studio Code Story.

I’d start with figuring that part out, and then from there that will affect how you synchronize between clients.

3 Likes

For context: I mostly hope to learn a lot from the experience of building the app. It will serve no other purpose, really.

But I’ll will be sure to check out the link you provided, though.

Exploring both options. Both data with and without markup. As practice. I noticed Google Docs and Word Online take different approaches to how they approach showing a word processing document to the user. Google now uses a canvas element for each document. Word creates an elaborate DOM for all the user input.

Just to clarify, my original question was meant more along the lines of: Would it possibly be a good Phoenix feature for LiveView to only send to the server those parts of a socket assign that have changed. Or are there good reasons that it wasn’t designed that way? I can imagine it not being such a good idea because it might bring a lot of extra complexity while only having added value in a uncommon subset of use cases. But I’d still be interested in picking the brains of those that have a far better understanding of software engineering than me ;-).

Was an interesting read. And thank you for your advice. I’ll keep it in mind.

Have you looked at things like ProseMirror?

1 Like

If you’re seeking inspiration for multi-user, collaborative editing implemented in Phoenix, https://livebook.dev should help get the creative juices flowing.

2 Likes

LiveView doesn’t send assigns to the server. It sends them to the client and does do it in a clever way to minimise data over the wire. When you’re sending to the server you’re generally sending from a phx-change in a form or pushing an event manually via LiveView’s JS command or from a hook.

If you look in the livebook source or at ProseMirror or the Delta package on Hex you’ll see they send a change instruction/delta rather than diffs.

2 Likes

At the risk of being pedantic: LiveView doesn’t send the assigns anywhere. LiveView takes the assigns and re-renders the page on the server, and then sends the diff of the change over the wire to the client.

3 Likes

But the client does send messages to the server over the socket, right? By means of phx-change, phx-submit, phx-etc.

And those client side phx attributes send the whole variable, not just the differences made to the value of it.

Thank you for clarifying, btw. Should have been more precise in my message.

You can have message based communication within liveview, even more so when using js hooks on the client side. Though the main focus of LiveView is not arbitrary message passing, but state + incoming events driving changes to markup, where the diff of said markup is sent back to the client.

You could probably do operational transforms or crdt resolution on the server and only send the resulting markup back to the client, but that also means the client has no higher level state about the markup it got. Given you likely need that state on the client it’s likely simpler to keep it on the client only. Let the server just be a simple relay of messages, either using the event handling of LV or even decoupled with plain channels.

1 Like

Are incoming events not messages? Are they based on the same or different protocols? LiveView starts a websocket with each visiting client, right? The phx events are sent over that connection (‘socket’), right? Not over another (AJAX request processed as conn on server?)?

They are, but LV is built primarily for the data being sent to the client being lightweight markup diffs, not arbitrary data. It’s a specialized channels implementation, which does a lot more than just be a two way message stream. For message exchange of e.g. opertational transforms you likely don’t need that.

This is not to say you may not use LV for everything around your editor though. You could also possibly run your editor with a client side hook and it’s message communication tools, but again this is going to be completely separate to all the assigns/change tracking/template diffing of liveview.

1 Like

Thanks for all these bits of knowledge, everyone. I appreciate it.

1 Like

This is great stuff. Hadn’t realized before that it’s open source. This will definitely get the creative juices going. Thanks. :pray: