From 625a6fd8557bc161704fda98fe5440228fdb6421 Mon Sep 17 00:00:00 2001 From: Finn Reilly Date: Wed, 4 Dec 2024 13:58:34 +0000 Subject: [PATCH 01/17] Create basic interface for shared rate limiter * to be optionally passed to `AmazonConnection` for better extensibility --- .../FikaAmazonAPI/Services/RequestService.cs | 35 ++++++++++--------- .../FikaAmazonAPI/Services/UploadService.cs | 1 + .../Utils/IRateLimitingHandler.cs | 19 ++++++++++ .../Utils/RateLimitingHandler.cs | 20 +++++++++++ Source/FikaAmazonAPI/Utils/RateLimits.cs | 1 + 5 files changed, 60 insertions(+), 16 deletions(-) create mode 100644 Source/FikaAmazonAPI/Utils/IRateLimitingHandler.cs create mode 100644 Source/FikaAmazonAPI/Utils/RateLimitingHandler.cs diff --git a/Source/FikaAmazonAPI/Services/RequestService.cs b/Source/FikaAmazonAPI/Services/RequestService.cs index 9c3e3ec9..23923897 100644 --- a/Source/FikaAmazonAPI/Services/RequestService.cs +++ b/Source/FikaAmazonAPI/Services/RequestService.cs @@ -96,26 +96,27 @@ protected async Task CreateAuthorizedRequestAsync(string url, RestSharp.Method m AddQueryParameters(queryParameters); } - protected void CreateAuthorizedPagedRequest(AmazonFilter filter, string url, RestSharp.Method method) - { - RefreshToken(); - if (filter.NextPage != null) - CreateRequest(filter.NextPage, method); - else - { - CreateRequest(url, method); - AddLimitHeader(filter.Limit); - } - - AddAccessToken(); - } + //protected void CreateAuthorizedPagedRequest(AmazonFilter filter, string url, RestSharp.Method method) + //{ + // RefreshToken(); + // if (filter.NextPage != null) + // CreateRequest(filter.NextPage, method); + // else + // { + // CreateRequest(url, method); + // AddLimitHeader(filter.Limit); + // } + + // AddAccessToken(); + //} /// /// Executes the request /// /// Type to parse response to /// Returns data of T type - protected async Task ExecuteRequestTry(RateLimitType rateLimitType = RateLimitType.UNSET, + protected async Task ExecuteRequestTry( + RateLimitType rateLimitType = RateLimitType.UNSET, CancellationToken cancellationToken = default) where T : new() { RestHeader(); @@ -233,8 +234,10 @@ public async Task ExecuteRequestAsync(RateLimitType rateLimitType = RateLi } } - private async Task SleepForRateLimit(IReadOnlyCollection headers, - RateLimitType rateLimitType = RateLimitType.UNSET, CancellationToken cancellationToken = default) + private async Task SleepForRateLimit( + IReadOnlyCollection headers, + RateLimitType rateLimitType = RateLimitType.UNSET, + CancellationToken cancellationToken = default) { try { diff --git a/Source/FikaAmazonAPI/Services/UploadService.cs b/Source/FikaAmazonAPI/Services/UploadService.cs index 4f7868b4..a5c9456f 100644 --- a/Source/FikaAmazonAPI/Services/UploadService.cs +++ b/Source/FikaAmazonAPI/Services/UploadService.cs @@ -15,6 +15,7 @@ public UploadService(AmazonCredential amazonCredential) : base(amazonCredential) public UploadDestination CreateUploadDestinationForResource(ParameterCreateUploadDestinationForResource parameterObj) => Task.Run(() => CreateUploadDestinationForResourceAsync(parameterObj)).ConfigureAwait(false).GetAwaiter().GetResult(); + public async Task CreateUploadDestinationForResourceAsync(ParameterCreateUploadDestinationForResource parameterObj, CancellationToken cancellationToken = default) { if (parameterObj.marketplaceIds == null || parameterObj.marketplaceIds.Count == 0) diff --git a/Source/FikaAmazonAPI/Utils/IRateLimitingHandler.cs b/Source/FikaAmazonAPI/Utils/IRateLimitingHandler.cs new file mode 100644 index 00000000..c4d141f1 --- /dev/null +++ b/Source/FikaAmazonAPI/Utils/IRateLimitingHandler.cs @@ -0,0 +1,19 @@ +using RestSharp; +using System.Threading; +using System.Threading.Tasks; + +namespace FikaAmazonAPI.Utils +{ + /// + /// This is designed to be used as a singleton, for the purposes of coordinating rate limiting between separate concurrent requests + /// + public interface IRateLimitingHandler + { + Task SafelyExecuteRequestAsync( + IRestClient restClient, + RestRequest restRequest, + AmazonCredential credential, + RateLimitType rateLimitType = RateLimitType.UNSET, + CancellationToken cancellationToken = default) where TResult : class; + } +} diff --git a/Source/FikaAmazonAPI/Utils/RateLimitingHandler.cs b/Source/FikaAmazonAPI/Utils/RateLimitingHandler.cs new file mode 100644 index 00000000..93c35a17 --- /dev/null +++ b/Source/FikaAmazonAPI/Utils/RateLimitingHandler.cs @@ -0,0 +1,20 @@ +using RestSharp; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace FikaAmazonAPI.Utils +{ + /// + /// This is designed to be used as a singleton, for the purposes of coordinating rate limiting between separate concurrent requests + /// + internal class RateLimitingHandler : IRateLimitingHandler + { + public Task SafelyExecuteRequestAsync(IRestClient restClient, RestRequest restRequest, AmazonCredential credential, RateLimitType rateLimitType = RateLimitType.UNSET, CancellationToken cancellationToken = default) where TResult : class + { + throw new NotImplementedException(); + } + } +} diff --git a/Source/FikaAmazonAPI/Utils/RateLimits.cs b/Source/FikaAmazonAPI/Utils/RateLimits.cs index e28fa847..79925c83 100644 --- a/Source/FikaAmazonAPI/Utils/RateLimits.cs +++ b/Source/FikaAmazonAPI/Utils/RateLimits.cs @@ -41,6 +41,7 @@ public async Task NextRate(RateLimitType rateLimitType) var LastRequestTime = LastRequest; while (true) { + // to confirm, but looks like we're guessing how long requests will take here? LastRequestTime = LastRequestTime.AddMilliseconds(ratePeriodMs); if (LastRequestTime > DateTime.UtcNow) break; From 4de5001a8fc382d16bf3776c9404369a6f4f637e Mon Sep 17 00:00:00 2001 From: Finn Reilly Date: Wed, 4 Dec 2024 15:54:53 +0000 Subject: [PATCH 02/17] crude refactor allows passing in of singleton rate limit handler --- Source/FikaAmazonAPI/AmazonConnection.cs | 83 ++++++++++--------- .../Services/AplusContentService.cs | 6 +- .../Services/AppIntegrationsV20240401.cs | 2 +- .../Services/AuthorizationService.cs | 3 +- .../Services/CatalogItemService.cs | 2 +- .../Services/EasyShip20220323Service.cs | 2 +- .../Services/FbaInboundEligibilityService.cs | 2 +- .../Services/FbaInboundService.cs | 6 +- .../Services/FbaInventoryService.cs | 2 +- .../Services/FbaOutboundService.cs | 6 +- .../Services/FbaSmallandLightService.cs | 2 +- Source/FikaAmazonAPI/Services/FeedService.cs | 2 +- .../Services/FinancialService.cs | 2 +- .../Services/FulFillmentInboundService.cs | 2 +- .../FulFillmentInboundServicev20240320.cs | 2 +- .../Services/FulFillmentOutboundService.cs | 2 +- .../Services/ListingsItemService.cs | 2 +- .../Services/MerchantFulfillmentService.cs | 2 +- .../Services/MessagingService.cs | 2 +- .../Services/NotificationService.cs | 2 +- Source/FikaAmazonAPI/Services/OrderService.cs | 3 +- .../Services/ProductFeeService.cs | 2 +- .../Services/ProductPricingService.cs | 2 +- .../Services/ProductTypeService.cs | 2 +- .../FikaAmazonAPI/Services/ReportService.cs | 2 +- .../FikaAmazonAPI/Services/RequestService.cs | 67 ++++----------- .../Services/RestrictionService.cs | 3 +- Source/FikaAmazonAPI/Services/SalesService.cs | 3 +- .../FikaAmazonAPI/Services/SellerService.cs | 2 +- .../FikaAmazonAPI/Services/ServicesService.cs | 6 +- .../Services/ShipmentInvoicingService.cs | 2 +- .../FikaAmazonAPI/Services/ShippingService.cs | 2 +- .../Services/ShippingServiceV2.cs | 2 +- .../Services/SolicitationService.cs | 2 +- Source/FikaAmazonAPI/Services/TokenService.cs | 6 +- .../FikaAmazonAPI/Services/UploadService.cs | 2 +- .../VendorDirectFulfillmentOrderService.cs | 2 +- Source/FikaAmazonAPI/Services/VendorOrders.cs | 2 +- .../Services/VendorTransactionStatus.cs | 2 +- .../Utils/IRateLimitingHandler.cs | 8 +- .../Utils/RateLimitingHandler.cs | 66 ++++++++++++++- 41 files changed, 189 insertions(+), 133 deletions(-) diff --git a/Source/FikaAmazonAPI/AmazonConnection.cs b/Source/FikaAmazonAPI/AmazonConnection.cs index 1c013731..64731b96 100644 --- a/Source/FikaAmazonAPI/AmazonConnection.cs +++ b/Source/FikaAmazonAPI/AmazonConnection.cs @@ -11,6 +11,8 @@ public class AmazonConnection { private AmazonCredential Credentials { get; set; } + private IRateLimitingHandler RateLimitingHandler { get; } + public AppIntegrationsServiceV20240401 AppIntegrationsServiceV20240401 => this._AppIntegrationsServiceV20240401 ?? throw _NoCredentials; public OrderService Orders => this._Orders ?? throw _NoCredentials; public ReportService Reports => this._Reports ?? throw _NoCredentials; @@ -91,10 +93,15 @@ public class AmazonConnection private UnauthorizedAccessException _NoCredentials = new UnauthorizedAccessException($"Error, you cannot make calls to Amazon without credentials!"); public string RefNumber { get; set; } - public AmazonConnection(AmazonCredential Credentials, string RefNumber = null, CultureInfo? cultureInfo = null) + public AmazonConnection( + AmazonCredential Credentials, + IRateLimitingHandler rateLimitingHandler = null, + string RefNumber = null, + CultureInfo? cultureInfo = null) { this.Authenticate(Credentials); this.RefNumber = RefNumber; + this.RateLimitingHandler = rateLimitingHandler ?? new RateLimitingHandler(); Thread.CurrentThread.CurrentCulture = cultureInfo ?? CultureInfo.CurrentCulture; } @@ -112,44 +119,44 @@ private void Init(AmazonCredential Credentials) this.Credentials = Credentials; - this._Authorization = new AuthorizationService(this.Credentials); - this._AppIntegrationsServiceV20240401 = new AppIntegrationsServiceV20240401(this.Credentials); - this._Orders = new OrderService(this.Credentials); - this._Reports = new ReportService(this.Credentials); - this._Solicitations = new SolicitationService(this.Credentials); - this._Financials = new FinancialService(this.Credentials); - this._CatalogItems = new CatalogItemService(this.Credentials); - this._ProductPricing = new ProductPricingService(this.Credentials); + this._Authorization = new AuthorizationService(this.Credentials, this.RateLimitingHandler); + this._AppIntegrationsServiceV20240401 = new AppIntegrationsServiceV20240401(this.Credentials, this.RateLimitingHandler); + this._Orders = new OrderService(this.Credentials, this.RateLimitingHandler); + this._Reports = new ReportService(this.Credentials, this.RateLimitingHandler); + this._Solicitations = new SolicitationService(this.Credentials, this.RateLimitingHandler); + this._Financials = new FinancialService(this.Credentials, this.RateLimitingHandler); + this._CatalogItems = new CatalogItemService(this.Credentials, this.RateLimitingHandler); + this._ProductPricing = new ProductPricingService(this.Credentials, this.RateLimitingHandler); - this._FbaInbound = new FbaInboundService(this.Credentials); - this._FbaInventory = new FbaInventoryService(this.Credentials); - this._FbaOutbound = new FbaOutboundService(this.Credentials); - this._FbaSmallandLight = new FbaSmallandLightService(this.Credentials); - this._FbaInboundEligibility = new FbaInboundEligibilityService(this.Credentials); - this._EasyShip20220323 = new EasyShip20220323Service(this.Credentials); - this._AplusContent = new AplusContentService(this.Credentials); - this._Feed = new FeedService(this.Credentials); - this._ListingsItem = new ListingsItemService(this.Credentials); - this._Restrictions = new RestrictionService(this.Credentials); - this._MerchantFulfillment = new MerchantFulfillmentService(this.Credentials); - this._Messaging = new MessagingService(this.Credentials); - this._Notification = new NotificationService(this.Credentials); - this._ProductFee = new ProductFeeService(this.Credentials); - this._ProductType = new ProductTypeService(this.Credentials); - this._Sales = new SalesService(this.Credentials); - this._Seller = new SellerService(this.Credentials); - this._Services = new ServicesService(this.Credentials); - this._ShipmentInvoicing = new ShipmentInvoicingService(this.Credentials); - this._Shipping = new ShippingService(this.Credentials); - this._ShippingV2 = new ShippingServiceV2(this.Credentials); - this._Upload = new UploadService(this.Credentials); - this._Tokens = new TokenService(this.Credentials); - this._FulFillmentInbound = new FulFillmentInboundService(this.Credentials); - this._FulFillmentInboundv20240320 = new FulFillmentInboundServicev20240320(this.Credentials); - this._FulFillmentOutbound = new FulFillmentOutboundService(this.Credentials); - this._VendorDirectFulfillmentOrders = new VendorDirectFulfillmentOrderService(this.Credentials); - this._VendorOrders = new VendorOrderService(this.Credentials); - this._VendorTransactionStatus = new VendorTransactionStatusService(this.Credentials); + this._FbaInbound = new FbaInboundService(this.Credentials, this.RateLimitingHandler); + this._FbaInventory = new FbaInventoryService(this.Credentials, this.RateLimitingHandler); + this._FbaOutbound = new FbaOutboundService(this.Credentials, this.RateLimitingHandler); + this._FbaSmallandLight = new FbaSmallandLightService(this.Credentials, this.RateLimitingHandler); + this._FbaInboundEligibility = new FbaInboundEligibilityService(this.Credentials, this.RateLimitingHandler); + this._EasyShip20220323 = new EasyShip20220323Service(this.Credentials, this.RateLimitingHandler); + this._AplusContent = new AplusContentService(this.Credentials, this.RateLimitingHandler); + this._Feed = new FeedService(this.Credentials, this.RateLimitingHandler); + this._ListingsItem = new ListingsItemService(this.Credentials, this.RateLimitingHandler); + this._Restrictions = new RestrictionService(this.Credentials, this.RateLimitingHandler); + this._MerchantFulfillment = new MerchantFulfillmentService(this.Credentials, this.RateLimitingHandler); + this._Messaging = new MessagingService(this.Credentials, this.RateLimitingHandler); + this._Notification = new NotificationService(this.Credentials, this.RateLimitingHandler); + this._ProductFee = new ProductFeeService(this.Credentials, this.RateLimitingHandler); + this._ProductType = new ProductTypeService(this.Credentials, this.RateLimitingHandler); + this._Sales = new SalesService(this.Credentials, this.RateLimitingHandler); + this._Seller = new SellerService(this.Credentials, this.RateLimitingHandler); + this._Services = new ServicesService(this.Credentials, this.RateLimitingHandler); + this._ShipmentInvoicing = new ShipmentInvoicingService(this.Credentials, this.RateLimitingHandler); + this._Shipping = new ShippingService(this.Credentials, this.RateLimitingHandler); + this._ShippingV2 = new ShippingServiceV2(this.Credentials, this.RateLimitingHandler); + this._Upload = new UploadService(this.Credentials, this.RateLimitingHandler); + this._Tokens = new TokenService(this.Credentials, this.RateLimitingHandler); + this._FulFillmentInbound = new FulFillmentInboundService(this.Credentials, this.RateLimitingHandler); + this._FulFillmentInboundv20240320 = new FulFillmentInboundServicev20240320(this.Credentials, this.RateLimitingHandler); + this._FulFillmentOutbound = new FulFillmentOutboundService(this.Credentials, this.RateLimitingHandler); + this._VendorDirectFulfillmentOrders = new VendorDirectFulfillmentOrderService(this.Credentials, this.RateLimitingHandler); + this._VendorOrders = new VendorOrderService(this.Credentials, this.RateLimitingHandler); + this._VendorTransactionStatus = new VendorTransactionStatusService(this.Credentials, this.RateLimitingHandler); AmazonCredential.DebugMode = this.Credentials.IsDebugMode; } diff --git a/Source/FikaAmazonAPI/Services/AplusContentService.cs b/Source/FikaAmazonAPI/Services/AplusContentService.cs index 3b7cceaa..a6150967 100644 --- a/Source/FikaAmazonAPI/Services/AplusContentService.cs +++ b/Source/FikaAmazonAPI/Services/AplusContentService.cs @@ -1,10 +1,12 @@ -namespace FikaAmazonAPI.Services +using FikaAmazonAPI.Utils; + +namespace FikaAmazonAPI.Services { public class AplusContentService : RequestService { - public AplusContentService(AmazonCredential amazonCredential) : base(amazonCredential) + public AplusContentService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/AppIntegrationsV20240401.cs b/Source/FikaAmazonAPI/Services/AppIntegrationsV20240401.cs index c567f6e4..019ebc20 100644 --- a/Source/FikaAmazonAPI/Services/AppIntegrationsV20240401.cs +++ b/Source/FikaAmazonAPI/Services/AppIntegrationsV20240401.cs @@ -10,7 +10,7 @@ namespace FikaAmazonAPI.Services { public class AppIntegrationsServiceV20240401: RequestService { - public AppIntegrationsServiceV20240401(AmazonCredential amazonCredential) : base(amazonCredential) + public AppIntegrationsServiceV20240401(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/AuthorizationService.cs b/Source/FikaAmazonAPI/Services/AuthorizationService.cs index f29ae1fb..f74569d7 100644 --- a/Source/FikaAmazonAPI/Services/AuthorizationService.cs +++ b/Source/FikaAmazonAPI/Services/AuthorizationService.cs @@ -1,6 +1,7 @@ using FikaAmazonAPI.AmazonSpApiSDK.Models.Authorization; using FikaAmazonAPI.AmazonSpApiSDK.Models.Token; using FikaAmazonAPI.Parameter.Authorization; +using FikaAmazonAPI.Utils; using System.Threading; using System.Threading.Tasks; using static FikaAmazonAPI.AmazonSpApiSDK.Models.Token.CacheTokenData; @@ -9,7 +10,7 @@ namespace FikaAmazonAPI.Services { public class AuthorizationService : RequestService { - public AuthorizationService(AmazonCredential amazonCredential) : base(amazonCredential) + public AuthorizationService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) { } public string GetAuthorizationCode(ParameterAuthorizationCode parameterGetOrderMetrics) => diff --git a/Source/FikaAmazonAPI/Services/CatalogItemService.cs b/Source/FikaAmazonAPI/Services/CatalogItemService.cs index cf190dd0..72ef2853 100644 --- a/Source/FikaAmazonAPI/Services/CatalogItemService.cs +++ b/Source/FikaAmazonAPI/Services/CatalogItemService.cs @@ -14,7 +14,7 @@ namespace FikaAmazonAPI.Services { public class CatalogItemService : RequestService { - public CatalogItemService(AmazonCredential amazonCredential) : base(amazonCredential) + public CatalogItemService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/EasyShip20220323Service.cs b/Source/FikaAmazonAPI/Services/EasyShip20220323Service.cs index 2ff353b1..90b4d42c 100644 --- a/Source/FikaAmazonAPI/Services/EasyShip20220323Service.cs +++ b/Source/FikaAmazonAPI/Services/EasyShip20220323Service.cs @@ -8,7 +8,7 @@ namespace FikaAmazonAPI.Services { public class EasyShip20220323Service : RequestService { - public EasyShip20220323Service(AmazonCredential amazonCredential) : base(amazonCredential) + public EasyShip20220323Service(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/FbaInboundEligibilityService.cs b/Source/FikaAmazonAPI/Services/FbaInboundEligibilityService.cs index 21674c68..4283433d 100644 --- a/Source/FikaAmazonAPI/Services/FbaInboundEligibilityService.cs +++ b/Source/FikaAmazonAPI/Services/FbaInboundEligibilityService.cs @@ -8,7 +8,7 @@ namespace FikaAmazonAPI.Services { public class FbaInboundEligibilityService : RequestService { - public FbaInboundEligibilityService(AmazonCredential amazonCredential) : base(amazonCredential) + public FbaInboundEligibilityService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/FbaInboundService.cs b/Source/FikaAmazonAPI/Services/FbaInboundService.cs index d035cb98..ba38df6a 100644 --- a/Source/FikaAmazonAPI/Services/FbaInboundService.cs +++ b/Source/FikaAmazonAPI/Services/FbaInboundService.cs @@ -1,8 +1,10 @@ -namespace FikaAmazonAPI.Services +using FikaAmazonAPI.Utils; + +namespace FikaAmazonAPI.Services { public class FbaInboundService : RequestService { - public FbaInboundService(AmazonCredential amazonCredential) : base(amazonCredential) + public FbaInboundService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/FbaInventoryService.cs b/Source/FikaAmazonAPI/Services/FbaInventoryService.cs index dd0a1f3e..f9b98924 100644 --- a/Source/FikaAmazonAPI/Services/FbaInventoryService.cs +++ b/Source/FikaAmazonAPI/Services/FbaInventoryService.cs @@ -10,7 +10,7 @@ namespace FikaAmazonAPI.Services public class FbaInventoryService : RequestService { - public FbaInventoryService(AmazonCredential amazonCredential) : base(amazonCredential) + public FbaInventoryService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/FbaOutboundService.cs b/Source/FikaAmazonAPI/Services/FbaOutboundService.cs index 8a6762c7..746e06f9 100644 --- a/Source/FikaAmazonAPI/Services/FbaOutboundService.cs +++ b/Source/FikaAmazonAPI/Services/FbaOutboundService.cs @@ -1,8 +1,10 @@ -namespace FikaAmazonAPI.Services +using FikaAmazonAPI.Utils; + +namespace FikaAmazonAPI.Services { public class FbaOutboundService : RequestService { - public FbaOutboundService(AmazonCredential amazonCredential) : base(amazonCredential) + public FbaOutboundService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/FbaSmallandLightService.cs b/Source/FikaAmazonAPI/Services/FbaSmallandLightService.cs index c90fcba7..d7b3413d 100644 --- a/Source/FikaAmazonAPI/Services/FbaSmallandLightService.cs +++ b/Source/FikaAmazonAPI/Services/FbaSmallandLightService.cs @@ -9,7 +9,7 @@ namespace FikaAmazonAPI.Services public class FbaSmallandLightService : RequestService { - public FbaSmallandLightService(AmazonCredential amazonCredential) : base(amazonCredential) + public FbaSmallandLightService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/FeedService.cs b/Source/FikaAmazonAPI/Services/FeedService.cs index 59bcd8cf..428ccb6a 100644 --- a/Source/FikaAmazonAPI/Services/FeedService.cs +++ b/Source/FikaAmazonAPI/Services/FeedService.cs @@ -19,7 +19,7 @@ namespace FikaAmazonAPI.Services public class FeedService : RequestService { - public FeedService(AmazonCredential amazonCredential) : base(amazonCredential) + public FeedService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/FinancialService.cs b/Source/FikaAmazonAPI/Services/FinancialService.cs index 57777e36..8e2d7f8a 100644 --- a/Source/FikaAmazonAPI/Services/FinancialService.cs +++ b/Source/FikaAmazonAPI/Services/FinancialService.cs @@ -9,7 +9,7 @@ namespace FikaAmazonAPI.Services { public class FinancialService : RequestService { - public FinancialService(AmazonCredential amazonCredential) : base(amazonCredential) + public FinancialService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/FulFillmentInboundService.cs b/Source/FikaAmazonAPI/Services/FulFillmentInboundService.cs index 21df238b..3aab3371 100644 --- a/Source/FikaAmazonAPI/Services/FulFillmentInboundService.cs +++ b/Source/FikaAmazonAPI/Services/FulFillmentInboundService.cs @@ -10,7 +10,7 @@ namespace FikaAmazonAPI.Services { public class FulFillmentInboundService : RequestService { - public FulFillmentInboundService(AmazonCredential amazonCredential) : base(amazonCredential) + public FulFillmentInboundService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/FulFillmentInboundServicev20240320.cs b/Source/FikaAmazonAPI/Services/FulFillmentInboundServicev20240320.cs index d0e5d360..5d2925a7 100644 --- a/Source/FikaAmazonAPI/Services/FulFillmentInboundServicev20240320.cs +++ b/Source/FikaAmazonAPI/Services/FulFillmentInboundServicev20240320.cs @@ -9,7 +9,7 @@ namespace FikaAmazonAPI.Services { public class FulFillmentInboundServicev20240320 : RequestService { - public FulFillmentInboundServicev20240320(AmazonCredential amazonCredential) : base(amazonCredential) + public FulFillmentInboundServicev20240320(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/FulFillmentOutboundService.cs b/Source/FikaAmazonAPI/Services/FulFillmentOutboundService.cs index 7077558e..e09fee97 100644 --- a/Source/FikaAmazonAPI/Services/FulFillmentOutboundService.cs +++ b/Source/FikaAmazonAPI/Services/FulFillmentOutboundService.cs @@ -10,7 +10,7 @@ namespace FikaAmazonAPI.Services { public class FulFillmentOutboundService : RequestService { - public FulFillmentOutboundService(AmazonCredential amazonCredential) : base(amazonCredential) + public FulFillmentOutboundService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/ListingsItemService.cs b/Source/FikaAmazonAPI/Services/ListingsItemService.cs index 6a53905c..c9af1f78 100644 --- a/Source/FikaAmazonAPI/Services/ListingsItemService.cs +++ b/Source/FikaAmazonAPI/Services/ListingsItemService.cs @@ -9,7 +9,7 @@ namespace FikaAmazonAPI.Services { public class ListingsItemService : RequestService { - public ListingsItemService(AmazonCredential amazonCredential) : base(amazonCredential) + public ListingsItemService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/MerchantFulfillmentService.cs b/Source/FikaAmazonAPI/Services/MerchantFulfillmentService.cs index 2c1ab2c0..972cbf0e 100644 --- a/Source/FikaAmazonAPI/Services/MerchantFulfillmentService.cs +++ b/Source/FikaAmazonAPI/Services/MerchantFulfillmentService.cs @@ -11,7 +11,7 @@ namespace FikaAmazonAPI.Services public class MerchantFulfillmentService : RequestService { - public MerchantFulfillmentService(AmazonCredential amazonCredential) : base(amazonCredential) + public MerchantFulfillmentService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/MessagingService.cs b/Source/FikaAmazonAPI/Services/MessagingService.cs index 357c93ae..c745d16b 100644 --- a/Source/FikaAmazonAPI/Services/MessagingService.cs +++ b/Source/FikaAmazonAPI/Services/MessagingService.cs @@ -9,7 +9,7 @@ namespace FikaAmazonAPI.Services public class MessagingService : RequestService { - public MessagingService(AmazonCredential amazonCredential) : base(amazonCredential) + public MessagingService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/NotificationService.cs b/Source/FikaAmazonAPI/Services/NotificationService.cs index 7b7378b2..65a92ee3 100644 --- a/Source/FikaAmazonAPI/Services/NotificationService.cs +++ b/Source/FikaAmazonAPI/Services/NotificationService.cs @@ -17,7 +17,7 @@ namespace FikaAmazonAPI.Services { public class NotificationService : RequestService { - public NotificationService(AmazonCredential amazonCredential) : base(amazonCredential) + public NotificationService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/OrderService.cs b/Source/FikaAmazonAPI/Services/OrderService.cs index 1349ffe2..708bdf32 100644 --- a/Source/FikaAmazonAPI/Services/OrderService.cs +++ b/Source/FikaAmazonAPI/Services/OrderService.cs @@ -6,12 +6,13 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using FikaAmazonAPI.Utils; namespace FikaAmazonAPI.Services { public class OrderService : RequestService { - public OrderService(AmazonCredential amazonCredential) : base(amazonCredential) + public OrderService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/ProductFeeService.cs b/Source/FikaAmazonAPI/Services/ProductFeeService.cs index 5b68d37f..2ce952f9 100644 --- a/Source/FikaAmazonAPI/Services/ProductFeeService.cs +++ b/Source/FikaAmazonAPI/Services/ProductFeeService.cs @@ -8,7 +8,7 @@ namespace FikaAmazonAPI.Services { public class ProductFeeService : RequestService { - public ProductFeeService(AmazonCredential amazonCredential) : base(amazonCredential) + public ProductFeeService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/ProductPricingService.cs b/Source/FikaAmazonAPI/Services/ProductPricingService.cs index e9ec1200..260921b0 100644 --- a/Source/FikaAmazonAPI/Services/ProductPricingService.cs +++ b/Source/FikaAmazonAPI/Services/ProductPricingService.cs @@ -14,7 +14,7 @@ namespace FikaAmazonAPI.Services { public class ProductPricingService : RequestService { - public ProductPricingService(AmazonCredential amazonCredential) : base(amazonCredential) + public ProductPricingService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/ProductTypeService.cs b/Source/FikaAmazonAPI/Services/ProductTypeService.cs index b2b9e377..44fd90dd 100644 --- a/Source/FikaAmazonAPI/Services/ProductTypeService.cs +++ b/Source/FikaAmazonAPI/Services/ProductTypeService.cs @@ -8,7 +8,7 @@ namespace FikaAmazonAPI.Services { public class ProductTypeService : RequestService { - public ProductTypeService(AmazonCredential amazonCredential) : base(amazonCredential) + public ProductTypeService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/ReportService.cs b/Source/FikaAmazonAPI/Services/ReportService.cs index 0f1fbcc9..141c8b4c 100644 --- a/Source/FikaAmazonAPI/Services/ReportService.cs +++ b/Source/FikaAmazonAPI/Services/ReportService.cs @@ -16,7 +16,7 @@ namespace FikaAmazonAPI.Services { public class ReportService : RequestService { - public ReportService(AmazonCredential amazonCredential) : base(amazonCredential) + public ReportService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) { } #region GetReport diff --git a/Source/FikaAmazonAPI/Services/RequestService.cs b/Source/FikaAmazonAPI/Services/RequestService.cs index 23923897..3603a2f2 100644 --- a/Source/FikaAmazonAPI/Services/RequestService.cs +++ b/Source/FikaAmazonAPI/Services/RequestService.cs @@ -24,7 +24,6 @@ public class RequestService : ApiUrls { public static readonly string AccessTokenHeaderName = "x-amz-access-token"; public static readonly string SecurityTokenHeaderName = "x-amz-security-token"; - private readonly string RateLimitLimitHeaderName = "x-amzn-RateLimit-Limit"; public static readonly string ShippingBusinessIdHeaderName = "x-amzn-shipping-business-id"; protected RestClient RequestClient { get; set; } protected RestRequest Request { get; set; } @@ -33,6 +32,7 @@ public class RequestService : ApiUrls protected string AmazonProductionUrl { get; set; } protected string AccessToken { get; set; } protected IList> LastHeaders { get; set; } + private IRateLimitingHandler RateLimitingHandler { get; } protected string ApiBaseUrl { @@ -45,10 +45,12 @@ protected string ApiBaseUrl /// /// Creates request base service /// - /// Contains api clients information - /// Contains current user's account api keys - public RequestService(AmazonCredential amazonCredential) + /// A credential containing the API user's information and cached token values + /// A singleton designed to handle concurrent requests based on the rate limiting policy + public RequestService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) { + RateLimitingHandler = rateLimitingHandler; + AmazonCredential = amazonCredential; AmazonSandboxUrl = amazonCredential.MarketPlace.Region.SandboxHostUrl; AmazonProductionUrl = amazonCredential.MarketPlace.Region.HostUrl; @@ -125,10 +127,18 @@ protected async Task ExecuteRequestTry( //Remove AWS authorization //Request = await TokenGeneration.SignWithSTSKeysAndSecurityTokenAsync(Request, RequestClient.Options.BaseUrl.Host, AmazonCredential, cancellationToken); - var response = await RequestClient.ExecuteAsync(Request, cancellationToken); - LogRequest(Request, response); - SaveLastRequestHeader(response.Headers); - await SleepForRateLimit(response.Headers, rateLimitType, cancellationToken); + var response = await RateLimitingHandler.SafelyExecuteRequestAsync( + RequestClient, + Request, + AmazonCredential, + rateLimitType, + responseCallback: response => + { + LogRequest(Request, response); + SaveLastRequestHeader(response.Headers); + }, + cancellationToken: cancellationToken); + ParseResponse(response); if (response.StatusCode == HttpStatusCode.OK && !string.IsNullOrEmpty(response.Content) && @@ -234,47 +244,6 @@ public async Task ExecuteRequestAsync(RateLimitType rateLimitType = RateLi } } - private async Task SleepForRateLimit( - IReadOnlyCollection headers, - RateLimitType rateLimitType = RateLimitType.UNSET, - CancellationToken cancellationToken = default) - { - try - { - decimal rate = 0; - var limitHeader = headers.FirstOrDefault(a => a.Name == RateLimitLimitHeaderName); - if (limitHeader != null) - { - var RateLimitValue = limitHeader.Value.ToString(); - decimal.TryParse(RateLimitValue, NumberStyles.Any, CultureInfo.InvariantCulture, out rate); - } - - if (AmazonCredential.IsActiveLimitRate) - { - if (rateLimitType == RateLimitType.UNSET) - { - if (rate > 0) - { - int sleepTime = (int)(1 / rate * 1000); - await Task.Delay(sleepTime, cancellationToken); - } - } - else - { - if (rate > 0) - { - AmazonCredential.UsagePlansTimings[rateLimitType].SetRateLimit(rate); - } - - await AmazonCredential.UsagePlansTimings[rateLimitType].NextRate(rateLimitType); - } - } - } - catch (Exception e) - { - } - } - protected void ParseResponse(RestResponse response) { if (response.StatusCode == HttpStatusCode.OK || response.StatusCode == HttpStatusCode.Accepted || diff --git a/Source/FikaAmazonAPI/Services/RestrictionService.cs b/Source/FikaAmazonAPI/Services/RestrictionService.cs index 7a2ff14e..20f8c752 100644 --- a/Source/FikaAmazonAPI/Services/RestrictionService.cs +++ b/Source/FikaAmazonAPI/Services/RestrictionService.cs @@ -1,5 +1,6 @@ using FikaAmazonAPI.AmazonSpApiSDK.Models.Restrictions; using FikaAmazonAPI.Parameter.Restrictions; +using FikaAmazonAPI.Utils; using System.Threading; using System.Threading.Tasks; @@ -7,7 +8,7 @@ namespace FikaAmazonAPI.Services { public class RestrictionService : RequestService { - public RestrictionService(AmazonCredential amazonCredential) : base(amazonCredential) + public RestrictionService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/SalesService.cs b/Source/FikaAmazonAPI/Services/SalesService.cs index 79b14c8d..6e3de1fb 100644 --- a/Source/FikaAmazonAPI/Services/SalesService.cs +++ b/Source/FikaAmazonAPI/Services/SalesService.cs @@ -1,5 +1,6 @@ using FikaAmazonAPI.AmazonSpApiSDK.Models.Sales; using FikaAmazonAPI.Parameter.Sales; +using FikaAmazonAPI.Utils; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; @@ -8,7 +9,7 @@ namespace FikaAmazonAPI.Services { public class SalesService : RequestService { - public SalesService(AmazonCredential amazonCredential) : base(amazonCredential) + public SalesService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/SellerService.cs b/Source/FikaAmazonAPI/Services/SellerService.cs index c7b0748d..e14d49b6 100644 --- a/Source/FikaAmazonAPI/Services/SellerService.cs +++ b/Source/FikaAmazonAPI/Services/SellerService.cs @@ -8,7 +8,7 @@ namespace FikaAmazonAPI.Services { public class SellerService : RequestService { - public SellerService(AmazonCredential amazonCredential) : base(amazonCredential) + public SellerService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/ServicesService.cs b/Source/FikaAmazonAPI/Services/ServicesService.cs index 03d52fe2..3deeb6c1 100644 --- a/Source/FikaAmazonAPI/Services/ServicesService.cs +++ b/Source/FikaAmazonAPI/Services/ServicesService.cs @@ -1,8 +1,10 @@ -namespace FikaAmazonAPI.Services +using FikaAmazonAPI.Utils; + +namespace FikaAmazonAPI.Services { public class ServicesService : RequestService { - public ServicesService(AmazonCredential amazonCredential) : base(amazonCredential) + public ServicesService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/ShipmentInvoicingService.cs b/Source/FikaAmazonAPI/Services/ShipmentInvoicingService.cs index cc1a7142..9ad4d1c2 100644 --- a/Source/FikaAmazonAPI/Services/ShipmentInvoicingService.cs +++ b/Source/FikaAmazonAPI/Services/ShipmentInvoicingService.cs @@ -8,7 +8,7 @@ namespace FikaAmazonAPI.Services { public partial class ShipmentInvoicingService : RequestService { - public ShipmentInvoicingService(AmazonCredential amazonCredential) : base(amazonCredential) + public ShipmentInvoicingService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/ShippingService.cs b/Source/FikaAmazonAPI/Services/ShippingService.cs index 131556b9..8a6c00c2 100644 --- a/Source/FikaAmazonAPI/Services/ShippingService.cs +++ b/Source/FikaAmazonAPI/Services/ShippingService.cs @@ -8,7 +8,7 @@ namespace FikaAmazonAPI.Services { public class ShippingService : RequestService { - public ShippingService(AmazonCredential amazonCredential) : base(amazonCredential) + public ShippingService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/ShippingServiceV2.cs b/Source/FikaAmazonAPI/Services/ShippingServiceV2.cs index 0addbd2d..29ab322c 100644 --- a/Source/FikaAmazonAPI/Services/ShippingServiceV2.cs +++ b/Source/FikaAmazonAPI/Services/ShippingServiceV2.cs @@ -8,7 +8,7 @@ namespace FikaAmazonAPI.Services { public class ShippingServiceV2 : RequestService { - public ShippingServiceV2(AmazonCredential amazonCredential) : base(amazonCredential) + public ShippingServiceV2(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/SolicitationService.cs b/Source/FikaAmazonAPI/Services/SolicitationService.cs index 168e8207..6322581d 100644 --- a/Source/FikaAmazonAPI/Services/SolicitationService.cs +++ b/Source/FikaAmazonAPI/Services/SolicitationService.cs @@ -8,7 +8,7 @@ namespace FikaAmazonAPI.Services { public class SolicitationService : RequestService { - public SolicitationService(AmazonCredential amazonCredential) : base(amazonCredential) + public SolicitationService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/TokenService.cs b/Source/FikaAmazonAPI/Services/TokenService.cs index 33c04310..7cb2b6a5 100644 --- a/Source/FikaAmazonAPI/Services/TokenService.cs +++ b/Source/FikaAmazonAPI/Services/TokenService.cs @@ -1,8 +1,10 @@ -namespace FikaAmazonAPI.Services +using FikaAmazonAPI.Utils; + +namespace FikaAmazonAPI.Services { public class TokenService : RequestService { - public TokenService(AmazonCredential amazonCredential) : base(amazonCredential) + public TokenService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/UploadService.cs b/Source/FikaAmazonAPI/Services/UploadService.cs index a5c9456f..9a61bda8 100644 --- a/Source/FikaAmazonAPI/Services/UploadService.cs +++ b/Source/FikaAmazonAPI/Services/UploadService.cs @@ -8,7 +8,7 @@ namespace FikaAmazonAPI.Services { public class UploadService : RequestService { - public UploadService(AmazonCredential amazonCredential) : base(amazonCredential) + public UploadService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/VendorDirectFulfillmentOrderService.cs b/Source/FikaAmazonAPI/Services/VendorDirectFulfillmentOrderService.cs index 4e8a99f1..081664f8 100644 --- a/Source/FikaAmazonAPI/Services/VendorDirectFulfillmentOrderService.cs +++ b/Source/FikaAmazonAPI/Services/VendorDirectFulfillmentOrderService.cs @@ -9,7 +9,7 @@ namespace FikaAmazonAPI.Services { public class VendorDirectFulfillmentOrderService : RequestService { - public VendorDirectFulfillmentOrderService(AmazonCredential amazonCredential) : base(amazonCredential) + public VendorDirectFulfillmentOrderService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/VendorOrders.cs b/Source/FikaAmazonAPI/Services/VendorOrders.cs index 9a979248..87d656e7 100644 --- a/Source/FikaAmazonAPI/Services/VendorOrders.cs +++ b/Source/FikaAmazonAPI/Services/VendorOrders.cs @@ -10,7 +10,7 @@ namespace FikaAmazonAPI.Services { public class VendorOrderService : RequestService { - public VendorOrderService(AmazonCredential amazonCredential) : base(amazonCredential) + public VendorOrderService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/VendorTransactionStatus.cs b/Source/FikaAmazonAPI/Services/VendorTransactionStatus.cs index b182fd76..1d21f93a 100644 --- a/Source/FikaAmazonAPI/Services/VendorTransactionStatus.cs +++ b/Source/FikaAmazonAPI/Services/VendorTransactionStatus.cs @@ -9,7 +9,7 @@ namespace FikaAmazonAPI.Services { public class VendorTransactionStatusService : RequestService { - public VendorTransactionStatusService(AmazonCredential amazonCredential) : base(amazonCredential) + public VendorTransactionStatusService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Utils/IRateLimitingHandler.cs b/Source/FikaAmazonAPI/Utils/IRateLimitingHandler.cs index c4d141f1..60ce1ccc 100644 --- a/Source/FikaAmazonAPI/Utils/IRateLimitingHandler.cs +++ b/Source/FikaAmazonAPI/Utils/IRateLimitingHandler.cs @@ -1,4 +1,5 @@ using RestSharp; +using System; using System.Threading; using System.Threading.Tasks; @@ -6,14 +7,17 @@ namespace FikaAmazonAPI.Utils { /// /// This is designed to be used as a singleton, for the purposes of coordinating rate limiting between separate concurrent requests + ///

+ /// See ///
public interface IRateLimitingHandler { - Task SafelyExecuteRequestAsync( + Task> SafelyExecuteRequestAsync( IRestClient restClient, RestRequest restRequest, AmazonCredential credential, RateLimitType rateLimitType = RateLimitType.UNSET, - CancellationToken cancellationToken = default) where TResult : class; + Action responseCallback = null, + CancellationToken cancellationToken = default) where TResponse : new(); } } diff --git a/Source/FikaAmazonAPI/Utils/RateLimitingHandler.cs b/Source/FikaAmazonAPI/Utils/RateLimitingHandler.cs index 93c35a17..c1d9bc49 100644 --- a/Source/FikaAmazonAPI/Utils/RateLimitingHandler.cs +++ b/Source/FikaAmazonAPI/Utils/RateLimitingHandler.cs @@ -1,6 +1,8 @@ using RestSharp; using System; using System.Collections.Generic; +using System.Globalization; +using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -9,12 +11,72 @@ namespace FikaAmazonAPI.Utils { /// /// This is designed to be used as a singleton, for the purposes of coordinating rate limiting between separate concurrent requests + ///

+ /// See ///
internal class RateLimitingHandler : IRateLimitingHandler { - public Task SafelyExecuteRequestAsync(IRestClient restClient, RestRequest restRequest, AmazonCredential credential, RateLimitType rateLimitType = RateLimitType.UNSET, CancellationToken cancellationToken = default) where TResult : class + private const string RateLimitLimitHeaderName = "x-amzn-RateLimit-Limit"; + + public async Task> SafelyExecuteRequestAsync( + IRestClient restClient, + RestRequest restRequest, + AmazonCredential credential, + RateLimitType rateLimitType = RateLimitType.UNSET, + Action action = null, + CancellationToken cancellationToken = default) where TResponse : new() { - throw new NotImplementedException(); + var response = await restClient.ExecuteAsync(restRequest, cancellationToken); + + action?.Invoke(response); + + await SleepForRateLimit(response.Headers, credential, rateLimitType, cancellationToken); + + return response; + } + + private async Task SleepForRateLimit( + IReadOnlyCollection headers, + AmazonCredential credential, + RateLimitType rateLimitType = RateLimitType.UNSET, + CancellationToken cancellationToken = default) + { + try + { + decimal rate = 0; + var limitHeader = headers.FirstOrDefault(a => a.Name == RateLimitLimitHeaderName); + if (limitHeader != null) + { + var RateLimitValue = limitHeader.Value.ToString(); + decimal.TryParse(RateLimitValue, NumberStyles.Any, CultureInfo.InvariantCulture, out rate); + } + + if (credential.IsActiveLimitRate) + { + if (rateLimitType == RateLimitType.UNSET + || !credential.UsagePlansTimings.TryGetValue(rateLimitType, out var planTimings)) + { + if (rate > 0) + { + int sleepTime = (int)(1 / rate * 1000); + await Task.Delay(sleepTime, cancellationToken); + } + } + else + { + if (rate > 0) + { + planTimings.SetRateLimit(rate); + } + + await planTimings.NextRate(rateLimitType); + } + } + } + catch (Exception e) + { + // not a big fan of this... + } } } } From c52f500745d46e929379b33e2b749bca27bd9156 Mon Sep 17 00:00:00 2001 From: Finn Reilly Date: Wed, 4 Dec 2024 16:08:36 +0000 Subject: [PATCH 03/17] separate concurrent dictionary of rate limit policy for each seller --- Source/FikaAmazonAPI/AmazonCredential.cs | 4 +++- .../Utils/RateLimitsDefinitions.cs | 23 +++++++++++++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/Source/FikaAmazonAPI/AmazonCredential.cs b/Source/FikaAmazonAPI/AmazonCredential.cs index cc3fedb6..ed4415ac 100644 --- a/Source/FikaAmazonAPI/AmazonCredential.cs +++ b/Source/FikaAmazonAPI/AmazonCredential.cs @@ -1,5 +1,6 @@ using FikaAmazonAPI.AmazonSpApiSDK.Models.Token; using FikaAmazonAPI.Utils; +using System.Collections.Concurrent; using System.Collections.Generic; using static FikaAmazonAPI.AmazonSpApiSDK.Models.Token.CacheTokenData; using static FikaAmazonAPI.Utils.Constants; @@ -39,6 +40,7 @@ public AmazonCredential(string AccessKey, string SecretKey, string RoleArn, stri this.RefreshToken = RefreshToken; this.ProxyAddress = ProxyAddress; CacheTokenData = new CacheTokenData(); + UsagePlansTimings = RateLimitsDefinitions.RateLimitsTimeForCredential(this); } public TokenResponse GetToken(TokenDataType tokenDataType) @@ -57,7 +59,7 @@ public void SetAWSAuthenticationTokenData(AWSAuthenticationTokenData tokenData) { CacheTokenData.SetAWSAuthenticationTokenData(tokenData); } - internal Dictionary UsagePlansTimings { get; set; } = RateLimitsDefinitions.RateLimitsTime(); + internal ConcurrentDictionary UsagePlansTimings { get; set; } } } diff --git a/Source/FikaAmazonAPI/Utils/RateLimitsDefinitions.cs b/Source/FikaAmazonAPI/Utils/RateLimitsDefinitions.cs index 6fea4b85..8eb55491 100644 --- a/Source/FikaAmazonAPI/Utils/RateLimitsDefinitions.cs +++ b/Source/FikaAmazonAPI/Utils/RateLimitsDefinitions.cs @@ -1,10 +1,29 @@ -using System.Collections.Generic; +using System.Collections.Concurrent; +using System.Collections.Generic; namespace FikaAmazonAPI.Utils { internal static class RateLimitsDefinitions { - internal static Dictionary RateLimitsTime() + private static ConcurrentDictionary> RateLimitsByCredentialKey = new ConcurrentDictionary>(); + + /// + /// Returns a concurrent dictionary of rate limit policies by rate limit type based on the credential's client and seller Id + /// + /// The credential to use + /// A concurrent dictionary of rate limit policies by rate limit type + internal static ConcurrentDictionary RateLimitsTimeForCredential(AmazonCredential credential) + { + var credentialKey = $"{credential.ClientId}_{credential.SellerID}"; + + if (RateLimitsByCredentialKey.TryGetValue(credentialKey, out var rateLimits)) { return rateLimits; } + + var rateLimitsForSeller = new ConcurrentDictionary(RateLimitsTime()); + RateLimitsByCredentialKey.TryAdd(credentialKey, rateLimitsForSeller); + return RateLimitsTimeForCredential(credential); + } + + private static Dictionary RateLimitsTime() { //This has to create a new list for each connection, so that rate limits are per seller, not overall. return new Dictionary() From 72caeabb1fbba191e5dba2a2cadc02da5da9e434 Mon Sep 17 00:00:00 2001 From: Finn Reilly Date: Wed, 4 Dec 2024 16:49:16 +0000 Subject: [PATCH 04/17] un-break calling code --- Source/FikaAmazonAPI/Services/AplusContentService.cs | 2 +- Source/FikaAmazonAPI/Services/AppIntegrationsV20240401.cs | 2 +- Source/FikaAmazonAPI/Services/AuthorizationService.cs | 2 +- Source/FikaAmazonAPI/Services/CatalogItemService.cs | 2 +- Source/FikaAmazonAPI/Services/EasyShip20220323Service.cs | 2 +- Source/FikaAmazonAPI/Services/FbaInboundEligibilityService.cs | 2 +- Source/FikaAmazonAPI/Services/FbaInboundService.cs | 2 +- Source/FikaAmazonAPI/Services/FbaInventoryService.cs | 2 +- Source/FikaAmazonAPI/Services/FbaOutboundService.cs | 2 +- Source/FikaAmazonAPI/Services/FbaSmallandLightService.cs | 2 +- Source/FikaAmazonAPI/Services/FeedService.cs | 2 +- Source/FikaAmazonAPI/Services/FinancialService.cs | 2 +- Source/FikaAmazonAPI/Services/FulFillmentInboundService.cs | 2 +- .../Services/FulFillmentInboundServicev20240320.cs | 2 +- Source/FikaAmazonAPI/Services/FulFillmentOutboundService.cs | 2 +- Source/FikaAmazonAPI/Services/ListingsItemService.cs | 2 +- Source/FikaAmazonAPI/Services/MerchantFulfillmentService.cs | 2 +- Source/FikaAmazonAPI/Services/MessagingService.cs | 2 +- Source/FikaAmazonAPI/Services/NotificationService.cs | 2 +- Source/FikaAmazonAPI/Services/OrderService.cs | 2 +- Source/FikaAmazonAPI/Services/ProductFeeService.cs | 2 +- Source/FikaAmazonAPI/Services/ProductPricingService.cs | 2 +- Source/FikaAmazonAPI/Services/ProductTypeService.cs | 2 +- Source/FikaAmazonAPI/Services/ReportService.cs | 2 +- Source/FikaAmazonAPI/Services/RequestService.cs | 4 ++-- Source/FikaAmazonAPI/Services/RestrictionService.cs | 2 +- Source/FikaAmazonAPI/Services/SalesService.cs | 2 +- Source/FikaAmazonAPI/Services/SellerService.cs | 2 +- Source/FikaAmazonAPI/Services/ServicesService.cs | 2 +- Source/FikaAmazonAPI/Services/ShipmentInvoicingService.cs | 2 +- Source/FikaAmazonAPI/Services/ShippingService.cs | 2 +- Source/FikaAmazonAPI/Services/ShippingServiceV2.cs | 2 +- Source/FikaAmazonAPI/Services/SolicitationService.cs | 2 +- Source/FikaAmazonAPI/Services/TokenService.cs | 2 +- Source/FikaAmazonAPI/Services/UploadService.cs | 2 +- .../Services/VendorDirectFulfillmentOrderService.cs | 2 +- Source/FikaAmazonAPI/Services/VendorOrders.cs | 2 +- Source/FikaAmazonAPI/Services/VendorTransactionStatus.cs | 2 +- 38 files changed, 39 insertions(+), 39 deletions(-) diff --git a/Source/FikaAmazonAPI/Services/AplusContentService.cs b/Source/FikaAmazonAPI/Services/AplusContentService.cs index a6150967..f91059dd 100644 --- a/Source/FikaAmazonAPI/Services/AplusContentService.cs +++ b/Source/FikaAmazonAPI/Services/AplusContentService.cs @@ -6,7 +6,7 @@ namespace FikaAmazonAPI.Services public class AplusContentService : RequestService { - public AplusContentService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) + public AplusContentService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/AppIntegrationsV20240401.cs b/Source/FikaAmazonAPI/Services/AppIntegrationsV20240401.cs index 019ebc20..3cb5dc08 100644 --- a/Source/FikaAmazonAPI/Services/AppIntegrationsV20240401.cs +++ b/Source/FikaAmazonAPI/Services/AppIntegrationsV20240401.cs @@ -10,7 +10,7 @@ namespace FikaAmazonAPI.Services { public class AppIntegrationsServiceV20240401: RequestService { - public AppIntegrationsServiceV20240401(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) + public AppIntegrationsServiceV20240401(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/AuthorizationService.cs b/Source/FikaAmazonAPI/Services/AuthorizationService.cs index f74569d7..4a05a71f 100644 --- a/Source/FikaAmazonAPI/Services/AuthorizationService.cs +++ b/Source/FikaAmazonAPI/Services/AuthorizationService.cs @@ -10,7 +10,7 @@ namespace FikaAmazonAPI.Services { public class AuthorizationService : RequestService { - public AuthorizationService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) + public AuthorizationService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler) { } public string GetAuthorizationCode(ParameterAuthorizationCode parameterGetOrderMetrics) => diff --git a/Source/FikaAmazonAPI/Services/CatalogItemService.cs b/Source/FikaAmazonAPI/Services/CatalogItemService.cs index 72ef2853..e98eccbc 100644 --- a/Source/FikaAmazonAPI/Services/CatalogItemService.cs +++ b/Source/FikaAmazonAPI/Services/CatalogItemService.cs @@ -14,7 +14,7 @@ namespace FikaAmazonAPI.Services { public class CatalogItemService : RequestService { - public CatalogItemService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) + public CatalogItemService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/EasyShip20220323Service.cs b/Source/FikaAmazonAPI/Services/EasyShip20220323Service.cs index 90b4d42c..41c7cebf 100644 --- a/Source/FikaAmazonAPI/Services/EasyShip20220323Service.cs +++ b/Source/FikaAmazonAPI/Services/EasyShip20220323Service.cs @@ -8,7 +8,7 @@ namespace FikaAmazonAPI.Services { public class EasyShip20220323Service : RequestService { - public EasyShip20220323Service(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) + public EasyShip20220323Service(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/FbaInboundEligibilityService.cs b/Source/FikaAmazonAPI/Services/FbaInboundEligibilityService.cs index 4283433d..4677a7f9 100644 --- a/Source/FikaAmazonAPI/Services/FbaInboundEligibilityService.cs +++ b/Source/FikaAmazonAPI/Services/FbaInboundEligibilityService.cs @@ -8,7 +8,7 @@ namespace FikaAmazonAPI.Services { public class FbaInboundEligibilityService : RequestService { - public FbaInboundEligibilityService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) + public FbaInboundEligibilityService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/FbaInboundService.cs b/Source/FikaAmazonAPI/Services/FbaInboundService.cs index ba38df6a..080afbed 100644 --- a/Source/FikaAmazonAPI/Services/FbaInboundService.cs +++ b/Source/FikaAmazonAPI/Services/FbaInboundService.cs @@ -4,7 +4,7 @@ namespace FikaAmazonAPI.Services { public class FbaInboundService : RequestService { - public FbaInboundService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) + public FbaInboundService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/FbaInventoryService.cs b/Source/FikaAmazonAPI/Services/FbaInventoryService.cs index f9b98924..ff1cff8b 100644 --- a/Source/FikaAmazonAPI/Services/FbaInventoryService.cs +++ b/Source/FikaAmazonAPI/Services/FbaInventoryService.cs @@ -10,7 +10,7 @@ namespace FikaAmazonAPI.Services public class FbaInventoryService : RequestService { - public FbaInventoryService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) + public FbaInventoryService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/FbaOutboundService.cs b/Source/FikaAmazonAPI/Services/FbaOutboundService.cs index 746e06f9..ababfeb3 100644 --- a/Source/FikaAmazonAPI/Services/FbaOutboundService.cs +++ b/Source/FikaAmazonAPI/Services/FbaOutboundService.cs @@ -4,7 +4,7 @@ namespace FikaAmazonAPI.Services { public class FbaOutboundService : RequestService { - public FbaOutboundService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) + public FbaOutboundService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/FbaSmallandLightService.cs b/Source/FikaAmazonAPI/Services/FbaSmallandLightService.cs index d7b3413d..75398c54 100644 --- a/Source/FikaAmazonAPI/Services/FbaSmallandLightService.cs +++ b/Source/FikaAmazonAPI/Services/FbaSmallandLightService.cs @@ -9,7 +9,7 @@ namespace FikaAmazonAPI.Services public class FbaSmallandLightService : RequestService { - public FbaSmallandLightService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) + public FbaSmallandLightService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/FeedService.cs b/Source/FikaAmazonAPI/Services/FeedService.cs index 428ccb6a..f8b2f2d6 100644 --- a/Source/FikaAmazonAPI/Services/FeedService.cs +++ b/Source/FikaAmazonAPI/Services/FeedService.cs @@ -19,7 +19,7 @@ namespace FikaAmazonAPI.Services public class FeedService : RequestService { - public FeedService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) + public FeedService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/FinancialService.cs b/Source/FikaAmazonAPI/Services/FinancialService.cs index 8e2d7f8a..0d48710c 100644 --- a/Source/FikaAmazonAPI/Services/FinancialService.cs +++ b/Source/FikaAmazonAPI/Services/FinancialService.cs @@ -9,7 +9,7 @@ namespace FikaAmazonAPI.Services { public class FinancialService : RequestService { - public FinancialService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) + public FinancialService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/FulFillmentInboundService.cs b/Source/FikaAmazonAPI/Services/FulFillmentInboundService.cs index 3aab3371..0ecd4ac2 100644 --- a/Source/FikaAmazonAPI/Services/FulFillmentInboundService.cs +++ b/Source/FikaAmazonAPI/Services/FulFillmentInboundService.cs @@ -10,7 +10,7 @@ namespace FikaAmazonAPI.Services { public class FulFillmentInboundService : RequestService { - public FulFillmentInboundService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) + public FulFillmentInboundService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/FulFillmentInboundServicev20240320.cs b/Source/FikaAmazonAPI/Services/FulFillmentInboundServicev20240320.cs index 5d2925a7..db13474e 100644 --- a/Source/FikaAmazonAPI/Services/FulFillmentInboundServicev20240320.cs +++ b/Source/FikaAmazonAPI/Services/FulFillmentInboundServicev20240320.cs @@ -9,7 +9,7 @@ namespace FikaAmazonAPI.Services { public class FulFillmentInboundServicev20240320 : RequestService { - public FulFillmentInboundServicev20240320(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) + public FulFillmentInboundServicev20240320(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/FulFillmentOutboundService.cs b/Source/FikaAmazonAPI/Services/FulFillmentOutboundService.cs index e09fee97..e4569f8b 100644 --- a/Source/FikaAmazonAPI/Services/FulFillmentOutboundService.cs +++ b/Source/FikaAmazonAPI/Services/FulFillmentOutboundService.cs @@ -10,7 +10,7 @@ namespace FikaAmazonAPI.Services { public class FulFillmentOutboundService : RequestService { - public FulFillmentOutboundService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) + public FulFillmentOutboundService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/ListingsItemService.cs b/Source/FikaAmazonAPI/Services/ListingsItemService.cs index c9af1f78..bd39aa88 100644 --- a/Source/FikaAmazonAPI/Services/ListingsItemService.cs +++ b/Source/FikaAmazonAPI/Services/ListingsItemService.cs @@ -9,7 +9,7 @@ namespace FikaAmazonAPI.Services { public class ListingsItemService : RequestService { - public ListingsItemService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) + public ListingsItemService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/MerchantFulfillmentService.cs b/Source/FikaAmazonAPI/Services/MerchantFulfillmentService.cs index 972cbf0e..f225a726 100644 --- a/Source/FikaAmazonAPI/Services/MerchantFulfillmentService.cs +++ b/Source/FikaAmazonAPI/Services/MerchantFulfillmentService.cs @@ -11,7 +11,7 @@ namespace FikaAmazonAPI.Services public class MerchantFulfillmentService : RequestService { - public MerchantFulfillmentService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) + public MerchantFulfillmentService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/MessagingService.cs b/Source/FikaAmazonAPI/Services/MessagingService.cs index c745d16b..cdc35865 100644 --- a/Source/FikaAmazonAPI/Services/MessagingService.cs +++ b/Source/FikaAmazonAPI/Services/MessagingService.cs @@ -9,7 +9,7 @@ namespace FikaAmazonAPI.Services public class MessagingService : RequestService { - public MessagingService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) + public MessagingService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/NotificationService.cs b/Source/FikaAmazonAPI/Services/NotificationService.cs index 65a92ee3..85b8374c 100644 --- a/Source/FikaAmazonAPI/Services/NotificationService.cs +++ b/Source/FikaAmazonAPI/Services/NotificationService.cs @@ -17,7 +17,7 @@ namespace FikaAmazonAPI.Services { public class NotificationService : RequestService { - public NotificationService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) + public NotificationService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/OrderService.cs b/Source/FikaAmazonAPI/Services/OrderService.cs index 708bdf32..81dd14d6 100644 --- a/Source/FikaAmazonAPI/Services/OrderService.cs +++ b/Source/FikaAmazonAPI/Services/OrderService.cs @@ -12,7 +12,7 @@ namespace FikaAmazonAPI.Services { public class OrderService : RequestService { - public OrderService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) + public OrderService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/ProductFeeService.cs b/Source/FikaAmazonAPI/Services/ProductFeeService.cs index 2ce952f9..cc2fac92 100644 --- a/Source/FikaAmazonAPI/Services/ProductFeeService.cs +++ b/Source/FikaAmazonAPI/Services/ProductFeeService.cs @@ -8,7 +8,7 @@ namespace FikaAmazonAPI.Services { public class ProductFeeService : RequestService { - public ProductFeeService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) + public ProductFeeService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/ProductPricingService.cs b/Source/FikaAmazonAPI/Services/ProductPricingService.cs index 260921b0..387b4ba4 100644 --- a/Source/FikaAmazonAPI/Services/ProductPricingService.cs +++ b/Source/FikaAmazonAPI/Services/ProductPricingService.cs @@ -14,7 +14,7 @@ namespace FikaAmazonAPI.Services { public class ProductPricingService : RequestService { - public ProductPricingService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) + public ProductPricingService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/ProductTypeService.cs b/Source/FikaAmazonAPI/Services/ProductTypeService.cs index 44fd90dd..de50d975 100644 --- a/Source/FikaAmazonAPI/Services/ProductTypeService.cs +++ b/Source/FikaAmazonAPI/Services/ProductTypeService.cs @@ -8,7 +8,7 @@ namespace FikaAmazonAPI.Services { public class ProductTypeService : RequestService { - public ProductTypeService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) + public ProductTypeService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/ReportService.cs b/Source/FikaAmazonAPI/Services/ReportService.cs index 141c8b4c..97897781 100644 --- a/Source/FikaAmazonAPI/Services/ReportService.cs +++ b/Source/FikaAmazonAPI/Services/ReportService.cs @@ -16,7 +16,7 @@ namespace FikaAmazonAPI.Services { public class ReportService : RequestService { - public ReportService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) + public ReportService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler) { } #region GetReport diff --git a/Source/FikaAmazonAPI/Services/RequestService.cs b/Source/FikaAmazonAPI/Services/RequestService.cs index 3603a2f2..23fe490d 100644 --- a/Source/FikaAmazonAPI/Services/RequestService.cs +++ b/Source/FikaAmazonAPI/Services/RequestService.cs @@ -47,9 +47,9 @@ protected string ApiBaseUrl /// /// A credential containing the API user's information and cached token values /// A singleton designed to handle concurrent requests based on the rate limiting policy - public RequestService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) + public RequestService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) { - RateLimitingHandler = rateLimitingHandler; + RateLimitingHandler = rateLimitingHandler ?? new RateLimitingHandler(); AmazonCredential = amazonCredential; AmazonSandboxUrl = amazonCredential.MarketPlace.Region.SandboxHostUrl; diff --git a/Source/FikaAmazonAPI/Services/RestrictionService.cs b/Source/FikaAmazonAPI/Services/RestrictionService.cs index 20f8c752..ca847556 100644 --- a/Source/FikaAmazonAPI/Services/RestrictionService.cs +++ b/Source/FikaAmazonAPI/Services/RestrictionService.cs @@ -8,7 +8,7 @@ namespace FikaAmazonAPI.Services { public class RestrictionService : RequestService { - public RestrictionService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) + public RestrictionService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/SalesService.cs b/Source/FikaAmazonAPI/Services/SalesService.cs index 6e3de1fb..921e47a9 100644 --- a/Source/FikaAmazonAPI/Services/SalesService.cs +++ b/Source/FikaAmazonAPI/Services/SalesService.cs @@ -9,7 +9,7 @@ namespace FikaAmazonAPI.Services { public class SalesService : RequestService { - public SalesService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) + public SalesService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/SellerService.cs b/Source/FikaAmazonAPI/Services/SellerService.cs index e14d49b6..e37e8f7c 100644 --- a/Source/FikaAmazonAPI/Services/SellerService.cs +++ b/Source/FikaAmazonAPI/Services/SellerService.cs @@ -8,7 +8,7 @@ namespace FikaAmazonAPI.Services { public class SellerService : RequestService { - public SellerService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) + public SellerService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/ServicesService.cs b/Source/FikaAmazonAPI/Services/ServicesService.cs index 3deeb6c1..8a379c62 100644 --- a/Source/FikaAmazonAPI/Services/ServicesService.cs +++ b/Source/FikaAmazonAPI/Services/ServicesService.cs @@ -4,7 +4,7 @@ namespace FikaAmazonAPI.Services { public class ServicesService : RequestService { - public ServicesService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) + public ServicesService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/ShipmentInvoicingService.cs b/Source/FikaAmazonAPI/Services/ShipmentInvoicingService.cs index 9ad4d1c2..99d616cc 100644 --- a/Source/FikaAmazonAPI/Services/ShipmentInvoicingService.cs +++ b/Source/FikaAmazonAPI/Services/ShipmentInvoicingService.cs @@ -8,7 +8,7 @@ namespace FikaAmazonAPI.Services { public partial class ShipmentInvoicingService : RequestService { - public ShipmentInvoicingService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) + public ShipmentInvoicingService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/ShippingService.cs b/Source/FikaAmazonAPI/Services/ShippingService.cs index 8a6c00c2..ff8356fb 100644 --- a/Source/FikaAmazonAPI/Services/ShippingService.cs +++ b/Source/FikaAmazonAPI/Services/ShippingService.cs @@ -8,7 +8,7 @@ namespace FikaAmazonAPI.Services { public class ShippingService : RequestService { - public ShippingService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) + public ShippingService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/ShippingServiceV2.cs b/Source/FikaAmazonAPI/Services/ShippingServiceV2.cs index 29ab322c..3862fcb1 100644 --- a/Source/FikaAmazonAPI/Services/ShippingServiceV2.cs +++ b/Source/FikaAmazonAPI/Services/ShippingServiceV2.cs @@ -8,7 +8,7 @@ namespace FikaAmazonAPI.Services { public class ShippingServiceV2 : RequestService { - public ShippingServiceV2(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) + public ShippingServiceV2(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/SolicitationService.cs b/Source/FikaAmazonAPI/Services/SolicitationService.cs index 6322581d..09320859 100644 --- a/Source/FikaAmazonAPI/Services/SolicitationService.cs +++ b/Source/FikaAmazonAPI/Services/SolicitationService.cs @@ -8,7 +8,7 @@ namespace FikaAmazonAPI.Services { public class SolicitationService : RequestService { - public SolicitationService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) + public SolicitationService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/TokenService.cs b/Source/FikaAmazonAPI/Services/TokenService.cs index 7cb2b6a5..ee0a18b2 100644 --- a/Source/FikaAmazonAPI/Services/TokenService.cs +++ b/Source/FikaAmazonAPI/Services/TokenService.cs @@ -4,7 +4,7 @@ namespace FikaAmazonAPI.Services { public class TokenService : RequestService { - public TokenService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) + public TokenService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/UploadService.cs b/Source/FikaAmazonAPI/Services/UploadService.cs index 9a61bda8..83ada89a 100644 --- a/Source/FikaAmazonAPI/Services/UploadService.cs +++ b/Source/FikaAmazonAPI/Services/UploadService.cs @@ -8,7 +8,7 @@ namespace FikaAmazonAPI.Services { public class UploadService : RequestService { - public UploadService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) + public UploadService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/VendorDirectFulfillmentOrderService.cs b/Source/FikaAmazonAPI/Services/VendorDirectFulfillmentOrderService.cs index 081664f8..cd3d114c 100644 --- a/Source/FikaAmazonAPI/Services/VendorDirectFulfillmentOrderService.cs +++ b/Source/FikaAmazonAPI/Services/VendorDirectFulfillmentOrderService.cs @@ -9,7 +9,7 @@ namespace FikaAmazonAPI.Services { public class VendorDirectFulfillmentOrderService : RequestService { - public VendorDirectFulfillmentOrderService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) + public VendorDirectFulfillmentOrderService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/VendorOrders.cs b/Source/FikaAmazonAPI/Services/VendorOrders.cs index 87d656e7..96da5ef1 100644 --- a/Source/FikaAmazonAPI/Services/VendorOrders.cs +++ b/Source/FikaAmazonAPI/Services/VendorOrders.cs @@ -10,7 +10,7 @@ namespace FikaAmazonAPI.Services { public class VendorOrderService : RequestService { - public VendorOrderService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) + public VendorOrderService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler) { } diff --git a/Source/FikaAmazonAPI/Services/VendorTransactionStatus.cs b/Source/FikaAmazonAPI/Services/VendorTransactionStatus.cs index 1d21f93a..5c732de9 100644 --- a/Source/FikaAmazonAPI/Services/VendorTransactionStatus.cs +++ b/Source/FikaAmazonAPI/Services/VendorTransactionStatus.cs @@ -9,7 +9,7 @@ namespace FikaAmazonAPI.Services { public class VendorTransactionStatusService : RequestService { - public VendorTransactionStatusService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler) : base(amazonCredential, rateLimitingHandler) + public VendorTransactionStatusService(AmazonCredential amazonCredential, IRateLimitingHandler rateLimitingHandler = null) : base(amazonCredential, rateLimitingHandler) { } From e28aec8755a6a50dafa5a4735bd723e26f9fc041 Mon Sep 17 00:00:00 2001 From: Finn Reilly Date: Wed, 4 Dec 2024 17:04:07 +0000 Subject: [PATCH 05/17] add factory class to explicitly create multiple connections for multithreading --- .../AmazonMultithreadedConnectionFactory.cs | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 Source/FikaAmazonAPI/AmazonMultithreadedConnectionFactory.cs diff --git a/Source/FikaAmazonAPI/AmazonMultithreadedConnectionFactory.cs b/Source/FikaAmazonAPI/AmazonMultithreadedConnectionFactory.cs new file mode 100644 index 00000000..5360a12c --- /dev/null +++ b/Source/FikaAmazonAPI/AmazonMultithreadedConnectionFactory.cs @@ -0,0 +1,46 @@ +using FikaAmazonAPI.Utils; +using System.Globalization; + +namespace FikaAmazonAPI +{ + public class AmazonMultithreadedConnectionFactory + { + private readonly string _accessKey; + private readonly string _secretKey; + private readonly string _roleArn; + private readonly string _clientId; + private readonly string _clientSecret; + private readonly string _refreshToken; + private readonly string _proxyAddress; + + private readonly IRateLimitingHandler _rateLimitingHandler; + + public AmazonMultithreadedConnectionFactory( + string AccessKey, + string SecretKey, + string RoleArn, + string ClientId, + string ClientSecret, + string RefreshToken, + string ProxyAddress = null, + IRateLimitingHandler rateLimitingHandler = null) + { + _accessKey = AccessKey; + _secretKey = SecretKey; + _roleArn = RoleArn; + _clientId = ClientId; + _clientSecret = ClientSecret; + _refreshToken = RefreshToken; + _proxyAddress = ProxyAddress; + + _rateLimitingHandler = rateLimitingHandler ?? new RateLimitingHandler(); + } + + public AmazonConnection RequestScopedConnection(string refCode = null, CultureInfo cultureInfo = null) + { + // need to create distinct credential/connection here so that token caching in credential is predicably kept within scope + var credential = new AmazonCredential(_accessKey, _secretKey, _roleArn, _clientId, _clientSecret, _refreshToken, _proxyAddress); + return new AmazonConnection(credential, _rateLimitingHandler, refCode, cultureInfo); + } + } +} From 653ee9be6823ba63f7f7b2ec6a3fbe38b8b94943 Mon Sep 17 00:00:00 2001 From: Finn Reilly Date: Thu, 5 Dec 2024 10:42:40 +0000 Subject: [PATCH 06/17] Refactor `RateLimits` * simplify token bucket algorithm code * improved xml documentation * no need for this to be a class - make it a struct * no need to have two methods for waiting on a token * remove dependency on `AmazonCredential.DebugMode` static property --- .../FikaAmazonAPI/Services/RequestService.cs | 2 +- .../Utils/RateLimitingHandler.cs | 75 +++++++++++------ Source/FikaAmazonAPI/Utils/RateLimits.cs | 84 ++++++++++--------- 3 files changed, 94 insertions(+), 67 deletions(-) diff --git a/Source/FikaAmazonAPI/Services/RequestService.cs b/Source/FikaAmazonAPI/Services/RequestService.cs index 23fe490d..833932d3 100644 --- a/Source/FikaAmazonAPI/Services/RequestService.cs +++ b/Source/FikaAmazonAPI/Services/RequestService.cs @@ -238,7 +238,7 @@ public async Task ExecuteRequestAsync(RateLimitType rateLimitType = RateLi cancellationToken.ThrowIfCancellationRequested(); - await AmazonCredential.UsagePlansTimings[rateLimitType].Delay(); + await AmazonCredential.UsagePlansTimings[rateLimitType].WaitForPermittedRequest(rateLimitType, cancellationToken, AmazonCredential.IsDebugMode); tryCount++; } } diff --git a/Source/FikaAmazonAPI/Utils/RateLimitingHandler.cs b/Source/FikaAmazonAPI/Utils/RateLimitingHandler.cs index c1d9bc49..64022050 100644 --- a/Source/FikaAmazonAPI/Utils/RateLimitingHandler.cs +++ b/Source/FikaAmazonAPI/Utils/RateLimitingHandler.cs @@ -26,57 +26,78 @@ public async Task> SafelyExecuteRequestAsync( Action action = null, CancellationToken cancellationToken = default) where TResponse : new() { + await SleepForRateLimit(credential, rateLimitType, cancellationToken: default); + var response = await restClient.ExecuteAsync(restRequest, cancellationToken); action?.Invoke(response); - await SleepForRateLimit(response.Headers, credential, rateLimitType, cancellationToken); + await SleepForRateLimit(credential, rateLimitType, cancellationToken: cancellationToken); return response; } private async Task SleepForRateLimit( - IReadOnlyCollection headers, AmazonCredential credential, RateLimitType rateLimitType = RateLimitType.UNSET, + decimal updatedLimitRate = default, CancellationToken cancellationToken = default) { - try + if (credential.IsActiveLimitRate) { - decimal rate = 0; - var limitHeader = headers.FirstOrDefault(a => a.Name == RateLimitLimitHeaderName); - if (limitHeader != null) - { - var RateLimitValue = limitHeader.Value.ToString(); - decimal.TryParse(RateLimitValue, NumberStyles.Any, CultureInfo.InvariantCulture, out rate); - } + var rateLimitPolicies = RateLimitsDefinitions.RateLimitsTimeForCredential(credential); - if (credential.IsActiveLimitRate) + if (rateLimitType == RateLimitType.UNSET + || !rateLimitPolicies.TryGetValue(rateLimitType, out var planTimings)) { - if (rateLimitType == RateLimitType.UNSET - || !credential.UsagePlansTimings.TryGetValue(rateLimitType, out var planTimings)) + if (updatedLimitRate > 0) { - if (rate > 0) - { - int sleepTime = (int)(1 / rate * 1000); - await Task.Delay(sleepTime, cancellationToken); - } + int sleepTime = (int)(1 / updatedLimitRate * 1000); + await Task.Delay(sleepTime, cancellationToken); } - else + } + else + { + if (updatedLimitRate > 0) { - if (rate > 0) - { - planTimings.SetRateLimit(rate); - } - - await planTimings.NextRate(rateLimitType); + planTimings.SetRateLimit(updatedLimitRate); } + + await planTimings.WaitForPermittedRequest(rateLimitType); } } - catch (Exception e) + } + + private void UpdateRateLimitPolicy(AmazonCredential credential, RateLimitType rateLimitType, decimal updatedRateLimit) + { + if (updatedRateLimit <= 0) { - // not a big fan of this... + return; } + + var rateLimitPolicies = RateLimitsDefinitions.RateLimitsTimeForCredential(credential); + + if (rateLimitType == RateLimitType.UNSET + || !rateLimitPolicies.TryGetValue(rateLimitType, out var planTimings)) + { + Console.WriteLine($"No rate limit policy found for {rateLimitType} for seller {credential.SellerID}"); + return; + } + + planTimings.SetRateLimit(updatedRateLimit); + } + + private decimal GetRateFromHeaders(IEnumerable headers) + { + decimal rate = 0; + var limitHeader = headers.FirstOrDefault(a => a.Name == RateLimitLimitHeaderName); + if (limitHeader != null) + { + var RateLimitValue = limitHeader.Value.ToString(); + decimal.TryParse(RateLimitValue, NumberStyles.Any, CultureInfo.InvariantCulture, out rate); + } + + return rate; } } } diff --git a/Source/FikaAmazonAPI/Utils/RateLimits.cs b/Source/FikaAmazonAPI/Utils/RateLimits.cs index 79925c83..65210d5c 100644 --- a/Source/FikaAmazonAPI/Utils/RateLimits.cs +++ b/Source/FikaAmazonAPI/Utils/RateLimits.cs @@ -1,76 +1,87 @@ using System; +using System.Threading; using System.Threading.Tasks; namespace FikaAmazonAPI.Utils { - internal class RateLimits + internal struct RateLimits { internal decimal Rate { get; set; } internal int Burst { get; set; } - internal DateTime LastRequest { get; set; } + internal DateTime LastRequestReplenished { get; set; } internal int RequestsSent { get; set; } + /// + /// Constructor for rate limits configuration object + /// + /// The number of permitted requests which will be added to the "Token bucket" per second + /// The maximum number of requests which can exist in the "Token bucket" at any time internal RateLimits(decimal Rate, int Burst) { this.Rate = Rate; this.Burst = Burst; - this.LastRequest = DateTime.UtcNow; + this.LastRequestReplenished = DateTime.UtcNow; this.RequestsSent = 0; } private int GetRatePeriodMs() { return (int)(((1 / Rate) * 1000) / 1); } - public async Task NextRate(RateLimitType rateLimitType) + + /// + /// Performs a wait based on the Token Bucket rate limiting algorithm described in documentation. + ///

+ /// See also + ///
+ /// An enum representing the rate limit policy for the resource in use + /// The cancellation token + /// + public async Task WaitForPermittedRequest(RateLimitType rateLimitType, CancellationToken cancellationToken = default, bool debugMode = true) { if (RequestsSent < 0) + { RequestsSent = 0; - + } int ratePeriodMs = GetRatePeriodMs(); var nextRequestsSent = RequestsSent + 1; var nextRequestsSentTxt = (nextRequestsSent > Burst) ? "FULL" : nextRequestsSent.ToString(); - if (AmazonCredential.DebugMode) + if (debugMode) { string output = $"[RateLimits ,{rateLimitType,15}]: {DateTime.UtcNow.ToString(),10}\t Request/Burst: {nextRequestsSentTxt}/{Burst}\t Rate: {Rate}/{ratePeriodMs}ms"; Console.WriteLine(output); } - - - if (RequestsSent >= Burst) + // if bucket is currently empty, enter wait loop for replenishment + while (RequestsSent >= Burst) { - var LastRequestTime = LastRequest; - while (true) + // wait until predicted next token available + var incomingRequestTokenTime = LastRequestReplenished.AddMilliseconds(ratePeriodMs); + if (incomingRequestTokenTime > DateTime.UtcNow) { - // to confirm, but looks like we're guessing how long requests will take here? - LastRequestTime = LastRequestTime.AddMilliseconds(ratePeriodMs); - if (LastRequestTime > DateTime.UtcNow) - break; - else - RequestsSent -= 1; + await Task.Delay(100); + continue; + } + else + { + // replenish token + LastRequestReplenished = incomingRequestTokenTime; + RequestsSent -= 1; + } - if (RequestsSent <= 0) - { - RequestsSent = 0; - break; - } + if (RequestsSent <= 0) + { + RequestsSent = 0; + break; } } - - if (RequestsSent >= Burst) + // now remove token from bucket for pending request + if (RequestsSent + 1 <= Burst) { - LastRequest = LastRequest.AddMilliseconds(ratePeriodMs); - var TempLastRequest = LastRequest; - while (TempLastRequest >= DateTime.UtcNow) //.AddMilliseconds(-100) - await Task.Delay(100); - + RequestsSent += 1; } - - - if (RequestsSent + 1 <= Burst) - RequestsSent += 1; - LastRequest = DateTime.UtcNow; + // can't hurt to have this, will probably make the algorithm a little more conservative in practice + LastRequestReplenished = DateTime.UtcNow; return this; } @@ -79,10 +90,5 @@ internal void SetRateLimit(decimal rate) { Rate = rate; } - - internal Task Delay() - { - return Task.Delay(GetRatePeriodMs()); - } } } From 9b5d5c72c07fc5809a8fdfc6e10e97842f1c42c4 Mon Sep 17 00:00:00 2001 From: Finn Reilly Date: Thu, 5 Dec 2024 10:58:51 +0000 Subject: [PATCH 07/17] no need to return anything from wait method --- Source/FikaAmazonAPI/Utils/RateLimits.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Source/FikaAmazonAPI/Utils/RateLimits.cs b/Source/FikaAmazonAPI/Utils/RateLimits.cs index 65210d5c..65ac780d 100644 --- a/Source/FikaAmazonAPI/Utils/RateLimits.cs +++ b/Source/FikaAmazonAPI/Utils/RateLimits.cs @@ -23,6 +23,7 @@ internal RateLimits(decimal Rate, int Burst) this.LastRequestReplenished = DateTime.UtcNow; this.RequestsSent = 0; } + private int GetRatePeriodMs() { return (int)(((1 / Rate) * 1000) / 1); } /// @@ -33,7 +34,7 @@ internal RateLimits(decimal Rate, int Burst) /// An enum representing the rate limit policy for the resource in use /// The cancellation token /// - public async Task WaitForPermittedRequest(RateLimitType rateLimitType, CancellationToken cancellationToken = default, bool debugMode = true) + public async Task WaitForPermittedRequest(RateLimitType rateLimitType, CancellationToken cancellationToken = default, bool debugMode = true) { if (RequestsSent < 0) { @@ -82,8 +83,6 @@ public async Task WaitForPermittedRequest(RateLimitType rateLimitTyp // can't hurt to have this, will probably make the algorithm a little more conservative in practice LastRequestReplenished = DateTime.UtcNow; - - return this; } internal void SetRateLimit(decimal rate) From 82d3ac1d46442306f6550cb9140d6c83460f82f0 Mon Sep 17 00:00:00 2001 From: Finn Reilly Date: Thu, 5 Dec 2024 12:04:53 +0000 Subject: [PATCH 08/17] rate limit type should be part of policy data - obviously --- Source/FikaAmazonAPI/Utils/RateLimits.cs | 13 +- .../Utils/RateLimitsDefinitions.cs | 476 +++++++++--------- 2 files changed, 246 insertions(+), 243 deletions(-) diff --git a/Source/FikaAmazonAPI/Utils/RateLimits.cs b/Source/FikaAmazonAPI/Utils/RateLimits.cs index 65ac780d..4e888b29 100644 --- a/Source/FikaAmazonAPI/Utils/RateLimits.cs +++ b/Source/FikaAmazonAPI/Utils/RateLimits.cs @@ -6,20 +6,23 @@ namespace FikaAmazonAPI.Utils { internal struct RateLimits { - internal decimal Rate { get; set; } - internal int Burst { get; set; } - internal DateTime LastRequestReplenished { get; set; } - internal int RequestsSent { get; set; } + internal decimal Rate { get; private set; } + internal int Burst { get; } + internal DateTime LastRequestReplenished { get; private set; } + internal int RequestsSent { get; private set; } + internal RateLimitType RateLimitType { get; } /// /// Constructor for rate limits configuration object /// /// The number of permitted requests which will be added to the "Token bucket" per second /// The maximum number of requests which can exist in the "Token bucket" at any time - internal RateLimits(decimal Rate, int Burst) + /// An enum representing the resource for which the rate limit policy is being set + internal RateLimits(decimal Rate, int Burst, RateLimitType type) { this.Rate = Rate; this.Burst = Burst; + this.RateLimitType = type; this.LastRequestReplenished = DateTime.UtcNow; this.RequestsSent = 0; } diff --git a/Source/FikaAmazonAPI/Utils/RateLimitsDefinitions.cs b/Source/FikaAmazonAPI/Utils/RateLimitsDefinitions.cs index 8eb55491..08bbb595 100644 --- a/Source/FikaAmazonAPI/Utils/RateLimitsDefinitions.cs +++ b/Source/FikaAmazonAPI/Utils/RateLimitsDefinitions.cs @@ -1,5 +1,6 @@ using System.Collections.Concurrent; using System.Collections.Generic; +using System.Linq; namespace FikaAmazonAPI.Utils { @@ -26,245 +27,244 @@ internal static ConcurrentDictionary RateLimitsTimeFo private static Dictionary RateLimitsTime() { //This has to create a new list for each connection, so that rate limits are per seller, not overall. - return new Dictionary() + return new List { - { RateLimitType.AppIntegrationsV20240401_CreateNotification, new RateLimits(1.0M, 5) }, - { RateLimitType.AppIntegrationsV20240401_DeleteNotifications, new RateLimits(1.0M, 5) }, - { RateLimitType.AppIntegrationsV20240401_RecordActionFeedback, new RateLimits(1.0M, 5) }, - - { RateLimitType.Order_GetOrders, new RateLimits(0.0167M, 20) }, - { RateLimitType.Order_GetOrder, new RateLimits(0.0167M, 20) }, - { RateLimitType.Order_GetOrderBuyerInfo, new RateLimits(0.0167M, 20) }, - { RateLimitType.Order_GetOrderAddress, new RateLimits(0.0167M, 20) }, - { RateLimitType.Order_GetOrderItems, new RateLimits(0.5M, 20) }, - { RateLimitType.Order_GetOrderItemsBuyerInfo, new RateLimits(0.5M, 30) }, - { RateLimitType.Order_UpdateShipmentStatus, new RateLimits(5M, 15) }, - { RateLimitType.Order_GetOrderRegulatedInfo, new RateLimits(0.5M, 15) }, - { RateLimitType.Order_UpdateVerificationStatus, new RateLimits(0.5M, 30) }, - { RateLimitType.Order_UpdateOrderItemsApprovals, new RateLimits(5M, 15) }, - { RateLimitType.Order_ShipmentConfirmation, new RateLimits(2M, 10) }, - - { RateLimitType.Report_GetReports, new RateLimits(0.0222M, 10) }, - { RateLimitType.Report_GetReport, new RateLimits(2.0M, 15) }, - { RateLimitType.Report_CreateReport, new RateLimits(0.0167M, 15) }, - { RateLimitType.Report_CancelReport, new RateLimits(0.0222M, 10) }, - { RateLimitType.Report_GetReportSchedules, new RateLimits(0.0222M, 10) }, - { RateLimitType.Report_CreateReportSchedule, new RateLimits(0.0222M, 10) }, - { RateLimitType.Report_GetReportSchedule, new RateLimits(0.0222M, 10) }, - { RateLimitType.Report_CancelReportSchedule, new RateLimits(0.0222M, 10) }, - { RateLimitType.Report_GetReportDocument, new RateLimits(0.0167M, 15) }, - - { RateLimitType.Financial_ListFinancialEventGroups, new RateLimits(0.5M, 30) }, - { RateLimitType.Financial_ListFinancialEventsByGroupId, new RateLimits(0.5M, 30) }, - { RateLimitType.Financial_ListFinancialEventsByOrderId, new RateLimits(0.5M, 30) }, - { RateLimitType.Financial_ListFinancialEvents, new RateLimits(0.5M, 30) }, - - { RateLimitType.Feed_GetFeeds, new RateLimits(0.0222M, 10) }, - { RateLimitType.Feed_CreateFeed, new RateLimits(0.0083M, 15) }, - { RateLimitType.Feed_GetFeed, new RateLimits(2.0M, 15) }, - { RateLimitType.Feed_CancelFeed, new RateLimits(0.0222M, 10) }, - { RateLimitType.Feed_CreateFeedDocument, new RateLimits(0.0083M, 15) }, - { RateLimitType.Feed_GetFeedDocument, new RateLimits(0.0222M, 10) }, - - { RateLimitType.ListingsItem_GetListingsItem, new RateLimits(5.0M, 10) }, - { RateLimitType.ListingsItem_PutListingsItem, new RateLimits(5.0M, 10) }, - { RateLimitType.ListingsItem_DeleteListingsItem, new RateLimits(5.0M, 10) }, - { RateLimitType.ListingsItem_PatchListingsItem, new RateLimits(5.0M, 10) }, - - { RateLimitType.Upload_CreateUploadDestinationForResource, new RateLimits(0.1M, 5) }, - - { RateLimitType.ShipmentInvoicing_GetShipmentDetails, new RateLimits(1.133M, 25) }, - { RateLimitType.ShipmentInvoicing_SubmitInvoice, new RateLimits(1.133M, 25) }, - { RateLimitType.ShipmentInvoicing_GetInvoiceStatus, new RateLimits(1.133M, 25) }, - - { RateLimitType.Shipping_CreateShipment, new RateLimits(5.0M, 15) }, - { RateLimitType.Shipping_GetShipment, new RateLimits(5.0M, 15) }, - { RateLimitType.Shipping_CancelShipment, new RateLimits(5.0M, 15) }, - { RateLimitType.Shipping_PurchaseLabels, new RateLimits(5.0M, 15) }, - { RateLimitType.Shipping_RetrieveShippingLabel, new RateLimits(5.0M, 15) }, - { RateLimitType.Shipping_PurchaseShipment, new RateLimits(5.0M, 15) }, - { RateLimitType.Shipping_GetRates, new RateLimits(5.0M, 15) }, - { RateLimitType.Shipping_GetAccount, new RateLimits(5.0M, 15) }, - { RateLimitType.Shipping_GetTrackingInformation, new RateLimits(1.0M, 1) }, - - { RateLimitType.ShippingV2_CancelShipment, new RateLimits(80.0M, 100) }, - { RateLimitType.ShippingV2_DirectPurchaseShipment, new RateLimits(80.0M, 100) }, - { RateLimitType.ShippingV2_GetAdditionalInputs, new RateLimits(80.0M, 100) }, - { RateLimitType.ShippingV2_GetRates, new RateLimits(80.0M, 100) }, - { RateLimitType.ShippingV2_GetShipmentDocument, new RateLimits(80.0M, 100) }, - { RateLimitType.ShippingV2_GetTracking, new RateLimits(80.0M, 100) }, - { RateLimitType.ShippingV2_PurchaseShipment, new RateLimits(80.0M, 100) }, - - { RateLimitType.CatalogItems_ListCatalogItems, new RateLimits(6.0M, 40) }, - { RateLimitType.CatalogItems_GetCatalogItem, new RateLimits(2.0M, 20) }, - { RateLimitType.CatalogItems_ListCatalogCategories, new RateLimits(1.0M, 40) }, - { RateLimitType.CatalogItems20220401_GetCatalogItem, new RateLimits(2.0M, 2) }, - { RateLimitType.CatalogItems20220401_SearchCatalogItems, new RateLimits(2.0M, 2) }, - - { RateLimitType.FbaInventory_GetInventorySummaries, new RateLimits(2.0M, 2) }, - - { RateLimitType.Authorization_GetAuthorizationCode, new RateLimits(1.0M, 5) }, - - { RateLimitType.FbaSmallandLight_GetSmallAndLightEnrollmentBySellerSKU, new RateLimits(2.0M, 10) }, - { RateLimitType.FbaSmallandLight_PutSmallAndLightEnrollmentBySellerSKU, new RateLimits(2.0M, 5) }, - { RateLimitType.FbaSmallandLight_DeleteSmallAndLightEnrollmentBySellerSKU, new RateLimits(2.0M, 5) }, - { RateLimitType.FbaSmallandLight_GetSmallAndLightEligibilityBySellerSKU, new RateLimits(2.0M, 10) }, - { RateLimitType.FbaSmallandLight_GetSmallAndLightFeePreview, new RateLimits(1.0M, 3) }, - - { RateLimitType.Restrictions_GetListingsRestrictions, new RateLimits(5.0M, 10) }, - - { RateLimitType.ProductTypes_GetDefinitionsProductType, new RateLimits(5.0M, 10) }, - { RateLimitType.ProductTypes_SearchDefinitionsProductTypes, new RateLimits(5.0M, 10) }, - - { RateLimitType.FBAInboundEligibility_GetItemEligibilityPreview, new RateLimits(1.0M, 1) }, - - - { RateLimitType.EasyShip_CreateScheduledPackage, new RateLimits(1.0M, 5) }, - { RateLimitType.EasyShip_GetScheduledPackage, new RateLimits(1.0M, 5) }, - { RateLimitType.EasyShip_ListHandoverSlots, new RateLimits(1.0M, 5) }, - { RateLimitType.EasyShip_UpdateScheduledPackages, new RateLimits(1.0M, 5) }, - - { RateLimitType.FulFillmentInbound_GetInboundGuidance, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentInbound_CreateInboundShipmentPlan, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentInbound_UpdateInboundShipment, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentInbound_CreateInboundShipment, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentInbound_GetPreorderInfo, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentInbound_ConfirmPreorder, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentInbound_GetPrepInstructions, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentInbound_GetTransportDetails, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentInbound_PutTransportDetails, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentInbound_VoidTransport, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentInbound_EstimateTransport, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentInbound_ConfirmTransport, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentInbound_GetLabels, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentInbound_GetBillOfLading, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentInbound_GetShipments, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentInbound_GetShipmentItemsByShipmentId,new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentInbound_GetShipmentItems, new RateLimits(2.0M, 30) }, - - { RateLimitType.FulFillmentInboundV20240320_ListInboundPlans, new RateLimits(2.0M, 6) }, - { RateLimitType.FulFillmentInboundV20240320_CreateInboundPlan, new RateLimits(2.0M, 2) }, - { RateLimitType.FulFillmentInboundV20240320_GetInboundPlan, new RateLimits(2.0M, 6) }, - { RateLimitType.FulFillmentInboundV20240320_ListInboundPlanBoxes, new RateLimits(2.0M, 6) }, - { RateLimitType.FulFillmentInboundV20240320_CancelInboundPlan, new RateLimits(2.0M, 2) }, - { RateLimitType.FulFillmentInboundV20240320_ListInboundPlanItems, new RateLimits(2.0M, 6) }, - { RateLimitType.FulFillmentInboundV20240320_UpdateInboundPlanName, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentInboundV20240320_ListPackingGroupBoxes, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentInboundV20240320_ListPackingGroupItems, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentInboundV20240320_SetPackingInformation, new RateLimits(2.0M, 2) }, - { RateLimitType.FulFillmentInboundV20240320_ListPackingOptions, new RateLimits(2.0M, 6) }, - { RateLimitType.FulFillmentInboundV20240320_GeneratePackingOptions, new RateLimits(2.0M, 2) }, - { RateLimitType.FulFillmentInboundV20240320_ConfirmPackingOption, new RateLimits(2.0M, 2) }, - { RateLimitType.FulFillmentInboundV20240320_ListInboundPlanPallets, new RateLimits(2.0M, 6) }, - { RateLimitType.FulFillmentInboundV20240320_ListPlacementOptions, new RateLimits(2.0M, 6) }, - { RateLimitType.FulFillmentInboundV20240320_GeneratePlacementOptions, new RateLimits(2.0M, 2) }, - { RateLimitType.FulFillmentInboundV20240320_ConfirmPlacementOption, new RateLimits(2.0M, 2) }, - { RateLimitType.FulFillmentInboundV20240320_GetShipment, new RateLimits(2.0M, 6) }, - { RateLimitType.FulFillmentInboundV20240320_ListShipmentBoxes, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentInboundV20240320_ListShipmentContentUpdatePreviews, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentInboundV20240320_GenerateShipmentContentUpdatePreviews,new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentInboundV20240320_GetShipmentContentUpdatePreview, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentInboundV20240320_ConfirmShipmentContentUpdatePreview, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentInboundV20240320_GetDeliveryChallanDocument, new RateLimits(2.0M, 6) }, - { RateLimitType.FulFillmentInboundV20240320_ListDeliveryWindowOptions, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentInboundV20240320_GenerateDeliveryWindowOptions, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentInboundV20240320_ConfirmDeliveryWindowOption, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentInboundV20240320_ListShipmentItems, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentInboundV20240320_UpdateShipmentName, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentInboundV20240320_ListShipmentPallets, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentInboundV20240320_CancelSelfShipAppointment, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentInboundV20240320_GetSelfShipAppointmentSlots, new RateLimits(2.0M, 6) }, - { RateLimitType.FulFillmentInboundV20240320_GenerateSelfShipAppointmentSlots, new RateLimits(2.0M, 2) }, - { RateLimitType.FulFillmentInboundV20240320_ScheduleSelfShipAppointment, new RateLimits(2.0M, 2) }, - { RateLimitType.FulFillmentInboundV20240320_UpdateShipmentSourceAddress, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentInboundV20240320_UpdateShipmentTrackingDetails, new RateLimits(2.0M, 2) }, - { RateLimitType.FulFillmentInboundV20240320_ListTransportationOptions, new RateLimits(2.0M, 6) }, - { RateLimitType.FulFillmentInboundV20240320_GenerateTransportationOptions, new RateLimits(2.0M, 2) }, - { RateLimitType.FulFillmentInboundV20240320_ConfirmTransportationOptions, new RateLimits(2.0M, 2) }, - { RateLimitType.FulFillmentInboundV20240320_ListItemComplianceDetails, new RateLimits(2.0M, 6) }, - { RateLimitType.FulFillmentInboundV20240320_UpdateItemComplianceDetails, new RateLimits(2.0M, 2) }, - { RateLimitType.FulFillmentInboundV20240320_CreateMarketplaceItemLabels, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentInboundV20240320_ListPrepDetails, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentInboundV20240320_SetPrepDetails, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentInboundV20240320_GetInboundOperationStatus, new RateLimits(2.0M, 6) }, - - - { RateLimitType.FulFillmentOutbound_GetFulfillmentPreview, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentOutbound_ListAllFulfillmentOrders, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentOutbound_CreateFulfillmentOrder, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentOutbound_GetPackageTrackingDetails, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentOutbound_ListReturnReasonCodes, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentOutbound_CreateFulfillmentReturn, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentOutbound_GetFulfillmentOrder, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentOutbound_UpdateFulfillmentOrder, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentOutbound_CancelFulfillmentOrder, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentOutbound_GetFeatures, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentOutbound_GetFeatureInventory, new RateLimits(2.0M, 30) }, - { RateLimitType.FulFillmentOutbound_GetFeatureSKU, new RateLimits(2.0M, 30) }, - - { RateLimitType.MerchantFulFillment_GetEligibleShipmentServicesOld,new RateLimits(1.0M, 1) }, - { RateLimitType.MerchantFulFillment_GetEligibleShipmentServices,new RateLimits(5.0M, 10) }, - { RateLimitType.MerchantFulFillment_GetShipment, new RateLimits(1.0M, 1) }, - { RateLimitType.MerchantFulFillment_CancelShipment, new RateLimits(1.0M, 1) }, - { RateLimitType.MerchantFulFillment_CancelShipmentOld, new RateLimits(1.0M, 1) }, - { RateLimitType.MerchantFulFillment_CreateShipment, new RateLimits(1.0M, 1) }, - { RateLimitType.MerchantFulFillment_GetAdditionalSellerInputsOld,new RateLimits(1.0M, 1) }, - { RateLimitType.MerchantFulFillment_GetAdditionalSellerInputs, new RateLimits(1.0M, 1) }, - - { RateLimitType.Messaging_GetMessagingActionsForOrder, new RateLimits(1.0M, 5) }, - { RateLimitType.Messaging_ConfirmCustomizationDetails, new RateLimits(1.0M, 5) }, - { RateLimitType.Messaging_CreateConfirmDeliveryDetails, new RateLimits(1.0M, 5) }, - { RateLimitType.Messaging_CreateLegalDisclosure, new RateLimits(1.0M, 5) }, - { RateLimitType.Messaging_CreateNegativeFeedbackRemoval, new RateLimits(1.0M, 5) }, - { RateLimitType.Messaging_CreateConfirmOrderDetails, new RateLimits(1.0M, 5) }, - { RateLimitType.Messaging_CreateConfirmServiceDetails, new RateLimits(1.0M, 5) }, - { RateLimitType.Messaging_CreateAmazonMotors, new RateLimits(1.0M, 5) }, - { RateLimitType.Messaging_CreateWarranty, new RateLimits(1.0M, 5) }, - { RateLimitType.Messaging_GetAttributes, new RateLimits(1.0M, 5) }, - { RateLimitType.Messaging_CreateDigitalAccessKey, new RateLimits(1.0M, 5) }, - { RateLimitType.Messaging_CreateUnexpectedProblem, new RateLimits(1.0M, 5) }, - - { RateLimitType.Notifications_GetSubscription, new RateLimits(1.0M, 5) }, - { RateLimitType.Notifications_CreateSubscription, new RateLimits(1.0M, 5) }, - { RateLimitType.Notifications_GetSubscriptionById, new RateLimits(1.0M, 5) }, - { RateLimitType.Notifications_DeleteSubscriptionById, new RateLimits(1.0M, 5) }, - { RateLimitType.Notifications_GetDestinations, new RateLimits(1.0M, 5) }, - { RateLimitType.Notifications_CreateDestination, new RateLimits(1.0M, 5) }, - { RateLimitType.Notifications_GetDestination, new RateLimits(1.0M, 5) }, - { RateLimitType.Notifications_DeleteDestination, new RateLimits(1.0M, 5) }, - - { RateLimitType.ProductFees_GetMyFeesEstimateForSKU, new RateLimits(1M, 2) }, - { RateLimitType.ProductFees_GetMyFeesEstimateForASIN, new RateLimits(1M, 2) }, - { RateLimitType.ProductFees_GetMyFeesEstimate, new RateLimits(0.5M, 1) }, - - { RateLimitType.ProductPricing_GetPricing, new RateLimits(0.5M, 1) }, - { RateLimitType.ProductPricing_GetCompetitivePricing, new RateLimits(0.5M, 1) }, - { RateLimitType.ProductPricing_GetListingOffers, new RateLimits(1M, 2) }, - { RateLimitType.ProductPricing_GetItemOffers, new RateLimits(0.5M, 1) }, - - { RateLimitType.ProductPricing_GetItemOffersBatch, new RateLimits(0.1M, 1) }, - { RateLimitType.ProductPricing_GetListingOffersBatch, new RateLimits(0.5M, 1) }, - - { RateLimitType.Sales_GetOrderMetrics, new RateLimits(0.5M, 15) }, - - { RateLimitType.Sellers_GetMarketplaceParticipations, new RateLimits(0.016M, 15) }, - - { RateLimitType.Solicitations_GetSolicitationActionsForOrder, new RateLimits(1.0M, 5) }, - { RateLimitType.Solicitations_CreateProductReviewAndSellerFeedbackSolicitation, new RateLimits(1.0M, 5) }, - - { RateLimitType.Token_CreateRestrictedDataToken, new RateLimits(1.0M, 10) }, - - { RateLimitType.VendorDirectFulfillmentOrdersV1_GetOrders, new RateLimits(10.0M, 10) }, - { RateLimitType.VendorDirectFulfillmentOrdersV1_GetOrder, new RateLimits(10.0M, 10) }, - { RateLimitType.VendorDirectFulfillmentOrdersV1_SubmitAcknowledgement, new RateLimits(10.0M, 10) }, - - { RateLimitType.VendorOrdersV1_GetPurchaseOrders, new RateLimits(10.0M, 10) }, - { RateLimitType.VendorOrdersV1_GetPurchaseOrder, new RateLimits(10.0M, 10) }, - { RateLimitType.VendorOrdersV1_SubmitAcknowledgement, new RateLimits(10.0M, 10) }, - { RateLimitType.VendorOrdersV1_GetPurchaseOrdersStatus, new RateLimits(10.0M, 10) }, - - { RateLimitType.VendorTransactionStatus_GetTransaction, new RateLimits(10.0M, 10) }, - }; + new RateLimits(1.0M, 5, RateLimitType.AppIntegrationsV20240401_CreateNotification), + new RateLimits(1.0M, 5, RateLimitType.AppIntegrationsV20240401_DeleteNotifications), + new RateLimits(1.0M, 5, RateLimitType.AppIntegrationsV20240401_RecordActionFeedback), + new RateLimits(0.0167M, 20, RateLimitType.Order_GetOrder), + new RateLimits(0.0167M, 20, RateLimitType.Order_GetOrders), + new RateLimits(0.0167M, 20, RateLimitType.Order_GetOrderBuyerInfo), + new RateLimits(0.0167M, 20, RateLimitType.Order_GetOrderAddress), + new RateLimits(0.5M, 20, RateLimitType.Order_GetOrderItems), + new RateLimits(0.5M, 20, RateLimitType.Order_GetOrderItemsBuyerInfo), + new RateLimits(5M, 15, RateLimitType.Order_UpdateShipmentStatus), + new RateLimits(0.5M, 15, RateLimitType.Order_GetOrderRegulatedInfo), + new RateLimits(0.5M, 30, RateLimitType.Order_UpdateVerificationStatus), + new RateLimits(5M, 15, RateLimitType.Order_UpdateOrderItemsApprovals),new RateLimits(2M, 10, RateLimitType.Order_ShipmentConfirmation), + + new RateLimits(0.0222M, 10, RateLimitType.Report_GetReports), + new RateLimits(2.0M, 15, RateLimitType.Report_GetReport), + new RateLimits(0.0167M, 15, RateLimitType.Report_CreateReport), + new RateLimits(0.0222M, 10, RateLimitType.Report_CancelReport), + new RateLimits(0.0222M, 10, RateLimitType.Report_GetReportSchedules), + new RateLimits(0.0222M, 10, RateLimitType.Report_CreateReportSchedule), + new RateLimits(0.0222M, 10, RateLimitType.Report_GetReportSchedule), + new RateLimits(0.0222M, 10, RateLimitType.Report_CancelReportSchedule), + new RateLimits(0.0167M, 15, RateLimitType.Report_GetReportDocument), + + new RateLimits(0.5M, 30, RateLimitType.Financial_ListFinancialEventGroups), + new RateLimits(0.5M, 30, RateLimitType.Financial_ListFinancialEventsByGroupId), + new RateLimits(0.5M, 30, RateLimitType.Financial_ListFinancialEventsByOrderId), + new RateLimits(0.5M, 30, RateLimitType.Financial_ListFinancialEvents), + + new RateLimits(0.0222M, 10, RateLimitType.Feed_GetFeeds), + new RateLimits(0.0083M, 15, RateLimitType.Feed_CreateFeed), + new RateLimits(2.0M, 15, RateLimitType.Feed_GetFeed), + new RateLimits(0.0222M, 10, RateLimitType.Feed_CancelFeed), + new RateLimits(0.0083M, 15, RateLimitType.Feed_CreateFeedDocument), + new RateLimits(0.0222M, 10, RateLimitType.Feed_GetFeedDocument), + + new RateLimits(5.0M, 10, RateLimitType.ListingsItem_GetListingsItem), + new RateLimits(5.0M, 10, RateLimitType.ListingsItem_PutListingsItem), + new RateLimits(5.0M, 10, RateLimitType.ListingsItem_DeleteListingsItem), + new RateLimits(5.0M, 10, RateLimitType.ListingsItem_PatchListingsItem), + + new RateLimits(0.1M, 5, RateLimitType.Upload_CreateUploadDestinationForResource), + + new RateLimits(1.133M, 25, RateLimitType.ShipmentInvoicing_GetShipmentDetails), + new RateLimits(1.133M, 25, RateLimitType.ShipmentInvoicing_SubmitInvoice), + new RateLimits(1.133M, 25, RateLimitType.ShipmentInvoicing_GetInvoiceStatus), + + new RateLimits(5.0M, 15, RateLimitType.Shipping_CreateShipment), + new RateLimits(5.0M, 15, RateLimitType.Shipping_GetShipment), + new RateLimits(5.0M, 15, RateLimitType.Shipping_CancelShipment), + new RateLimits(5.0M, 15, RateLimitType.Shipping_PurchaseLabels), + new RateLimits(5.0M, 15, RateLimitType.Shipping_RetrieveShippingLabel), + new RateLimits(5.0M, 15, RateLimitType.Shipping_PurchaseShipment), + new RateLimits(5.0M, 15, RateLimitType.Shipping_GetRates), + new RateLimits(5.0M, 15, RateLimitType.Shipping_GetAccount), + new RateLimits(1.0M, 1, RateLimitType.Shipping_GetTrackingInformation), + + new RateLimits(80.0M, 100, RateLimitType.ShippingV2_CancelShipment), + new RateLimits(80.0M, 100, RateLimitType.ShippingV2_DirectPurchaseShipment), + new RateLimits(80.0M, 100, RateLimitType.ShippingV2_GetAdditionalInputs), + new RateLimits(80.0M, 100, RateLimitType.ShippingV2_GetRates), + new RateLimits(80.0M, 100, RateLimitType.ShippingV2_GetShipmentDocument), + new RateLimits(80.0M, 100, RateLimitType.ShippingV2_GetTracking), + new RateLimits(80.0M, 100, RateLimitType.ShippingV2_PurchaseShipment), + + new RateLimits(6.0M, 40, RateLimitType.CatalogItems_ListCatalogItems), + new RateLimits(2.0M, 20, RateLimitType.CatalogItems_GetCatalogItem), + new RateLimits(1.0M, 40, RateLimitType.CatalogItems_ListCatalogCategories), + new RateLimits(2.0M, 2, RateLimitType.CatalogItems20220401_GetCatalogItem), + new RateLimits(2.0M, 2, RateLimitType.CatalogItems20220401_SearchCatalogItems), + + new RateLimits(2.0M, 2, RateLimitType.FbaInventory_GetInventorySummaries), + + new RateLimits(1.0M, 5, RateLimitType.Authorization_GetAuthorizationCode), + + new RateLimits(2.0M, 10, RateLimitType.FbaSmallandLight_GetSmallAndLightEnrollmentBySellerSKU), + new RateLimits(2.0M, 5, RateLimitType.FbaSmallandLight_PutSmallAndLightEnrollmentBySellerSKU), + new RateLimits(2.0M, 5, RateLimitType.FbaSmallandLight_DeleteSmallAndLightEnrollmentBySellerSKU), + new RateLimits(2.0M, 10, RateLimitType.FbaSmallandLight_GetSmallAndLightEligibilityBySellerSKU), + new RateLimits(1.0M, 3, RateLimitType.FbaSmallandLight_GetSmallAndLightFeePreview), + + new RateLimits(5.0M, 10, RateLimitType.Restrictions_GetListingsRestrictions), + + new RateLimits(5.0M, 10, RateLimitType.ProductTypes_GetDefinitionsProductType), + new RateLimits(5.0M, 10, RateLimitType.ProductTypes_SearchDefinitionsProductTypes), + + new RateLimits(1.0M, 1, RateLimitType.FBAInboundEligibility_GetItemEligibilityPreview), + + + new RateLimits(1.0M, 5, RateLimitType.EasyShip_CreateScheduledPackage), + new RateLimits(1.0M, 5, RateLimitType.EasyShip_GetScheduledPackage), + new RateLimits(1.0M, 5, RateLimitType.EasyShip_ListHandoverSlots), + new RateLimits(1.0M, 5, RateLimitType.EasyShip_UpdateScheduledPackages), + + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_GetInboundGuidance), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_CreateInboundShipmentPlan), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_UpdateInboundShipment), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_CreateInboundShipment), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_GetPreorderInfo), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_ConfirmPreorder), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_GetPrepInstructions), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_GetTransportDetails), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_PutTransportDetails), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_VoidTransport), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_EstimateTransport), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_ConfirmTransport), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_GetLabels), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_GetBillOfLading), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_GetShipments), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_GetShipmentItemsByShipmentId), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_GetShipmentItems), + + new RateLimits(2.0M, 6, RateLimitType.FulFillmentInboundV20240320_ListInboundPlans), + new RateLimits(2.0M, 2, RateLimitType.FulFillmentInboundV20240320_CreateInboundPlan), + new RateLimits(2.0M, 6, RateLimitType.FulFillmentInboundV20240320_GetInboundPlan), + new RateLimits(2.0M, 6, RateLimitType.FulFillmentInboundV20240320_ListInboundPlanBoxes), + new RateLimits(2.0M, 2, RateLimitType.FulFillmentInboundV20240320_CancelInboundPlan), + new RateLimits(2.0M, 6, RateLimitType.FulFillmentInboundV20240320_ListInboundPlanItems), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_UpdateInboundPlanName), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_ListPackingGroupBoxes), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_ListPackingGroupItems), + new RateLimits(2.0M, 2, RateLimitType.FulFillmentInboundV20240320_SetPackingInformation), + new RateLimits(2.0M, 6, RateLimitType.FulFillmentInboundV20240320_ListPackingOptions), + new RateLimits(2.0M, 2, RateLimitType.FulFillmentInboundV20240320_GeneratePackingOptions), + new RateLimits(2.0M, 2, RateLimitType.FulFillmentInboundV20240320_ConfirmPackingOption), + new RateLimits(2.0M, 6, RateLimitType.FulFillmentInboundV20240320_ListInboundPlanPallets), + new RateLimits(2.0M, 6, RateLimitType.FulFillmentInboundV20240320_ListPlacementOptions), + new RateLimits(2.0M, 2, RateLimitType.FulFillmentInboundV20240320_GeneratePlacementOptions), + new RateLimits(2.0M, 2, RateLimitType.FulFillmentInboundV20240320_ConfirmPlacementOption), + new RateLimits(2.0M, 6, RateLimitType.FulFillmentInboundV20240320_GetShipment), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_ListShipmentBoxes), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_ListShipmentContentUpdatePreviews), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_GenerateShipmentContentUpdatePreviews), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_GetShipmentContentUpdatePreview), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_ConfirmShipmentContentUpdatePreview), + new RateLimits(2.0M, 6, RateLimitType.FulFillmentInboundV20240320_GetDeliveryChallanDocument), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_ListDeliveryWindowOptions), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_GenerateDeliveryWindowOptions), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_ConfirmDeliveryWindowOption), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_ListShipmentItems), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_UpdateShipmentName), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_ListShipmentPallets), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_CancelSelfShipAppointment), + new RateLimits(2.0M, 6, RateLimitType.FulFillmentInboundV20240320_GetSelfShipAppointmentSlots), + new RateLimits(2.0M, 2, RateLimitType.FulFillmentInboundV20240320_GenerateSelfShipAppointmentSlots), + new RateLimits(2.0M, 2, RateLimitType.FulFillmentInboundV20240320_ScheduleSelfShipAppointment), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_UpdateShipmentSourceAddress), + new RateLimits(2.0M, 2, RateLimitType.FulFillmentInboundV20240320_UpdateShipmentTrackingDetails), + new RateLimits(2.0M, 6, RateLimitType.FulFillmentInboundV20240320_ListTransportationOptions), + new RateLimits(2.0M, 2, RateLimitType.FulFillmentInboundV20240320_GenerateTransportationOptions), + new RateLimits(2.0M, 2, RateLimitType.FulFillmentInboundV20240320_ConfirmTransportationOptions), + new RateLimits(2.0M, 6, RateLimitType.FulFillmentInboundV20240320_ListItemComplianceDetails), + new RateLimits(2.0M, 2, RateLimitType.FulFillmentInboundV20240320_UpdateItemComplianceDetails), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_CreateMarketplaceItemLabels), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_ListPrepDetails), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_SetPrepDetails), + new RateLimits(2.0M, 6, RateLimitType.FulFillmentInboundV20240320_GetInboundOperationStatus), + + + new RateLimits(2.0M, 30, RateLimitType.FulFillmentOutbound_GetFulfillmentPreview), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentOutbound_ListAllFulfillmentOrders), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentOutbound_CreateFulfillmentOrder), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentOutbound_GetPackageTrackingDetails), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentOutbound_ListReturnReasonCodes), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentOutbound_CreateFulfillmentReturn), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentOutbound_GetFulfillmentOrder), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentOutbound_UpdateFulfillmentOrder), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentOutbound_CancelFulfillmentOrder), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentOutbound_GetFeatures), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentOutbound_GetFeatureInventory), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentOutbound_GetFeatureSKU), + + new RateLimits(1.0M, 1, RateLimitType.MerchantFulFillment_GetEligibleShipmentServicesOld), + new RateLimits(5.0M, 10, RateLimitType.MerchantFulFillment_GetEligibleShipmentServices), + new RateLimits(1.0M, 1, RateLimitType.MerchantFulFillment_GetShipment), + new RateLimits(1.0M, 1, RateLimitType.MerchantFulFillment_CancelShipment), + new RateLimits(1.0M, 1, RateLimitType.MerchantFulFillment_CancelShipmentOld), + new RateLimits(1.0M, 1, RateLimitType.MerchantFulFillment_CreateShipment), + new RateLimits(1.0M, 1, RateLimitType.MerchantFulFillment_GetAdditionalSellerInputsOld), + new RateLimits(1.0M, 1, RateLimitType.MerchantFulFillment_GetAdditionalSellerInputs), + + new RateLimits(1.0M, 5, RateLimitType.Messaging_GetMessagingActionsForOrder), + new RateLimits(1.0M, 5, RateLimitType.Messaging_ConfirmCustomizationDetails), + new RateLimits(1.0M, 5, RateLimitType.Messaging_CreateConfirmDeliveryDetails), + new RateLimits(1.0M, 5, RateLimitType.Messaging_CreateLegalDisclosure), + new RateLimits(1.0M, 5, RateLimitType.Messaging_CreateNegativeFeedbackRemoval), + new RateLimits(1.0M, 5, RateLimitType.Messaging_CreateConfirmOrderDetails), + new RateLimits(1.0M, 5, RateLimitType.Messaging_CreateConfirmServiceDetails), + new RateLimits(1.0M, 5, RateLimitType.Messaging_CreateAmazonMotors), + new RateLimits(1.0M, 5, RateLimitType.Messaging_CreateWarranty), + new RateLimits(1.0M, 5, RateLimitType.Messaging_GetAttributes), + new RateLimits(1.0M, 5, RateLimitType.Messaging_CreateDigitalAccessKey), + new RateLimits(1.0M, 5, RateLimitType.Messaging_CreateUnexpectedProblem), + + new RateLimits(1.0M, 5, RateLimitType.Notifications_GetSubscription), + new RateLimits(1.0M, 5, RateLimitType.Notifications_CreateSubscription), + new RateLimits(1.0M, 5, RateLimitType.Notifications_GetSubscriptionById), + new RateLimits(1.0M, 5, RateLimitType.Notifications_DeleteSubscriptionById), + new RateLimits(1.0M, 5, RateLimitType.Notifications_GetDestinations), + new RateLimits(1.0M, 5, RateLimitType.Notifications_CreateDestination), + new RateLimits(1.0M, 5, RateLimitType.Notifications_GetDestination), + new RateLimits(1.0M, 5, RateLimitType.Notifications_DeleteDestination), + + new RateLimits(1M, 2, RateLimitType.ProductFees_GetMyFeesEstimateForSKU), + new RateLimits(1M, 2, RateLimitType.ProductFees_GetMyFeesEstimateForASIN), + new RateLimits(0.5M, 1, RateLimitType.ProductFees_GetMyFeesEstimate), + + new RateLimits(0.5M, 1, RateLimitType.ProductPricing_GetPricing), + new RateLimits(0.5M, 1, RateLimitType.ProductPricing_GetCompetitivePricing), + new RateLimits(1M, 2, RateLimitType.ProductPricing_GetListingOffers), + new RateLimits(0.5M, 1, RateLimitType.ProductPricing_GetItemOffers), + + new RateLimits(0.1M, 1, RateLimitType.ProductPricing_GetItemOffersBatch), + new RateLimits(0.5M, 1, RateLimitType.ProductPricing_GetListingOffersBatch), + + new RateLimits(0.5M, 15, RateLimitType.Sales_GetOrderMetrics), + + new RateLimits(0.016M, 15, RateLimitType.Sellers_GetMarketplaceParticipations), + + new RateLimits(1.0M, 5, RateLimitType.Solicitations_GetSolicitationActionsForOrder), + new RateLimits(1.0M, 5, RateLimitType.Solicitations_CreateProductReviewAndSellerFeedbackSolicitation), + + new RateLimits(1.0M, 10, RateLimitType.Token_CreateRestrictedDataToken), + + new RateLimits(10.0M, 10, RateLimitType.VendorDirectFulfillmentOrdersV1_GetOrders), + new RateLimits(10.0M, 10, RateLimitType.VendorDirectFulfillmentOrdersV1_GetOrder), + new RateLimits(10.0M, 10, RateLimitType.VendorDirectFulfillmentOrdersV1_SubmitAcknowledgement), + + new RateLimits(10.0M, 10, RateLimitType.VendorOrdersV1_GetPurchaseOrders), + new RateLimits(10.0M, 10, RateLimitType.VendorOrdersV1_GetPurchaseOrder), + new RateLimits(10.0M, 10, RateLimitType.VendorOrdersV1_SubmitAcknowledgement), + new RateLimits(10.0M, 10, RateLimitType.VendorOrdersV1_GetPurchaseOrdersStatus), + + new RateLimits(10.0M, 10, RateLimitType.VendorTransactionStatus_GetTransaction), + + }.ToDictionary(x => x.RateLimitType); } } } From da078cae3e2db08c23786d10d52ad817ae2a2da4 Mon Sep 17 00:00:00 2001 From: Finn Reilly Date: Thu, 5 Dec 2024 12:26:38 +0000 Subject: [PATCH 09/17] No need to pass RateLimitType into wait method now * tidy up default rate limit definitions --- .../FikaAmazonAPI/Services/RequestService.cs | 2 +- .../Utils/RateLimitingHandler.cs | 2 +- Source/FikaAmazonAPI/Utils/RateLimits.cs | 4 +- .../Utils/RateLimitsDefinitions.cs | 440 +++++++++--------- 4 files changed, 221 insertions(+), 227 deletions(-) diff --git a/Source/FikaAmazonAPI/Services/RequestService.cs b/Source/FikaAmazonAPI/Services/RequestService.cs index 833932d3..a1395e49 100644 --- a/Source/FikaAmazonAPI/Services/RequestService.cs +++ b/Source/FikaAmazonAPI/Services/RequestService.cs @@ -238,7 +238,7 @@ public async Task ExecuteRequestAsync(RateLimitType rateLimitType = RateLi cancellationToken.ThrowIfCancellationRequested(); - await AmazonCredential.UsagePlansTimings[rateLimitType].WaitForPermittedRequest(rateLimitType, cancellationToken, AmazonCredential.IsDebugMode); + await AmazonCredential.UsagePlansTimings[rateLimitType].WaitForPermittedRequest(cancellationToken, AmazonCredential.IsDebugMode); tryCount++; } } diff --git a/Source/FikaAmazonAPI/Utils/RateLimitingHandler.cs b/Source/FikaAmazonAPI/Utils/RateLimitingHandler.cs index 64022050..249e9f76 100644 --- a/Source/FikaAmazonAPI/Utils/RateLimitingHandler.cs +++ b/Source/FikaAmazonAPI/Utils/RateLimitingHandler.cs @@ -63,7 +63,7 @@ private async Task SleepForRateLimit( planTimings.SetRateLimit(updatedLimitRate); } - await planTimings.WaitForPermittedRequest(rateLimitType); + await planTimings.WaitForPermittedRequest(cancellationToken); } } } diff --git a/Source/FikaAmazonAPI/Utils/RateLimits.cs b/Source/FikaAmazonAPI/Utils/RateLimits.cs index 4e888b29..169ebe70 100644 --- a/Source/FikaAmazonAPI/Utils/RateLimits.cs +++ b/Source/FikaAmazonAPI/Utils/RateLimits.cs @@ -37,7 +37,7 @@ internal RateLimits(decimal Rate, int Burst, RateLimitType type) /// An enum representing the rate limit policy for the resource in use /// The cancellation token /// - public async Task WaitForPermittedRequest(RateLimitType rateLimitType, CancellationToken cancellationToken = default, bool debugMode = true) + public async Task WaitForPermittedRequest(CancellationToken cancellationToken = default, bool debugMode = true) { if (RequestsSent < 0) { @@ -50,7 +50,7 @@ public async Task WaitForPermittedRequest(RateLimitType rateLimitType, Cancellat var nextRequestsSentTxt = (nextRequestsSent > Burst) ? "FULL" : nextRequestsSent.ToString(); if (debugMode) { - string output = $"[RateLimits ,{rateLimitType,15}]: {DateTime.UtcNow.ToString(),10}\t Request/Burst: {nextRequestsSentTxt}/{Burst}\t Rate: {Rate}/{ratePeriodMs}ms"; + string output = $"[RateLimits ,{this.RateLimitType,15}]: {DateTime.UtcNow.ToString(),10}\t Request/Burst: {nextRequestsSentTxt}/{Burst}\t Rate: {Rate}/{ratePeriodMs}ms"; Console.WriteLine(output); } diff --git a/Source/FikaAmazonAPI/Utils/RateLimitsDefinitions.cs b/Source/FikaAmazonAPI/Utils/RateLimitsDefinitions.cs index 08bbb595..0e076c87 100644 --- a/Source/FikaAmazonAPI/Utils/RateLimitsDefinitions.cs +++ b/Source/FikaAmazonAPI/Utils/RateLimitsDefinitions.cs @@ -27,7 +27,7 @@ internal static ConcurrentDictionary RateLimitsTimeFo private static Dictionary RateLimitsTime() { //This has to create a new list for each connection, so that rate limits are per seller, not overall. - return new List + return new List { new RateLimits(1.0M, 5, RateLimitType.AppIntegrationsV20240401_CreateNotification), new RateLimits(1.0M, 5, RateLimitType.AppIntegrationsV20240401_DeleteNotifications), @@ -41,228 +41,222 @@ private static Dictionary RateLimitsTime() new RateLimits(5M, 15, RateLimitType.Order_UpdateShipmentStatus), new RateLimits(0.5M, 15, RateLimitType.Order_GetOrderRegulatedInfo), new RateLimits(0.5M, 30, RateLimitType.Order_UpdateVerificationStatus), - new RateLimits(5M, 15, RateLimitType.Order_UpdateOrderItemsApprovals),new RateLimits(2M, 10, RateLimitType.Order_ShipmentConfirmation), - - new RateLimits(0.0222M, 10, RateLimitType.Report_GetReports), - new RateLimits(2.0M, 15, RateLimitType.Report_GetReport), - new RateLimits(0.0167M, 15, RateLimitType.Report_CreateReport), - new RateLimits(0.0222M, 10, RateLimitType.Report_CancelReport), - new RateLimits(0.0222M, 10, RateLimitType.Report_GetReportSchedules), - new RateLimits(0.0222M, 10, RateLimitType.Report_CreateReportSchedule), - new RateLimits(0.0222M, 10, RateLimitType.Report_GetReportSchedule), - new RateLimits(0.0222M, 10, RateLimitType.Report_CancelReportSchedule), - new RateLimits(0.0167M, 15, RateLimitType.Report_GetReportDocument), - - new RateLimits(0.5M, 30, RateLimitType.Financial_ListFinancialEventGroups), - new RateLimits(0.5M, 30, RateLimitType.Financial_ListFinancialEventsByGroupId), - new RateLimits(0.5M, 30, RateLimitType.Financial_ListFinancialEventsByOrderId), - new RateLimits(0.5M, 30, RateLimitType.Financial_ListFinancialEvents), - - new RateLimits(0.0222M, 10, RateLimitType.Feed_GetFeeds), - new RateLimits(0.0083M, 15, RateLimitType.Feed_CreateFeed), - new RateLimits(2.0M, 15, RateLimitType.Feed_GetFeed), - new RateLimits(0.0222M, 10, RateLimitType.Feed_CancelFeed), - new RateLimits(0.0083M, 15, RateLimitType.Feed_CreateFeedDocument), - new RateLimits(0.0222M, 10, RateLimitType.Feed_GetFeedDocument), - - new RateLimits(5.0M, 10, RateLimitType.ListingsItem_GetListingsItem), - new RateLimits(5.0M, 10, RateLimitType.ListingsItem_PutListingsItem), - new RateLimits(5.0M, 10, RateLimitType.ListingsItem_DeleteListingsItem), - new RateLimits(5.0M, 10, RateLimitType.ListingsItem_PatchListingsItem), - - new RateLimits(0.1M, 5, RateLimitType.Upload_CreateUploadDestinationForResource), - - new RateLimits(1.133M, 25, RateLimitType.ShipmentInvoicing_GetShipmentDetails), - new RateLimits(1.133M, 25, RateLimitType.ShipmentInvoicing_SubmitInvoice), - new RateLimits(1.133M, 25, RateLimitType.ShipmentInvoicing_GetInvoiceStatus), - - new RateLimits(5.0M, 15, RateLimitType.Shipping_CreateShipment), - new RateLimits(5.0M, 15, RateLimitType.Shipping_GetShipment), - new RateLimits(5.0M, 15, RateLimitType.Shipping_CancelShipment), - new RateLimits(5.0M, 15, RateLimitType.Shipping_PurchaseLabels), - new RateLimits(5.0M, 15, RateLimitType.Shipping_RetrieveShippingLabel), - new RateLimits(5.0M, 15, RateLimitType.Shipping_PurchaseShipment), - new RateLimits(5.0M, 15, RateLimitType.Shipping_GetRates), - new RateLimits(5.0M, 15, RateLimitType.Shipping_GetAccount), - new RateLimits(1.0M, 1, RateLimitType.Shipping_GetTrackingInformation), - - new RateLimits(80.0M, 100, RateLimitType.ShippingV2_CancelShipment), - new RateLimits(80.0M, 100, RateLimitType.ShippingV2_DirectPurchaseShipment), - new RateLimits(80.0M, 100, RateLimitType.ShippingV2_GetAdditionalInputs), - new RateLimits(80.0M, 100, RateLimitType.ShippingV2_GetRates), - new RateLimits(80.0M, 100, RateLimitType.ShippingV2_GetShipmentDocument), - new RateLimits(80.0M, 100, RateLimitType.ShippingV2_GetTracking), - new RateLimits(80.0M, 100, RateLimitType.ShippingV2_PurchaseShipment), - - new RateLimits(6.0M, 40, RateLimitType.CatalogItems_ListCatalogItems), - new RateLimits(2.0M, 20, RateLimitType.CatalogItems_GetCatalogItem), - new RateLimits(1.0M, 40, RateLimitType.CatalogItems_ListCatalogCategories), - new RateLimits(2.0M, 2, RateLimitType.CatalogItems20220401_GetCatalogItem), - new RateLimits(2.0M, 2, RateLimitType.CatalogItems20220401_SearchCatalogItems), - - new RateLimits(2.0M, 2, RateLimitType.FbaInventory_GetInventorySummaries), - - new RateLimits(1.0M, 5, RateLimitType.Authorization_GetAuthorizationCode), - - new RateLimits(2.0M, 10, RateLimitType.FbaSmallandLight_GetSmallAndLightEnrollmentBySellerSKU), - new RateLimits(2.0M, 5, RateLimitType.FbaSmallandLight_PutSmallAndLightEnrollmentBySellerSKU), - new RateLimits(2.0M, 5, RateLimitType.FbaSmallandLight_DeleteSmallAndLightEnrollmentBySellerSKU), - new RateLimits(2.0M, 10, RateLimitType.FbaSmallandLight_GetSmallAndLightEligibilityBySellerSKU), - new RateLimits(1.0M, 3, RateLimitType.FbaSmallandLight_GetSmallAndLightFeePreview), - - new RateLimits(5.0M, 10, RateLimitType.Restrictions_GetListingsRestrictions), - - new RateLimits(5.0M, 10, RateLimitType.ProductTypes_GetDefinitionsProductType), - new RateLimits(5.0M, 10, RateLimitType.ProductTypes_SearchDefinitionsProductTypes), - - new RateLimits(1.0M, 1, RateLimitType.FBAInboundEligibility_GetItemEligibilityPreview), - - - new RateLimits(1.0M, 5, RateLimitType.EasyShip_CreateScheduledPackage), - new RateLimits(1.0M, 5, RateLimitType.EasyShip_GetScheduledPackage), - new RateLimits(1.0M, 5, RateLimitType.EasyShip_ListHandoverSlots), - new RateLimits(1.0M, 5, RateLimitType.EasyShip_UpdateScheduledPackages), - - new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_GetInboundGuidance), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_CreateInboundShipmentPlan), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_UpdateInboundShipment), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_CreateInboundShipment), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_GetPreorderInfo), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_ConfirmPreorder), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_GetPrepInstructions), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_GetTransportDetails), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_PutTransportDetails), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_VoidTransport), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_EstimateTransport), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_ConfirmTransport), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_GetLabels), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_GetBillOfLading), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_GetShipments), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_GetShipmentItemsByShipmentId), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_GetShipmentItems), - - new RateLimits(2.0M, 6, RateLimitType.FulFillmentInboundV20240320_ListInboundPlans), - new RateLimits(2.0M, 2, RateLimitType.FulFillmentInboundV20240320_CreateInboundPlan), - new RateLimits(2.0M, 6, RateLimitType.FulFillmentInboundV20240320_GetInboundPlan), - new RateLimits(2.0M, 6, RateLimitType.FulFillmentInboundV20240320_ListInboundPlanBoxes), - new RateLimits(2.0M, 2, RateLimitType.FulFillmentInboundV20240320_CancelInboundPlan), - new RateLimits(2.0M, 6, RateLimitType.FulFillmentInboundV20240320_ListInboundPlanItems), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_UpdateInboundPlanName), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_ListPackingGroupBoxes), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_ListPackingGroupItems), - new RateLimits(2.0M, 2, RateLimitType.FulFillmentInboundV20240320_SetPackingInformation), - new RateLimits(2.0M, 6, RateLimitType.FulFillmentInboundV20240320_ListPackingOptions), - new RateLimits(2.0M, 2, RateLimitType.FulFillmentInboundV20240320_GeneratePackingOptions), - new RateLimits(2.0M, 2, RateLimitType.FulFillmentInboundV20240320_ConfirmPackingOption), - new RateLimits(2.0M, 6, RateLimitType.FulFillmentInboundV20240320_ListInboundPlanPallets), - new RateLimits(2.0M, 6, RateLimitType.FulFillmentInboundV20240320_ListPlacementOptions), - new RateLimits(2.0M, 2, RateLimitType.FulFillmentInboundV20240320_GeneratePlacementOptions), - new RateLimits(2.0M, 2, RateLimitType.FulFillmentInboundV20240320_ConfirmPlacementOption), - new RateLimits(2.0M, 6, RateLimitType.FulFillmentInboundV20240320_GetShipment), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_ListShipmentBoxes), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_ListShipmentContentUpdatePreviews), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_GenerateShipmentContentUpdatePreviews), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_GetShipmentContentUpdatePreview), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_ConfirmShipmentContentUpdatePreview), - new RateLimits(2.0M, 6, RateLimitType.FulFillmentInboundV20240320_GetDeliveryChallanDocument), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_ListDeliveryWindowOptions), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_GenerateDeliveryWindowOptions), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_ConfirmDeliveryWindowOption), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_ListShipmentItems), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_UpdateShipmentName), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_ListShipmentPallets), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_CancelSelfShipAppointment), - new RateLimits(2.0M, 6, RateLimitType.FulFillmentInboundV20240320_GetSelfShipAppointmentSlots), - new RateLimits(2.0M, 2, RateLimitType.FulFillmentInboundV20240320_GenerateSelfShipAppointmentSlots), - new RateLimits(2.0M, 2, RateLimitType.FulFillmentInboundV20240320_ScheduleSelfShipAppointment), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_UpdateShipmentSourceAddress), - new RateLimits(2.0M, 2, RateLimitType.FulFillmentInboundV20240320_UpdateShipmentTrackingDetails), - new RateLimits(2.0M, 6, RateLimitType.FulFillmentInboundV20240320_ListTransportationOptions), - new RateLimits(2.0M, 2, RateLimitType.FulFillmentInboundV20240320_GenerateTransportationOptions), - new RateLimits(2.0M, 2, RateLimitType.FulFillmentInboundV20240320_ConfirmTransportationOptions), - new RateLimits(2.0M, 6, RateLimitType.FulFillmentInboundV20240320_ListItemComplianceDetails), - new RateLimits(2.0M, 2, RateLimitType.FulFillmentInboundV20240320_UpdateItemComplianceDetails), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_CreateMarketplaceItemLabels), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_ListPrepDetails), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_SetPrepDetails), - new RateLimits(2.0M, 6, RateLimitType.FulFillmentInboundV20240320_GetInboundOperationStatus), - - - new RateLimits(2.0M, 30, RateLimitType.FulFillmentOutbound_GetFulfillmentPreview), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentOutbound_ListAllFulfillmentOrders), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentOutbound_CreateFulfillmentOrder), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentOutbound_GetPackageTrackingDetails), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentOutbound_ListReturnReasonCodes), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentOutbound_CreateFulfillmentReturn), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentOutbound_GetFulfillmentOrder), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentOutbound_UpdateFulfillmentOrder), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentOutbound_CancelFulfillmentOrder), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentOutbound_GetFeatures), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentOutbound_GetFeatureInventory), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentOutbound_GetFeatureSKU), - - new RateLimits(1.0M, 1, RateLimitType.MerchantFulFillment_GetEligibleShipmentServicesOld), - new RateLimits(5.0M, 10, RateLimitType.MerchantFulFillment_GetEligibleShipmentServices), - new RateLimits(1.0M, 1, RateLimitType.MerchantFulFillment_GetShipment), - new RateLimits(1.0M, 1, RateLimitType.MerchantFulFillment_CancelShipment), - new RateLimits(1.0M, 1, RateLimitType.MerchantFulFillment_CancelShipmentOld), - new RateLimits(1.0M, 1, RateLimitType.MerchantFulFillment_CreateShipment), - new RateLimits(1.0M, 1, RateLimitType.MerchantFulFillment_GetAdditionalSellerInputsOld), - new RateLimits(1.0M, 1, RateLimitType.MerchantFulFillment_GetAdditionalSellerInputs), - - new RateLimits(1.0M, 5, RateLimitType.Messaging_GetMessagingActionsForOrder), - new RateLimits(1.0M, 5, RateLimitType.Messaging_ConfirmCustomizationDetails), - new RateLimits(1.0M, 5, RateLimitType.Messaging_CreateConfirmDeliveryDetails), - new RateLimits(1.0M, 5, RateLimitType.Messaging_CreateLegalDisclosure), - new RateLimits(1.0M, 5, RateLimitType.Messaging_CreateNegativeFeedbackRemoval), - new RateLimits(1.0M, 5, RateLimitType.Messaging_CreateConfirmOrderDetails), - new RateLimits(1.0M, 5, RateLimitType.Messaging_CreateConfirmServiceDetails), - new RateLimits(1.0M, 5, RateLimitType.Messaging_CreateAmazonMotors), - new RateLimits(1.0M, 5, RateLimitType.Messaging_CreateWarranty), - new RateLimits(1.0M, 5, RateLimitType.Messaging_GetAttributes), - new RateLimits(1.0M, 5, RateLimitType.Messaging_CreateDigitalAccessKey), - new RateLimits(1.0M, 5, RateLimitType.Messaging_CreateUnexpectedProblem), - - new RateLimits(1.0M, 5, RateLimitType.Notifications_GetSubscription), - new RateLimits(1.0M, 5, RateLimitType.Notifications_CreateSubscription), - new RateLimits(1.0M, 5, RateLimitType.Notifications_GetSubscriptionById), - new RateLimits(1.0M, 5, RateLimitType.Notifications_DeleteSubscriptionById), - new RateLimits(1.0M, 5, RateLimitType.Notifications_GetDestinations), - new RateLimits(1.0M, 5, RateLimitType.Notifications_CreateDestination), - new RateLimits(1.0M, 5, RateLimitType.Notifications_GetDestination), - new RateLimits(1.0M, 5, RateLimitType.Notifications_DeleteDestination), - - new RateLimits(1M, 2, RateLimitType.ProductFees_GetMyFeesEstimateForSKU), - new RateLimits(1M, 2, RateLimitType.ProductFees_GetMyFeesEstimateForASIN), - new RateLimits(0.5M, 1, RateLimitType.ProductFees_GetMyFeesEstimate), - - new RateLimits(0.5M, 1, RateLimitType.ProductPricing_GetPricing), - new RateLimits(0.5M, 1, RateLimitType.ProductPricing_GetCompetitivePricing), - new RateLimits(1M, 2, RateLimitType.ProductPricing_GetListingOffers), - new RateLimits(0.5M, 1, RateLimitType.ProductPricing_GetItemOffers), - - new RateLimits(0.1M, 1, RateLimitType.ProductPricing_GetItemOffersBatch), - new RateLimits(0.5M, 1, RateLimitType.ProductPricing_GetListingOffersBatch), - - new RateLimits(0.5M, 15, RateLimitType.Sales_GetOrderMetrics), - - new RateLimits(0.016M, 15, RateLimitType.Sellers_GetMarketplaceParticipations), - - new RateLimits(1.0M, 5, RateLimitType.Solicitations_GetSolicitationActionsForOrder), - new RateLimits(1.0M, 5, RateLimitType.Solicitations_CreateProductReviewAndSellerFeedbackSolicitation), - - new RateLimits(1.0M, 10, RateLimitType.Token_CreateRestrictedDataToken), - - new RateLimits(10.0M, 10, RateLimitType.VendorDirectFulfillmentOrdersV1_GetOrders), - new RateLimits(10.0M, 10, RateLimitType.VendorDirectFulfillmentOrdersV1_GetOrder), - new RateLimits(10.0M, 10, RateLimitType.VendorDirectFulfillmentOrdersV1_SubmitAcknowledgement), - - new RateLimits(10.0M, 10, RateLimitType.VendorOrdersV1_GetPurchaseOrders), - new RateLimits(10.0M, 10, RateLimitType.VendorOrdersV1_GetPurchaseOrder), - new RateLimits(10.0M, 10, RateLimitType.VendorOrdersV1_SubmitAcknowledgement), - new RateLimits(10.0M, 10, RateLimitType.VendorOrdersV1_GetPurchaseOrdersStatus), - - new RateLimits(10.0M, 10, RateLimitType.VendorTransactionStatus_GetTransaction), + new RateLimits(5M, 15, RateLimitType.Order_UpdateOrderItemsApprovals), + new RateLimits(2M, 10, RateLimitType.Order_ShipmentConfirmation), + new RateLimits(0.0222M, 10, RateLimitType.Report_GetReports), + new RateLimits(2.0M, 15, RateLimitType.Report_GetReport), + new RateLimits(0.0167M, 15, RateLimitType.Report_CreateReport), + new RateLimits(0.0222M, 10, RateLimitType.Report_CancelReport), + new RateLimits(0.0222M, 10, RateLimitType.Report_GetReportSchedules), + new RateLimits(0.0222M, 10, RateLimitType.Report_CreateReportSchedule), + new RateLimits(0.0222M, 10, RateLimitType.Report_GetReportSchedule), + new RateLimits(0.0222M, 10, RateLimitType.Report_CancelReportSchedule), + new RateLimits(0.0167M, 15, RateLimitType.Report_GetReportDocument), + new RateLimits(0.5M, 30, RateLimitType.Financial_ListFinancialEventGroups), + new RateLimits(0.5M, 30, RateLimitType.Financial_ListFinancialEventsByGroupId), + new RateLimits(0.5M, 30, RateLimitType.Financial_ListFinancialEventsByOrderId), + new RateLimits(0.5M, 30, RateLimitType.Financial_ListFinancialEvents), + new RateLimits(0.0222M, 10, RateLimitType.Feed_GetFeeds), + new RateLimits(0.0083M, 15, RateLimitType.Feed_CreateFeed), + new RateLimits(2.0M, 15, RateLimitType.Feed_GetFeed), + new RateLimits(0.0222M, 10, RateLimitType.Feed_CancelFeed), + new RateLimits(0.0083M, 15, RateLimitType.Feed_CreateFeedDocument), + new RateLimits(0.0222M, 10, RateLimitType.Feed_GetFeedDocument), + new RateLimits(5.0M, 10, RateLimitType.ListingsItem_GetListingsItem), + new RateLimits(5.0M, 10, RateLimitType.ListingsItem_PutListingsItem), + new RateLimits(5.0M, 10, RateLimitType.ListingsItem_DeleteListingsItem), + new RateLimits(5.0M, 10, RateLimitType.ListingsItem_PatchListingsItem), + new RateLimits(0.1M, 5, RateLimitType.Upload_CreateUploadDestinationForResource), + + new RateLimits(1.133M, 25, RateLimitType.ShipmentInvoicing_GetShipmentDetails), + new RateLimits(1.133M, 25, RateLimitType.ShipmentInvoicing_SubmitInvoice), + new RateLimits(1.133M, 25, RateLimitType.ShipmentInvoicing_GetInvoiceStatus), + + new RateLimits(5.0M, 15, RateLimitType.Shipping_CreateShipment), + new RateLimits(5.0M, 15, RateLimitType.Shipping_GetShipment), + new RateLimits(5.0M, 15, RateLimitType.Shipping_CancelShipment), + new RateLimits(5.0M, 15, RateLimitType.Shipping_PurchaseLabels), + new RateLimits(5.0M, 15, RateLimitType.Shipping_RetrieveShippingLabel), + new RateLimits(5.0M, 15, RateLimitType.Shipping_PurchaseShipment), + new RateLimits(5.0M, 15, RateLimitType.Shipping_GetRates), + new RateLimits(5.0M, 15, RateLimitType.Shipping_GetAccount), + new RateLimits(1.0M, 1, RateLimitType.Shipping_GetTrackingInformation), + + new RateLimits(80.0M, 100, RateLimitType.ShippingV2_CancelShipment), + new RateLimits(80.0M, 100, RateLimitType.ShippingV2_DirectPurchaseShipment), + new RateLimits(80.0M, 100, RateLimitType.ShippingV2_GetAdditionalInputs), + new RateLimits(80.0M, 100, RateLimitType.ShippingV2_GetRates), + new RateLimits(80.0M, 100, RateLimitType.ShippingV2_GetShipmentDocument), + new RateLimits(80.0M, 100, RateLimitType.ShippingV2_GetTracking), + new RateLimits(80.0M, 100, RateLimitType.ShippingV2_PurchaseShipment), + + new RateLimits(6.0M, 40, RateLimitType.CatalogItems_ListCatalogItems), + new RateLimits(2.0M, 20, RateLimitType.CatalogItems_GetCatalogItem), + new RateLimits(1.0M, 40, RateLimitType.CatalogItems_ListCatalogCategories), + new RateLimits(2.0M, 2, RateLimitType.CatalogItems20220401_GetCatalogItem), + new RateLimits(2.0M, 2, RateLimitType.CatalogItems20220401_SearchCatalogItems), + + new RateLimits(2.0M, 2, RateLimitType.FbaInventory_GetInventorySummaries), + + new RateLimits(1.0M, 5, RateLimitType.Authorization_GetAuthorizationCode), + + new RateLimits(2.0M, 10, RateLimitType.FbaSmallandLight_GetSmallAndLightEnrollmentBySellerSKU), + new RateLimits(2.0M, 5, RateLimitType.FbaSmallandLight_PutSmallAndLightEnrollmentBySellerSKU), + new RateLimits(2.0M, 5, RateLimitType.FbaSmallandLight_DeleteSmallAndLightEnrollmentBySellerSKU), + new RateLimits(2.0M, 10, RateLimitType.FbaSmallandLight_GetSmallAndLightEligibilityBySellerSKU), + new RateLimits(1.0M, 3, RateLimitType.FbaSmallandLight_GetSmallAndLightFeePreview), + + new RateLimits(5.0M, 10, RateLimitType.Restrictions_GetListingsRestrictions), + + new RateLimits(5.0M, 10, RateLimitType.ProductTypes_GetDefinitionsProductType), + new RateLimits(5.0M, 10, RateLimitType.ProductTypes_SearchDefinitionsProductTypes), + + new RateLimits(1.0M, 1, RateLimitType.FBAInboundEligibility_GetItemEligibilityPreview), + + new RateLimits(1.0M, 5, RateLimitType.EasyShip_CreateScheduledPackage), + new RateLimits(1.0M, 5, RateLimitType.EasyShip_GetScheduledPackage), + new RateLimits(1.0M, 5, RateLimitType.EasyShip_ListHandoverSlots), + new RateLimits(1.0M, 5, RateLimitType.EasyShip_UpdateScheduledPackages), + + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_GetInboundGuidance), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_CreateInboundShipmentPlan), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_UpdateInboundShipment), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_CreateInboundShipment), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_GetPreorderInfo), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_ConfirmPreorder), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_GetPrepInstructions), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_GetTransportDetails), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_PutTransportDetails), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_VoidTransport), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_EstimateTransport), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_ConfirmTransport), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_GetLabels), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_GetBillOfLading), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_GetShipments), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_GetShipmentItemsByShipmentId), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_GetShipmentItems), + + new RateLimits(2.0M, 6, RateLimitType.FulFillmentInboundV20240320_ListInboundPlans), + new RateLimits(2.0M, 2, RateLimitType.FulFillmentInboundV20240320_CreateInboundPlan), + new RateLimits(2.0M, 6, RateLimitType.FulFillmentInboundV20240320_GetInboundPlan), + new RateLimits(2.0M, 6, RateLimitType.FulFillmentInboundV20240320_ListInboundPlanBoxes), + new RateLimits(2.0M, 2, RateLimitType.FulFillmentInboundV20240320_CancelInboundPlan), + new RateLimits(2.0M, 6, RateLimitType.FulFillmentInboundV20240320_ListInboundPlanItems), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_UpdateInboundPlanName), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_ListPackingGroupBoxes), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_ListPackingGroupItems), + new RateLimits(2.0M, 2, RateLimitType.FulFillmentInboundV20240320_SetPackingInformation), + new RateLimits(2.0M, 6, RateLimitType.FulFillmentInboundV20240320_ListPackingOptions), + new RateLimits(2.0M, 2, RateLimitType.FulFillmentInboundV20240320_GeneratePackingOptions), + new RateLimits(2.0M, 2, RateLimitType.FulFillmentInboundV20240320_ConfirmPackingOption), + new RateLimits(2.0M, 6, RateLimitType.FulFillmentInboundV20240320_ListInboundPlanPallets), + new RateLimits(2.0M, 6, RateLimitType.FulFillmentInboundV20240320_ListPlacementOptions), + new RateLimits(2.0M, 2, RateLimitType.FulFillmentInboundV20240320_GeneratePlacementOptions), + new RateLimits(2.0M, 2, RateLimitType.FulFillmentInboundV20240320_ConfirmPlacementOption), + new RateLimits(2.0M, 6, RateLimitType.FulFillmentInboundV20240320_GetShipment), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_ListShipmentBoxes), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_ListShipmentContentUpdatePreviews), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_GenerateShipmentContentUpdatePreviews), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_GetShipmentContentUpdatePreview), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_ConfirmShipmentContentUpdatePreview), + new RateLimits(2.0M, 6, RateLimitType.FulFillmentInboundV20240320_GetDeliveryChallanDocument), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_ListDeliveryWindowOptions), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_GenerateDeliveryWindowOptions), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_ConfirmDeliveryWindowOption), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_ListShipmentItems), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_UpdateShipmentName), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_ListShipmentPallets), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_CancelSelfShipAppointment), + new RateLimits(2.0M, 6, RateLimitType.FulFillmentInboundV20240320_GetSelfShipAppointmentSlots), + new RateLimits(2.0M, 2, RateLimitType.FulFillmentInboundV20240320_GenerateSelfShipAppointmentSlots), + new RateLimits(2.0M, 2, RateLimitType.FulFillmentInboundV20240320_ScheduleSelfShipAppointment), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_UpdateShipmentSourceAddress), + new RateLimits(2.0M, 2, RateLimitType.FulFillmentInboundV20240320_UpdateShipmentTrackingDetails), + new RateLimits(2.0M, 6, RateLimitType.FulFillmentInboundV20240320_ListTransportationOptions), + new RateLimits(2.0M, 2, RateLimitType.FulFillmentInboundV20240320_GenerateTransportationOptions), + new RateLimits(2.0M, 2, RateLimitType.FulFillmentInboundV20240320_ConfirmTransportationOptions), + new RateLimits(2.0M, 6, RateLimitType.FulFillmentInboundV20240320_ListItemComplianceDetails), + new RateLimits(2.0M, 2, RateLimitType.FulFillmentInboundV20240320_UpdateItemComplianceDetails), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_CreateMarketplaceItemLabels), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_ListPrepDetails), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_SetPrepDetails), + new RateLimits(2.0M, 6, RateLimitType.FulFillmentInboundV20240320_GetInboundOperationStatus), + + new RateLimits(2.0M, 30, RateLimitType.FulFillmentOutbound_GetFulfillmentPreview), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentOutbound_ListAllFulfillmentOrders), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentOutbound_CreateFulfillmentOrder), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentOutbound_GetPackageTrackingDetails), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentOutbound_ListReturnReasonCodes), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentOutbound_CreateFulfillmentReturn), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentOutbound_GetFulfillmentOrder), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentOutbound_UpdateFulfillmentOrder), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentOutbound_CancelFulfillmentOrder), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentOutbound_GetFeatures), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentOutbound_GetFeatureInventory), + new RateLimits(2.0M, 30, RateLimitType.FulFillmentOutbound_GetFeatureSKU), + + new RateLimits(1.0M, 1, RateLimitType.MerchantFulFillment_GetEligibleShipmentServicesOld), + new RateLimits(5.0M, 10, RateLimitType.MerchantFulFillment_GetEligibleShipmentServices), + new RateLimits(1.0M, 1, RateLimitType.MerchantFulFillment_GetShipment), + new RateLimits(1.0M, 1, RateLimitType.MerchantFulFillment_CancelShipment), + new RateLimits(1.0M, 1, RateLimitType.MerchantFulFillment_CancelShipmentOld), + new RateLimits(1.0M, 1, RateLimitType.MerchantFulFillment_CreateShipment), + new RateLimits(1.0M, 1, RateLimitType.MerchantFulFillment_GetAdditionalSellerInputsOld), + new RateLimits(1.0M, 1, RateLimitType.MerchantFulFillment_GetAdditionalSellerInputs), + + new RateLimits(1.0M, 5, RateLimitType.Messaging_GetMessagingActionsForOrder), + new RateLimits(1.0M, 5, RateLimitType.Messaging_ConfirmCustomizationDetails), + new RateLimits(1.0M, 5, RateLimitType.Messaging_CreateConfirmDeliveryDetails), + new RateLimits(1.0M, 5, RateLimitType.Messaging_CreateLegalDisclosure), + new RateLimits(1.0M, 5, RateLimitType.Messaging_CreateNegativeFeedbackRemoval), + new RateLimits(1.0M, 5, RateLimitType.Messaging_CreateConfirmOrderDetails), + new RateLimits(1.0M, 5, RateLimitType.Messaging_CreateConfirmServiceDetails), + new RateLimits(1.0M, 5, RateLimitType.Messaging_CreateAmazonMotors), + new RateLimits(1.0M, 5, RateLimitType.Messaging_CreateWarranty), + new RateLimits(1.0M, 5, RateLimitType.Messaging_GetAttributes), + new RateLimits(1.0M, 5, RateLimitType.Messaging_CreateDigitalAccessKey), + new RateLimits(1.0M, 5, RateLimitType.Messaging_CreateUnexpectedProblem), + + new RateLimits(1.0M, 5, RateLimitType.Notifications_GetSubscription), + new RateLimits(1.0M, 5, RateLimitType.Notifications_CreateSubscription), + new RateLimits(1.0M, 5, RateLimitType.Notifications_GetSubscriptionById), + new RateLimits(1.0M, 5, RateLimitType.Notifications_DeleteSubscriptionById), + new RateLimits(1.0M, 5, RateLimitType.Notifications_GetDestinations), + new RateLimits(1.0M, 5, RateLimitType.Notifications_CreateDestination), + new RateLimits(1.0M, 5, RateLimitType.Notifications_GetDestination), + new RateLimits(1.0M, 5, RateLimitType.Notifications_DeleteDestination), + + new RateLimits(1M, 2, RateLimitType.ProductFees_GetMyFeesEstimateForSKU), + new RateLimits(1M, 2, RateLimitType.ProductFees_GetMyFeesEstimateForASIN), + new RateLimits(0.5M, 1, RateLimitType.ProductFees_GetMyFeesEstimate), + + new RateLimits(0.5M, 1, RateLimitType.ProductPricing_GetPricing), + new RateLimits(0.5M, 1, RateLimitType.ProductPricing_GetCompetitivePricing), + new RateLimits(1M, 2, RateLimitType.ProductPricing_GetListingOffers), + new RateLimits(0.5M, 1, RateLimitType.ProductPricing_GetItemOffers), + + new RateLimits(0.1M, 1, RateLimitType.ProductPricing_GetItemOffersBatch), + new RateLimits(0.5M, 1, RateLimitType.ProductPricing_GetListingOffersBatch), + + new RateLimits(0.5M, 15, RateLimitType.Sales_GetOrderMetrics), + + new RateLimits(0.016M, 15, RateLimitType.Sellers_GetMarketplaceParticipations), + + new RateLimits(1.0M, 5, RateLimitType.Solicitations_GetSolicitationActionsForOrder), + new RateLimits(1.0M, 5, RateLimitType.Solicitations_CreateProductReviewAndSellerFeedbackSolicitation), + + new RateLimits(1.0M, 10, RateLimitType.Token_CreateRestrictedDataToken), + + new RateLimits(10.0M, 10, RateLimitType.VendorDirectFulfillmentOrdersV1_GetOrders), + new RateLimits(10.0M, 10, RateLimitType.VendorDirectFulfillmentOrdersV1_GetOrder), + new RateLimits(10.0M, 10, RateLimitType.VendorDirectFulfillmentOrdersV1_SubmitAcknowledgement), + + new RateLimits(10.0M, 10, RateLimitType.VendorOrdersV1_GetPurchaseOrders), + new RateLimits(10.0M, 10, RateLimitType.VendorOrdersV1_GetPurchaseOrder), + new RateLimits(10.0M, 10, RateLimitType.VendorOrdersV1_SubmitAcknowledgement), + new RateLimits(10.0M, 10, RateLimitType.VendorOrdersV1_GetPurchaseOrdersStatus), + + new RateLimits(10.0M, 10, RateLimitType.VendorTransactionStatus_GetTransaction), }.ToDictionary(x => x.RateLimitType); } From 55478f47a2755c0ebbf74dee3e9f8002feaabd12 Mon Sep 17 00:00:00 2001 From: Finn Reilly Date: Thu, 5 Dec 2024 13:12:30 +0000 Subject: [PATCH 10/17] additional improvements to `RateLimitingHandler` * all rate limit defined waits to come from this interface now (allows for future distributed implementations) * create policy with default burst rate where header contains limit rate but no policy exists for seller and rate limit type * actually think `RateLimits` does need to be a reference type for `SetRateLimit` to work as intended --- .../FikaAmazonAPI/Services/RequestService.cs | 2 +- .../Utils/IRateLimitingHandler.cs | 2 + .../Utils/RateLimitingHandler.cs | 55 ++++++++----------- Source/FikaAmazonAPI/Utils/RateLimits.cs | 2 +- .../Utils/RateLimitsDefinitions.cs | 28 ---------- 5 files changed, 27 insertions(+), 62 deletions(-) diff --git a/Source/FikaAmazonAPI/Services/RequestService.cs b/Source/FikaAmazonAPI/Services/RequestService.cs index a1395e49..57dadda7 100644 --- a/Source/FikaAmazonAPI/Services/RequestService.cs +++ b/Source/FikaAmazonAPI/Services/RequestService.cs @@ -238,7 +238,7 @@ public async Task ExecuteRequestAsync(RateLimitType rateLimitType = RateLi cancellationToken.ThrowIfCancellationRequested(); - await AmazonCredential.UsagePlansTimings[rateLimitType].WaitForPermittedRequest(cancellationToken, AmazonCredential.IsDebugMode); + await RateLimitingHandler.WaitForLimitTypeAsync(AmazonCredential, rateLimitType, cancellationToken); tryCount++; } } diff --git a/Source/FikaAmazonAPI/Utils/IRateLimitingHandler.cs b/Source/FikaAmazonAPI/Utils/IRateLimitingHandler.cs index 60ce1ccc..2d4f32db 100644 --- a/Source/FikaAmazonAPI/Utils/IRateLimitingHandler.cs +++ b/Source/FikaAmazonAPI/Utils/IRateLimitingHandler.cs @@ -19,5 +19,7 @@ Task> SafelyExecuteRequestAsync( RateLimitType rateLimitType = RateLimitType.UNSET, Action responseCallback = null, CancellationToken cancellationToken = default) where TResponse : new(); + + Task WaitForLimitTypeAsync(AmazonCredential credential, RateLimitType rateLimitType, CancellationToken cancellationToken = default); } } diff --git a/Source/FikaAmazonAPI/Utils/RateLimitingHandler.cs b/Source/FikaAmazonAPI/Utils/RateLimitingHandler.cs index 249e9f76..0b2867a7 100644 --- a/Source/FikaAmazonAPI/Utils/RateLimitingHandler.cs +++ b/Source/FikaAmazonAPI/Utils/RateLimitingHandler.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; -using System.Text; using System.Threading; using System.Threading.Tasks; @@ -17,6 +16,7 @@ namespace FikaAmazonAPI.Utils internal class RateLimitingHandler : IRateLimitingHandler { private const string RateLimitLimitHeaderName = "x-amzn-RateLimit-Limit"; + private const int FallbackDefaultBurstRate = 1; public async Task> SafelyExecuteRequestAsync( IRestClient restClient, @@ -26,45 +26,35 @@ public async Task> SafelyExecuteRequestAsync( Action action = null, CancellationToken cancellationToken = default) where TResponse : new() { - await SleepForRateLimit(credential, rateLimitType, cancellationToken: default); + await WaitForLimitTypeAsync(credential, rateLimitType, cancellationToken: default); var response = await restClient.ExecuteAsync(restRequest, cancellationToken); action?.Invoke(response); - await SleepForRateLimit(credential, rateLimitType, cancellationToken: cancellationToken); + if (response != null && TryGetRateFromHeaders(response.Headers, out var headersRate)) + { + UpdateRateLimitPolicy(credential, rateLimitType, headersRate); + } return response; } - private async Task SleepForRateLimit( + public async Task WaitForLimitTypeAsync( AmazonCredential credential, - RateLimitType rateLimitType = RateLimitType.UNSET, - decimal updatedLimitRate = default, + RateLimitType rateLimitType, CancellationToken cancellationToken = default) { - if (credential.IsActiveLimitRate) + if (!credential.IsActiveLimitRate) { - var rateLimitPolicies = RateLimitsDefinitions.RateLimitsTimeForCredential(credential); + return; + } - if (rateLimitType == RateLimitType.UNSET - || !rateLimitPolicies.TryGetValue(rateLimitType, out var planTimings)) - { - if (updatedLimitRate > 0) - { - int sleepTime = (int)(1 / updatedLimitRate * 1000); - await Task.Delay(sleepTime, cancellationToken); - } - } - else - { - if (updatedLimitRate > 0) - { - planTimings.SetRateLimit(updatedLimitRate); - } + var policies = RateLimitsDefinitions.RateLimitsTimeForCredential(credential); - await planTimings.WaitForPermittedRequest(cancellationToken); - } + if (policies.TryGetValue(rateLimitType, out var policy)) + { + await policy.WaitForPermittedRequest(cancellationToken, credential.IsDebugMode); } } @@ -77,27 +67,28 @@ private void UpdateRateLimitPolicy(AmazonCredential credential, RateLimitType ra var rateLimitPolicies = RateLimitsDefinitions.RateLimitsTimeForCredential(credential); - if (rateLimitType == RateLimitType.UNSET - || !rateLimitPolicies.TryGetValue(rateLimitType, out var planTimings)) + if (!rateLimitPolicies.TryGetValue(rateLimitType, out var planTimings)) { - Console.WriteLine($"No rate limit policy found for {rateLimitType} for seller {credential.SellerID}"); + Console.WriteLine( + $"No rate limit policy found for {rateLimitType} for seller {credential.SellerID}, will create new policy for seller with default burst rate of {FallbackDefaultBurstRate}"); + rateLimitPolicies.TryAdd(rateLimitType, new RateLimits(Rate: updatedRateLimit, Burst: FallbackDefaultBurstRate, type: rateLimitType)); return; } planTimings.SetRateLimit(updatedRateLimit); } - private decimal GetRateFromHeaders(IEnumerable headers) + private bool TryGetRateFromHeaders(IEnumerable headers, out decimal rate) { - decimal rate = 0; + rate = 0; var limitHeader = headers.FirstOrDefault(a => a.Name == RateLimitLimitHeaderName); if (limitHeader != null) { var RateLimitValue = limitHeader.Value.ToString(); - decimal.TryParse(RateLimitValue, NumberStyles.Any, CultureInfo.InvariantCulture, out rate); + return decimal.TryParse(RateLimitValue, NumberStyles.Any, CultureInfo.InvariantCulture, out rate); } - return rate; + return false; } } } diff --git a/Source/FikaAmazonAPI/Utils/RateLimits.cs b/Source/FikaAmazonAPI/Utils/RateLimits.cs index 169ebe70..17398dac 100644 --- a/Source/FikaAmazonAPI/Utils/RateLimits.cs +++ b/Source/FikaAmazonAPI/Utils/RateLimits.cs @@ -4,7 +4,7 @@ namespace FikaAmazonAPI.Utils { - internal struct RateLimits + internal class RateLimits { internal decimal Rate { get; private set; } internal int Burst { get; } diff --git a/Source/FikaAmazonAPI/Utils/RateLimitsDefinitions.cs b/Source/FikaAmazonAPI/Utils/RateLimitsDefinitions.cs index 0e076c87..c3c566e6 100644 --- a/Source/FikaAmazonAPI/Utils/RateLimitsDefinitions.cs +++ b/Source/FikaAmazonAPI/Utils/RateLimitsDefinitions.cs @@ -67,11 +67,9 @@ private static Dictionary RateLimitsTime() new RateLimits(5.0M, 10, RateLimitType.ListingsItem_DeleteListingsItem), new RateLimits(5.0M, 10, RateLimitType.ListingsItem_PatchListingsItem), new RateLimits(0.1M, 5, RateLimitType.Upload_CreateUploadDestinationForResource), - new RateLimits(1.133M, 25, RateLimitType.ShipmentInvoicing_GetShipmentDetails), new RateLimits(1.133M, 25, RateLimitType.ShipmentInvoicing_SubmitInvoice), new RateLimits(1.133M, 25, RateLimitType.ShipmentInvoicing_GetInvoiceStatus), - new RateLimits(5.0M, 15, RateLimitType.Shipping_CreateShipment), new RateLimits(5.0M, 15, RateLimitType.Shipping_GetShipment), new RateLimits(5.0M, 15, RateLimitType.Shipping_CancelShipment), @@ -81,7 +79,6 @@ private static Dictionary RateLimitsTime() new RateLimits(5.0M, 15, RateLimitType.Shipping_GetRates), new RateLimits(5.0M, 15, RateLimitType.Shipping_GetAccount), new RateLimits(1.0M, 1, RateLimitType.Shipping_GetTrackingInformation), - new RateLimits(80.0M, 100, RateLimitType.ShippingV2_CancelShipment), new RateLimits(80.0M, 100, RateLimitType.ShippingV2_DirectPurchaseShipment), new RateLimits(80.0M, 100, RateLimitType.ShippingV2_GetAdditionalInputs), @@ -89,35 +86,26 @@ private static Dictionary RateLimitsTime() new RateLimits(80.0M, 100, RateLimitType.ShippingV2_GetShipmentDocument), new RateLimits(80.0M, 100, RateLimitType.ShippingV2_GetTracking), new RateLimits(80.0M, 100, RateLimitType.ShippingV2_PurchaseShipment), - new RateLimits(6.0M, 40, RateLimitType.CatalogItems_ListCatalogItems), new RateLimits(2.0M, 20, RateLimitType.CatalogItems_GetCatalogItem), new RateLimits(1.0M, 40, RateLimitType.CatalogItems_ListCatalogCategories), new RateLimits(2.0M, 2, RateLimitType.CatalogItems20220401_GetCatalogItem), new RateLimits(2.0M, 2, RateLimitType.CatalogItems20220401_SearchCatalogItems), - new RateLimits(2.0M, 2, RateLimitType.FbaInventory_GetInventorySummaries), - new RateLimits(1.0M, 5, RateLimitType.Authorization_GetAuthorizationCode), - new RateLimits(2.0M, 10, RateLimitType.FbaSmallandLight_GetSmallAndLightEnrollmentBySellerSKU), new RateLimits(2.0M, 5, RateLimitType.FbaSmallandLight_PutSmallAndLightEnrollmentBySellerSKU), new RateLimits(2.0M, 5, RateLimitType.FbaSmallandLight_DeleteSmallAndLightEnrollmentBySellerSKU), new RateLimits(2.0M, 10, RateLimitType.FbaSmallandLight_GetSmallAndLightEligibilityBySellerSKU), new RateLimits(1.0M, 3, RateLimitType.FbaSmallandLight_GetSmallAndLightFeePreview), - new RateLimits(5.0M, 10, RateLimitType.Restrictions_GetListingsRestrictions), - new RateLimits(5.0M, 10, RateLimitType.ProductTypes_GetDefinitionsProductType), new RateLimits(5.0M, 10, RateLimitType.ProductTypes_SearchDefinitionsProductTypes), - new RateLimits(1.0M, 1, RateLimitType.FBAInboundEligibility_GetItemEligibilityPreview), - new RateLimits(1.0M, 5, RateLimitType.EasyShip_CreateScheduledPackage), new RateLimits(1.0M, 5, RateLimitType.EasyShip_GetScheduledPackage), new RateLimits(1.0M, 5, RateLimitType.EasyShip_ListHandoverSlots), new RateLimits(1.0M, 5, RateLimitType.EasyShip_UpdateScheduledPackages), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_GetInboundGuidance), new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_CreateInboundShipmentPlan), new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_UpdateInboundShipment), @@ -135,7 +123,6 @@ private static Dictionary RateLimitsTime() new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_GetShipments), new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_GetShipmentItemsByShipmentId), new RateLimits(2.0M, 30, RateLimitType.FulFillmentInbound_GetShipmentItems), - new RateLimits(2.0M, 6, RateLimitType.FulFillmentInboundV20240320_ListInboundPlans), new RateLimits(2.0M, 2, RateLimitType.FulFillmentInboundV20240320_CreateInboundPlan), new RateLimits(2.0M, 6, RateLimitType.FulFillmentInboundV20240320_GetInboundPlan), @@ -181,7 +168,6 @@ private static Dictionary RateLimitsTime() new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_ListPrepDetails), new RateLimits(2.0M, 30, RateLimitType.FulFillmentInboundV20240320_SetPrepDetails), new RateLimits(2.0M, 6, RateLimitType.FulFillmentInboundV20240320_GetInboundOperationStatus), - new RateLimits(2.0M, 30, RateLimitType.FulFillmentOutbound_GetFulfillmentPreview), new RateLimits(2.0M, 30, RateLimitType.FulFillmentOutbound_ListAllFulfillmentOrders), new RateLimits(2.0M, 30, RateLimitType.FulFillmentOutbound_CreateFulfillmentOrder), @@ -194,7 +180,6 @@ private static Dictionary RateLimitsTime() new RateLimits(2.0M, 30, RateLimitType.FulFillmentOutbound_GetFeatures), new RateLimits(2.0M, 30, RateLimitType.FulFillmentOutbound_GetFeatureInventory), new RateLimits(2.0M, 30, RateLimitType.FulFillmentOutbound_GetFeatureSKU), - new RateLimits(1.0M, 1, RateLimitType.MerchantFulFillment_GetEligibleShipmentServicesOld), new RateLimits(5.0M, 10, RateLimitType.MerchantFulFillment_GetEligibleShipmentServices), new RateLimits(1.0M, 1, RateLimitType.MerchantFulFillment_GetShipment), @@ -203,7 +188,6 @@ private static Dictionary RateLimitsTime() new RateLimits(1.0M, 1, RateLimitType.MerchantFulFillment_CreateShipment), new RateLimits(1.0M, 1, RateLimitType.MerchantFulFillment_GetAdditionalSellerInputsOld), new RateLimits(1.0M, 1, RateLimitType.MerchantFulFillment_GetAdditionalSellerInputs), - new RateLimits(1.0M, 5, RateLimitType.Messaging_GetMessagingActionsForOrder), new RateLimits(1.0M, 5, RateLimitType.Messaging_ConfirmCustomizationDetails), new RateLimits(1.0M, 5, RateLimitType.Messaging_CreateConfirmDeliveryDetails), @@ -216,7 +200,6 @@ private static Dictionary RateLimitsTime() new RateLimits(1.0M, 5, RateLimitType.Messaging_GetAttributes), new RateLimits(1.0M, 5, RateLimitType.Messaging_CreateDigitalAccessKey), new RateLimits(1.0M, 5, RateLimitType.Messaging_CreateUnexpectedProblem), - new RateLimits(1.0M, 5, RateLimitType.Notifications_GetSubscription), new RateLimits(1.0M, 5, RateLimitType.Notifications_CreateSubscription), new RateLimits(1.0M, 5, RateLimitType.Notifications_GetSubscriptionById), @@ -225,39 +208,28 @@ private static Dictionary RateLimitsTime() new RateLimits(1.0M, 5, RateLimitType.Notifications_CreateDestination), new RateLimits(1.0M, 5, RateLimitType.Notifications_GetDestination), new RateLimits(1.0M, 5, RateLimitType.Notifications_DeleteDestination), - new RateLimits(1M, 2, RateLimitType.ProductFees_GetMyFeesEstimateForSKU), new RateLimits(1M, 2, RateLimitType.ProductFees_GetMyFeesEstimateForASIN), new RateLimits(0.5M, 1, RateLimitType.ProductFees_GetMyFeesEstimate), - new RateLimits(0.5M, 1, RateLimitType.ProductPricing_GetPricing), new RateLimits(0.5M, 1, RateLimitType.ProductPricing_GetCompetitivePricing), new RateLimits(1M, 2, RateLimitType.ProductPricing_GetListingOffers), new RateLimits(0.5M, 1, RateLimitType.ProductPricing_GetItemOffers), - new RateLimits(0.1M, 1, RateLimitType.ProductPricing_GetItemOffersBatch), new RateLimits(0.5M, 1, RateLimitType.ProductPricing_GetListingOffersBatch), - new RateLimits(0.5M, 15, RateLimitType.Sales_GetOrderMetrics), - new RateLimits(0.016M, 15, RateLimitType.Sellers_GetMarketplaceParticipations), - new RateLimits(1.0M, 5, RateLimitType.Solicitations_GetSolicitationActionsForOrder), new RateLimits(1.0M, 5, RateLimitType.Solicitations_CreateProductReviewAndSellerFeedbackSolicitation), - new RateLimits(1.0M, 10, RateLimitType.Token_CreateRestrictedDataToken), - new RateLimits(10.0M, 10, RateLimitType.VendorDirectFulfillmentOrdersV1_GetOrders), new RateLimits(10.0M, 10, RateLimitType.VendorDirectFulfillmentOrdersV1_GetOrder), new RateLimits(10.0M, 10, RateLimitType.VendorDirectFulfillmentOrdersV1_SubmitAcknowledgement), - new RateLimits(10.0M, 10, RateLimitType.VendorOrdersV1_GetPurchaseOrders), new RateLimits(10.0M, 10, RateLimitType.VendorOrdersV1_GetPurchaseOrder), new RateLimits(10.0M, 10, RateLimitType.VendorOrdersV1_SubmitAcknowledgement), new RateLimits(10.0M, 10, RateLimitType.VendorOrdersV1_GetPurchaseOrdersStatus), - new RateLimits(10.0M, 10, RateLimitType.VendorTransactionStatus_GetTransaction), - }.ToDictionary(x => x.RateLimitType); } } From 56dbf16b459f71999e6ebdf57fe17f7f032cef54 Mon Sep 17 00:00:00 2001 From: Finn Reilly Date: Thu, 5 Dec 2024 13:21:06 +0000 Subject: [PATCH 11/17] remove rate limits policies from credential * not causing any problems * _but_ gives false impression of a class which can be seen as a singleton, in reality this is used as a scoped token cache * also this property is no longer referenced anywhere --- Source/FikaAmazonAPI/AmazonCredential.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Source/FikaAmazonAPI/AmazonCredential.cs b/Source/FikaAmazonAPI/AmazonCredential.cs index ed4415ac..5ac49fb2 100644 --- a/Source/FikaAmazonAPI/AmazonCredential.cs +++ b/Source/FikaAmazonAPI/AmazonCredential.cs @@ -1,7 +1,5 @@ using FikaAmazonAPI.AmazonSpApiSDK.Models.Token; using FikaAmazonAPI.Utils; -using System.Collections.Concurrent; -using System.Collections.Generic; using static FikaAmazonAPI.AmazonSpApiSDK.Models.Token.CacheTokenData; using static FikaAmazonAPI.Utils.Constants; @@ -40,7 +38,6 @@ public AmazonCredential(string AccessKey, string SecretKey, string RoleArn, stri this.RefreshToken = RefreshToken; this.ProxyAddress = ProxyAddress; CacheTokenData = new CacheTokenData(); - UsagePlansTimings = RateLimitsDefinitions.RateLimitsTimeForCredential(this); } public TokenResponse GetToken(TokenDataType tokenDataType) @@ -55,11 +52,10 @@ public AWSAuthenticationTokenData GetAWSAuthenticationTokenData() { return CacheTokenData.GetAWSAuthenticationTokenData(); } + public void SetAWSAuthenticationTokenData(AWSAuthenticationTokenData tokenData) { CacheTokenData.SetAWSAuthenticationTokenData(tokenData); } - - internal ConcurrentDictionary UsagePlansTimings { get; set; } } } From 5a2efb23b622693d36d521890e076b7c041398eb Mon Sep 17 00:00:00 2001 From: Finn Reilly Date: Thu, 5 Dec 2024 13:53:20 +0000 Subject: [PATCH 12/17] update test for multithreading, tweak factory constructor --- Source/FikaAmazonAPI.SampleCode/Program.cs | 45 +++++++------------ .../AmazonMultithreadedConnectionFactory.cs | 20 ++++++--- .../Utils/RateLimitingHandler.cs | 2 +- 3 files changed, 33 insertions(+), 34 deletions(-) diff --git a/Source/FikaAmazonAPI.SampleCode/Program.cs b/Source/FikaAmazonAPI.SampleCode/Program.cs index ba0d40c5..fde138d1 100644 --- a/Source/FikaAmazonAPI.SampleCode/Program.cs +++ b/Source/FikaAmazonAPI.SampleCode/Program.cs @@ -1,4 +1,6 @@ -using Microsoft.Extensions.Configuration; +using FikaAmazonAPI.AmazonSpApiSDK.Models.FbaSmallandLight; +using FikaAmazonAPI.Utils; +using Microsoft.Extensions.Configuration; namespace FikaAmazonAPI.SampleCode { @@ -12,40 +14,27 @@ static async Task Main(string[] args) .AddUserSecrets() .Build(); + var connectionFactory = new AmazonMultithreadedConnectionFactory( + ClientId: config.GetSection("FikaAmazonAPI:ClientId").Value, + ClientSecret: config.GetSection("FikaAmazonAPI:ClientSecret").Value, + RefreshToken: config.GetSection("FikaAmazonAPI:RefreshToken").Value, + rateLimitingHandler: new RateLimitingHandler()); - AmazonConnection amazonConnection = new AmazonConnection(new AmazonCredential() + var tasks = new[] { 1..10 }.Select(x => + Task.Run(() => { - //AccessKey = config.GetSection("FikaAmazonAPI:AccessKey").Value, - //SecretKey = config.GetSection("FikaAmazonAPI:SecretKey").Value, - //RoleArn = config.GetSection("FikaAmazonAPI:RoleArn").Value, - ClientId = config.GetSection("FikaAmazonAPI:ClientId").Value, - ClientSecret = config.GetSection("FikaAmazonAPI:ClientSecret").Value, - RefreshToken = config.GetSection("FikaAmazonAPI:RefreshToken").Value, - MarketPlaceID = config.GetSection("FikaAmazonAPI:MarketPlaceID").Value, - SellerID = config.GetSection("FikaAmazonAPI:SellerId").Value, - IsDebugMode = true - }); - - - ReportManagerSample reportManagerSample = new ReportManagerSample(amazonConnection); - reportManagerSample.CallReport(); - //var error = amazonConnection.Reports.CreateReportAndDownloadFile(Utils.Constants.ReportTypes.GET_STRANDED_INVENTORY_UI_DATA); - //var dddd = amazonConnection.Reports.CreateReportAndDownloadFile(Utils.Constants.ReportTypes.GET_FBA_MYI_ALL_INVENTORY_DATA); - //var dddd = amazonConnection.Reports.CreateReportAndDownloadFile(Utils.Constants.ReportTypes.GET_FBA_MYI_UNSUPPRESSED_INVENTORY_DATA); - //ReportManager reportManager = new ReportManager(amazonConnection); - - //var dddddd = reportManager.GetAFNInventoryQtyAsync().ConfigureAwait(false).GetAwaiter().GetResult(); + var amazonConnection = connectionFactory.RequestScopedConnection( + marketPlaceId: config.GetSection("FikaAmazonAPI:MarketPlaceID").Value, + sellerId: config.GetSection("FikaAmazonAPI:SellerId").Value); + ReportManagerSample reportManagerSample = new ReportManagerSample(amazonConnection); + reportManagerSample.CallReport(); + })); + await Task.WhenAll(tasks); Console.ReadLine(); } - - - - - - } } diff --git a/Source/FikaAmazonAPI/AmazonMultithreadedConnectionFactory.cs b/Source/FikaAmazonAPI/AmazonMultithreadedConnectionFactory.cs index 5360a12c..a4f1aa06 100644 --- a/Source/FikaAmazonAPI/AmazonMultithreadedConnectionFactory.cs +++ b/Source/FikaAmazonAPI/AmazonMultithreadedConnectionFactory.cs @@ -16,12 +16,13 @@ public class AmazonMultithreadedConnectionFactory private readonly IRateLimitingHandler _rateLimitingHandler; public AmazonMultithreadedConnectionFactory( - string AccessKey, - string SecretKey, - string RoleArn, + string ClientId, string ClientSecret, string RefreshToken, + string AccessKey = null, + string SecretKey = null, + string RoleArn = null, string ProxyAddress = null, IRateLimitingHandler rateLimitingHandler = null) { @@ -36,10 +37,19 @@ public AmazonMultithreadedConnectionFactory( _rateLimitingHandler = rateLimitingHandler ?? new RateLimitingHandler(); } - public AmazonConnection RequestScopedConnection(string refCode = null, CultureInfo cultureInfo = null) + public AmazonConnection RequestScopedConnection( + string marketPlaceId = null, + string sellerId = null, + string refCode = null, + CultureInfo cultureInfo = null) { // need to create distinct credential/connection here so that token caching in credential is predicably kept within scope - var credential = new AmazonCredential(_accessKey, _secretKey, _roleArn, _clientId, _clientSecret, _refreshToken, _proxyAddress); + var credential = new AmazonCredential(_accessKey, _secretKey, _roleArn, _clientId, _clientSecret, _refreshToken, _proxyAddress) + { + MarketPlaceID = marketPlaceId, + SellerID = sellerId, + }; + return new AmazonConnection(credential, _rateLimitingHandler, refCode, cultureInfo); } } diff --git a/Source/FikaAmazonAPI/Utils/RateLimitingHandler.cs b/Source/FikaAmazonAPI/Utils/RateLimitingHandler.cs index 0b2867a7..ccb64212 100644 --- a/Source/FikaAmazonAPI/Utils/RateLimitingHandler.cs +++ b/Source/FikaAmazonAPI/Utils/RateLimitingHandler.cs @@ -13,7 +13,7 @@ namespace FikaAmazonAPI.Utils ///

/// See ///
- internal class RateLimitingHandler : IRateLimitingHandler + public class RateLimitingHandler : IRateLimitingHandler { private const string RateLimitLimitHeaderName = "x-amzn-RateLimit-Limit"; private const int FallbackDefaultBurstRate = 1; From ed4fd4e8312798a2749a1271ac35272170481d3f Mon Sep 17 00:00:00 2001 From: Finn Reilly Date: Thu, 5 Dec 2024 15:49:34 +0000 Subject: [PATCH 13/17] Post self-review * replace commented out method - not related to change * whitespace stuff --- .../AmazonMultithreadedConnectionFactory.cs | 1 - .../FikaAmazonAPI/Services/RequestService.cs | 26 +++++++++---------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/Source/FikaAmazonAPI/AmazonMultithreadedConnectionFactory.cs b/Source/FikaAmazonAPI/AmazonMultithreadedConnectionFactory.cs index a4f1aa06..b5836746 100644 --- a/Source/FikaAmazonAPI/AmazonMultithreadedConnectionFactory.cs +++ b/Source/FikaAmazonAPI/AmazonMultithreadedConnectionFactory.cs @@ -16,7 +16,6 @@ public class AmazonMultithreadedConnectionFactory private readonly IRateLimitingHandler _rateLimitingHandler; public AmazonMultithreadedConnectionFactory( - string ClientId, string ClientSecret, string RefreshToken, diff --git a/Source/FikaAmazonAPI/Services/RequestService.cs b/Source/FikaAmazonAPI/Services/RequestService.cs index 57dadda7..f8e8410c 100644 --- a/Source/FikaAmazonAPI/Services/RequestService.cs +++ b/Source/FikaAmazonAPI/Services/RequestService.cs @@ -98,19 +98,19 @@ protected async Task CreateAuthorizedRequestAsync(string url, RestSharp.Method m AddQueryParameters(queryParameters); } - //protected void CreateAuthorizedPagedRequest(AmazonFilter filter, string url, RestSharp.Method method) - //{ - // RefreshToken(); - // if (filter.NextPage != null) - // CreateRequest(filter.NextPage, method); - // else - // { - // CreateRequest(url, method); - // AddLimitHeader(filter.Limit); - // } - - // AddAccessToken(); - //} + protected void CreateAuthorizedPagedRequest(AmazonFilter filter, string url, RestSharp.Method method) + { + RefreshToken(); + if (filter.NextPage != null) + CreateRequest(filter.NextPage, method); + else + { + CreateRequest(url, method); + AddLimitHeader(filter.Limit); + } + + AddAccessToken(); + } /// /// Executes the request From 6099d197627bd67234e141e2e09a61292ee1895f Mon Sep 17 00:00:00 2001 From: Finn Reilly Date: Thu, 5 Dec 2024 16:56:44 +0000 Subject: [PATCH 14/17] Post test changes * allow easier configuration of credential --- Source/FikaAmazonAPI.SampleCode/Program.cs | 8 ++++++-- .../FikaAmazonAPI/AmazonMultithreadedConnectionFactory.cs | 3 +++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Source/FikaAmazonAPI.SampleCode/Program.cs b/Source/FikaAmazonAPI.SampleCode/Program.cs index fde138d1..92765325 100644 --- a/Source/FikaAmazonAPI.SampleCode/Program.cs +++ b/Source/FikaAmazonAPI.SampleCode/Program.cs @@ -25,7 +25,12 @@ static async Task Main(string[] args) { var amazonConnection = connectionFactory.RequestScopedConnection( marketPlaceId: config.GetSection("FikaAmazonAPI:MarketPlaceID").Value, - sellerId: config.GetSection("FikaAmazonAPI:SellerId").Value); + sellerId: config.GetSection("FikaAmazonAPI:SellerId").Value, + credentialConfiguration: cred => + { + cred.IsActiveLimitRate = true; + cred.IsDebugMode = true; + }); ReportManagerSample reportManagerSample = new ReportManagerSample(amazonConnection); reportManagerSample.CallReport(); @@ -34,7 +39,6 @@ static async Task Main(string[] args) await Task.WhenAll(tasks); Console.ReadLine(); - } } } diff --git a/Source/FikaAmazonAPI/AmazonMultithreadedConnectionFactory.cs b/Source/FikaAmazonAPI/AmazonMultithreadedConnectionFactory.cs index b5836746..73a5e399 100644 --- a/Source/FikaAmazonAPI/AmazonMultithreadedConnectionFactory.cs +++ b/Source/FikaAmazonAPI/AmazonMultithreadedConnectionFactory.cs @@ -1,4 +1,5 @@ using FikaAmazonAPI.Utils; +using System; using System.Globalization; namespace FikaAmazonAPI @@ -40,6 +41,7 @@ public AmazonConnection RequestScopedConnection( string marketPlaceId = null, string sellerId = null, string refCode = null, + Action credentialConfiguration = null, CultureInfo cultureInfo = null) { // need to create distinct credential/connection here so that token caching in credential is predicably kept within scope @@ -48,6 +50,7 @@ public AmazonConnection RequestScopedConnection( MarketPlaceID = marketPlaceId, SellerID = sellerId, }; + credentialConfiguration?.Invoke(credential); return new AmazonConnection(credential, _rateLimitingHandler, refCode, cultureInfo); } From e16191f877e390bfa69ba40801191f3cf9f53f9b Mon Sep 17 00:00:00 2001 From: Finn Reilly Date: Fri, 6 Dec 2024 11:06:08 +0000 Subject: [PATCH 15/17] update README.md --- README.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/README.md b/README.md index 2ad16bc9..fe681410 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,41 @@ AmazonConnection amazonConnection = new AmazonConnection(new AmazonCredential() ``` +### Multithreaded connections +If multithreading, the following should be done to avoid inadvertantly passing incorrect token data between different threads: +```CSharp + +// Note - you may also write and pass your own implementation of the IRateLimitingHandler interface if required + +var connectionFactory = new AmazonMultithreadedConnectionFactory( + ClientId: "amzn1.application-XXX-client.XXXXXXXXXXXXXXXXXXXXXXXXXXXX", + ClientSecret: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + RefreshToken: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + rateLimitingHandler: new RateLimitingHandler()); + +// Then in each concurrent thread/request scope, a new connection can be created like so +var amazonConnection = connectionFactory.RequestScopedConnection( + marketPlaceId: "A2VIGQ35RCS4UG", + sellerId: "MySellerId", + // credentialConfiguration is an optional parameter that allows additional configuration of the AmazonCredential + credentialConfiguration: cred => + { + cred.IsActiveLimitRate = true; + cred.IsDebugMode = true; + }); + +// or (remember either Marketplace OR Marketplace ID must be provided) +var amazonConnection = connectionFactory.RequestScopedConnection( + sellerId: "MySellerId", + credentialConfiguration: cred => + { + cred.IsActiveLimitRate = true; + cred.IsDebugMode = true; + cred.Marketplace = MarketPlace.UnitedArabEmirates + }); + +``` + ### Configuration using a proxy Please see [here](https://github.com/abuzuhri/Amazon-SP-API-CSharp/blob/main/Source/FikaAmazonAPI.SampleCode/Program.cs) for the relevant code file. >```csharp From 607f82f61e9f445a4f588f3bf3e615e0cc554791 Mon Sep 17 00:00:00 2001 From: Finn Reilly Date: Fri, 6 Dec 2024 12:46:41 +0000 Subject: [PATCH 16/17] Address multiple issues with Token Bucket code * if there has been a long wait since the last request, bucket should be replenished according to time elapsed * only one thread at a time should be able to replenish the bucket to ensure replenishment at expected rate * only one thread at a time should be able to decrement the amount of tokens available in the bucket * if a thread fails to decrement the amount of tokens available, it should wait for the next available token --- README.md | 1 + Source/FikaAmazonAPI/Utils/RateLimits.cs | 100 ++++++++++++++++------- 2 files changed, 70 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index fe681410..cd2b7731 100644 --- a/README.md +++ b/README.md @@ -130,6 +130,7 @@ var connectionFactory = new AmazonMultithreadedConnectionFactory( ClientId: "amzn1.application-XXX-client.XXXXXXXXXXXXXXXXXXXXXXXXXXXX", ClientSecret: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", RefreshToken: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + // a singleton that handles rate limiting across multiple threads rateLimitingHandler: new RateLimitingHandler()); // Then in each concurrent thread/request scope, a new connection can be created like so diff --git a/Source/FikaAmazonAPI/Utils/RateLimits.cs b/Source/FikaAmazonAPI/Utils/RateLimits.cs index 17398dac..fae7151a 100644 --- a/Source/FikaAmazonAPI/Utils/RateLimits.cs +++ b/Source/FikaAmazonAPI/Utils/RateLimits.cs @@ -6,10 +6,11 @@ namespace FikaAmazonAPI.Utils { internal class RateLimits { + private object _locker = new object(); internal decimal Rate { get; private set; } internal int Burst { get; } internal DateTime LastRequestReplenished { get; private set; } - internal int RequestsSent { get; private set; } + internal int RequestsUsed { get; private set; } internal RateLimitType RateLimitType { get; } /// @@ -24,7 +25,7 @@ internal RateLimits(decimal Rate, int Burst, RateLimitType type) this.Burst = Burst; this.RateLimitType = type; this.LastRequestReplenished = DateTime.UtcNow; - this.RequestsSent = 0; + this.RequestsUsed = 0; } private int GetRatePeriodMs() { return (int)(((1 / Rate) * 1000) / 1); } @@ -39,14 +40,22 @@ internal RateLimits(decimal Rate, int Burst, RateLimitType type) /// public async Task WaitForPermittedRequest(CancellationToken cancellationToken = default, bool debugMode = true) { - if (RequestsSent < 0) + if (RequestsUsed < 0) { - RequestsSent = 0; + RequestsUsed = 0; } int ratePeriodMs = GetRatePeriodMs(); + var requestsUsedAtOnset = RequestsUsed; - var nextRequestsSent = RequestsSent + 1; + // when requests used more than zero, replenish according to time elapsed + var requestsToReplenish = requestsUsedAtOnset == 0 || ratePeriodMs == 0 ? 0 : (DateTime.UtcNow - LastRequestReplenished).Milliseconds / ratePeriodMs; + if (requestsToReplenish > 0) + { + IncrementAvailableTokens(requestsToReplenish, requestsUsedAtOnset); + } + + var nextRequestsSent = RequestsUsed + 1; var nextRequestsSentTxt = (nextRequestsSent > Burst) ? "FULL" : nextRequestsSent.ToString(); if (debugMode) { @@ -54,43 +63,72 @@ public async Task WaitForPermittedRequest(CancellationToken cancellationToken = Console.WriteLine(output); } - // if bucket is currently empty, enter wait loop for replenishment - while (RequestsSent >= Burst) + var requestIsPermitted = false; + + while (!requestIsPermitted) { - // wait until predicted next token available - var incomingRequestTokenTime = LastRequestReplenished.AddMilliseconds(ratePeriodMs); - if (incomingRequestTokenTime > DateTime.UtcNow) + // if bucket is currently empty, enter wait loop for replenishment + while (RequestsUsed >= Burst) { - await Task.Delay(100); - continue; - } - else - { - // replenish token - LastRequestReplenished = incomingRequestTokenTime; - RequestsSent -= 1; - } + var currentRequestsUsed = RequestsUsed; + // wait until predicted next token available + var incomingRequestTokenTime = LastRequestReplenished.AddMilliseconds(ratePeriodMs); + if (incomingRequestTokenTime > DateTime.UtcNow) + { + await Task.Delay(100); + continue; + } + else + { + // replenish token + IncrementAvailableTokens(1, currentRequestsUsed); + } - if (RequestsSent <= 0) - { - RequestsSent = 0; - break; + if (RequestsUsed <= 0) + { + RequestsUsed = 0; + break; + } } - } - // now remove token from bucket for pending request - if (RequestsSent + 1 <= Burst) - { - RequestsSent += 1; + // now remove token from bucket for pending request + requestIsPermitted = TryDecrementAvailableTokens(); } - - // can't hurt to have this, will probably make the algorithm a little more conservative in practice - LastRequestReplenished = DateTime.UtcNow; } internal void SetRateLimit(decimal rate) { Rate = rate; } + + // increments available tokens, unless another thread has already incremented them. + private void IncrementAvailableTokens(int amountToIncrementBy, int initialAmount) + { + lock (_locker) + { + if (RequestsUsed >= initialAmount) + { + RequestsUsed = Math.Max(RequestsUsed - amountToIncrementBy, 0); + LastRequestReplenished = DateTime.UtcNow; + } + } + } + + // will try to decrement available tokens, will fail if another thread has used last of burst quota + private bool TryDecrementAvailableTokens() + { + var succeeded = false; + + lock (_locker) + { + if (RequestsUsed < Burst) + { + RequestsUsed++; + succeeded = true; + } + } + + return succeeded; + } } } From 0daec7593131969d37802521255b942661a29687 Mon Sep 17 00:00:00 2001 From: Finn Reilly Date: Fri, 6 Dec 2024 13:40:30 +0000 Subject: [PATCH 17/17] Fix minor issues with multithreaded rate limiting * also add extensive debug logging --- Source/FikaAmazonAPI/Utils/RateLimits.cs | 52 ++++++++++++++++-------- 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/Source/FikaAmazonAPI/Utils/RateLimits.cs b/Source/FikaAmazonAPI/Utils/RateLimits.cs index fae7151a..5237b4d3 100644 --- a/Source/FikaAmazonAPI/Utils/RateLimits.cs +++ b/Source/FikaAmazonAPI/Utils/RateLimits.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime; using System.Threading; using System.Threading.Tasks; @@ -49,19 +50,12 @@ public async Task WaitForPermittedRequest(CancellationToken cancellationToken = var requestsUsedAtOnset = RequestsUsed; // when requests used more than zero, replenish according to time elapsed - var requestsToReplenish = requestsUsedAtOnset == 0 || ratePeriodMs == 0 ? 0 : (DateTime.UtcNow - LastRequestReplenished).Milliseconds / ratePeriodMs; - if (requestsToReplenish > 0) - { - IncrementAvailableTokens(requestsToReplenish, requestsUsedAtOnset); - } + IncrementAvailableTokens(debugMode); var nextRequestsSent = RequestsUsed + 1; var nextRequestsSentTxt = (nextRequestsSent > Burst) ? "FULL" : nextRequestsSent.ToString(); - if (debugMode) - { - string output = $"[RateLimits ,{this.RateLimitType,15}]: {DateTime.UtcNow.ToString(),10}\t Request/Burst: {nextRequestsSentTxt}/{Burst}\t Rate: {Rate}/{ratePeriodMs}ms"; - Console.WriteLine(output); - } + + WriteDebug($"[RateLimits ,{this.RateLimitType,15}]: {DateTime.UtcNow.ToString(),10}\t Request/Burst: {nextRequestsSentTxt}/{Burst}\t Rate: {Rate}/{ratePeriodMs}ms", debugMode); var requestIsPermitted = false; @@ -75,13 +69,14 @@ public async Task WaitForPermittedRequest(CancellationToken cancellationToken = var incomingRequestTokenTime = LastRequestReplenished.AddMilliseconds(ratePeriodMs); if (incomingRequestTokenTime > DateTime.UtcNow) { + WriteDebug($"Next token expected at {incomingRequestTokenTime}, waiting", debugMode); await Task.Delay(100); continue; } else { // replenish token - IncrementAvailableTokens(1, currentRequestsUsed); + IncrementAvailableTokens(debugMode); } if (RequestsUsed <= 0) @@ -92,7 +87,7 @@ public async Task WaitForPermittedRequest(CancellationToken cancellationToken = } // now remove token from bucket for pending request - requestIsPermitted = TryDecrementAvailableTokens(); + requestIsPermitted = TryDecrementAvailableTokens(debugMode); } } @@ -102,20 +97,28 @@ internal void SetRateLimit(decimal rate) } // increments available tokens, unless another thread has already incremented them. - private void IncrementAvailableTokens(int amountToIncrementBy, int initialAmount) + private void IncrementAvailableTokens(bool isDebug) { + WriteDebug($"Attempting to increment tokens", isDebug); lock (_locker) { - if (RequestsUsed >= initialAmount) + var ratePeriodMilliseconds = GetRatePeriodMs(); + var requestsToReplenish = ratePeriodMilliseconds == 0 ? 0 : (DateTime.UtcNow - LastRequestReplenished).Milliseconds / ratePeriodMilliseconds; + WriteDebug($"{requestsToReplenish} tokens to replenish since {LastRequestReplenished}", isDebug); + if (requestsToReplenish == 0 || RequestsUsed == 0) { - RequestsUsed = Math.Max(RequestsUsed - amountToIncrementBy, 0); - LastRequestReplenished = DateTime.UtcNow; + return; } + + RequestsUsed = Math.Max(RequestsUsed - requestsToReplenish, 0); + LastRequestReplenished = DateTime.UtcNow; + + WriteDebug($"Incremented tokens by {requestsToReplenish}", isDebug); } } // will try to decrement available tokens, will fail if another thread has used last of burst quota - private bool TryDecrementAvailableTokens() + private bool TryDecrementAvailableTokens(bool isDebug) { var succeeded = false; @@ -123,12 +126,25 @@ private bool TryDecrementAvailableTokens() { if (RequestsUsed < Burst) { + WriteDebug($"Token is available for use, requests used = {RequestsUsed}", isDebug); RequestsUsed++; succeeded = true; - } + } + else + { + WriteDebug($"Caller will need to wait for token, {Burst} requests used", isDebug); + } } return succeeded; } + + private void WriteDebug(string message, bool isDebug) + { + if (isDebug) + { + Console.WriteLine(message); + } + } } }