Version 2

    Recently, I spent a fair amount of time tearing my hair out (figuratively), trying to figure out why users were reporting symptoms that looked like conversation timeouts after 30 minutes, even though I had set the conversation timeout to 60 minutes on the page in question.  I finally figured out the answer, but I haven't seen it discussed in this context, so I'm sharing my findings with the Seam community.

     

    The best discussion I've seen of Seam's conversation timeout handling is here.  The important thing to know is that conversations are subject to timeout only when they have been in the background for the specified length of time.  A conversation timeout is detected because it causes a ViewExpiredException, which can be seen in the log file, and which forwards the user to an appropriate error page.

     

    But there is another way that views from background conversations can expire, causing symptoms that look exactly like conversation timeouts.  This is related to two JSF settings: numberOfViewsInSession and numberOfLogicalViews.  Both default to 15.  A great explanation of them can be found here.  In brief, their intention is to govern how many times you can click the Back button, and still have a functional form.  But they don't distinguish between clicks in the same window/tab, and clicks in a different window/tab.  So views in your background conversations are subject to expiration by this mechanism.

     

    So to trigger the problem, simply do the following:

    1. Open up two windows, one with a form and long-running conversation, and one without.
    2. In the window without the long-running conversation, click random links 15 times.  (Or more or less, if you have changed the parameters from the default.)
    3. Go back to the window with the long-running conversation, and try to submit the form.
    4. Observe the timeout error message, even though the conversation timeout limit hasn't been reached!

     

    This behavior unfortunately subverts the intention of Seam's conversation functionality.  I wish that there were some way to make views in long-running conversations somehow independent of views in other conversations when the numberOfViewsInSession parameter is applied.

     

    Until somebody clever figures out a way to make that happen, possible solutions include raising the values of those parameters, or using client-side view state rather than server-side view state.  Both solutions, however, can create potential performance problems, and so should be approached with care.

     

    An Update:

    As David Baddeley mentioned in the comments, the version of Mojarra I am using appears to flip the use of the numberOfViewsInSession and numberOfLogicalViews parameters, so increasing numberOfLogicalViews while decreasing numberOfViewsInSession is effective at preventing these ViewExpiredExceptions, while keeping memory consumption under control.

     

    However, I find new problems when I increase numberOfLogicalViews well above 25, and perform the above test with 25 or more clicks.  I no longer get a ViewExpiredException, but the form values don't get applied upon form submission.  It's as if the Apply Request Values phase of the JSF lifecycle is being skipped.  I don't know why this happens, or why the number 25 seems to be special.  That number does not appear to be affected by the two parameters under discussion here, nor can I find any other Mojarra parameter that defaults to 25.  Also, it seems that only 25 GET requests trigger the issue; not 25 form submissions.

     

    For now, I have solved this by means of a probably overengineered solution.  I've added Javascript to every page in the application to track in localStorage how many pages have been rendered:

    if (typeof(Storage) != "undefined") {
      if (localStorage.pagesRendered)
        localStorage.pagesRendered = Number(localStorage.pagesRendered) + 1;
      else
        localStorage.pagesRendered = 1;
    }
    

     

    Then I adapted the timeout prevention solution I posted here to add an additional check on a 5 second interval.  If it finds that pagesRendered has increased by 15 since the last keep-alive, then it pings the server to keep itself intact.  So unless a user does 10 speed-clicks in 5 seconds, their form data should always remain intact, and they should never trigger a ViewExpiredException -- unless they really have a timeout, that is.