jeremyjh
LiveView and Rolling Restarts
One thing I’ve wondered about since LiveView is announced is how well it can work in an environment where continuous deployment and rolling restarts are used. At work we don’t have fully automated continuous deployment but in practice we usually deploy as soon as changes are merged to master and a new container is built, often several times a day. In a larger team I could easily see many deployments per day. These events do not impact users today at all, whether you are using an SPA or a traditional server-rendered application.
In LiveView, if a user is in the middle of editing a form when new code deploys, they are going to lose all the contents of the form they’ve entered by default as their current connection disconnects and they connect to a new server which calls mount again. We could go to heroic lengths to prevent such tragedies via server-side strategies including hot reloading or stashing the state somewhere, but that introduces significant new complexity to the app. Also, at least as far as forms are concerned, it is unnecessary since we already have a copy of the state we care about (what the user has typed in) in the client.
Does anyone know if there any plans on the horizon to stash/restore form input state on disconnect/reconnect events on the client side? I expect at some point there will be a lifecycle API so that you could manually do this yourself with client-side code. But it seems like the requirements of basic form state would be pretty common across applications.
Marked As Solved
chrismccord
Duping what I posted on the phoenix issue for posterity:
Form recovery is supported with js hooks, but is not yet automatic. Automatic recovery is on the roadmap, but we will have to have the client check to ensure all its stashed inputs match the latest rendered state from the server. So in some cases for multi-step or heavily dynamic forms, auto recovery won’t work, but for the basic cases we can have the client automatically recover. In the meantime and for advanced forms, you can annotate your form with phx-hook="SavedForm", then define a JS hook which stashes the form state and passes it back up via connect params, for example:
# <%= f = form_for @changeset, "#", phx_hook: "SavedForm", phx_change: :validate, phx_submit: :save %>
def mount(_session, socket) do
changeset =
case get_connect_params(socket) do
%{"stashed_form" => encoded} ->
%User{}
|> Accounts.change_user(Plug.Conn.Query.decode(encoded)["user"])
|> Map.put(:action, :insert)
_ ->
Accounts.change_user(%User{})
end
{:ok, assign(socket, changeset: changeset)}
end
let serializeForm = (form) => {
let formData = new FormData(form)
let params = new URLSearchParams()
for(let [key, val] of formData.entries()){ params.append(key, val) }
return params.toString()
}
let Params = {
data: {},
set(namespace, key, val){
if(!this.data[namespace]){ this.data[namespace] = {}}
this.data[namespace][key] = val
},
get(namespace){ return this.data[namespace] || {} }
}
Hooks.SavedForm = {
mounted(){
this.el.addEventListener("input", e => {
Params.set(this.viewName, "stashed_form", serializeForm(this.el))
})
}
}
let socket = new LiveSocket("/live", {hooks: Hooks, params: (view) => Params.get(view)})
socket.connect()
Note that the above url encodes the form, which requires decoding it on the server. You could write more JS to serialize the form as json to avoid this step ![]()
Also Liked
jeremyjh
I think it does not replace input contents when it receives updates in a reply message, but it does something different on reconnect/mount. But yes maybe that is just a bug, that would be great if it can simply be solved at that level. I can recreate it using the User edit page in the example repo. I did do a mix deps.update phoenix_live_view to confirm its happening on master.
I think if forms are handled in a general way, other kinds of wierdness could probably be handled by either using query params or maybe some custom client code using life-cycle hooks (I understand they plan to implement those). If the client has a way to capture arbitrary state at the disconnect event, it could send a message at reconnect to help restore that state for example.
Another option would be to fallback to HTML controllers on disconnect. This would even work in the form example; you’d lose your live validations but it would be nice if you could prevent a disconnect from disabling the UI. Then all you have to do is implement a controller for the PUT/POST route. It already works that way if you disable Javascript and implement that one controller method, but on disconnect you get to watch the spinning ball without recourse.
benwilson512
Is this true? I got the impression that morphdom was configured to not eliminate form contents. Maybe you’re running into a bug for this specific thing?
However, the general point stands. If there are items on the screen that you hold in the live view state, a deploy will eliminate that state and the page will look weird. Some of this can be handled by pushing state into the params, but it’d be nice if there was maybe a local storage API? If that worked across pages then it’d be rather complicated though.
jeremyjh
Yes its not very scalable unless you introduce a dedicated backing store and if you think about the issues involved in doing it reliably I think you’ll realize its harder than supporting hot upgrades, because you still have to solve the state versioning/migration issue. This is why I think doing as much on the client as possible is the better course.
Popular in Discussions
Other popular topics
Latest Phoenix Threads
Chat & Discussions>Discussions
Latest on Elixir Forum
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #elixirconf-us
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #security
- #hex










