Sitecore GeoIP: Helping The First Visit

MaxMind - GeoIP

GeoIP

GeoIP via MaxMind is a great feature of Sitecore Analytics. Everything is built-in and all you need to do is inspect various attributes of the VisitorDataSet.VisitsRow, such as Region, Country, City, MetroCode, etc.

 

Here’s how it works (simplified, of course). Sitecore runs UpdateGeoIpData as part of two Analytics pipelines:

<createVisit>
    ...
    <processor type="Sitecore.Analytics.Pipelines.CreateVisits.UpdateGeoIpData,Sitecore.Analytics"/>
    ...
</createVisit>

and

<startTracking>
    ...
    <processor type="Sitecore.Analytics.Pipelines.StartTracking.UpdateGeoIpData,Sitecore.Analytics" />
    ...
</startTracking>

In the Sitecore MVC world startTracking is executed as part of mvc.requestBegin and it runs createVisit once to create the current visit. Both UpdateGeoIpData processors essentially delegate to Sitecore.Analytics.Lookups.GeoIpManager.GetGeoIpData() and then update the current visit accordingly. Almost always.

First Request

The GeoIpManager does something along the lines of:

      GeoIpHandle handle = GeoIpManager.cache.Get(options.Ip);
      if (handle == null)
      {
        handle = new GeoIpHandle(options);
        GeoIpManager.cache.Add(handle);
      }

      return handle;

When the cache is cold the handle will come back empty and Unresolved. The GeoIpManager will then asynchronously:

   GeoIpManager.StartResolvingThread(cachedGeoIpHandle);

and will wait for the data to come back. The problem is that the timeout passed into this method by the pipelines is 0*. Both pipelines call UpdateGeoIpData() on the visit with no arguments and it defaults to new TimeSpan(0, 0, 0, 0, 0).

The result?

First request of a new visit from an IP that is not in the GeoIP cache will be Unresolved when it renders

The GeoIP will catch up but it’s a little too late for that very first request.

Try Harder

We need GeoIP to work on the very first request even with the cold cache. It may time out and catch up later but it has to at least try harder. The out of the box UpdateGeoIpData that runs as part of createVisit looks like this:

public override void Process(CreateVisitArgs args)
{
    Assert.ArgumentNotNull(args, "args");
    args.Visit.UpdateGeoIpData();
}

we can patch:instead it with:

public class UpdateGeoIpDataWithTimeout : UpdateGeoIpData
{
   private const int WaitForGeoIp = 500; // max wait time (in millis) for GeoIP to come back

   public override void Process(CreateVisitArgs args)
   {
       Assert.ArgumentNotNull(args, "args");

       args.Visit.UpdateGeoIpData(new TimeSpan(0, 0, 0, 0, WaitForGeoIp));

       Log.Debug(string.Format("Executed UpdateGeoIpData with Timeout: {0} = {1}",
           new IPAddress(args.Visit.Ip),
           args.Visit.Country));
   }
}

There’s also something we had to keep in the session based on GeoIp so we needed one more processor to run at the end of startTracking. That one basically ensures that session state catches up with the GeoIp in a rare case when createVisit times out on it.

In my tests GeoIp consistently comes back under 200 milliseconds. Acceptable one-time penalty to try and have GeoIp personalization run on the very first request of a new visit.

Thoughts

The only thought I have is that maybe I am missing something. As far as I can tell that default zero timeout leaves very little chance for the very first request of a new visit to be personalized based on GeoIp.

What is your experience?

UPDATE:

@fdevelop tweeted me a link to the Sitecore Support package 396075 that fixes the “first visit” problem. It seems to do exactly the thing I did so it’s good to know I was not missing anything.

* Sitecore 7.0 Update 2. If you know it’s different in later versions please let me know.

Add a Comment

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

Or request call back