Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion async-http-faraday.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@ Gem::Specification.new do |spec|

spec.required_ruby_version = ">= 3.3"

spec.add_dependency "async-http", "~> 0.42"
spec.add_dependency "async-http", "~> 0.99"
spec.add_dependency "faraday"
end
15 changes: 15 additions & 0 deletions context/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,21 @@ connection = Faraday.new(...) do |builder|
end
~~~

### Retrying Remote Failures

When a remote endpoint fails while processing a request, the adapter raises {ruby Faraday::ConnectionFailed}. You can use the `faraday-retry` middleware to retry idempotent requests:

~~~ruby
require "faraday/retry"

connection = Faraday.new(...) do |builder|
builder.request :retry, exceptions: [Faraday::ConnectionFailed]
builder.adapter :async_http
end
~~~

By default, `faraday-retry` only retries idempotent methods. Configure its retry policy explicitly before retrying other methods.

The value of isolation cannot be overstated - if you can design you program using a share-nothing (between threads) architecture, you will have a much easier time debugging and reasoning about your program, however this comes at the cost of increased resource usage.

Alternatively, if you do not want to cache client connections, you can use the `Async::HTTP::Faraday::Clients` interface, which closes the connection after each request:
Expand Down
1 change: 1 addition & 0 deletions gems.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
gem "bake-test-external"

gem "faraday-multipart"
gem "faraday-retry"

gem "sus-fixtures-async"
gem "sus-fixtures-async-http"
Expand Down
1 change: 1 addition & 0 deletions lib/async/http/faraday/adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ def self.setup_parallel_manager(**options)

# The exceptions that are considered connection errors and result in a `Faraday::ConnectionFailed` exception.
CONNECTION_EXCEPTIONS = [
::Protocol::HTTP::RemoteError,
Errno::EADDRNOTAVAIL,
Errno::ECONNABORTED,
Errno::ECONNREFUSED,
Expand Down
37 changes: 37 additions & 0 deletions test/async/http/faraday/adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

require "faraday"
require "faraday/multipart"
require "faraday/retry"

require "protocol/http/body/file"
require "protocol/multipart"
Expand Down Expand Up @@ -268,6 +269,42 @@ def get_response(url = bound_url, path = "/index", adapter_options: {})
end
end

with "a remote failure" do
it "can retry using Faraday middleware" do
attempts = 0
client = Object.new
client.define_singleton_method(:call) do |request|
attempts += 1

if attempts == 1
body = Protocol::HTTP::Body::Writable.new
body.close_write(Protocol::HTTP::RemoteError.new("The remote endpoint failed!"))
Protocol::HTTP::Response[200, {}, body]
else
Protocol::HTTP::Response[200, {}, ["Hello World"]]
end
end

clients = Object.new
clients.define_singleton_method(:with_client) do |endpoint, &block|
block.call(client)
end
clients.define_singleton_method(:close){}

connection = Faraday.new("https://example.com") do |builder|
builder.request :retry, max: 1, exceptions: [Faraday::ConnectionFailed]
builder.adapter :async_http, clients: proc{clients}
end

response = connection.get("/")

expect(response.body).to be == "Hello World"
expect(attempts).to be == 2
ensure
connection&.close
end
end

with "a multi-part post body" do
include Sus::Fixtures::Async::HTTP::ServerContext

Expand Down
Loading