Timeout - Ajax Patterns

Timeout

From Ajax Patterns

(Difference between revisions)
Revision as of 00:22, 28 November 2005
Mahemoff (Talk | contribs)
Alternative Patterns
← Previous diff
Revision as of 00:23, 28 November 2005
Mahemoff (Talk | contribs)
Code Examples
Next diff →
Line 108: Line 108:
<!-- -------------------------------------------------------------------- --> <!-- -------------------------------------------------------------------- -->
-= Code Examples = 
-<!-- -------------------------------------------------------------------- --> 
= Refactoring Illustration = = Refactoring Illustration =

Revision as of 00:23, 28 November 2005

Evidence: 2/3

Tags: Logout Session Suspend Timeout


Contents

In A Blink

Message "Your session was terminated after 30 minutes of inactivity."


Goal Story

Tracy was so keen to leave the office on Friday afternoon that she forgot to shut down the market news website. Fortunately, the website monitors mouse movements, meaning that if she doesn't interact with the page for an hour, it will suspend itself, saving a lot of wasted refreshes. On Monday morning, Tracy walks in and sees a screensaver-like animation on the page, with a message indicating that updates have ceased. She clicks a mouse button and sees the screen update with the latest prices.


Problem

How can you tell if the user's still working with the application?


Forces

  • Users often leave an application in a browser window, but don't actually interact with it for hours or days. Many times, they abandon it altogether. The practice is becoming more prevalent as all major browsers now support multi-tab browsing.
  • Ajaxian applications often use Periodic Refresh to keep informed of server state. The user may not care about the application anymore, but if it's still in the browser, a naieve implementation will continue to poll the server indefinitely: a massive waste of bandwidth and resources.
  • Ajaxian applications often contain sensitive data which is at risk if the user forgets to log out when leaving a public terminal.
  • Ajaxian applications often involve collaboration and communication between users. To cultivate an awareness of other users and their activities, the server must track the state of individual browsers.


Solution

Have the browser timeout the user after a period of inactivity, and optionally inform the server. After a period of inactivity, the application is suspended, requiring the user to manually resume it; or shut down, requiring the user to restart.

This pattern raises the whole question of sessions in Ajax. Conventional applications usually have server-side session objects which represent the user's current session. Typically, this means short-lived information such as shopping cart contents or recently-viewed items.

In Ajax, server-side sessions are not so useful. In fact, if you rely solely on RESTful Services, you may not need those session objects at all. In browser-centric Ajax applications, the browser is a fully-fledged application with all of the session state held within. So the browser state is the session. The server-side session, if used at all, is only relevant to authentication and has no impact on the response to any particular query. See RESTful Service for more details.

How is all of this relevant to Timeout? Well, consider this familiar nightmare scenario:

The user opens up a form, spends two hours populating it while researching the answers, clicks on submit. Browser then responds with "Your session has expired" and adds insult to injury by presenting a new blank form.

Scenarios like this are all too common because server-side session timeouts ignore what's happening in the browser. If the user hasn't submitted a form in, say, 30 minutes, the session times out and all the data is lost. That's a problem if the user has been actively working on the browser side. With an Ajax application, the server does have a better chance of staying in sync with the browser because of XMLHttpRequest Calls. In most environments, the server-side session will be automatically refreshed when an XMLHttpRequest Call comes in. However, even that's not optimal because the server still doesn't know why browser calls are occurring. For applications using Periodic Refresh, for example, a server-side session will stay alive indefinitely even if the user has left the building.

By relying instead on browser-based timeout detection, we can be more intelligent about how timeouts occur. Browser-based timeout detection is based on Event Scheduling. A timer is established to count down to timeout state. Each significant activity cancels the timer and starts a new one in its place.

What activities are significant enough to restart the timer? Mouse movement is one candidate. You can monitor each mouse movement on the page, and one of the Refactoring Illustrations below does exactly that. Mouse movements are a very broad indicator, and you might be concerned about "false positives": suggestions that a user is still working with an application when they're in fact not, e.g. they just happened to move the mouse across the browser window while cycling through each open window . You'll also get some "false negatives": a user working only with the keyboard is liable to be timed out. If you'd prefer to keep the session going only if the user is actively changing data, you can instead catch events such as button clicks and keypresses within input fields.

Sudden timeouts can be frustrating for users, so consider these feedback measures:

  • Unobtrusively show timeout status, e.g. number of minutes remaining. You might highlight the status as timeout approaches, and also offer some background and instruction on avoiding the timeout.
  • When timeout is approaching, offer a warning and an opportunity to explicitly restart the timer.

Once you've detected the user is inactive, what do you do? Timeout can be used in several ways.

  • Stopping Periodic Refresh: Conventional applications tend not to do anything when the user is idle, but many Ajaxian applications continuously poll the server. Thus, the timeout should trigger cancellation of any Periodic Refresh timers.
  • Clear data: Sometimes, an inactive application is a hint that the user may no longer be in control of a terminal. There's a risk someone else could gain access to the user's data and application permissions, e.g. in an internet cafe or a malicious employee in an office. A convenient way to do this might be to set document.url to the main application URL, so as to refresh the page. In addition, you might delete cookies holding sensitive data.
  • Save data: If you clear data, you might also consider saving it on the server first. There's nothing that's more annoying than arriving back from lunch only to find a partially filled form has been cleared for security purposes. With an XMLHttpRequest Call, you have the option of saving the user's progress as a "draft".
  • Inform user: If you're performing these other activities, you'll need to provide some feedback to the user about what's happening and what they should do about it.
  • Inform server: Although it's the browser that controls session timeouts, the server can neverthless be kept informed and can use server-side sessions for this purpose.

On the last point of informing the server, there are quite a few applications of server-side session tracking:

  • Invalidating Server-Side Session: The server can invalidate any server-side session data, for security purposes.
  • Historical Records: The server can retain timeout data to help personalise the website, provide feedback to users, and also to you, the site operator.
  • Multi-User Awareness: In multi-user systems and especially any sites hoping to foster a community, users should be aware of each others' activities. Indeed, the "live web" - or, as Technorati puts it, "What's Happening on the Web Right Now?" - is becoming a key phenomenon of Web 2.0. When the server is able to track what users are working on, and how long they have been idle, this information (subject to privacy considerations) can be conveyed to other users. That's especially important when users are collaborating on the same workspace, for instance in an Ajaxian wiki environment.
  • Pessimistic Locking: Pessimistic locking is a technique used to prevent two users working on the same thing at once. In a wiki, for example, a user can lock the article to have sole access to it until it's saved or until the user goes idle for a while. While this can be open to abuse on the public web, it would actually be quite a good model for many intranet applications. However, it's not been practical because of the risk the user might walk away and leave the lock intact. Server-side session timeouts don't help much, because the user might be busily working in the browser, but not yet ready to submit content. But once the server is aware of what the user's doing in the browser, pessimistic locking starts to become practical. If the user hasn't done anything for the last 30 minutes, for example, they lose the lock. Again, Ajax helps here, because with Periodic Refresh, you can immediately inform the user they've lost the lock.
  • Work Scheduling: In a variant of Predictive Fetch, it's possible for the server to proactively prepare for future actions. By tracking active users, the process becomes more focused. For example, a blog aggregator could run a periodic background process to build a list of unread articles for each active user, should they decide to view that list.

So informing the server of a timeout is a good thing. But there's still a small problem with this approach: what if the user quits the browser or moves away to another site? Then, a timeout will never occur and the browser will blissfully continue assuming the user's logged in for all eternity. To alleviate this problem, have the server refresh a user's record upon each incoming request that suggests user activity. However, there remains that risk of inadvertently timing out a user who's performing browser-only activity. So what we need is a way for the browser to keep telling the server the user's still active - explictly noting that a timeout hasn't actually occurred. And that's what the Heartbeat pattern's about - see that pattern for more details.


Decisions

How long will the timeout period be?

The timeout period will depend on the purpose for having the timeout period in the first place. For example, if the purpose is primarily to stop Periodic Refresh, you need to weigh up the benefits of reduced bandwidth and server costs, against the frustration caused to users who will have to re-activate the page and wait a few seconds for the latest data. In practice, there are probably several reasons to use Timeout and the needs of each must be taken into consideration.

How will timeout affect the user-interface?

A Timeout can impact the interface in different ways. There are several options:

  • Wipe the entire display and show a message that the user was timed out, or the session is suspended. This would be worthwhile if the data is sensitive.
  • As a variant on the previous point, reload the entry point for the application (as if the user had typed the main URL in).
  • Perform a DOM Rearrangement to insert a new message about the timeout into the page.

In addition, if you want to be really sure that users know they've been timed out, you could produce an alertbox. Use Timeout alertboxes with caution: it's quite obtrusive, and you could probably make the Timeout equally obvious with a carefully considered page element. A less obtrusive mechanism would be a Popup.


Real-World Examples

Many conventional websites have a session timeout feature, though it's implemented server-side rather than the way described here.

Lace Chat

Brett Stimmerman's Lace Chat is an Ajaxian chat application. As such, it requires a Periodic Refresh to keep showing new messages, a serious bandwidth concern if the user's no longer interested in the conversation. So, after some idle time, a dialog box appears indicating Lace has stopped and includes a button allowing the user to resume. Lace also has a Status Area that always shows whether the application is running - i.e. if the server is being polled. In fact, you can pause and resume it manually too. The timeout mechanism hooks into this state, by forcing the application to pause upon timeout.

Session Warning Demo

Eric Pascarello has explained a demo system which has a standard server-side session timeout, but also pops up a warning near timeout allowing the user to renew the timeout. There are implementations in both VB.net and JSP.

Operating System Timeouts

A good precursor to Ajaxian Timeout is with operating systems, such as Apple OSX, that show a warning when they are about to enter standby mode. A popup dialog explains something like "You haven't used the mouse or keyboard for 15 minutes. Standby mode will start in 30 seconds." The dialog will count down ,and the user can prevent it with a keyboard or mouse action, or explicitly hitting a Cancel button. A similar precursor is present in remote terminal systems that output an idle warning to the console.



Refactoring Illustration

Introducing Timeout to the Wiki

Timeout is a very useful pattern for a wiki, on several counts. First, it cuts down on Periodic Refresh wastage. Second, it helps users understand what others are up to. Third, it improves security if users are logged on. For all those reasons, we'll now be adding some Timeout functionality to the Basic Wiki Demo. (The Periodic Refresh Time Demo also has a couple of similar timeout refactorings under it.)

Initial Refactoring: Unconditional Timeout

This initial version, Timeout Wiki Demo, is merciless. Log into the wiki and, no matter what you do, you're timed out a few seconds later. The point, of course, is to introduce and prove the basic timeout functionality.

To the initial HTML, a timeout message is added, which will initially be hidden.

  <div id="timeoutMessage">
    Timed out. Please <a href=".">reload the page</a> to continue.<br/>
  </div>

The script begins by hiding the timeout message and kicking off a timeout timer.

 window.onload = function() {
   ...
   $("timeoutMessage").style.display = "none";
   timeoutTimer = setTimeout(onTimeout, TIMEOUT_TIME);
 }

When timeout occurs, the timeout message is made visible (with some fanfare, courtesy of a Scriptaculous effect). Most importantly, the periodic sync stops, so no more polling the server. All the messages are removed from the page as well.

 function onTimeout() {
   new Effect.BlindDown($("timeoutMessage"));
   stopPeriodicSync();
   removeMessages();
 }
 ...
 function removeMessages() {
   while ($("messages").hasChildNodes()) {
     $("messages").removeChild($("messages").firstChild);
   }
 }

Warning that Timeout is Pending

If only for the selfish reason that we want to avoid being flamed by the user, it would be nice to warn them a timeout's about to occur. And nicer still to let them prevent it from occurring. So this demo introduces a warning mechanism.

To the previous timeout message, we add a warning message as well. Both are initially invisible. The warning includes a renew button, which will invoke renewSession().

  <div id="warningMessage">
    Near timeout. Please <button id="renew">Renew</button> your session now.
  </div>
 window.onload = function() {
   ...
   $("renew").onclick=renewSession;
   ...
 }

renewSession() is not only used on renew, but also on startup. It resets both the timeoutTimer and the warningTimer. Note that these timers have been introduced as variables in the script, precisely so we can cancel them in this method. In addition, renewSession() kicks off the standard wiki sync timer and hides the warning and timeout messages in case either is showing.

 var timeoutTimer = null;
 var warningTimer = null;
 ...
 function renewSession() {
   $("warningMessage").style.display = "none";
   $("timeoutMessage").style.display = "none";
   clearInterval(warningTimer);
   clearInterval(timeoutTimer);
   warningTimer = setTimeout(onWarning, WARNING_TIME);
   timeoutTimer = setTimeout(onTimeout, TIMEOUT_TIME);
   startPeriodicSync();
 }

So as long as the user clicks on the renew button while it's showing, they can force all timers to be restarted. But if they don't click on it, timeout will proceed. We simply have to ensure the warning message is shown before the timeout message, i.e. WARNING_TIME must fall short of TIMEOUT_TIME. When timeout occurs, we can assume the warning's already being shown, so we replace it with the timeout message. And as before, we stop synchronisation and remove all messages.

 function onTimeout() {
   new Effect.BlindUp($("warningMessage"));
   new Effect.Appear($("timeoutMessage"));
   stopPeriodicSync();
   removeMessages();
 }

Monitoring mouse movements

A warning's better than nothing, but it does have a slight odour of techno-centeredness. From the user's perspective, why should they have to explicitly renew the session. What's this timeout business anyway? For most users, it's better to handle timeout on their behalf. This demo builds on the initial Timeout demo to suspend activity when the user's idle.

Here, we'll make an assumption that the user is working with the mouse. That is, we can assume the user's idle if (and only if) they haven't used the mouse for a while. As before, we only have one timeout message, which we've altered to help the user understand how to bring all those messages back again.

  <div id="timeoutMessage">
    Timed out. Please wiggle your mouse to continue.<br/>
  </div>

The key to this pattern is watching for mouse movements on the page, and renewing the session when that happens. Actually, that's easy enough to achieve.

 window.onload = function() {
   ...
   document.getElementsByTagName("body")[0].onmouseover = function(event) {
     renewSession();
   }
   ...
 }

Calling a function on each mouse movement could get grossly inefficient. In this demo, I'm not worried about that. But in real life, you might want to be just a little more intelligent about this. For example, use some sort of throttling algorithm to ensure renewSession isn't called more than once every few seconds.

renewSession itself works similarly to the warning demo above: it hides the timeout message and kicks off sync and timeout timers.

 function renewSession() {
   if ($("timeoutMessage").style.display!="none") {
     new Effect.BlindUp($("timeoutMessage"));
   }
   clearInterval(timeoutTimer);
   if (!syncTimer) {
     startPeriodicSync();
   }
   timeoutTimer = setTimeout(onTimeout, TIMEOUT_TIME);
 }


Alternatives


Related Patterns

Heartbeat

Heartbeat is a companion pattern to help the server to monitor if the user is still working in the browser.

Periodic Refresh

For Ajaxian applications, reducing Periodic Refreshes is a big motivation for Timeout functionality.

Progress Indicator

Progress Indicator is normally used for feedback during an XMLHttpRequest Call. However, another application would be to count down towards the timeout. The display could be present as a permanent fixture, or alternatively you could introduce it near timeout.

Direct Login

If your timeout requires re-authentication to continue working with the system, you probably want to offer a Direct Login.

Status Area

You can show timeout-related messages and countdowns in a Status Area.

Popup

You can show timeout-related messages and countdowns in Popups.


Visual Metaphor

If a car engine remains idle long enough, you're probably going to assume it's dead and give up trying to start it. To keep going would only be a waste of energy.


Want to Know More?