Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Timeout bigger than 5s does not work with hackney adapter #151

Open
IvanIvanoff opened this issue Jan 12, 2018 · 14 comments
Open

Timeout bigger than 5s does not work with hackney adapter #151

IvanIvanoff opened this issue Jan 12, 2018 · 14 comments
Labels
bug 🔥 hackney Issues related to hackney adapter workaround

Comments

@IvanIvanoff
Copy link

IvanIvanoff commented Jan 12, 2018

In hackney version 1.3.1 they've reduced the default recv_timeout from infinity to 5s (source)

So, if you use Tesla.Middleware.Timeout with timeout bigger than 5s hackney will timeout regardless of your timeout.
A solution is to pass that recv_timeout to hackney's request method as HTTPoison does

There is no problem with the default httpc adapter as its default timeout is infinity

@teamon
Copy link
Member

teamon commented Jan 12, 2018

Right, currently the workaround would be to use for example

adapter :hackney, recv_timeout: 30_000

The general solution would mean "a middleware can set adapter-specific options" which does not sound good. A specific one would be to default hackney timeout to infinity unless overridden but I'm not 100% sold on that idea.

@emerleite
Copy link
Contributor

Maybe we could use the Middleware timeout to add adapter specific timeouts too

@HugoLnx
Copy link

HugoLnx commented Feb 12, 2018

The default could be to set the adapter specific timeout configuration to 500 milliseconds ahead of the middleware timeout. The adapter timeout would work as a "backup" and would not conflict with the Middleware behaviour.

But I think the best way to do that would be to extend the adapter interface to include a generic configuration to connection and request timeouts. So it would be adapter responsability to translate the generic timeout configuration to the client, and the middleware would use the adapter configuration instead of knowing the client specifics about timeout configuration.

What do you guys think?

@chulkilee
Copy link
Contributor

For those hit this problem but with Tesla.build_client/2 and Tesla.request/2 - pass :opts - e.g. opts: [recv_timeout: 30_000].

@teamon teamon added the bug 🔥 label Mar 5, 2018
@teamon
Copy link
Member

teamon commented Mar 26, 2018

Note for 1.0 users: You need to wrap adapter opts under :adapter key:

adapter :hackney, recv_timeout: 30_000
# or
MyClient.get("/", opts: [adapter: [recv_timeout: 30_000]])

@teamon teamon added the hackney Issues related to hackney adapter label May 29, 2018
@teamon teamon added the blocked label Aug 29, 2018
@lasseebert
Copy link

I'm not experiencing this issue.

Tesla 1.2.1
Hackney 1.15.1

Using it like this:

middleware = [
  {Tesla.Middleware.Timeout, timeout: timeout}
]

client = Tesla.client(middleware)
Tesla.get(client, url, opts)

I get the timeout I expect both for timeout values below and above 5s.

qgadrian added a commit to qgadrian/smokex that referenced this issue Nov 19, 2020
qgadrian added a commit to qgadrian/smokex that referenced this issue Nov 19, 2020
qgadrian added a commit to qgadrian/smokex that referenced this issue Nov 22, 2020
@pankaj-ag
Copy link

Is there any solution for this one? I have tried all the approaches and still facing the same issue.

@jasonbarbee
Copy link

jasonbarbee commented Aug 12, 2021

I got hackney working with this as middleware, it works - I'm pulling in a 10 second request from Tesla.
Mix versions - Tesla 1.3.0, Hackney - 1.16

Tesla.client(
          [
some other awesome middleware,
{Tesla.Adapter.Hackney, recv_timeout: 30_000}
])

Tesla.post!(
              client,
              url,
              payload,
              headers: [
                {"content-type", "text/xml"}
              ]
            )

@feld
Copy link

feld commented Sep 15, 2021

The general solution would mean "a middleware can set adapter-specific options" which does not sound good.

This is exactly what I expected Tesla.Middleware.Timeout to do: set the appropriate timeout option for each adapter or implement one if the adapter doesn't have one. I wonder how many users out there have set a timeout value but don't realize it isn't having the effect they expect because they haven't encountered a failure case yet? And when they do, they'll be rather confused why the timeout behavior works as expected when they disable their chosen adapter and are using :httpc which has a default timeout of infinity, thus the Tesla.Middleware.Timeout does what they expect?

If there really is no appetite for making Tesla translate common options to the backend adapters please rename Tesla.Middleware.Timeout to something else like Tesla.Middleware.CircuitBreaker.

edit: not trying to be harsh, just want to help others avoid this issue. Thanks for making Tesla!

@teamon teamon added this to Roadmap May 8, 2022
@teamon teamon moved this to Todo in Roadmap May 8, 2022
@teamon teamon removed the blocked label May 8, 2022
@austinarbor
Copy link

For anyone reaching this thread , in 1.4.0 the syntax in suggested fixes was not working for me. I got the Hackney timeout to work by passing the adapter module and options in as a tuple

defmodule MyModule do
  use Tesla
  adapter({Tesla.Adapter.Hackney, recv_timeout: 30_000})
...

or config.exs

config :tesla, adapter: {Tesla.Adapter.Hackney, recv_timeout: 30_000}

@yordis
Copy link
Member

yordis commented Feb 16, 2023

Hey peeps, I am going to close the issue for now. The situation is trivial to fix in your app compared to the complexity of changing the behavior since it will create a lot of coupling between the adapters and this particular middleware.

Feel free to open a PR with your proposed solution if you feel strongly about it.

For future reference, people need to pay attention to the configuration of the adapter and the middleware combination; therefore:

  1. The Adapters may have some timeout configuration
  2. Tesla.Middleware.Timeout has its timeout configuration

So you need to configure the values on your end to ensure they are working as intended.

@yordis yordis closed this as not planned Won't fix, can't repro, duplicate, stale Feb 16, 2023
@github-project-automation github-project-automation bot moved this from Todo to Done in Roadmap Feb 16, 2023
@yordis
Copy link
Member

yordis commented Sep 10, 2023

Leaving a note, we will try to address the situation by reserving a configuration for such value.

@arcanemachine
Copy link
Contributor

arcanemachine commented Dec 25, 2023

Thank you @austinarbor, that worked like a charm!

I had to use the first strategy since the second strategy would be overwritten by my test config which uses the Tesla Mock adapter.

@yordis
Copy link
Member

yordis commented Oct 25, 2024

@feld I would love to provide some Circuit Breaker middleware if possible.

I keep returning to this.

  • Should the middleware manipulate the adapter in any way?

I feel the answer should definitely be "NO." However, the middleware could set a reserved option for the adapter to read from.

  • Should the middleware pick inside the %Tesla.Env{} and see if the adapter implements timeout?

Yes, I am thinking on adding another optional callback that could tell the "features" a given adapter implements;

Pseudo-code:

defmodule Tesla.Middleware.Timeout do
  @behaviour Tesla.Middleware

  @default_timeout 1_000

  @impl Tesla.Middleware
  def call(env, next, opts) do
    timeout = Keyword.get(opts, :timeout, @default_timeout)

    with adapter <- Tesla.Client.adapter(env.__client__),
         true <- adapter != nil,
         true <- function_exported?(adapter, :features, 0),
         features <- apply(adapter, :features, []),
         true <- :timeout in features do
      env
      |> Tesla.put_new_opt(:timeout, timeout)
      |> Tesla.run(next)
    else
      _ -> run_task(env, next, opts)
    end
  end

  defp run_task(env, next, opts) do
    # ...
  end
end

@teamon what do think about this one?

@yordis yordis reopened this Oct 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug 🔥 hackney Issues related to hackney adapter workaround
Projects
Status: Done
Development

No branches or pull requests