Section 17.5. Heartbeat


17.5. Heartbeat

ACK, Announcement, Flash, Heartbeat, Monitor, Signal

Figure 17-14. Heartbeat


17.5.1. Goal Story

For auditing purposes, Frank must run the factory decision support system all day long, so the server needs to ensure that it's always alive in the browser. But requests won't come in very often because it's a fat client, with the business logic in JavaScript. To ensure that the server keeps getting requests, the browser explicitly uploads a Heartbeat message every 10 minutes.

17.5.2. Problem

How do you know the user still has an application in the browser and is actively working with it?

17.5.3. Forces

  • It's useful for the server to track whether the user is active, with several applications highlighted in the Solution of Timeout.

  • Due to the stateless nature of HTTP, the server doesn't know when the user has quit the browser, experienced a browser crash, or surfed away to a different URL. Note that you could use the JavaScript onunload event to catch the last of these, but it won't work for the first two. In tracking the user's activity, the server may assume the user is still active when he has in fact abandoned the application.

  • The user sometimes spends a long time working in the browser, but in a manner which yields no calls to the serverfor example, filling out a long form or playing a game. The server has no way of knowing that the user's still around if no calls are made.

17.5.4. Solution

Have the browser periodically upload Heartbeat messages to indicate that the application is still loaded in the browser and the user is still active. The server keeps track of each user's "last Heartbeat." If the Heartbeat interval is 10 minutes and the user's last Heartbeat was longer than 10 minutes ago, the server knows that the user is no longer active. The objective is to help the server track which users are active. Session tracking isn't essential in Ajax Apps, where it's possible to hold all session data as JavaScript state, but, as described in the Solution of Timeout, there are still reasons to do it.

Heartbeat messages are uploaded to a special "Heartbeat service" using XMLHttpRequest Calls. Because they affect server state, they should be POSTed in. The Heartbeat service will update the user's last Heartbeat record, but how does it associate a Heartbeat message with a user? Heartbeat relies on some form of session management. One approach is to use cookies, either directly or via a cookie-based session framework. However, if you do that, the conversation will be stateful, thus violating a fundamental RESTful Service principle. A cleaner approach is to explicitly include the session ID in the Heartbeat body. Note that you shouldn't just upload the user ID, as others could easily fake the user ID.

Heartbeat is closely related to Timeout, but they work in different ways. You can use either independently, but Heartbeat works best as a supplement to Timeout. The point of Timeout is to stop the browser application after an idle period for security and bandwidth reduction. Notifying the server of Timeout events yields extra benefits, but it only works if the application is still sitting in the browser and working fine. That's why we use Heartbeat messages, which are, in a sense, the inverse of Timeouts, and therefore a good supplement. Whereas a Timeout message has the browser announce when a timeout has occurred (Figure 17-15), a Heartbeat message has it continuously announced that a Timeout hasn't occurred (Figure 17-16). As soon as the server detects a missed Heartbeat, it can assume the application is no longer running.

Figure 17-15. Heartbeat sequence


Figure 17-16. Timeout sequence


As an alternative to Heartbeat, you can maintain a "Last Seen" or "Last Request" field to track the last time the user issued a request. Thus, not only Heartbeat messages but any other messages will refresh the user's record. The Heartbeat is just a backup. Using these fields might paint a more meaningful picture if you're showing the timestamp to other users or feeding it into analysis.

This pattern is purely speculative in the context of Ajax applications. However, Heartbeats are commonplace in enterprise messaging systems, where they are used to monitor the status of components throughout a network.

17.5.5. Decisions

17.5.5.1. How will you maintain user records?

There are two main ways to maintain user records:


In memory

The data is not persisted and will be lost once the user is timed out. In most environments, this will happen if you rely on standard session objects.


Directly in the database

Within persistent user records. There's some extra storage and data maintenance involved, but the benefit is that you always know when the user was last seen.

17.5.5.2. What, if anything, will cause the browser application to stop sending Heartbeats?

One option is for the browser application to always send Heartbeats. This lets the server track whether the application is still sitting in the browser, which may be useful for analysis purposes. More likely, though, you probably want to send Heartbeats only if the user is actively working with the browser. Thus, as mentioned in the preceding Solution, the Heartbeat is effectively a message that says "the user has not yet timed out."

17.5.5.3. How much time between Heartbeats? How much delay until a user is declared inactive?

You need to decide on the time period between Heartbeats. If it's too long, the information will not be very useful. If it's too short, you'll be placing a strain on the network, as well as impacting browser and server performance.

The appropriate figure could vary from subseconds to up to 30 minutes or more depending on the following factors:


Application

In some applications, up-to-the-second information is more critical. In a multiuser system, for example, users' work may be dictated by whoever else is present. If you wait 10 minutes to tell Alice that Bob has quit the chess game, she might be annoyed that she wasted the last 10 minutes thinking about her next move.


Available resources

Ideally, the period should be as short as possible, though you can't always justify this. For an intranet application, you're likely to use a shorter period in recognition of better resources per user.

17.5.6. Real-World Examples

As this pattern is speculative, there are no real-world examples at this time.

17.5.7. Code Refactoring: AjaxPatterns Heartbeat Wiki

The Timeout pattern (see earlier in this chapter) refactored the wiki to produce the Timeout Wiki Demo (http://ajaxify.com/run/wiki/timeout), and two further refactorings from there. The present refactoring (http://ajaxify.com/run/wiki/timeout/heartbeat), as seen in Figure 17-17, creates a third version of the basic Timeout demo, introducing a Heartbeat. Note that Heartbeat can work independently of Timeout but works better in tandem, as this pattern demonstrates.

Figure 17-17. Showing user status via Heartbeat


To illustrate Heartbeat, I'll show one of its main applicationsan application that lets users see who else is "currently online", i.e., who else has a recent Heartbeat registered. Thus, each user is assigned a random ID, and a Heartbeat mechanism is used to maintain the "Currently Online" list.

But first, let's look at the Heartbeat mechanism. Server side, the Author class contains a lastRequest property:

   class Author {     private $id;     ...     private $lastRequest;     ...   } 

The Heartbeat service accepts a message with an author ID and updates the user's lastRequest. Note that a production system should accept a session ID rather than a user ID. The service extracts the author from the database and updates its lastRequest. If the author does not yet exist, a new record is created with lastRequest set to the present. Finally, the record is persisted:

   if (isset($_POST['renew']) && $_POST['renew']=="true") {     $authorId = $_POST['author'];     $now = time( );     $author = $authorDAO->fetch($authorId);     logInfo("Got author record for $authorId");     if ($author) {       logInfo("Updating timestamp for user $authorId");       $author->setLastRequest($now);     } else {       logInfo("Adding author $authorId");       $author = new Author($authorId, $now, $now);     }     $authorDAO->persist($author);   } 

A periodic loop is set up to access the Heartbeat service. Because the Heartbeat call is "fire-and-forget," an empty callback function is used:

   window.onload = function( ) {     ...     heartbeatTimer = setInterval(sendHeartbeat, HEARTBEAT_PERIOD);     ...   }   function sendHeartbeat( ) {     vars = {       renew: true,       author: authorId     };     ajaxCaller.postForPlainText("session.phtml", vars, function( ) {});   } 

And that's the basic Heartbeat pattern. But there's a bit more to do because we're integrating it into Timeout. We want to stop sending Heartbeats when a timeout has occurred in the browser. In the preceding code, we created a variable, heartbeatTimer, to track the periodic Heartbeat calls. Since we have a handle on the periodic process, we are able to cancel it upon timeout:

   function onTimeout( ) {     ...     clearInterval(heartbeatTimer);     ...   } 

The other feature here is the list of online users, which illustrates one way to use the Heartbeat data. session.phtml exposes an XML list of currently online users (http://ajaxify.com/run/wiki/timeout/heartbeat/session.phtml?current). The fetchRecentlyActive method accepts a parameter indicating just how recently active the records should be. In this case, we ask for users with a request in the past 10 seconds:

   if ($_GET="current") {     header("Content-type: text/xml");     echo "<authors>";     foreach ($authorDAO->fetchRecentlyActive(10) as $author) {       echo "<author>{$author->getId( )}</author>";     }   echo "</authors>"; 

In the browser, there's a div to contain the list:

     <h1>Who's Online?</h1>     <div ></div> 

A timer is established to perform a Periodic Refresh on the current data. Every few seconds, the XML is requested and the callback function transforms it into HTML for display:

   currentlyOnlineTimer = setInterval(updateCurrentlyOnline, CURRENTLY_ONLINE_PERIOD);   function updateCurrentlyOnline( ) {     ajaxCaller.getXML("session.phtml?current", function(xml) {       $("currentlyOnline").innerHTML = "<ul>";       var authors = xml.getElementsByTagName("author");       for (var i=0; i<authors.length; i++) {         var authorName = authors[i].firstChild.nodeValue;         $("currentlyOnline").innerHTML += "<li> " + authorName + "</li>";       }       $("currentlyOnline").innerHTML += "</ul>";     });   } 

As with the Heartbeat timer, the "currently online" timer is cancelled upon timeout, so no more Periodic Refreshes will occur:

   function onTimeout( ) {     ...     clearInterval(currentlyOnlineTimer);     ...   } 

17.5.8. Related Patterns

17.5.8.1. Timeout

Timeout (see earlier) is a companion pattern, as discussed in the preceding "Solution."

17.5.8.2. Submission Throttling

Heartbeat resembles Submission Throttling and Periodic Refresh (Chapter 6) insofar as all have a continuous XMLHttpRequest Call cycle. However, Heartbeat has a more specific aimnamely, informing the server of browser state.

17.5.9. Metaphor

The name "Heartbeat" is, of course, taken from a metaphor with the biological heart.

17.5.10. Want to Know More?

Go to http://www.mindspring.com/~mgrand/pattern_synopses3.htm#Heartbeat for a brief summary of Enterprise Java Heartbeat pattern. See Mark Grand's "Java Enterprise Design Patterns" for the full pattern.




Ajax Design Patterns
Ajax Design Patterns
ISBN: 0596101805
EAN: 2147483647
Year: 2007
Pages: 169

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net