ATG RepeatingRequestMonitor

During a recent period of high traffic level on our site, we found we had several threads blocked on a particular component. We found that the component contained a synchronized block of code, which means only one request can be serviced at a time. Our issue was that the shopper did not know this of course, so they would get frustrated and continually hit the F5 or refresh button on their browser. This led to more and more queuing, as each request was blocked by the previous one. Our thread count soared, and CPU utilization went through the roof.

We found the RepeatingRequestMonitor, which is a very simple out of the box ATG component, but quite effective. All it does is essentially the following:

1) For the session in question, create a HashMap if it doesn’t exist.
2) As requests are made, insert a record into this hash map with the unique name (normally the component or operation) and the current system timestamp in milliseconds, plus a configurable but default value of 3 seconds
3) If the next request with the same component name in the HashMap structure has a current system timestamp less than the previous request + the delay we mentioned, then the new request is not run but silently ignored.

This allows a shopper to refresh until her heart is content, as behind the scenes we silently ignore that request for the same component.

Below is a simple JSP example. Normally, you would call the RepeatingRequestMonitor component from a formhandler.

<%@page import="atg.commerce.util.*;"%>
<%
  try {
    RepeatingRequestMonitor rrm = null;
    if (session.getAttribute("RRM") == null) {
      rrm = new RepeatingRequestMonitor();
      session.setAttribute("RRM",rrm);
      out.println("created new RRM");
    }
    else {
      rrm = (RepeatingRequestMonitor)session.getAttribute("RRM");
      out.println("found RRM");
    }
    boolean b = rrm.isUniqueRequestEntry("foobar",5000);
    out.println(b);
    if(b == true) {
      //Thread.sleep(Integer.parseInt(request.getParameter("seconds")) * 1000);
      synchronized(this) {
        Thread.sleep(10000);
        //remove the request from the map in case it runs really fast and we want to allow
        //  the next request, even before the delay has ended
        rrm.removeRequestEntry("foobar");
      }
    }
    else {
      out.println("request already in progress");
    }
  }
  catch (Exception e) {
    out.println(e.getMessage());
  }
%>

One thing we noticed is that if the current request runs longer than the delay, the next request will be gladly serviced even though the first request is still running. As such, set the delay appropriately, or better yet, tune your pages/components 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.