···2828 | Invalid_config of string
2929 | Invalid_endpoint of string
30303131-exception Pool_error of error
3232-3331let pp_error ppf = function
3432 | Dns_resolution_failed { hostname } ->
3533 Fmt.pf ppf "DNS resolution failed for hostname: %s" hostname
···114112115113let resolve_endpoint (pool : ('clock, 'net) internal) endpoint =
116114 Log.debug (fun m -> m "Resolving %a..." Endpoint.pp endpoint);
117117- let addrs =
118118- Eio.Net.getaddrinfo_stream pool.net (Endpoint.host endpoint)
119119- ~service:(string_of_int (Endpoint.port endpoint))
120120- in
121121- Log.debug (fun m -> m "Got address list for %a" Endpoint.pp endpoint);
122122- match addrs with
123123- | addr :: _ ->
124124- Log.debug (fun m ->
125125- m "Resolved %a to %a" Endpoint.pp endpoint Eio.Net.Sockaddr.pp addr);
126126- addr
127127- | [] ->
128128- Log.err (fun m ->
129129- m "Failed to resolve hostname: %s" (Endpoint.host endpoint));
130130- raise
131131- (Pool_error
132132- (Dns_resolution_failed { hostname = Endpoint.host endpoint }))
115115+ try
116116+ let addrs =
117117+ Eio.Net.getaddrinfo_stream pool.net (Endpoint.host endpoint)
118118+ ~service:(string_of_int (Endpoint.port endpoint))
119119+ in
120120+ Log.debug (fun m -> m "Got address list for %a" Endpoint.pp endpoint);
121121+ match addrs with
122122+ | addr :: _ ->
123123+ Log.debug (fun m ->
124124+ m "Resolved %a to %a" Endpoint.pp endpoint Eio.Net.Sockaddr.pp addr);
125125+ addr
126126+ | [] ->
127127+ Log.err (fun m ->
128128+ m "Failed to resolve hostname: %s" (Endpoint.host endpoint));
129129+ raise (err (Dns_resolution_failed { hostname = Endpoint.host endpoint }))
130130+ with Eio.Io _ as ex ->
131131+ let bt = Printexc.get_raw_backtrace () in
132132+ Eio.Exn.reraise_with_context ex bt "resolving %a" Endpoint.pp endpoint
133133134134(** {1 Connection Creation with Retry} *)
135135···140140 Log.err (fun m ->
141141 m "Failed to connect to %a after %d attempts" Endpoint.pp endpoint
142142 retry_count);
143143- raise
144144- (Pool_error
145145- (Connection_failed { endpoint; attempts = retry_count; last_error }))
143143+ raise (err (Connection_failed { endpoint; attempts = retry_count; last_error }))
146144 end;
147145148146 Log.debug (fun m ->
···155153156154 (* Connect with optional timeout *)
157155 let socket =
158158- match Config.connect_timeout pool.config with
159159- | Some timeout ->
160160- Eio.Time.with_timeout_exn pool.clock timeout (fun () ->
161161- Eio.Net.connect ~sw:pool.sw pool.net addr)
162162- | None -> Eio.Net.connect ~sw:pool.sw pool.net addr
156156+ try
157157+ match Config.connect_timeout pool.config with
158158+ | Some timeout ->
159159+ Eio.Time.with_timeout_exn pool.clock timeout (fun () ->
160160+ Eio.Net.connect ~sw:pool.sw pool.net addr)
161161+ | None -> Eio.Net.connect ~sw:pool.sw pool.net addr
162162+ with Eio.Io _ as ex ->
163163+ let bt = Printexc.get_raw_backtrace () in
164164+ Eio.Exn.reraise_with_context ex bt "connecting to %a" Endpoint.pp endpoint
163165 in
164166165167 Log.debug (fun m ->
···170172 | None ->
171173 (socket :> connection)
172174 | Some tls_config ->
173173- Log.debug (fun m ->
174174- m "Initiating TLS handshake with %a" Endpoint.pp endpoint);
175175- let host =
176176- Domain_name.(host_exn (of_string_exn (Endpoint.host endpoint)))
177177- in
178178- let tls_flow = Tls_eio.client_of_flow ~host tls_config socket in
179179- Log.info (fun m ->
180180- m "TLS connection established to %a" Endpoint.pp endpoint);
181181- (tls_flow :> connection)
175175+ try
176176+ Log.debug (fun m ->
177177+ m "Initiating TLS handshake with %a" Endpoint.pp endpoint);
178178+ let host =
179179+ Domain_name.(host_exn (of_string_exn (Endpoint.host endpoint)))
180180+ in
181181+ let tls_flow = Tls_eio.client_of_flow ~host tls_config socket in
182182+ Log.info (fun m ->
183183+ m "TLS connection established to %a" Endpoint.pp endpoint);
184184+ (tls_flow :> connection)
185185+ with Eio.Io _ as ex ->
186186+ let bt = Printexc.get_raw_backtrace () in
187187+ Eio.Exn.reraise_with_context ex bt "TLS handshake with %a" Endpoint.pp endpoint
182188 in
183189184190 let now = get_time pool in
···192198 mutex = Eio.Mutex.create ();
193199 }
194200 with
195195- | Eio.Time.Timeout as e ->
201201+ | Eio.Time.Timeout ->
196202 Log.warn (fun m ->
197203 m "Connection timeout to %a (attempt %d)" Endpoint.pp endpoint attempt);
198198- let error_msg = Printexc.to_string e in
199204 if attempt >= Config.connect_retry_count pool.config then
200205 (* Last attempt - convert to our error type *)
201206 match Config.connect_timeout pool.config with
202207 | Some timeout ->
203203- raise (Pool_error (Connection_timeout { endpoint; timeout }))
208208+ raise (err (Connection_timeout { endpoint; timeout }))
204209 | None ->
205205- raise
206206- (Pool_error
207207- (Connection_failed
208208- { endpoint; attempts = attempt; last_error = error_msg }))
210210+ raise (err (Connection_failed
211211+ { endpoint; attempts = attempt; last_error = "Timeout" }))
209212 else begin
210213 (* Retry with exponential backoff *)
211214 let delay =
···213216 *. (2.0 ** float_of_int (attempt - 1))
214217 in
215218 Eio.Time.sleep pool.clock delay;
216216- create_connection_with_retry pool endpoint (attempt + 1) error_msg
219219+ create_connection_with_retry pool endpoint (attempt + 1) "Timeout"
217220 end
218218- | e ->
219219- (* Other errors - retry with backoff *)
220220- let error_msg = Printexc.to_string e in
221221+ | Eio.Io _ as ex ->
222222+ (* Eio IO errors - retry with backoff and add context on final failure *)
223223+ let error_msg = Printexc.to_string ex in
221224 Log.warn (fun m ->
222225 m "Connection attempt %d to %a failed: %s" attempt Endpoint.pp
223226 endpoint error_msg);
···229232 Eio.Time.sleep pool.clock delay;
230233 create_connection_with_retry pool endpoint (attempt + 1) error_msg)
231234 else
232232- raise
233233- (Pool_error
234234- (Connection_failed
235235- { endpoint; attempts = attempt; last_error = error_msg }))
235235+ let bt = Printexc.get_raw_backtrace () in
236236+ Eio.Exn.reraise_with_context ex bt "after %d retry attempts" attempt
236237237238let create_connection (pool : ('clock, 'net) internal) endpoint =
238239 create_connection_with_retry pool endpoint 1 "No attempts made"
···556557557558let with_connection t endpoint f =
558559 Eio.Switch.run (fun sw -> f (connection ~sw t endpoint))
559559-560560-let with_connection_exn t endpoint f =
561561- try with_connection t endpoint f with Pool_error e -> raise (err e)
562560563561(** {1 Public API - Statistics} *)
564562
+21-16
lib/conpool.mli
···4747 | Invalid_config of string (** Invalid configuration parameter *)
4848 | Invalid_endpoint of string (** Invalid endpoint specification *)
49495050-exception Pool_error of error
5151-(** Exception raised by pool operations.
5050+type Eio.Exn.err += E of error
5151+(** Extension of Eio's error type for connection pool errors.
5252+5353+ Pool operations raise [Eio.Io] exceptions with context information added at
5454+ each layer. The innermost error is often [E error], wrapped with context
5555+ strings that describe the operation being performed.
52565353- Most pool operations can raise this exception. Use {!pp_error} to get
5454- human-readable error messages. *)
5757+ Example error message:
5858+ {[
5959+ Eio.Io Conpool Dns_resolution_failed { hostname = "invalid.example" },
6060+ resolving invalid.example:443,
6161+ connecting to invalid.example:443,
6262+ after 3 retry attempts
6363+ ]}
55645656-type Eio.Exn.err += E of error
5757-(** Extension of Eio's error type for connection pool errors. *)
6565+ Use {!pp_error} to format just the error code, or let Eio format the full
6666+ exception with context. *)
58675968val err : error -> exn
6069(** [err e] is [Eio.Exn.create (E e)].
61706271 This converts a connection pool error to an Eio exception, allowing it to
6363- be handled uniformly with other Eio I/O errors. *)
7272+ be handled uniformly with other Eio I/O errors and enabling context to be
7373+ added via [Eio.Exn.reraise_with_context]. *)
64746575val pp_error : error Fmt.t
6666-(** Pretty-printer for error values. *)
7676+(** Pretty-printer for error values (without context).
7777+7878+ For full error messages including context, use [Eio.Exn.pp] or simply let
7979+ the exception be printed naturally. *)
67806881(** {1 Connection Types} *)
6982···142155 let buf = Eio.Buf_read.of_flow conn ~max_size:4096 in
143156 Eio.Buf_read.take_all buf)
144157 ]} *)
145145-146146-val with_connection_exn : t -> Endpoint.t -> (connection -> 'a) -> 'a
147147-(** [with_connection_exn pool endpoint fn] is like {!with_connection} but
148148- converts {!Pool_error} exceptions to [Eio.Io] exceptions for better
149149- integration with Eio error handling.
150150-151151- This is useful when you want pool errors to be handled uniformly with other
152152- Eio I/O errors. *)
153158154159(** {1 Statistics & Monitoring} *)
155160