Reloading on assets change is causing data loss in forms with validation errors

Hi everyone,

Reloading on assets change is important feature of Turbo. Unfortunately there is a problem when user opens form and assets change when user is filling the form, if all validation pass during form submission everything is fine, but if there are validation errors Turbo receives form with validation errors with info about new assets and triggers page reload which causes loss of all data and validation errors in the form.

Reloading on assets change

It is quite unpleasant. I am actually not sure what is desired behaviour here. On one side I understand that something could have been changed in the form itself and new behaviour has to be loaded on the other side potential loss of data in a form is not good behaviour. My solution is to turn off Turbo for almost all forms in our app for now.

Did you encounter similar problems? What was your solution for this problem or is it no issue for you?

1 Like

I guess, I’m having a hard time understanding why you would have assets changing, especially in production, while users are using the app? I can’t think of a use case for that…

Assets are meant to be static, compiled down, shaken, and made as small as possible, then tagged with a unique id so that they can be cached, only loaded once by the browser, etc. Like I said, I can’t think of a use case where you wouldn’t want this to happen.

Hi Dave, the video is only demonstration. Sorry, it wasn’t clear from my description - in production we are talking about deploys. During deploy assets usually change which could lead to some unhappy users.

Well, I don’t know how you’re deploying, but most deployments change server code and assets, and unless you use sophisticated deployment schemes (or frameworks / languages that have special features) you’re going to have potential user disruption when you are deploying. But that’s not a Turbo or Rails problem, that’s an everyone problem.

I also am not sure how your example would really work in production, when assets aren’t being recompiled all the time like dev. Rails does a lot of magic in dev to make your development experience smoother. But in production, your assets are compiled once, tagged with a unique id, and the browser is told to load those specific assets which it then caches. If the user has already downloaded the page, and you are using Turbo, then the links to those assets in your HEAD aren’t going to change - that was the entire point of Turbolinks, to not reload those extra files when they weren’t needed.

You said “Turbo receives form with validation errors with info about new assets and triggers page reload” Unless I’m misunderstanding you, no that’s not how Turbo works - in fact, if it should be doing the exact opposite, it should only be replacing the body tag, or individual frames it receives from the server. The client shouldn’t be reloading those assets - even if they changed on the server - unless they’ve refreshed the page, or you’ve redirected them around Turbo.

If the page is refreshing after assets are changed on the server, you maybe have some hot reload going on in dev?

Back to deployments, the way you control user disruption is that you deploy when users are less likely to be using the service. Or, you build sophisticated deployments that can roll changes out to each server only after that server has been drained of user connections. Or a dozen or so different strategies. But its a problem that I’m sure has hundreds of thousands of devops articles written about it because every web based app has these challenges.

Example from the video is vanilla rails app with one controller, turbo-rails and importmap-rails gems - no hot reloading, only thing running is rails server.

I am talking about reloading when assets change being active when form returns HTML validation errors. It checks data-turbo-track elements and if they changed it reloads page and loses all data filled in the form.

Of course this doesn’t happen while application running in production as you said, it is compiled once during deployment. But by navigating forms via turbo drive I can and will encounter occasional loss of user data in this case:

  1. User opens form (assets in HTML head have data-turbo-track="reload")
  2. Application is deployed with changes in assets (JS or CSS) - they get new fingerprint in the name after compilation
  3. User submits form with invalid data via Turbo Drive
  4. Application receives 422 response with html including head tag with newly generated assets during deploy and body with validation errors and submitted data
  5. Turbo Drive receives server response checks elements with data-turbo-track recognises that assets changed, reloads the page and forgets server response with all data in it

I tested this workflow on our staging server and it worked exactly this way. There is no rails magic here and this problem doesn’t exist if Turbo is turned off for form submissions.

I understand it can be partially mitigated by deploying during “off hours” but because this is purely client side behaviour I am not sure if it can be fully mitigated by changes in deployments of server side code - sooner or later you have to send all users to new servers and same thing can happen.

So far most of our deployments are completely without user disruptions. I would like to keep it that way which is why I asked this question. To find out if someone found any strategies how to get around this behaviour. If it is problem for someone or if it is so minor that it doesn’t really matter to anyone or maybe people didn’t even realise it works this way.

The easiest way to get around this is to turn off Turbo for forms (our solution for now), but I would like to find a way to keep it turned on and prevent user disruptions after deploys.

2 Likes

Or just reload the assets and not the entire form ?

Oh wow, yeah I see what you mean now. Sorry, I knew there was something I was missing. :slight_smile:

Should data-turbo-track on the assets be turned off in production then? Wouldn’t this problem be solved if Turbo didn’t try to reload the assets?

The other option I could see would be to use frames / streams for the form validation return, and not send any layout with new asset links. I built some forms that way in an older project earlier this year, but as I’m trying to work with it now I’m running into issues. (Mainly with wanting to not redirect after a form submit, which I know why is happening, but not ideal for my UX.)

I need to learn how to explain myself better :slight_smile:

That is the thing, I don’t know. You definitely need and want this reloading in production. It works in every case except this one with invalid forms. Without reloading users would be stuck with old javascript/css and new HTML, not particularly great outcome, so reloading is definitely needed.

To narrow the problem it needs to be solved for forms with request methods POST/PUT/PATCH/DELETE, GET is stateless so there should not be any problems with reloading.

There are two simple solutions but they are not without drawbacks:

  1. Don’t reload page for non-GET form submissions and turn turbo off for next submittion
  2. Resubmit form via standard browser request

No. 1 could lead to new HTML in form without javascript/css support. No. 2 should be fine most of the times. Server should return the same response as for the first request but I can imagine it could also lead to some weird hard to debug errors.

More complicated (but probably ideal) solution could be to somehow notify Turbo (probably via websockets/ActionCable in rails) about assets change in advance and Turbo could turn itself off for following form submission.

In all cases above data-turbo-method links should probably still be handled via turbo.

I thought about turbo streams too (didn’t get to it yet) but it is pretty much the same solution as no. 1 above, but with the caveat that it has to be implemented manually for every form in an app. And I don’t think there is turbo event after turbo stream is rendered which could be an issue for us because reloading of our JS environment still relies on javascript events. But it could be probably handled via MutationObserver.

Do you? They would still get reloaded if the user refreshed the page after a deployment, because the full HTML would hit the browser and the asset links would be different, so it would refresh it then. All you’re wanting to do (I think) is not reload them within a turbo reload of the body. If you turn off the turbo reload in production, then wouldn’t that stop turbo from seeing the new and not replace it, and thus, not reload assets?

Is that a test you can do in your staging environment maybe?

I am not sure if I am missing something - how is user supposed to know when the page should be reloaded?

Oh, they wouldn’t. I mean in the normal course of things the page would reload. Either they would finish their work and then next time they come back would get a new version of the full page, or you do a form redirect after a successful form and use the target="_top" and force a refresh. But in general they aren’t going to sit there with the same page html forever.

Now that’s not going to help if you’ve changed some fundamental thing on the backend that any old JS isn’t going to work with. But again, I think you’re getting into edge case territory there, right?

I am not sure if it is edge case, if you have thousands of users using your app every day. You are pretty much guaranteed some of them will visit page where something changed. Of course consequences will vary based on whether changes are on dashboard/home page where everyone goes or some edge setting of some other setting in the app. Users can encounter from unstyled link to completely broken page without any CSS and JS support.

Anyway it also isn’t great customer experience. It is less severe than losing data in the invalid form on the other hand it can affect more users.