Faraday: Bedakan TimeoutErrors untuk membuka dan membaca timeout

Dibuat pada 9 Agu 2017  ·  32Komentar  ·  Sumber: lostisland/faraday

Di faraday/adapter/rack.rb, TimeoutError dimunculkan untuk timeout buka dan baca:

timeout  = env[:request][:timeout] || env[:request][:open_timeout]
response = if timeout
  Timer.timeout(timeout, Faraday::Error::TimeoutError) { execute_request(env, rack_env) }
else ... end

Menurut https://stackoverflow.com/questions/10322283/what-is-timeout-and-open-timeout-in-faraday , open_timeout adalah untuk koneksi tcp dan batas waktu untuk respons yang dibaca.

Akan menyenangkan untuk memiliki jenis pengecualian terpisah untuk batas waktu ini. Kemudian kami dapat menentukan apakah akan mencoba kembali permintaan tersebut atau tidak. Apakah menambahkan sesuatu seperti Faraday::Error::OpenTimeoutError dan Faraday::Error::ResponseTimeoutError dan menggunakannya di sini masuk akal?

feature help wanted

Komentar yang paling membantu

Hai @coberlin Saya percaya ini mungkin tambahan yang bagus, saya hanya takut tentang kompatibilitas mundur.
Namun, solusi yang mungkin untuk ini mungkin adalah memiliki OpenTimeoutError dan ResponseTimeoutError untuk mewarisi dari TimeoutError , sehingga rescue yang ada akan tetap berfungsi seperti yang diharapkan.
Ini pasti layak untuk diuji

Semua 32 komentar

Hai @coberlin Saya percaya ini mungkin tambahan yang bagus, saya hanya takut tentang kompatibilitas mundur.
Namun, solusi yang mungkin untuk ini mungkin adalah memiliki OpenTimeoutError dan ResponseTimeoutError untuk mewarisi dari TimeoutError , sehingga rescue yang ada akan tetap berfungsi seperti yang diharapkan.
Ini pasti layak untuk diuji

rack_adapter mungkin tempat yang salah untuk fitur ini. Saya pikir aplikasi Rack tidak selalu membedakan antara buka dan baca batas waktu. Mungkin fitur ini akan berfungsi di adaptor HTTPClient atau adaptor lain? Dari adaptor/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 memiliki 3 subkelas ConnectTimeoutError, ReceiveTimeoutError, SendTimeoutError, lihat misalnya http://www.rubydoc.info/gems/httpclient/2.1.5.2/HTTPClient/TimeoutError

Faraday memiliki Faraday::Error::ConnectionFailed sudah. Apakah itu sesuai untuk ConnectTimeoutError? Faraday::Error::TimeoutError dapat disubklasifikasikan menjadi Faraday::Error::ReceiveTimeoutError dan Faraday::Error::SendTimeoutError.

Faraday memiliki Faraday::Error::ConnectionFailed sudah. Apakah itu sesuai untuk ConnectTimeoutError?

Ini masuk akal, tetapi tidak akan kompatibel ke belakang. Kita harus ingat bahwa orang sudah menangkap Faraday::Error::TimeoutError dalam aplikasi mereka sehingga beralih ke ConnectionFailed akan menghentikan kasus tersebut.
Apa yang ingin kita lakukan, sebagai gantinya, adalah mendefinisikan 2 subclass untuk Faraday::Error::TimeoutError yang namanya harus se-generik mungkin:

  • Faraday::Error::OpenTimeoutError
  • Faraday::Error::ReadTimeoutError

Langkah selanjutnya adalah masuk ke setiap adaptor dan memetakan pengecualian adaptor yang sesuai. Misalnya untuk HTTPClient:

  • HTTPClient::ConnectTimeoutError ==> Faraday::Error::OpenTimeoutError
  • HTTPClient::ReceiveTimeoutError ==> Faraday::Error::ReadTimeoutError
  • HTTPClient::TimeoutError ==> Faraday::Error::TimeoutError (ini juga akan menangkap SendTimeoutError, yang saya tidak yakin memiliki pemetaan yang sesuai di Faraday atau pengaturan tertentu)

Akhirnya, tes harus ditambahkan jika memungkinkan :)

Hai teman-teman.

Kami melakukan diskusi ini "sedikit" beberapa waktu lalu (https://github.com/lostisland/faraday/pull/324).

Saya mencoba lagi (https://github.com/mistersourcerer/faraday/tree/718_mrsrcr_timeout-wrapping-2nd-chance), akan mencoba dan membuka PR baru segera setelah saya memiliki beberapa kemajuan.

Hai @mistersourcerer , terima kasih atas dorongannya, saya sama sekali tidak menyadari bahwa diskusi terjadi.
Saya agak bingung ketika saya melihat PR ditutup, tetapi perubahan kodenya, mungkin Anda tahu bahwa perubahan Anda entah bagaimana akhirnya digabungkan
Bantuan Anda akan dihargai dalam kasus ini karena saya pikir Anda sudah nyaman dengan pengujian Timeout dari pekerjaan Anda sebelumnya (meskipun kita berbicara tentang 3 tahun yang lalu!).
Saya harap penjelasan saya tentang OpenTimeoutError dan ReadTimeoutError jelas, tetapi jika tidak demikian, beri tahu saya.
Luangkan waktu Anda dan buka permintaan tarik setelah Anda selesai 👍

Hai @iMacTia.

Jika saya ingat dengan benar, kami tidak berhasil menyelesaikan situasi saat itu. Tapi saya tidak yakin persis mengapa.
Masalah utamanya adalah menulis tes yang gagal secara konsisten di antara semua adaptor. Jadi, saya tidak berpikir kode saya digabung sama sekali pada saat itu.
Anyways, saya punya ide untuk ini beberapa tahun setelah haha, mari kita lihat bagaimana kelanjutannya.

Saat ini, pengujian untuk _EMSynchrony_ gagal di Travis, tetapi tidak secara lokal. Mencoba untuk mencari tahu. Saya sedang berpikir untuk membuka PR "awal" jadi mungkin kita bisa mendiskusikan ini.

Dan penjelasan Anda sangat jelas, sepertinya cara yang tepat untuk melakukannya.

Terima kasih untuk pekerjaan luar biasa ini, kawan.

Terima kasih @mistersourcerer!

RE perubahan Anda: Saya tidak begitu yakin dengan apa yang terjadi, tetapi saya melihat @mislav akhirnya menggabungkan perubahan Anda di sini: https://github.com/lostisland/faraday/commit/f73d13ee09814fa68b37efa7bddafa47331948c2

Jadi bersukacitalah, Errno::ETIMEDOUT sudah dibungkus dengan Faraday::Error::TimeoutError pada sebagian besar (jika tidak semua) adaptor

Terima kasih telah mengerjakan @mistersourcerer ini!

Melihat komit Anda di sini , saya ingin tahu apakah untuk kompatibilitas mundur, kami memerlukan 2 subkelas baru: OpenConnectionError < ConnectionError untuk Net::HTTP dan OpenTimeoutError < TimeoutError untuk HttpClient?

Tampaknya ada beberapa kebingungan di sekitar masalah ini.
Alasannya adalah bahwa keputusan diambil pada #438 untuk menangani kesalahan "waktu tunggu terbuka" sebagai ConnectionFailed . Itu bisa dibilang keputusan terbaik, tetapi kenyataannya adalah seseorang memutuskan untuk menempuh rute itu.
Sekarang, ini tidak hanya memengaruhi adaptor Rack tetapi juga semua adaptor lainnya, dan perilakunya mungkin bahkan tidak konsisten.
Saya berencana untuk membakukannya pada perilaku yang sama dengan v1.0 dan saya akan menyimpan masalah ini sebagai referensi.

Tindak lanjut di komentar saya sebelumnya.

Pada dasarnya, saat ini kami meningkatkan kesalahan Faraday::ConnectionFailed jika waktu tunggu terbuka, sementara kami menaikkan Faraday::TimeoutError untuk batas waktu baca. Meskipun adaptor yang berbeda saat ini berperilaku dengan cara yang berbeda, ini tampaknya menjadi perilaku yang paling umum.
Ini diputuskan sekitar 3 tahun yang lalu, tetapi di sini kita sedang mendiskusikan tentang memiliki Faraday::TimeoutError untuk kasus sebelumnya juga (dengan sub-kelas yang tepat untuk membedakan antara buka dan tutup).

Di satu sisi saya mengerti itu akan lebih dekat dengan kenyataan, tetapi jika saya menganalisis masalah dari sudut pandang implementasi, saya merasa sulit untuk membenarkan perubahan ini.
Jika saya menelepon layanan dan saya mendapatkan kembali ConnectionFailed , saya tahu bahwa panggilan saya tidak mungkin telah diproses. Saya mungkin tidak mencapai server, atau tidak dapat menyelesaikan nama host, atau sesuatu yang lain terjadi.
Jika saya mendapatkan kembali TimeoutError , maka permintaan saya mungkin telah diproses, atau sebagian diproses, dan saya mungkin melewatkan tanggapannya. Itu kasus yang sama sekali berbeda dan perlu memeriksa ulang dengan server yang saya panggil apa yang terjadi.

Menjadikan batas waktu terbuka sebagai sub-kategori TimeoutError berarti mengambil situasi sederhana (permintaan tidak diproses) di bawah domain yang lebih kompleks, dan tentunya memerlukan pemeriksaan tambahan untuk memutuskan apa yang harus dilakukan: apakah itu batas waktu terbuka atau pembacaan waktu habis?

Kita harus:

  1. Putuskan bagaimana meningkatkan waktu tunggu terbuka
  2. Standarisasi semua adaptor dengan perilaku yang sama

@coberlin @erik-escobedo @mislav @mistersourcerer ingin mendengar pendapat Anda setelah mempertimbangkan hal di atas

Pergi dengan ConnectionFailed untuk kesalahan batas waktu terbuka masuk akal bagi saya dan akan memberikan apa yang saya harapkan dengan membedakan kesalahan batas waktu terbuka dari kesalahan batas waktu lainnya. Untuk konsistensi adaptor, ini berarti, misalnya, Net::HTTP tidak apa-apa, tetapi HTTPClient akan berubah, dengan ConnectTimeoutErrors memetakan ke ConnectionFailed alih-alih ke TimeoutError.

Tidak apa-apa, setelah kami memutuskan kami akan menstandardisasi semua adaptor ke perilaku yang sama (jelas di v1.0, karena ini akan menjadi tidak kompatibel)

Melempar use-case ke dalam ring:

Di tempat kerja, kami mengalami beberapa Open Timeout karena Nginx + Kubernetes gagal merutekan ke pod gantung (atau semacamnya). Bagaimanapun, NetHTTP digunakan untuk membuang kesalahan OpenTimeout dan ReadTimeout, dan itu sangat berguna bagi kami untuk men-debug yang mana.

Sekarang kami telah beralih ke Typhoeus, sayangnya kami memiliki semua timeout yang disatukan, dan sulit bagi kami untuk mengetahui apakah pekerjaan kami pada masalah nginx + kuber telah ditingkatkan, atau jika kami baru saja berhasil membuat lebih banyak permintaan ke yang semakin berjuang sistem. Either way jumlah timeout hampir sama, dan tanpa memisahkannya, kami agak terjebak menebak.

Saya rasa hanya menambahkan Faraday::OpenTimeoutError sudah cukup, kita harus memiliki Faraday::OpenTimeoutError dan Faraday::ReadTimeoutError dari Faraday::TimeoutError IMO.

@philsturgeon dan bagaimana dengan solusi lain yang diusulkan, apakah itu akan membantu juga?

Buka batas waktu -> Faraday::ConnectionFailed
Baca batas waktu -> Faraday::TimeoutError

Itu seharusnya menjadi perilaku pada semua adaptor, tetapi sayangnya beberapa tidak berperilaku seperti yang diharapkan (yaitu Typhoeus)

Saya merasa itu adalah hal yang berbeda.

ConnectionFailed sepertinya "Saya tidak tahu bagaimana berbicara dengan server ini", seperti DNS/IP yang tidak valid, dll.

OpenTimeout adalah "Saya tahu di mana server ini saya hanya menunggu untuk melakukan sesuatu"

OpenTimeout adalah "Saya tahu di mana server ini saya hanya menunggu untuk melakukan sesuatu"

Saya tidak setuju dengan itu, saya lebih suka mengatakan:

Open Timeout: Saya mencoba menghubungi server, tetapi saya tidak dapat menjangkaunya (Catatan: koneksi belum dibuat atau "dibuka").
Baca Timeout: Saya telah membuat koneksi dengan server tetapi saya menunggu untuk melakukan sesuatu (membaca output).

Firewall/proxy/load_balancer yang rusak hanyalah contoh sederhana tentang bagaimana Anda mungkin mendapatkan waktu tunggu terbuka, tetapi dalam semua kasus ini, koneksi ke server belum dimulai. Itu bagian terpenting bagi saya. "ConnectionFailed" bagi saya berarti: Saya tidak dapat terhubung ke server. Dan itu sangat cocok untuk kasus ini.

Jika Anda masih berpikir bahwa Faraday::OpenTimeoutError tertentu harus ada, maka saya sarankan untuk mewarisi dari ConnectionFailed daripada TimeoutError tetapi saya setuju itu akan sedikit membingungkan dan tidak yakin caranya itu akan membantu dalam praktek.
Silakan lihat https://github.com/lostisland/faraday/issues/718#issuecomment -343957963 saya sebelumnya tentang bagaimana ini sebenarnya dapat membantu mengelola kesalahan.

Apakah masuk akal? Saya ingin menemukan solusi yang cocok untuk semua orang

Saya menerima definisi Anda yang lebih akurat untuk batas waktu terbuka tetapi saya sampai pada kesimpulan yang berbeda.

Anda menganggap open timeout dianggap sebagai kegagalan koneksi karena jumlah waktu yang Anda inginkan untuk menunggu koneksi tersebut dianggap sebagai bagian dari koneksi. "Gagal membuat koneksi dalam 5 detik" tentu masuk akal jika Anda menjelaskannya seperti itu, tetapi bukan itu yang dipikirkan banyak orang.

Bagi banyak orang, batas waktu terbuka hanya berarti belum terjadi. Itu membuatnya kurang dari pernyataan definitif daripada kebanyakan kegagalan koneksi, yaitu "Server sedang down" atau "DNS ini sampah".

Saya kira itu tidak masalah, karena kegagalan koneksi dan batas waktu terbuka keduanya harus dicoba lagi, di sini sebagai batas waktu baca dapat dianggap sebagai alasan untuk mundur?

Saya setuju, kita dapat berdebat sebanyak yang kita inginkan pada bacaan yang dapat diterapkan padanya, tetapi kepraktisan poin saya adalah apa yang Anda katakan juga: Jika Anda mendapatkan Batas Waktu Terbuka, itu berarti Anda dapat mencoba kembali permintaan, jika Anda mendapatkan a Read Timeout artinya Anda harus SANGAT berhati-hati karena permintaan Anda mungkin telah diproses (seluruhnya atau sebagian). Secara kebetulan, arti praktis dari batas waktu terbuka cocok dengan salah satu koneksi yang gagal, maka saya akan membuatnya mewarisi dari sana.

Hari ini orang-orang menangkap pengecualian ConnectionFailed dan TimeoutError dan logika di baliknya sangat mungkin mencerminkan apa yang kami katakan sebelumnya. Jika kami memperkenalkan pengecualian baru sebagai subkelas dari ConnectionFailed maka kemungkinan besar sebagian besar (Jika tidak semua) aplikasi tidak memerlukan perubahan apa pun.

Saya mengerti (dan setuju) dari sudut pandang semantik meskipun OpenTimeout hanyalah jenis Timeout lainnya.

Tapi hei, bagaimana jika kita menyebutnya ConnectionTimedOut saja?

Akan ada beberapa kebingungan di sekitar open_timeout: X menjadi nama properti yang mengatakan berapa lama untuk menunggu sampai melempar ConnectionTimedOut .

Poin bagus

Sebut saja ConnectionOpenTimeout ? Itu memperjelas masalah koneksi dan membuatnya jelas bahwa ini adalah batas waktu pembukaan. Saya pikir nama ini terus dipahami sejalan dengan arti "Gagal membuat koneksi dalam X detik", meskipun beberapa orang mungkin masih bertanya-tanya mengapa batas waktu bukan batas waktu. 😅

Kedengarannya bagus untuk saya 👍!

@iMacTia hei, jika Anda bisa memberi saya beberapa petunjuk di mana untuk memulai, saya bisa mencoba melakukan ini.

Terima kasih @philsturgeon , itu akan sangat bagus! Izinkan saya untuk merangkum poin-poin utama seputar ini:

  1. Semua perubahan perlu dilakukan terhadap cabang v1.0 (karena tidak kompatibel).
  2. Perilaku manajemen waktu habis tidak konsisten di seluruh adaptor, jadi kami perlu menstandarkannya.
  3. Perilaku yang disepakati adalah sebagai berikut:
  4. Jika waktu OPEN habis, kami akan memunculkan kesalahan ConnectionOpenTimeout yang akan diturunkan dari ConnectionFailed .
  5. Jika batas waktu READ, kami akan menaikkan TimeoutError .

Apakah saya melewatkan sesuatu?

Apakah ConnectionOpenTimeout akan ditambahkan ke default Faraday::Request::Retry menangani pengecualian?

@mjhoy itu poin yang bagus tetapi saat ini coba lagi middleware tidak mencoba lagi jika ada masalah koneksi. Itu hanya mencoba ulang permintaan jika koneksi berhasil tetapi ada batas waktu. Sebenarnya saya tidak yakin masuk akal untuk mencoba kembali permintaan jika layanan yang Anda panggil tidak dapat dijangkau sama sekali, Anda mungkin lebih suka mendapatkan pengecualian kembali dan melakukan sesuatu yang lain dalam kasus itu.

Namun, Faraday::Request::Retry dapat dikonfigurasi sehingga tidak ada yang menghentikan Anda untuk menambahkan ConnectionOpenTimeout atau bahkan ConnectionFailed ke daftar pengecualian yang ingin Anda tangani.

Saya ingin melihat lebih banyak "opini komunitas" sebelum menambahkannya ke daftar pengecualian default

Kami mengalami kesalahan OpenTimeout dengan titik akhir API sesekali yang perlu dicoba lagi; tampaknya dari logika Anda di Errno::ETIMEDOUT, Timeout::Error, Error::TimeoutError , dan Net::OpenTimeout adalah subkelas dari Timeout::Error ; tidak terlalu jelas bahwa Faraday memperlakukan mereka secara berbeda. Jadi mungkin dokumentasinya harus diperbarui? Bagaimanapun, ya, kami mengonfigurasi middleware; Saya hanya ingin tahu apakah defaultnya masuk akal.

@mjhoy Anda benar mengatakan bahwa Timeout::Error termasuk Net::OpenTimeout juga jadi dengan implementasi saat ini sepertinya open timeout juga harus dicoba lagi. Selain itu, Timeout::Error diselamatkan dan dinaikkan kembali oleh adaptor dalam keadaan normal, jadi kehadirannya dalam daftar pengecualian mungkin tidak diperlukan atau hanya untuk keamanan ekstra.

Setelah kita selesai dengan pengecualian refactoring, Net::OpenTimeout akan dimunculkan sebagai pengecualian baru dan seperti yang saya katakan dalam komentar saya, mengubah perilaku default saat ini.

Saya masih percaya itu tidak boleh menjadi bagian dari default, tapi itu pasti sesuatu yang perlu dipertimbangkan saat melakukan pekerjaan.

Terima kasih telah membesarkan ini

Hei, maaf ini mendekam di tim saya yang menumpuk selama setahun dan sekarang prioritas kami telah banyak berubah. Saya tidak akan melakukan pekerjaan apa pun untuk masalah ini, tetapi semoga berhasil!

@iMacTia Apakah ada yang mengerjakan perubahan ini? Saya tidak keberatan mengambil ini untuk v2.0.

Hai @ragav0102 , terima kasih atas dukungannya!
Belum ada yang mengerjakan ini, karena kami masih berusaha untuk mengeluarkan v1.0 dari pintu.

Kami sangat menghargai bantuannya, tetapi kami belum memiliki rencana untuk v2.0 jadi saya tidak tahu kapan itu akan dirilis, jadi perubahan Anda mungkin perlu menunggu berbulan-bulan sebelum dapat digunakan.

Jika Anda membutuhkan ini di salah satu proyek Anda, maka itu mungkin tidak layak.
Jika Anda baru saja lulus dan ingin berkontribusi, saya sarankan Anda memilih sesuatu yang dijadwalkan untuk v1.0 karena akan dirilis lebih cepat

Mengerti!

Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

asf-stripe picture asf-stripe  ·  3Komentar

subvertallchris picture subvertallchris  ·  5Komentar

mattmill30 picture mattmill30  ·  4Komentar

ryanbyon picture ryanbyon  ·  3Komentar

jedeleh picture jedeleh  ·  3Komentar