GWT application freezes when new version is deployed while using it

1331 views
Skip to first unread message

mayumi

unread,
Dec 30, 2011, 1:35:24 AM12/30/11
to Google Web Toolkit
While using the GWT application, I deployed the new version of the
application. Once deployment is complete my application freezes until
I do the full refresh. I originally thought this was caching problem,
so I assigned all the files to be no-cache by setting all response
headers using the filter. But still with this filter application
freezes when I deploy a new version.

What could be a cause of this?

What I am trying to achieve here is to be able to deploy a new version
of GWT application while user is using the application. This means
when the JavaScript changes I want user to get it from the server
instead of using the cached one without any glitch (maybe slow
response time since request need to go to the server in this case).

mayumi

unread,
Dec 30, 2011, 2:50:01 AM12/30/11
to Google Web Toolkit
This is observed when deployed with the changes in the code, so I am
guessing one of the cache.js file.
When deployed without any changes to a code this is not observed.

Thomas Broyer

unread,
Dec 30, 2011, 7:14:49 AM12/30/11
to google-we...@googlegroups.com
What do you mean by a "freeze"? and a "full refresh"?

  1. users won't have the new version unless they refresh (F5) the page. For this though, you have to ensure the *.nocache.js is effectively not cached by browsers or proxies (or at a minimum cached with a "must-revalidate" condition)
  2. any change to a GWT-RPC class (particularly objects that are transfered through RPC) will break older versions that are still in use, with an IncompatibleRemoteServiceException, because client and server must use the same serialization policies; you can track this to tell users to refresh the page (I guess this is what the GWT-based Google Groups UI does)
  3. if you use code splitting (runAsync) and you remove the old *.cache.* files, users won't be able to download the fragments while they're using the old version of the app. You can, again, catch this in the onFailure of the RunAsyncCallback to tell users to refresh the page.
  4. Every compilation should produce differently named *.cache.* files, so you can safely set long cache times for these files: http://code.google.com/webtoolkit/doc/latest/DevGuideCompilingAndDebugging.html#perfect_caching

mayumi

unread,
Dec 30, 2011, 9:19:30 AM12/30/11
to Google Web Toolkit

Hi Thomas! Thanks for the reply.

"Freeze" means the whole app becomes unresponsive (buttons are not
clickable, etc)
You can also observe in the developers tool that no further requests
are made to the server from
that point.

"Full refresh" means F5 as you guessed.

I am thinking the problem is #3 . We are using code splitting and this
freeze occurs only
when the code changes (meaning a new cache.js file is created meaning
old once are not effective any more)


On Dec 30, 6:14 am, Thomas Broyer <t.bro...@gmail.com> wrote:
> What do you mean by a "freeze"? and a "full refresh"?
>
>    1. users won't have the new version unless they refresh (F5) the page.
>    For this though, you have to ensure the *.nocache.js is effectively not
>    cached by browsers or proxies (or at a minimum cached with a
>    "must-revalidate" condition)
>    2. any change to a GWT-RPC class (particularly objects that are
>    transfered through RPC) will break older versions that are still in use,
>    with an IncompatibleRemoteServiceException, because client and server must
>    use the same serialization policies; you can track this to tell users to
>    refresh the page (I guess this is what the GWT-based Google Groups UI does)
>    3. if you use code splitting (runAsync) and you remove the old *.cache.*
>    files, users won't be able to download the fragments while they're using
>    the old version of the app. You can, again, catch this in the onFailure of
>    the RunAsyncCallback to tell users to refresh the page.
>    4. Every compilation should produce differently named *.cache.* files,
>    so you can safely set long cache times for these files:
>    http://code.google.com/webtoolkit/doc/latest/DevGuideCompilingAndDebu...

Kyle Baley

unread,
Dec 31, 2011, 9:36:26 AM12/31/11
to google-we...@googlegroups.com
Thomas,
 
In the case of code-splitting, it sometimes doesn't make RPC calls when the user navigates around. Rather, it tries to load the .cache.js files required for the "page" the user is navigating to. In the case where a new version is deployed and the user hasn't refreshed, these files of course won't exist. How do you handle this gracefully?

We're using AppEngine so new versions don't have the old .cache.js files by default.

Thomas Broyer

unread,
Dec 31, 2011, 6:46:27 PM12/31/11
to google-we...@googlegroups.com
runAsync are not expected to do RPC, these are separated concerns.

But you pass a RunAsyncCallback to GWT.runAsync(), and the onFailure of this callback is indeed called when the fragment cannot be loaded.

Kyle Baley

unread,
Jan 4, 2012, 9:15:52 PM1/4/12
to google-we...@googlegroups.com
Thanks Thomas. This helped us find the ultimate root of the problem. We had mapped a servlet (quite by accident) to a URL pattern that matched our module name. So after deploying a new version, requests to the old .cache.js files were being picked up by this servlet rather than throwing a 404 error and generating an onFailure.

Kyle Baley

unread,
Jan 5, 2012, 7:39:37 AM1/5/12
to google-we...@googlegroups.com
Now that I've determined our problem, I have another question. Is there a clean way to *not* require the user to refresh the page?

Raphael André Bauer

unread,
Jan 5, 2012, 7:43:51 AM1/5/12
to google-we...@googlegroups.com
On Thu, Jan 5, 2012 at 1:39 PM, Kyle Baley <ky...@baley.org> wrote:
> Now that I've determined our problem, I have another question. Is there a
> clean way to *not* require the user to refresh the page?

Don't think that's possible, but you can do the refreshing "user
friendly" by simply reloading the app and pointing to the "place"
where the user currently is... So there will be a reload, but it will
not break ux...

Raphael

>
> --
> You received this message because you are subscribed to the Google Groups
> "Google Web Toolkit" group.
> To view this discussion on the web visit
> https://groups.google.com/d/msg/google-web-toolkit/-/xyHkZ_2-o0YJ.
>
> To post to this group, send email to google-we...@googlegroups.com.
> To unsubscribe from this group, send email to
> google-web-tool...@googlegroups.com.
> For more options, visit this group at
> http://groups.google.com/group/google-web-toolkit?hl=en.

--
inc: http://ars-machina.raphaelbauer.com
tech: http://ars-codia.raphaelbauer.com
web: http://raphaelbauer.com

Thomas Broyer

unread,
Jan 5, 2012, 7:49:11 AM1/5/12
to google-we...@googlegroups.com
Maybe keeping the old *.gwt.rpc files around (provided you didn't change the remote service interfaces and transported types), or maybe it was a feature of the abandoned deRPC (ever noticed that Google Groups –which uses GWT-RPC– regularly asks you to reload the page to get the newer version? (luch more likely to happen if you keep your browser/tab open for days)).

For runAsync, keeping the old fragments will do it (obviously, the users will still be running the old version, so your server should be prepared to handle calls from this version).

Unless you're deploying several times a day, it's much easier to just have users reload the app: only a few users should ever notice it, and most likely when coming back to the app after they let it open in their browser during the night/lunch (of course plan your deployment depending on your app's traffic)

Thomas Broyer

unread,
Jan 5, 2012, 7:51:49 AM1/5/12
to google-we...@googlegroups.com
Oh, forgot to say RequestFactory doesn't have this issue that GWTP-RPC has, and/or can be tweaked (@PropertyName and/or ServiceLayerDecorator) so that the server can handle requests from different versions of the app (and/or new versions of the app generate bakwards-compatible messages despite property renamings)

jhulford

unread,
Jan 5, 2012, 9:31:51 AM1/5/12
to Google Web Toolkit
If you're using Tomcat, Tomcat 7 supports updating a webapp and
keeping the old instance running along side it for situations like
this. As I understand it (I don't use it yet), http sessions/requests
tied to the old webapp get routed to the old instance and new sessions
get routed to the newly deployed one. After all the sessions are dead
from the old web app it's undeployed. Obviously, this won't work for
everyone since some people have issues hot deploying their app, but
it's something you could look into.

Kyle Baley

unread,
Jan 11, 2012, 8:56:59 AM1/11/12
to google-we...@googlegroups.com
We've gone Thomas's original suggest route of informing the user. But in our original pass at this, we threw up a dialog with an OK button on it. It said something to the effect of "There's a new version. Please log in again". Clicking OK is intended to log the user out and forcibly refresh the page.

What we're finding is that for the code-splitting case, the dialog does indeed come up but the whole page becomes unresponsive afterward. That is, you can't click the OK button. I've verified that this happens regardless of whether we put a dialog up or not. Nothing on the page is clickable at all. Wondering if this is what others see as well and if so, if there's a way around it.

coderinabstract

unread,
Feb 28, 2012, 10:38:32 PM2/28/12
to google-we...@googlegroups.com
Experiencing a similar problem and this seems to be very frustrating for user experience management especially if you are doing agile build life cycle with frequent releases.

The situation is that AsyncCallback failure is NOT firing and when we have code split points and there are ONLY User interface changes i.e. ux changes, and a user has navigated on all pages of the previous version of the app, even with the cache control header (with a servlet filter), the failure does not fire i.e. the old UI works fine with the services and cannot detect the new UI client on the server unless the user explicitly refreshes UI does not refresh.

Any thoughts... examples, best practice. I sincerely hope I am missing something as this is very tricky trapping all the what if scenarios when there is a new app.

Cheers and Thanks....

Thomas Broyer

unread,
Feb 29, 2012, 8:39:15 AM2/29/12
to google-we...@googlegroups.com


On Wednesday, February 29, 2012 4:38:32 AM UTC+1, coderinabstract wrote:
Experiencing a similar problem and this seems to be very frustrating for user experience management especially if you are doing agile build life cycle with frequent releases.

The situation is that AsyncCallback failure is NOT firing and when we have code split points and there are ONLY User interface changes i.e. ux changes, and a user has navigated on all pages of the previous version of the app, even with the cache control header (with a servlet filter), the failure does not fire i.e. the old UI works fine with the services and cannot detect the new UI client on the server unless the user explicitly refreshes UI does not refresh.

Any thoughts... examples, best practice. I sincerely hope I am missing something as this is very tricky trapping all the what if scenarios when there is a new app.

You could, e.g. add headers to your RPC responses (and/or requests), handled through an RpcRequestBuilder, that communicates a "version number" for the app.

Or more easily add a "heartbeat" RPC that periodically checks if the UI needs a refresh (e.g. using the permutation ID as the "version number": send the permutation ID and check on the server if the corresponding *.cache.* file exists in the webapp, returning a simple boolean for instance).

Stephen Haberman

unread,
Feb 29, 2012, 11:36:15 AM2/29/12
to google-we...@googlegroups.com

> Is there a clean way to *not* require the user to refresh the page?

Everything Thomas said is right, and I'll chime in with my own experience. Basically:

- If you use IsSerializable and no type-name elision you won't have to keep old GWT RPC policy files around between builds (the legacy serialization policy will handle new/old clients as long as your DTOs don't have changes, which for frequent builds is probably more often the case--AFAIK this makes GWT RPC comparable to RF in handling non-breaking-changes to the DTOs)

- As Thomas said, handle IncompatibleRemoteServiceExceptions for when DTOs do have breaking changes

- As Thomas said, handle 404s in runAsync onFailures (or just not use async loading if your app is small enough :-)

- Have a clean switch between new/old servers (e.g. if you have 10 servers, then start 10 new servers, and slowly turn off the old ones, you'll run into problems, which I walked through in a blog post [1]). Ideally you can immediately switch from the old server(s) to the new.

Having seamless deployments is surprisingly hard for GWT apps--although I don't think the problems are unique to GWT vs. regular webapps, its approach to perfect caching highlights the problems of new/old servers/clients trying to talk nicely to each other. I'd be interested in hearing internal/Google best practices for this sort of stuff, although it looks like Thomas has already inferred a lot of it by watching how the Google Groups UI behaves.

- Stephen





On Thursday, January 5, 2012 6:39:37 AM UTC-6, Kyle Baley wrote:
Now that I've determined our problem, I have another question. Is there a clean way to *not* require the user to refresh the page?

On Thursday, January 5, 2012 6:39:37 AM UTC-6, Kyle Baley wrote:
Now that I've determined our problem, I have another question. Is there a clean way to *not* require the user to refresh the page?

On Thursday, January 5, 2012 6:39:37 AM UTC-6, Kyle Baley wrote:
Now that I've determined our problem, I have another question. Is there a clean way to *not* require the user to refresh the page?

Joseph Lust

unread,
Feb 29, 2012, 8:55:50 PM2/29/12
to Google Web Toolkit
Kyle,

Can you hardcode the onclick="window.location.reload();" method in the
"OK" button, so it should still work even if the GWT frontend is
hosed?

Sincerely,
Joe

Joseph Lust

unread,
Feb 29, 2012, 9:04:36 PM2/29/12
to Google Web Toolkit
To add to Stephen's points,

- Using legacy mode, or keep the last set of RPC files around (so you
have N & N-1) should let the users fail gracefully w/o deserialization
errors. Then you can check the app version number (say put this check
on your async interfaces) and prompt for reload.

- App Engine lets you deploy multiple versions of your app to
<version>.myapp.appspot.com. If you had an 'A' and 'B' version, and
alternated them each release, you could use the graceful shedding to
move everyone from A.myapp.appspot.com to B.myapp.appspot.com and then
turn off A (or at least have no more entry points to A).

- To prevent this in our enterprise apps we have taken the slightly
draconian policy of a 30min UI timeout. There is a listener that
resets the timer every time there is a click interaction with the UI.
Thus, if someone leaves the app untouched for 30min, they get a locked
out modal with a button to reload.

- Finally, do the cut over for your app in hours of nadir load. We do
ours in the wee hours on the weekend, so nobody should be using it at
the time.


Sincerely,
Joe

On Feb 29, 11:36 am, Stephen Haberman <stephen.haber...@gmail.com>
wrote:

Kyle Baley

unread,
Feb 29, 2012, 11:30:54 PM2/29/12
to google-we...@googlegroups.com
Joe,

We've done something similar. Rather than show a pretty dialog, we embedded some JSNI code in the onAsyncCallFail call. This shows a standard "window.confirm" dialog, then calls $wnd.location.href = href (I seem to recall window.location.reload didn't work). We also deploy in the wee weekend hours as well.

I'm liking a lot of the suggestions here, particularly the one about a heartbeat call. The problem I've been wrestling with is: Whenever we want the user to use the new version, we have to wait until they actual do something (i.e. make an RPC call or hit a code-split point). Which means they're trying to get something done and we're interrupting them. A heartbeat mitigates this a little. And assuming we structure things properly, we could notify the user of the new version without actually forcing them to reload. Maybe with a message at the top. I believe Google Groups does this now, yesno?

That's the theory anyway...


--
You received this message because you are subscribed to the Google Groups "Google Web Toolkit" group.
Reply all
Reply to author
Forward
0 new messages