В faraday / adapter / rack.rb возникает ошибка TimeoutError как для тайм-аутов открытия, так и для чтения:
timeout = env[:request][:timeout] || env[:request][:open_timeout]
response = if timeout
Timer.timeout(timeout, Faraday::Error::TimeoutError) { execute_request(env, rack_env) }
else ... end
Согласно https://stackoverflow.com/questions/10322283/what-is-timeout-and-open-timeout-in-faraday , open_timeout предназначен для TCP-соединения, а таймаут - для чтения ответа.
Было бы неплохо иметь отдельные типы исключений для этих таймаутов. Тогда мы могли определить, следует ли повторять запрос. Имеет ли смысл добавлять что-то вроде Faraday :: Error :: OpenTimeoutError и Faraday :: Error :: ResponseTimeoutError и использовать их здесь?
Привет @coberlin Я считаю, что это может быть хорошим дополнением, я просто боюсь обратной совместимости.
Однако возможное решение для этого может заключаться в том, чтобы унаследовать OpenTimeoutError
и ResponseTimeoutError
от TimeoutError
, чтобы существующие rescue
s продолжали работать должным образом.
Определенно стоит попробовать 😃
Rack_adapter может быть неподходящим местом для этой функции. Я думаю, что Rack-приложения не обязательно различают тайм-ауты открытия и чтения. Возможно, эта функция будет работать в адаптере HTTPClient или других адаптерах? Из адаптера / httpclient.rb:
@app.call env
rescue ::HTTPClient::TimeoutError, Errno::ETIMEDOUT
raise Faraday::Error::TimeoutError, $!
rescue ::HTTPClient::BadResponseError => err
if err.message.include?('status 407')
raise Faraday::Error::ConnectionFailed, %{407 "Proxy Authentication Required "}
else
raise Faraday::Error::ClientError, $!
end
rescue Errno::ECONNREFUSED, IOError, SocketError
raise Faraday::Error::ConnectionFailed, $!
rescue => err
if defined?(OpenSSL) && OpenSSL::SSL::SSLError === err
raise Faraday::SSLError, err
else
raise
end
:: HTTPClient :: TimeoutError имеет 3 подкласса ConnectTimeoutError, ReceiveTimeoutError, SendTimeoutError, см., Например, http://www.rubydoc.info/gems/httpclient/2.1.5.2/HTTPClient/TimeoutError
У Фарадея уже есть Faraday :: Error :: ConnectionFailed. Подходит ли это для ConnectTimeoutError? Faraday :: Error :: TimeoutError можно подразделить на Faraday :: Error :: ReceiveTimeoutError и Faraday :: Error :: SendTimeoutError.
У Фарадея уже есть Faraday :: Error :: ConnectionFailed. Подходит ли это для ConnectTimeoutError?
Это имеет смысл, но не будет иметь обратной совместимости. Мы должны помнить, что люди уже ловят Faraday::Error::TimeoutError
в своем приложении, поэтому переход на ConnectionFailed
остановит эти случаи.
Вместо этого мы хотим определить 2 подкласса для Faraday::Error::TimeoutError
, имена которых должны быть как можно более общими:
Следующий шаг - перейти к каждому адаптеру и соответствующим образом сопоставить исключения адаптера. Например, для HTTPClient:
Напоследок надо добавить тесты там, где это возможно :)
Эй, ребята.
У нас было это обсуждение «немного» назад (https://github.com/lostisland/faraday/pull/324).
Я попробую еще раз (https://github.com/mistersourcerer/faraday/tree/718_mrsrcr_timeout-wrapping-2nd-chance), попробую открыть новый PR, как только у меня будет какой-то прогресс.
Привет, @mistersourcerer , спасибо за толчок, я совершенно не знал, что обсуждение имело место.
Я немного сбит с толку, когда вижу, что PR закрыт, но изменение кода, должно быть, я знаю, что вы каким-то образом слили свое изменение в конце 😄
В этом случае мы будем признательны за вашу помощь, поскольку я думаю, что вы уже довольны тестированием Timeout из вашей предыдущей работы (хотя мы говорим о 3 года назад!).
Я надеюсь, что мои объяснения по поводу OpenTimeoutError
и ReadTimeoutError
ясны, но если это не так, дайте мне знать.
Не торопитесь и откройте пул-реквест, когда закончите 👍
Привет, @iMacTia.
Если я правильно помню, тогда нам не удалось разрешить ситуацию. Но я не совсем уверен, почему.
Основная проблема заключалась в том, чтобы написать тест, который неизменно давал сбой для всех адаптеров. Так что я не думаю, что в то время мой код вообще был объединен.
В любом случае, у меня есть идея по этому поводу через несколько лет, ха-ха, давайте посмотрим, как это пойдет.
Прямо сейчас тесты для _EMSynchrony_ не работают на Трэвисе, но не локально. Пытаюсь понять это. Я думаю даже об открытом "раннем" пиаре, так что, может быть, мы сможем это обсудить.
И ваше объяснение кристально ясное, кажется, идеальный способ согласиться с этим.
Спасибо за отличную работу, чувак.
Спасибо @mistersourcerer!
RE ваши изменения: я не совсем уверен в том, что произошло, но я вижу, что @mislav наконец объединил ваши изменения здесь: https://github.com/lostisland/faraday/commit/f73d13ee09814fa68b37efa7bddafa47331948c2
Так что радуйтесь, Errno::ETIMEDOUT
уже обернут в Faraday::Error::TimeoutError
на большинстве (если не на всех) адаптерах 😄
Спасибо за работу над этим @mistersourcerer!
Глядя на ваш коммит здесь , мне интересно, нужны ли нам для обратной совместимости 2 новых подкласса: OpenConnectionError < ConnectionError
для Net :: HTTP и OpenTimeoutError < TimeoutError
для HttpClient?
Похоже, в этом вопросе есть некоторая путаница.
Причина в том, что на # 438 было принято решение обрабатывать ошибки «тайм-аут открытия» как ConnectionFailed
. Возможно, это лучшее решение, но на самом деле кто-то решил пойти по этому пути.
Теперь это влияет не только на адаптер стойки, но и на все остальные адаптеры, и их поведение, вероятно, даже не согласовано.
Я планирую стандартизировать их поведение на том же уровне, что и в версии 1.0, и оставлю эту проблему для справки.
Продолжение моего предыдущего комментария.
По сути, в настоящее время мы генерируем ошибку Faraday::ConnectionFailed
в случае тайм-аута открытия, в то время как мы поднимаем Faraday::TimeoutError
для тайм-аута чтения. Хотя разные адаптеры в настоящее время ведут себя по-разному, это, по-видимому, наиболее распространенное поведение.
Это было решено примерно 3 года назад, но здесь мы обсуждаем наличие Faraday::TimeoutError
для первого случая (с соответствующими подклассами для различения между открытием и закрытием).
С одной стороны, я понимаю, что это было бы ближе к реальности, но если я проанализирую проблему с точки зрения реализации, мне будет трудно оправдать это изменение.
Если я позвоню в службу и получу в ответ ConnectionFailed
, я знаю, что мой вызов не может быть обработан. Возможно, я не подключился к серверу, или не смог разрешить имя хоста, или произошло что-то еще.
Если я верну TimeoutError
, мой запрос мог быть обработан или частично обработан, и я мог пропустить ответ. Это совершенно другой случай, и он требует перепроверить с сервером, на который я звонил, что произошло.
Превращение тайм-аута открытия в подкатегорию TimeoutError
означает принятие простой ситуации (запрос не обработан) в более сложном домене и, безусловно, требует дополнительных проверок, чтобы решить, что делать: было ли это тайм-аут открытия или чтение тайм-аут?
Нам нужно:
@coberlin @ Erik-Эскобедо @mislav @mistersourcerer хотел бы услышать ваши мысли после рассмотрения выше 😄
Использование ConnectionFailed
для ошибок тайм-аута открытия имеет для меня смысл и обеспечит то, что я надеялся получить, отличая ошибки тайм-аута открытия от других ошибок тайм-аута. Для согласованности адаптера это будет означать, например, что Net::HTTP
в порядке, но HTTPClient
изменится с сопоставлением ConnectTimeoutErrors с ConnectionFailed вместо TimeoutError.
Ничего страшного, как только мы решим стандартизировать все адаптеры для одного и того же поведения (очевидно, в версии 1.0, поскольку это будет обратно несовместимо)
Выбрасывая вариант использования на ринг:
На работе мы страдали от некоторых открытых таймаутов из-за того, что Nginx + Kubernetes не мог направить к зависшим модулям (или чему-то еще). Как бы то ни было, NetHTTP выдавал ошибки OpenTimeout и ReadTimeout, и это было действительно удобно для отладки, которая была какой.
Теперь, когда мы перешли на Typhoeus, у нас, к сожалению, все таймауты отключены вместе, и нам сложно сказать, улучшилась ли наша работа над проблемами nginx + kuber, или мы только сейчас успешно делаем больше запросов к все более затрудняющимся система. В любом случае количество тайм-аутов примерно одинаково, и, не разделяя их, мы как бы застреваем в догадках.
Я не думаю, что простого добавления Faraday::OpenTimeoutError
достаточно, у нас должны быть Faraday::OpenTimeoutError
и Faraday::ReadTimeoutError
продолжающиеся от Faraday::TimeoutError
IMO.
@philsturgeon, а что насчет другого предлагаемого решения, это тоже поможет?
Тайм-аут открытия -> Faraday::ConnectionFailed
Тайм-аут чтения -> Faraday::TimeoutError
Это должно быть поведение на всех адаптерах, но, к сожалению, некоторые из них работают не так, как ожидалось (например, Typhoeus).
Я чувствую, что это разные вещи.
ConnectionFailed выглядит как «Я понятия не имею, как разговаривать с этим сервером», например, неверный DNS / IP и т. Д.
OpenTimeout: «Я знаю, где находится этот сервер, и просто жду, чтобы он что-то сделал»
OpenTimeout: «Я знаю, где находится этот сервер, и просто жду, чтобы он что-то сделал»
Я не согласен с этим, скорее скажу:
Тайм-аут открытия: я пытаюсь связаться с сервером, но не могу связаться с ним (Примечание: соединение еще не установлено или «открыто»).
Тайм-аут чтения: я установил соединение с сервером, но я жду, пока он что-то сделает (чтение вывода).
Неисправный брандмауэр / прокси / load_balancer - это просто простые примеры того, как вы можете получить тайм-аут открытия, но во всех этих случаях соединение с сервером еще не началось. Для меня это самый важный момент. «ConnectionFailed» для меня просто означает: я не могу подключиться к серверу. И он идеально подходит для этих случаев.
Если вы все еще думаете, что должна существовать конкретная ошибка Faraday :: OpenTimeoutError, я бы посоветовал унаследовать ее от ConnectionFailed
а не от TimeoutError
но я согласен, что это немного сбивает с толку и не уверен, как это помогло бы на практике.
См. Мой предыдущий https://github.com/lostisland/faraday/issues/718#issuecomment -343957963 о том, как это может действительно помочь справиться с ошибкой.
Имеет ли это смысл? Я хотел бы найти решение, подходящее всем
Я принимаю ваши более точные определения тайм-аута открытия, но прихожу к другому выводу.
Вы считаете, что время ожидания открытия считается ошибкой соединения, поскольку время, в течение которого вы хотите дождаться этого соединения, считается частью соединения. «Не удалось установить соединение за 5 секунд», конечно, имеет смысл, если вы объясните это так, но многие люди думают иначе.
Для многих, открытая таймаут просто означает , что до сих пор не произошло. Это делает его менее однозначным заявлением, чем большинство сбоев соединения, например «Сервер не работает» или «Этот DNS - мусор».
Я полагаю, это не имеет большого значения, так как сбои подключения и тайм-ауты открытия должны быть повторены, так как тайм-аут чтения может считаться основанием для отказа?
Я согласен, мы можем сколько угодно спорить о чтении, которое можно применить к нему, но практичность моей точки зрения - это то, что вы сказали: если вы получаете тайм-аут открытия, это означает, что вы можете повторить запрос, если вы получите Тайм-аут чтения означает, что вы должны быть ОЧЕНЬ осторожны, поскольку ваш запрос мог быть обработан (полностью или частично). По совпадению практическое значение открытого тайм-аута совпадает с практическим значением неудачного соединения, поэтому я бы сделал его унаследованным оттуда.
Сегодня люди ловят исключения ConnectionFailed
и TimeoutError
и логика, лежащая в основе, очень вероятно, отражает то, что мы говорили ранее. Если мы введем новое исключение как подкласс ConnectionFailed
тогда высоки шансы, что большая часть (если не все) приложение не потребует никаких изменений.
Я понимаю (и согласен) с семантической точки зрения, хотя OpenTimeout - это просто еще один тип тайм-аута.
Но что, если мы вместо этого назовем это ConnectionTimedOut
?
Возникла бы некоторая путаница в том, что open_timeout: X
- это имя свойства, которое говорит, сколько времени ждать до выдачи ConnectionTimedOut
.
Хороший момент 😞
Назовите это ConnectionOpenTimeout
? Он проясняет проблему с подключением и дает понять, что это тайм-аут открытия. Я думаю, это имя сохраняет понимание в соответствии со значением «Не удалось установить соединение за X секунд», хотя некоторые люди все еще могут задаться вопросом, почему тайм-аут не является тайм-аутом. 😅
Мне нравится 👍!
@iMacTia, эй, если бы ты мог мне
Спасибо @philsturgeon , было бы здорово! Позвольте мне резюмировать основные моменты по этому поводу:
ConnectionOpenTimeout
которая будет унаследована от ConnectionFailed
.TimeoutError
.Я что-нибудь пропустил?
Будет ли ConnectionOpenTimeout
добавлено к исключениям по умолчанию, обработанным Faraday :: Request :: Retry?
@mjhoy , это хороший
Однако Faraday::Request::Retry
можно настроить, поэтому ничто не мешает вам добавить ConnectionOpenTimeout
или даже ConnectionFailed
в список исключений, которые вы хотите обработать.
Я хотел бы узнать больше «мнения сообщества», прежде чем добавлять их в список исключений по умолчанию.
Иногда мы сталкиваемся с ошибками OpenTimeout
с конечной точкой API, которые необходимо повторить; из приведенной выше логики кажется, что время ожидания открытия безопасно для повторной попытки (более безопасно, чем тайм-аут чтения). Мы также предполагали, что с промежуточным программным обеспечением retry будут повторяться тайм-ауты открытия и чтения; в документации говорится: «По умолчанию повторяется 2 попытки и обрабатываются только исключения из-за тайм-аута». По умолчанию обрабатываются исключения Errno::ETIMEDOUT, Timeout::Error,
Error::TimeoutError
, а Net::OpenTimeout
является подклассом Timeout::Error
; не было особенно ясно, что Фарадей относился к ним иначе. Так что, возможно, следует обновить документацию? В любом случае да, мы настроили промежуточное ПО; Мне просто интересно, имеет ли смысл значение по умолчанию.
@mjhoy Вы правы, говоря, что Timeout::Error
включает Net::OpenTimeout
поэтому с текущей реализацией кажется, что время ожидания открытия также следует повторить. Более того, Timeout::Error
восстанавливается и повторно вызывается адаптером при нормальных обстоятельствах, поэтому его присутствие в списке исключений может быть ненужным или просто для дополнительной безопасности.
Как только мы закончим рефакторинг исключений, Net::OpenTimeout
будет вызвано как новое исключение и, как я сказал в своем комментарии, изменит текущее поведение по умолчанию.
Я все еще считаю, что это не должно быть частью настроек по умолчанию, но это определенно то, что нужно учитывать при выполнении работы.
Спасибо, что подняли этот вопрос 😄
Привет, извините, это томилось в моей команде в течение года, и теперь наши приоритеты сильно изменились. Я не буду заниматься этим вопросом, но удачи!
@iMacTia Кто-нибудь работает над этим изменением? Я не против использовать это в версии 2.0.
Привет @ ragav0102 , спасибо за поддержку!
Никто еще не работает над этим, так как мы все еще настаиваем на выпуске версии 1.0.
Мы определенно будем признательны за помощь, но у нас пока нет плана для версии 2.0, поэтому я не могу сказать, когда она будет выпущена, поэтому вашим изменениям, возможно, придется подождать несколько месяцев, прежде чем их можно будет использовать.
Если вам это нужно в одном из ваших проектов, то, вероятно, это невозможно.
Если вы только что сдали экзамен и хотели бы внести свой вклад, я бы посоветовал вам выбрать что-нибудь, запланированное на v1.0, поскольку оно будет выпущено гораздо раньше 😄
Понятно!
Самый полезный комментарий
Привет @coberlin Я считаю, что это может быть хорошим дополнением, я просто боюсь обратной совместимости.
Однако возможное решение для этого может заключаться в том, чтобы унаследовать
OpenTimeoutError
иResponseTimeoutError
отTimeoutError
, чтобы существующиеrescue
s продолжали работать должным образом.Определенно стоит попробовать 😃