
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:
1 | <createVisit> |
3 | <processor type="Sitecore.Analytics.Pipelines.CreateVisits.UpdateGeoIpData,Sitecore.Analytics"/> |
5 | </createVisit> |
and
1 | <startTracking> |
3 | <processor type="Sitecore.Analytics.Pipelines.StartTracking.UpdateGeoIpData,Sitecore.Analytics" /> |
5 | </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:
1 | GeoIpHandle handle = GeoIpManager.cache.Get(options.Ip); |
4 | handle = new GeoIpHandle(options); |
5 | GeoIpManager.cache.Add(handle); |
When the cache is cold the handle will come back empty and Unresolved. The GeoIpManager
will then asynchronously:
1 | 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?
[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 of createVisit
looks like this:
1 | public override void Process(CreateVisitArgs args) |
3 | Assert.ArgumentNotNull(args, "args" ); |
4 | args.Visit.UpdateGeoIpData(); |
we can patch:instead
it with:
1 | public class UpdateGeoIpDataWithTimeout : UpdateGeoIpData |
3 | private const int WaitForGeoIp = 500; |
5 | public override void Process(CreateVisitArgs args) |
7 | Assert.ArgumentNotNull(args, "args" ); |
9 | args.Visit.UpdateGeoIpData( new TimeSpan(0, 0, 0, 0, WaitForGeoIp)); |
11 | Log.Debug( string .Format( "Executed UpdateGeoIpData with Timeout: {0} = {1}" , |
12 | new IPAddress(args.Visit.Ip), |
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.
[su_divider][/su_divider]
- Sitecore 7.0 Update 2. If you know it’s different in later versions please let me know.