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 ofmvc.requestBegin
and it runscreateVisit
once to create the current visit. BothUpdateGeoIpData
processors essentially delegate toSitecore.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 tonew TimeSpan(0, 0, 0, 0, 0)
.The result?
[su_note note_color=”#fafafa”]First request of a new visit from an IP that is not in the GeoIP cache will be Unresolved when it renders[/su_note]
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 ofcreateVisit
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 whencreateVisit
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.
[su_divider][/su_divider]
- Sitecore 7.0 Update 2. If you know it’s different in later versions please let me know.