public override void Process(ErrorPipelineContext ctx)
        {
            if (Settings.Current.WebsiteMode == WebsiteMode.Dev)
            {
                return;
            }

            // Throttle errors by client ip address to no more than X every 5 minutes.
            string clientIp = null;

            if (ctx.Error.RequestInfo != null && !String.IsNullOrEmpty(ctx.Error.RequestInfo.ClientIpAddress))
            {
                clientIp = ctx.Error.RequestInfo.ClientIpAddress;
            }

            if (String.IsNullOrEmpty(clientIp))
            {
                return;
            }

            string throttleCacheKey = String.Concat("bot:", clientIp, ":", DateTime.Now.Floor(_throttlingPeriod).Ticks);
            var    requestCount     = _cacheClient.Get <int?>(throttleCacheKey);

            if (requestCount != null)
            {
                _cacheClient.Increment(throttleCacheKey, 1);
                requestCount++;
            }
            else
            {
                _cacheClient.Set(throttleCacheKey, 1, _throttlingPeriod);
                requestCount = 1;
            }

            if (requestCount < Settings.Current.BotThrottleLimit)
            {
                return;
            }

            Log.Info().Message("Bot throttle triggered. IP: {0} Time: {1} Project: {2}", clientIp, DateTime.Now.Floor(_throttlingPeriod), ctx.Error.ProjectId).Project(ctx.Error.ProjectId).Write();
            // the throttle was triggered, go and delete all the errors that triggered the throttle to reduce bot noise in the system
            Task.Run(() => _errorRepository.RemoveAllByClientIpAndDateAsync(clientIp, DateTime.Now.Floor(_throttlingPeriod), DateTime.Now.Ceiling(_throttlingPeriod)));
            ctx.IsCancelled = true;
        }