Fork me on GitHub
#off-topic
<
2022-08-12
>
Ivar Refsdal12:08:28

TIL that if you use .http.HttpClient, you do not have any timeout guarantees when reading the HTTP body. This is also true even if you set connectTimeout and request timeout. https://bugs.openjdk.org/browse/JDK-8258397 And sure enough a scheduling job doing a HTTP call got stuck. However the container it was running thought everything was fine and were not restarted at any point, and thus the scheduling job did not run for a couple of weeks. So it goes ๐Ÿ™ƒ (clj-http and Apache HttpComponents do time out properly when told to do so though.)

๐Ÿ‘ 3
๐Ÿ˜ฉ 2
1
1
๐Ÿ˜ฒ 1
mpenet14:08:57

via :exoscale.telex.response.body-handler/timeout

mpenet14:08:21

it uses a separate lib that brings in a few middleware, one of which does enable this (https://github.com/mizosoft/methanol)

Ivar Refsdal17:08:57

Thanks! Yeah, I found methanol also when googling this issue. It's also solvable using plain java/clojure with HttpClient/sendAsync like this:

(defn get-or-cancel [^CompletableFuture fut]
  (try
    (.get fut 2 TimeUnit/SECONDS)
    (catch TimeoutException _timeout
      (.cancel fut false))))

(defonce client (HttpClient/newHttpClient))

(comment
  (let [req (-> (HttpRequest/newBuilder)
                (.uri (URI. ""))
                (.timeout (Duration/ofSeconds 2))
                (.GET)
                (.build))]
    (try
      (let [resp (.sendAsync client req (HttpResponse$BodyHandlers/ofString))]
        (get-or-cancel resp))
      (catch Throwable t
        (println "exception..." t)
        (println (ex-message t))))))
That said it would be much better if there was a simple .readTimeout and/or .requestTimeout that applied to the regular HttpClient/send.

Ivar Refsdal17:08:11

I wonder how much human time has been spent on debugging what basically amounts to the absence of socket timeouts. I know I've spent a lot. The network in azure container instances is not very stable.

mpenet18:08:02

I am not sure your solution would actually trigger cancelation of the underlying stream consumer. But I am on mobile, too lazy to check.

Ivar Refsdal18:08:26

> The default HttpClient implementation returns CompletableFuture objects that are cancelable. CompletableFuture objects derived from cancelable futures are themselves cancelable. Invoking cancel(true) on a cancelable future that is not completed, attempts to cancel the HTTP exchange in an effort to release underlying resources as soon as possible. No guarantee is made as to exactly when the cancellation request may be taken into account. In particular, the request might still get sent to the server, as its processing might already have started asynchronously in another thread, and the underlying resources may only be released asynchronously. That sounds good enough? https://docs.oracle.com/en/java/javase/18/docs/api/java.net.http/java/net/http/HttpClient.html#sendAsync(java.net.http.HttpRequest,java.net.http.HttpResponse.BodyHandler,java.net.http.HttpResponse.PushPromiseHandler)

Ivar Refsdal18:08:10

I have not tried to verify it myself though

Ivar Refsdal18:08:37

I did verify that the future respects the timeout

mpenet18:08:02

Sounds good. That might be a good way to avoid methanol indeed. I will test that after my holidays. Thanks for the tip

Ivar Refsdal18:08:47

Thanks for your tip as well. +1 for adding a default timeout value for telex. More libraries should do this, I think.

mpenet18:08:33

Learned that issue the hard way too heh

๐Ÿงก 1
Noah Bogart20:08:18

what's the best channel to ask code architecture/design system questions?

Darin Douglass20:08:38

#architecture probably ๐Ÿ™‚

Noah Bogart20:08:18

thanks, that seems adjacent to my potential question!