예제 #1
0
            public void SetState(LeakyBucketState bucketInfo)
            {
                //Note that when the capacity doubles, the leak rate also doubles. So, not only can request bursts be larger, it is also possible to sustain a faster rate over the long term.
                if (bucketInfo.Capacity > _bucketCapacity)
                {
                    lock (_semaphore)
                    {
                        if (bucketInfo.Capacity > _bucketCapacity)
                        {
                            _semaphore.Release(bucketInfo.Capacity - _bucketCapacity);
                            _bucketCapacity = bucketInfo.Capacity;
                            _leakRate       = bucketInfo.Capacity / DEFAULT_BUCKET_CAPACITY;
                        }
                    }
                }
                //Corrects the grant capacity of the bucket based on the size returned by SquareSpace.
                //SquareSpace may know that the remaining capacity is less than we think it is (for example if multiple programs are using that same token)
                //SquareSpace may also think that the remaining capacity is more than we know, but we do not ever empty the bucket because SquareSpace is not
                //considering requests that we know are already in flight.
                int grantCapacity = _bucketCapacity - bucketInfo.CurrentFillLevel;

                while (_semaphore.CurrentCount > grantCapacity)
                {
                    //We refill the virtual bucket accordingly.
                    _semaphore.Wait();
                }
            }
            public void SetState(LeakyBucketState bucketInfo)
            {
                //Wix Plus customers have a bucket that is twice the size (80) so we resize the bucket capacity accordingly
                //It is apparently possible to request the bucket size to be even larger
                //https://ecommerce.Wix.com/c/Wix-apis-and-technology/t/what-is-the-default-api-call-limit-on-Wix-stores-407292
                //Note that when the capacity doubles, the leak rate also doubles. So, not only can request bursts be larger, it is also possible to sustain a faster rate over the long term.
                if (bucketInfo.Capacity > _bucketCapacity)
                {
                    lock (_semaphore)
                    {
                        if (bucketInfo.Capacity > _bucketCapacity)
                        {
                            _semaphore.Release(bucketInfo.Capacity - _bucketCapacity);
                            _bucketCapacity = bucketInfo.Capacity;
                            _leakRate       = bucketInfo.Capacity / DEFAULT_BUCKET_CAPACITY;
                        }
                    }
                }
                //Corrects the grant capacity of the bucket based on the size returned by Wix.
                //Wix may know that the remaining capacity is less than we think it is (for example if multiple programs are using that same token)
                //Wix may also think that the remaining capacity is more than we know, but we do not ever empty the bucket because Wix is not
                //considering requests that we know are already in flight.
                int grantCapacity = _bucketCapacity - bucketInfo.CurrentFillLevel;

                while (_semaphore.CurrentCount > grantCapacity)
                {
                    //We refill the virtual bucket accordingly.
                    _semaphore.Wait();
                }
            }
예제 #3
0
 public ShopifyRateLimitException(HttpResponseMessage response,
                                  HttpStatusCode httpStatusCode,
                                  IEnumerable <string> errors,
                                  string message,
                                  string jsonError,
                                  string requestId,
                                  LeakyBucketState leakyBucket)
     : base(response, httpStatusCode, errors, message, jsonError, requestId)
 {
     LeakyBucket = leakyBucket;
     ExtractRetryAfterSeconds(response);
 }
예제 #4
0
        /// <summary>
        /// Checks a response for exceptions or invalid status codes. Throws an exception when necessary.
        /// </summary>
        /// <param name="response">The response.</param>
        /// <<param name="rawResponse">The response body returned by Shopify.</param>
        public static void CheckResponseExceptions(HttpResponseMessage response, string rawResponse)
        {
            var statusCode = (int)response.StatusCode;

            // No error if response was between 200 and 300.
            if (statusCode >= 200 && statusCode < 300)
            {
                return;
            }

            var requestIdHeader = response.Headers.FirstOrDefault(h => h.Key.Equals("X-Request-Id", StringComparison.OrdinalIgnoreCase));
            var requestId       = requestIdHeader.Value?.FirstOrDefault();
            var code            = response.StatusCode;
            var statusMessage   = $"{(int)code} {response.ReasonPhrase}";

            // If the error was caused by reaching the API rate limit, throw a rate limit exception.
            if ((int)code == 429 /* Too many requests */)
            {
                string rateExceptionMessage;
                IEnumerable <string> errors;

                if (TryParseErrorJson(rawResponse, out var rateLimitErrors))
                {
                    rateExceptionMessage = $"({statusMessage}) {rateLimitErrors.First()}";
                    errors = rateLimitErrors;
                }
                else
                {
                    var baseMessage = "Exceeded the rate limit for api client. Reduce request rates to resume uninterrupted service.";
                    rateExceptionMessage = $"({statusMessage}) {baseMessage}";
                    errors = new List <string> {
                        baseMessage
                    };
                }

                throw new ShopifyRateLimitException(response, code, errors, rateExceptionMessage, rawResponse, requestId, LeakyBucketState.Get(response));
            }

            var contentType = response.Content.Headers.GetValues("Content-Type").FirstOrDefault();

            if (contentType.StartsWithIgnoreCase("application/json") || contentType.StartsWithIgnoreCase("text/json"))
            {
                IEnumerable <string> errors;
                string exceptionMessage;

                if (TryParseErrorJson(rawResponse, out var parsedErrors))
                {
                    var firstError       = parsedErrors.First();
                    var totalErrors      = parsedErrors.Count();
                    var baseErrorMessage = $"({statusMessage}) {firstError}";

                    switch (totalErrors)
                    {
                    case 1:
                        exceptionMessage = baseErrorMessage;
                        break;

                    case 2:
                        exceptionMessage = $"{baseErrorMessage} (and one other error)";
                        break;

                    default:
                        exceptionMessage = $"{baseErrorMessage} (and {totalErrors} other errors)";
                        break;
                    }

                    errors = parsedErrors;
                }
                else
                {
                    exceptionMessage = $"({statusMessage}) Shopify returned {statusMessage}, but ShopifySharp was unable to parse the response JSON.";
                    errors           = new List <string>
                    {
                        exceptionMessage
                    };
                }

                throw new ShopifyException(response, code, errors, exceptionMessage, rawResponse, requestId);
            }

            var message      = $"({statusMessage}) Shopify returned {statusMessage}, but there was no JSON to parse into an error message.";
            var customErrors = new List <string>
            {
                message
            };

            throw new ShopifyException(response, code, customErrors, message, rawResponse, requestId);
        }