Skip to content

Commit

Permalink
Fix retries
Browse files Browse the repository at this point in the history
- Calculate delay correctly
- Only retry when rate-limited
- Don't progress delays between retries
- Round unprecise delays
- Extract rate-limiting to a method
  • Loading branch information
hakanensari committed Sep 12, 2024
1 parent e10dd01 commit 66b48c4
Show file tree
Hide file tree
Showing 52 changed files with 297 additions and 281 deletions.
15 changes: 12 additions & 3 deletions bin/generate-code
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ module Generator
operation.merge(
"path" => path,
"method" => method.upcase,
"rate_limit" => extract_rate_limit(operation),
"retriable_delay" => extract_retriable_delay(operation),
"parameters" => parameters,
"body_param" => parameters&.find { |p| p["in"] == "body" },
"query_params" => parameters&.select { |p| p["in"] == "query" },
Expand Down Expand Up @@ -219,16 +219,25 @@ module Generator
end.join("\n")
end

def extract_rate_limit(operation)
def extract_retriable_delay(operation)
description = operation["description"]
return unless description

# Match rate limit from tables with or without "Plan type" column
# Format 1: | Plan type | Rate (requests per second) | Burst |
# Format 2: | Rate (requests per second) | Burst |
table_match = description.match(/Burst \|\n\|(?: *---- *\|){2,3}\n(?:\|[^|]*){0,1}\| (\S+) \|[^|]*\|/)
return unless table_match

table_match[1].to_f if table_match
rate_limit = table_match[1].to_f
retriable_delay = 1.0 / rate_limit

# Round to nearest integer if more than 3 decimal places
if (retriable_delay * 1000).round != (retriable_delay * 1000)
retriable_delay.round.to_f
else
retriable_delay
end
end
end

Expand Down
4 changes: 2 additions & 2 deletions bin/templates/api.rb.erb
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ module Peddler
<% method_args << "body:" if operation["body_param"] %>
<% method_args << "params:" if operation["query_params"]&.any? %>

<% if operation["rate_limit"] %>
retriable(delay: proc { |i| <%= operation["rate_limit"] %> * i }).<%= http_method %>(<%= method_args.join(", ") %>)
<% if operation["retriable_delay"] %>
rate_limit(<%= operation["retriable_delay"] %>).<%= http_method %>(<%= method_args.join(", ") %>)
<% else %>
<%= http_method %>(<%= method_args.join(", ") %>)
<% end %>
Expand Down
15 changes: 11 additions & 4 deletions lib/peddler/api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,16 @@ def http
)
end

# Retries with a rate limit when the API returns a 429
#
# @param [Float] delay The delay in seconds before retrying
# @return [self]
def rate_limit(delay)
# TODO: Remove when HTTP 6.0 is released
@http = @http.retriable(delay: delay, retry_statuses: [429]) if @http.respond_to?(:retriable)
self
end

# @!method use(*features)
# Turn on [HTTP](https://github.com/httprb/http) features
#
Expand All @@ -58,15 +68,12 @@ def http
# @return [self]
#
# @!method retriable(**options)
# Retries requests if they fail due to socket or `5xx` errors.
# Retries requests if they fail due to socket or `5xx` errors
#
# @param (see Performer#initialize)
# @return [self]
[:via, :use, :retriable].each do |method|
define_method(method) do |*args, &block|
# TODO: Remove when HTTP 6.0 is released
return self if method == :retriable

@http = http.send(method, *args, &block)
self
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def get_inbound_shipment(shipment_id, sku_quantities: nil)
"skuQuantities" => sku_quantities,
}.compact

retriable(delay: proc { |i| 2.0 * i }).get(path, params:)
rate_limit(0.5).get(path, params:)
end

# Retrieves a summary of all the inbound AWD shipments associated with a merchant, with the ability to apply
Expand Down Expand Up @@ -51,7 +51,7 @@ def list_inbound_shipments(sort_by: nil, sort_order: nil, shipment_status: nil,
"nextToken" => next_token,
}.compact

retriable(delay: proc { |i| 1.0 * i }).get(path, params:)
rate_limit(1.0).get(path, params:)
end

# Lists AWD inventory associated with a merchant with the ability to apply optional filters.
Expand All @@ -73,7 +73,7 @@ def list_inventory(sku: nil, sort_order: nil, details: nil, next_token: nil, max
"maxResults" => max_results,
}.compact

retriable(delay: proc { |i| 2.0 * i }).get(path, params:)
rate_limit(0.5).get(path, params:)
end
end
end
Expand Down
20 changes: 10 additions & 10 deletions lib/peddler/api/aplus_content_2020_11_01.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def search_content_documents(marketplace_id, page_token: nil)
"pageToken" => page_token,
}.compact

retriable(delay: proc { |i| 10.0 * i }).get(path, params:)
rate_limit(0.1).get(path, params:)
end

# Creates a new A+ Content document.
Expand All @@ -44,7 +44,7 @@ def create_content_document(marketplace_id, post_content_document_request)
"marketplaceId" => marketplace_id,
}.compact

retriable(delay: proc { |i| 10.0 * i }).post(path, body:, params:)
rate_limit(0.1).post(path, body:, params:)
end

# Returns an A+ Content document, if available.
Expand All @@ -62,7 +62,7 @@ def get_content_document(content_reference_key, marketplace_id, included_data_se
"includedDataSet" => included_data_set,
}.compact

retriable(delay: proc { |i| 10.0 * i }).get(path, params:)
rate_limit(0.1).get(path, params:)
end

# Updates an existing A+ Content document.
Expand All @@ -80,7 +80,7 @@ def update_content_document(content_reference_key, marketplace_id, post_content_
"marketplaceId" => marketplace_id,
}.compact

retriable(delay: proc { |i| 10.0 * i }).post(path, body:, params:)
rate_limit(0.1).post(path, body:, params:)
end

# Returns a list of ASINs related to the specified A+ Content document, if available. If you do not include the
Expand Down Expand Up @@ -109,7 +109,7 @@ def list_content_document_asin_relations(content_reference_key, marketplace_id,
"pageToken" => page_token,
}.compact

retriable(delay: proc { |i| 10.0 * i }).get(path, params:)
rate_limit(0.1).get(path, params:)
end

# Replaces all ASINs related to the specified A+ Content document, if available. This may add or remove ASINs,
Expand All @@ -130,7 +130,7 @@ def post_content_document_asin_relations(content_reference_key, marketplace_id,
"marketplaceId" => marketplace_id,
}.compact

retriable(delay: proc { |i| 10.0 * i }).post(path, body:, params:)
rate_limit(0.1).post(path, body:, params:)
end

# Checks if the A+ Content document is valid for use on a set of ASINs.
Expand All @@ -147,7 +147,7 @@ def validate_content_document_asin_relations(marketplace_id, post_content_docume
"asinSet" => asin_set,
}.compact

retriable(delay: proc { |i| 10.0 * i }).post(path, body:, params:)
rate_limit(0.1).post(path, body:, params:)
end

# Searches for A+ Content publishing records, if available.
Expand All @@ -168,7 +168,7 @@ def search_content_publish_records(marketplace_id, asin, page_token: nil)
"pageToken" => page_token,
}.compact

retriable(delay: proc { |i| 10.0 * i }).get(path, params:)
rate_limit(0.1).get(path, params:)
end

# Submits an A+ Content document for review, approval, and publishing.
Expand All @@ -184,7 +184,7 @@ def post_content_document_approval_submission(content_reference_key, marketplace
"marketplaceId" => marketplace_id,
}.compact

retriable(delay: proc { |i| 10.0 * i }).post(path, params:)
rate_limit(0.1).post(path, params:)
end

# Submits a request to suspend visible A+ Content. This neither deletes the content document nor the ASIN
Expand All @@ -201,7 +201,7 @@ def post_content_document_suspend_submission(content_reference_key, marketplace_
"marketplaceId" => marketplace_id,
}.compact

retriable(delay: proc { |i| 10.0 * i }).post(path, params:)
rate_limit(0.1).post(path, params:)
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/peddler/api/application_management_2023_11_30.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class ApplicationManagement20231130 < API
def rotate_application_client_secret
path = "/applications/2023-11-30/clientSecret"

retriable(delay: proc { |i| 0.0167 * i }).post(path)
rate_limit(60.0).post(path)
end
end
end
Expand Down
4 changes: 2 additions & 2 deletions lib/peddler/api/catalog_items_2020_12_01.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def search_catalog_items(keywords, marketplace_ids, included_data: nil, brand_na
"locale" => locale,
}.compact

retriable(delay: proc { |i| 2.0 * i }).get(path, params:)
rate_limit(0.5).get(path, params:)
end

# Retrieves details for an item in the Amazon catalog.
Expand All @@ -63,7 +63,7 @@ def get_catalog_item(asin, marketplace_ids, included_data: nil, locale: nil)
"locale" => locale,
}.compact

retriable(delay: proc { |i| 2.0 * i }).get(path, params:)
rate_limit(0.5).get(path, params:)
end
end
end
Expand Down
4 changes: 2 additions & 2 deletions lib/peddler/api/catalog_items_2022_04_01.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def search_catalog_items(
"keywordsLocale" => keywords_locale,
}.compact

retriable(delay: proc { |i| 2.0 * i }).get(path, params:)
rate_limit(0.5).get(path, params:)
end

# Retrieves details for an item in the Amazon catalog.
Expand All @@ -76,7 +76,7 @@ def get_catalog_item(asin, marketplace_ids, included_data: nil, locale: nil)
"locale" => locale,
}.compact

retriable(delay: proc { |i| 2.0 * i }).get(path, params:)
rate_limit(0.5).get(path, params:)
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/peddler/api/catalog_items_v0.rb
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ def list_catalog_categories(marketplace_id, asin: nil, seller_sku: nil)
"SellerSKU" => seller_sku,
}.compact

retriable(delay: proc { |i| 1.0 * i }).get(path, params:)
rate_limit(1.0).get(path, params:)
end
end
end
Expand Down
10 changes: 5 additions & 5 deletions lib/peddler/api/data_kiosk_2023_11_15.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def get_queries(processing_statuses: nil, page_size: nil, created_since: nil, cr
"paginationToken" => pagination_token,
}.compact

retriable(delay: proc { |i| 0.0222 * i }).get(path, params:)
rate_limit(45.0).get(path, params:)
end

# Creates a Data Kiosk query request.
Expand All @@ -50,7 +50,7 @@ def create_query(body)
path = "/dataKiosk/2023-11-15/queries"
body = body

retriable(delay: proc { |i| 0.0167 * i }).post(path, body:)
rate_limit(60.0).post(path, body:)
end

# Cancels the query specified by the `queryId` parameter. Only queries with a non-terminal `processingStatus`
Expand All @@ -64,7 +64,7 @@ def create_query(body)
def cancel_query(query_id)
path = "/dataKiosk/2023-11-15/queries/#{query_id}"

retriable(delay: proc { |i| 0.0222 * i }).delete(path)
rate_limit(45.0).delete(path)
end

# Returns query details for the query specified by the `queryId` parameter. See the `createQuery` operation for
Expand All @@ -75,7 +75,7 @@ def cancel_query(query_id)
def get_query(query_id)
path = "/dataKiosk/2023-11-15/queries/##{query_id}"

retriable(delay: proc { |i| 2.0 * i }).get(path)
rate_limit(0.5).get(path)
end

# Returns the information required for retrieving a Data Kiosk document's contents. See the `createQuery`
Expand All @@ -86,7 +86,7 @@ def get_query(query_id)
def get_document(document_id)
path = "/dataKiosk/2023-11-15/documents/#{document_id}"

retriable(delay: proc { |i| 0.0167 * i }).get(path)
rate_limit(60.0).get(path)
end
end
end
Expand Down
10 changes: 5 additions & 5 deletions lib/peddler/api/easy_ship_2022_03_23.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def list_handover_slots(list_handover_slots_request: nil)
path = "/easyShip/2022-03-23/timeSlot"
body = list_handover_slots_request

retriable(delay: proc { |i| 1.0 * i }).post(path, body:)
rate_limit(1.0).post(path, body:)
end

# Returns information about a package, including dimensions, weight, time slot information for handover, invoice
Expand All @@ -42,7 +42,7 @@ def get_scheduled_package(amazon_order_id, marketplace_id)
"marketplaceId" => marketplace_id,
}.compact

retriable(delay: proc { |i| 1.0 * i }).get(path, params:)
rate_limit(1.0).get(path, params:)
end

# Schedules an Easy Ship order and returns the scheduled package information. This operation does the following: *
Expand All @@ -62,7 +62,7 @@ def create_scheduled_package(create_scheduled_package_request)
path = "/easyShip/2022-03-23/package"
body = create_scheduled_package_request

retriable(delay: proc { |i| 1.0 * i }).post(path, body:)
rate_limit(1.0).post(path, body:)
end

# Updates the time slot for handing over the package indicated by the specified `scheduledPackageId`. You can get
Expand All @@ -77,7 +77,7 @@ def update_scheduled_packages(update_scheduled_packages_request: nil)
path = "/easyShip/2022-03-23/package"
body = update_scheduled_packages_request

retriable(delay: proc { |i| 1.0 * i }).patch(path, body:)
rate_limit(1.0).patch(path, body:)
end

# This operation automatically schedules a time slot for all the `amazonOrderId`s given as input, generating the
Expand All @@ -101,7 +101,7 @@ def create_scheduled_package_bulk(create_scheduled_packages_request)
path = "/easyShip/2022-03-23/packages/bulk"
body = create_scheduled_packages_request

retriable(delay: proc { |i| 1.0 * i }).post(path, body:)
rate_limit(1.0).post(path, body:)
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/peddler/api/fba_inbound_eligibility_v1.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def get_item_eligibility_preview(asin, program, marketplace_ids: nil)
"program" => program,
}.compact

retriable(delay: proc { |i| 1.0 * i }).get(path, params:)
rate_limit(1.0).get(path, params:)
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/peddler/api/fba_inventory_v1.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def get_inventory_summaries(granularity_type, granularity_id, marketplace_ids, d
"marketplaceIds" => marketplace_ids,
}.compact

retriable(delay: proc { |i| 2.0 * i }).get(path, params:)
rate_limit(0.5).get(path, params:)
end

# Requests that Amazon create product-details in the Sandbox Inventory in the sandbox environment. This is a
Expand Down
Loading

0 comments on commit 66b48c4

Please sign in to comment.