2 * Copyright (C) 1996-2025 The Squid Software Foundation and contributors
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
9 /* DEBUG: section 84 Helper process maintenance */
11 #ifndef SQUID_SRC_HELPER_H
12 #define SQUID_SRC_HELPER_H
14 #include "base/AsyncCall.h"
15 #include "base/InstanceId.h"
16 #include "base/RefCount.h"
18 #include "comm/forward.h"
20 #include "helper/ChildConfig.h"
21 #include "helper/forward.h"
22 #include "helper/Reply.h"
23 #include "helper/Request.h"
24 #include "helper/ReservationId.h"
25 #include "ip/Address.h"
26 #include "sbuf/SBuf.h"
31 #include <unordered_map>
33 class CommTimeoutCbParams
;
40 /// Holds the required data to serve a helper request.
42 MEMPROXY_CLASS(Helper::Xaction
);
44 Xaction(HLPCB
*c
, void *d
, const char *b
): request(c
, d
, b
) {}
45 Helper::Request request
;
52 * Managers a set of individual helper processes with a common queue of requests.
54 * With respect to load, a helper goes through these states (roughly):
55 * idle: no processes are working on requests (and no requests are queued);
56 * normal: some, but not all processes are working (and no requests are queued);
57 * busy: all processes are working (and some requests are possibly queued);
58 * overloaded: a busy helper with more than queue-size requests in the queue.
60 * A busy helper queues new requests and issues a WARNING every 10 minutes or so.
61 * An overloaded helper either drops new requests or keeps queuing them, depending on
62 * whether the caller can handle dropped requests (trySubmit vs helperSubmit APIs).
63 * If an overloaded helper has been overloaded for 3+ minutes, an attempt to use
64 * it results in on-persistent-overload action, which may kill worker.
66 class Client
: public RefCountable
69 using Pointer
= RefCount
<Client
>;
71 /// \returns a newly created instance of the named helper client
72 /// \param name admin-visible helper category (with this process lifetime)
73 static Pointer
Make(const char *name
);
77 /// \returns next request in the queue, or nil.
78 Xaction
*nextRequest();
80 /// If possible, submit request. Otherwise, either kill Squid or return false.
81 bool trySubmit(const char *buf
, HLPCB
* callback
, void *data
);
83 /// Submits a request to the helper or add it to the queue if none of
84 /// the servers is available.
85 void submitRequest(Xaction
*);
87 /// Dump some stats about the helper state to a Packable object
88 void packStatsInto(Packable
*p
, const char *label
= nullptr) const;
89 /// whether the helper will be in "overloaded" state after one more request
90 /// already overloaded helpers return true
91 bool willOverload() const;
93 /// Updates internal statistics and starts new helper processes after
94 /// an unexpected server exit
95 void handleKilledServer(SessionBase
*);
97 /// Reacts to unexpected helper process death(s), including a failure to start helper(s)
98 /// and an unexpected exit of a previously started helper. \sa handleKilledServer()
99 /// \param madeProgress whether the died helper(s) responded to any requests
100 void handleFewerServers(bool madeProgress
);
102 /// satisfies all queued requests with a Helper::Unknown answer
103 /// \prec no existing servers will be able to process queued requests
104 /// \sa SessionBase::dropQueued()
107 /// sends transaction response to the transaction initiator
108 void callBack(Xaction
&);
110 /// Starts required helper process(es).
111 /// The caller is responsible for checking that new processes are needed.
112 virtual void openSessions();
115 wordlist
*cmdline
= nullptr;
117 std::queue
<Xaction
*> queue
;
118 const char *id_name
= nullptr;
119 ChildConfig childs
; ///< Configuration settings for number running.
122 unsigned int droppedRequests
= 0; ///< requests not sent during helper overload
123 time_t overloadStart
= 0; ///< when the helper became overloaded (zero if it is not)
124 time_t last_queue_warn
= 0;
125 time_t last_restart
= 0;
126 time_t timeout
= 0; ///< Requests timeout
127 bool retryTimedOut
= false; ///< Whether the timed-out requests must retried
128 bool retryBrokenHelper
= false; ///< Whether the requests must retried on BH replies
129 SBuf onTimedOutResponse
; ///< The response to use when helper response timedout
130 char eom
= '\n'; ///< The char which marks the end of (response) message, normally '\n'
137 int avg_svc_time
= 0;
141 /// \param name admin-visible helper category (with this process lifetime)
142 explicit Client(const char * const name
): id_name(name
) {}
144 bool queueFull() const;
145 bool overloaded() const;
146 void syncQueueStats();
148 void submit(const char *buf
, HLPCB
* callback
, void *data
);
151 } // namespace Helper
153 // TODO: Rename to a *Client.
154 class statefulhelper
: public Helper::Client
157 using Pointer
= RefCount
<statefulhelper
>;
158 typedef std::unordered_map
<Helper::ReservationId
, helper_stateful_server
*> Reservations
;
160 ~statefulhelper() override
= default;
162 static Pointer
Make(const char *name
);
164 /// reserve the given server
165 void reserveServer(helper_stateful_server
* srv
);
167 /// undo reserveServer(), clear the reservation and kick the queue
168 void cancelReservation(const Helper::ReservationId reservation
);
170 /* Helper::Client API */
171 void openSessions() override
;
174 friend void helperStatefulSubmit(const statefulhelper::Pointer
&, const char *buf
, HLPCB
*, void *cbData
, const Helper::ReservationId
&);
176 explicit statefulhelper(const char * const name
): Helper::Client(name
) {}
178 /// \return the previously reserved server (if the reservation is still valid) or nil
179 helper_stateful_server
*findServer(const Helper::ReservationId
& reservation
);
181 void submit(const char *buf
, HLPCB
* callback
, void *data
, const Helper::ReservationId
& reservation
);
182 bool trySubmit(const char *buf
, HLPCB
* callback
, void *data
, const Helper::ReservationId
& reservation
);
184 ///< reserved servers indexed by reservation IDs
185 Reservations reservations
;
191 /// represents a single helper process
192 class SessionBase
: public CbdataParent
195 ~SessionBase() override
;
197 /// close handler to handle exited server processes
198 static void HelperServerClosed(SessionBase
*);
200 /** Closes pipes to the helper safely.
201 * Handles the case where the read and write pipes are the same FD.
203 void closePipesSafely();
205 /** Closes the reading pipe.
206 * If the read and write sockets are the same the write pipe will
207 * also be closed. Otherwise its left open for later handling.
209 void closeWritePipeSafely();
211 // TODO: Teach each child to report its child-specific state instead.
212 /// whether the server is locked for exclusive use by a client
213 virtual bool reserved() = 0;
215 /// our creator (parent) object
216 virtual Client
&helper() const = 0;
218 /// dequeues and sends an Unknown answer to all queued requests
219 virtual void dropQueued();
222 /// Helper program identifier; does not change when contents do,
223 /// including during assignment
224 const InstanceId
<SessionBase
> index
;
228 Comm::ConnectionPointer readPipe
;
229 Comm::ConnectionPointer writePipe
;
236 struct timeval dispatch_time
;
237 struct timeval answer_time
;
241 struct _helper_flags
{
247 using Requests
= std::list
<Xaction
*>;
248 Requests requests
; ///< requests in order of submission/expiration
251 uint64_t uses
; //< requests sent to this helper
252 uint64_t replies
; //< replies received from this helper
253 uint64_t pending
; //< queued lookups waiting to be sent to this helper
254 uint64_t releases
; //< times release() has been called on this helper (if stateful)
255 uint64_t timedout
; //< requests which timed-out
260 /// represents a single "stateless helper" process;
261 /// supports concurrent helper requests
262 class Session
: public SessionBase
264 CBDATA_CHILD(Session
);
267 uint64_t nextRequestId
;
272 Client::Pointer parent
;
274 /// The helper request Xaction object for the current reply .
275 /// A helper reply may be distributed to more than one of the retrieved
276 /// packets from helper. This member stores the Xaction object as long as
277 /// the end-of-message for current reply is not retrieved.
278 Xaction
*replyXaction
;
280 /// Whether to ignore current message, because it is timed-out or other reason
283 // STL says storing std::list iterators is safe when changing the list
284 typedef std::map
<uint64_t, Requests::iterator
> RequestIndex
;
285 RequestIndex requestsIndex
; ///< maps request IDs to requests
289 /// Search in queue for the request with requestId, return the related
290 /// Xaction object and remove it from queue.
291 /// If concurrency is disabled then the requestId is ignored and the
292 /// Xaction of the next request in queue is retrieved.
293 Xaction
*popRequest(int requestId
);
295 /// Run over the active requests lists and forces a retry, or timedout reply
296 /// or the configured "on timeout response" for timedout requests.
297 void checkForTimedOutRequests(bool const retry
);
299 /* SessionBase API */
300 bool reserved() override
{return false;}
301 void dropQueued() override
;
302 Client
&helper() const override
{ return *parent
; }
304 /// Read timeout handler
305 static void requestTimeout(const CommTimeoutCbParams
&io
);
308 } // namespace Helper
310 // TODO: Rename to a *Session, matching renamed statefulhelper.
311 /// represents a single "stateful helper" process;
312 /// supports exclusive transaction reservations
313 class helper_stateful_server
: public Helper::SessionBase
315 CBDATA_CHILD(helper_stateful_server
);
318 ~helper_stateful_server() override
;
320 void clearReservation();
322 /* Helper::SessionBase API */
323 bool reserved() override
{return reservationId
.reserved();}
324 Helper::Client
&helper() const override
{ return *parent
; }
326 statefulhelper::Pointer parent
;
328 // Reservations temporary lock the server for an exclusive "client" use. The
329 // client keeps the reservation ID as a proof of her reservation. If a
330 // reservation expires, and the server is reserved for another client, then
331 // the reservation ID presented by the late client will not match ours.
332 Helper::ReservationId reservationId
; ///< "confirmation ID" of the last
333 time_t reservationStart
; ///< when the last `reservation` was made
336 void helperSubmit(const Helper::Client::Pointer
&, const char *buf
, HLPCB
*, void *cbData
);
337 void helperStatefulSubmit(const statefulhelper::Pointer
&, const char *buf
, HLPCB
*, void *cbData
, uint64_t reservation
);
338 void helperShutdown(const Helper::Client::Pointer
&);
339 void helperStatefulShutdown(const statefulhelper::Pointer
&);
341 #endif /* SQUID_SRC_HELPER_H */