]>
Commit | Line | Data |
---|---|---|
51ee7c82 | 1 | /* |
b8ae064d | 2 | * Copyright (C) 1996-2023 The Squid Software Foundation and contributors |
51ee7c82 | 3 | * |
bbc27441 AJ |
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. | |
51ee7c82 | 7 | */ |
8 | ||
bbc27441 AJ |
9 | /* DEBUG: section 84 Helper process maintenance */ |
10 | ||
ff9d9458 FC |
11 | #ifndef SQUID_SRC_HELPER_H |
12 | #define SQUID_SRC_HELPER_H | |
51ee7c82 | 13 | |
37dedc58 | 14 | #include "base/AsyncCall.h" |
e237d339 | 15 | #include "base/InstanceId.h" |
3bd118d6 | 16 | #include "base/RefCount.h" |
aa839030 | 17 | #include "cbdata.h" |
e0d28505 | 18 | #include "comm/forward.h" |
582c2af2 | 19 | #include "dlink.h" |
76d9b994 | 20 | #include "helper/ChildConfig.h" |
24438ec5 | 21 | #include "helper/forward.h" |
ddc77a2e | 22 | #include "helper/Reply.h" |
c8f9dea2 | 23 | #include "helper/Request.h" |
a56fcf0b | 24 | #include "helper/ReservationId.h" |
602d9612 | 25 | #include "ip/Address.h" |
65e41a45 | 26 | #include "sbuf/SBuf.h" |
32fd6d8a CT |
27 | |
28 | #include <list> | |
a8067a2e | 29 | #include <map> |
6215dc18 | 30 | #include <queue> |
a8067a2e | 31 | #include <unordered_map> |
aa839030 | 32 | |
e05a9d51 EB |
33 | class CommTimeoutCbParams; |
34 | class MemBuf; | |
bf3e8d5a | 35 | class Packable; |
541b581e | 36 | class wordlist; |
bf3e8d5a | 37 | |
ddc77a2e CT |
38 | namespace Helper |
39 | { | |
40 | /// Holds the required data to serve a helper request. | |
41 | class Xaction { | |
42 | MEMPROXY_CLASS(Helper::Xaction); | |
43 | public: | |
44 | Xaction(HLPCB *c, void *d, const char *b): request(c, d, b) {} | |
45 | Helper::Request request; | |
46 | Helper::Reply reply; | |
47 | }; | |
ddc77a2e | 48 | |
e05a9d51 EB |
49 | class SessionBase; |
50 | ||
6825b101 CT |
51 | /** |
52 | * Managers a set of individual helper processes with a common queue of requests. | |
53 | * | |
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); | |
6082a0e2 | 58 | * overloaded: a busy helper with more than queue-size requests in the queue. |
6825b101 | 59 | * |
6082a0e2 EB |
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 | |
6825b101 | 62 | * whether the caller can handle dropped requests (trySubmit vs helperSubmit APIs). |
6082a0e2 EB |
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. | |
6825b101 | 65 | */ |
e05a9d51 | 66 | class Client: public RefCountable |
10044c9b | 67 | { |
48d54e4d | 68 | public: |
e05a9d51 | 69 | using Pointer = RefCount<Client>; |
3bd118d6 | 70 | |
e05a9d51 | 71 | /// \returns a newly created instance of the named helper client |
91ce75c9 | 72 | /// \param name admin-visible helper category (with this process lifetime) |
3bd118d6 EB |
73 | static Pointer Make(const char *name); |
74 | ||
e05a9d51 | 75 | virtual ~Client(); |
48d54e4d | 76 | |
6215dc18 | 77 | /// \returns next request in the queue, or nil. |
e05a9d51 | 78 | Xaction *nextRequest(); |
6215dc18 | 79 | |
6082a0e2 | 80 | /// If possible, submit request. Otherwise, either kill Squid or return false. |
6825b101 CT |
81 | bool trySubmit(const char *buf, HLPCB * callback, void *data); |
82 | ||
f53969cc | 83 | /// Submits a request to the helper or add it to the queue if none of |
32fd6d8a | 84 | /// the servers is available. |
e05a9d51 | 85 | void submitRequest(Xaction *); |
bf3e8d5a AJ |
86 | |
87 | /// Dump some stats about the helper state to a Packable object | |
aee3523a | 88 | void packStatsInto(Packable *p, const char *label = nullptr) const; |
6082a0e2 EB |
89 | /// whether the helper will be in "overloaded" state after one more request |
90 | /// already overloaded helpers return true | |
91 | bool willOverload() const; | |
bf3e8d5a | 92 | |
e05a9d51 | 93 | /// Updates internal statistics and starts new helper processes after |
a56fcf0b | 94 | /// an unexpected server exit |
569605b9 | 95 | void handleKilledServer(SessionBase *); |
a56fcf0b | 96 | |
e05a9d51 EB |
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 | |
654835f0 EB |
100 | void handleFewerServers(bool madeProgress); |
101 | ||
569605b9 EB |
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() | |
105 | void dropQueued(); | |
106 | ||
26b6afaf EB |
107 | /// sends transaction response to the transaction initiator |
108 | void callBack(Xaction &); | |
109 | ||
bd71920d EB |
110 | /// Starts required helper process(es). |
111 | /// The caller is responsible for checking that new processes are needed. | |
112 | virtual void openSessions(); | |
113 | ||
48d54e4d | 114 | public: |
3bd118d6 | 115 | wordlist *cmdline = nullptr; |
aa839030 | 116 | dlink_list servers; |
e05a9d51 | 117 | std::queue<Xaction *> queue; |
3bd118d6 | 118 | const char *id_name = nullptr; |
e05a9d51 | 119 | ChildConfig childs; ///< Configuration settings for number running. |
3bd118d6 | 120 | int ipc_type = 0; |
b7ac5457 | 121 | Ip::Address addr; |
3bd118d6 EB |
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 | |
895dba0a | 129 | SBuf onTimedOutResponse; ///< The response to use when helper response timedout |
3bd118d6 | 130 | char eom = '\n'; ///< The char which marks the end of (response) message, normally '\n' |
aa839030 | 131 | |
48d54e4d | 132 | struct _stats { |
3bd118d6 EB |
133 | int requests = 0; |
134 | int replies = 0; | |
135 | int timedout = 0; | |
136 | int queue_size = 0; | |
137 | int avg_svc_time = 0; | |
2fadd50d | 138 | } stats; |
6825b101 CT |
139 | |
140 | protected: | |
3bd118d6 | 141 | /// \param name admin-visible helper category (with this process lifetime) |
e05a9d51 | 142 | explicit Client(const char * const name): id_name(name) {} |
3bd118d6 | 143 | |
6082a0e2 EB |
144 | bool queueFull() const; |
145 | bool overloaded() const; | |
146 | void syncQueueStats(); | |
147 | bool prepSubmit(); | |
6825b101 | 148 | void submit(const char *buf, HLPCB * callback, void *data); |
aa839030 | 149 | }; |
150 | ||
e05a9d51 EB |
151 | } // namespace Helper |
152 | ||
153 | // TODO: Rename to a *Client. | |
154 | class statefulhelper: public Helper::Client | |
10044c9b | 155 | { |
48d54e4d | 156 | public: |
3bd118d6 | 157 | using Pointer = RefCount<statefulhelper>; |
a56fcf0b CT |
158 | typedef std::unordered_map<Helper::ReservationId, helper_stateful_server *> Reservations; |
159 | ||
e05a9d51 | 160 | ~statefulhelper() override = default; |
48d54e4d | 161 | |
3bd118d6 EB |
162 | static Pointer Make(const char *name); |
163 | ||
a56fcf0b CT |
164 | /// reserve the given server |
165 | void reserveServer(helper_stateful_server * srv); | |
166 | ||
167 | /// undo reserveServer(), clear the reservation and kick the queue | |
168 | void cancelReservation(const Helper::ReservationId reservation); | |
169 | ||
bd71920d EB |
170 | /* Helper::Client API */ |
171 | void openSessions() override; | |
172 | ||
6825b101 | 173 | private: |
3bd118d6 EB |
174 | friend void helperStatefulSubmit(const statefulhelper::Pointer &, const char *buf, HLPCB *, void *cbData, const Helper::ReservationId &); |
175 | ||
e05a9d51 | 176 | explicit statefulhelper(const char * const name): Helper::Client(name) {} |
a56fcf0b CT |
177 | |
178 | /// \return the previously reserved server (if the reservation is still valid) or nil | |
179 | helper_stateful_server *findServer(const Helper::ReservationId & reservation); | |
180 | ||
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); | |
183 | ||
895dba0a | 184 | ///< reserved servers indexed by reservation IDs |
a56fcf0b | 185 | Reservations reservations; |
aa839030 | 186 | }; |
187 | ||
e05a9d51 EB |
188 | namespace Helper |
189 | { | |
190 | ||
191 | /// represents a single helper process | |
192 | class SessionBase: public CbdataParent | |
10044c9b | 193 | { |
e0d28505 | 194 | public: |
e05a9d51 EB |
195 | ~SessionBase() override; |
196 | ||
0a958e42 EB |
197 | /// close handler to handle exited server processes |
198 | static void HelperServerClosed(SessionBase *); | |
199 | ||
e0d28505 AJ |
200 | /** Closes pipes to the helper safely. |
201 | * Handles the case where the read and write pipes are the same FD. | |
202 | */ | |
0a958e42 | 203 | void closePipesSafely(); |
e0d28505 AJ |
204 | |
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. | |
208 | */ | |
0a958e42 | 209 | void closeWritePipeSafely(); |
e0d28505 | 210 | |
a56fcf0b CT |
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; | |
214 | ||
0a958e42 EB |
215 | /// our creator (parent) object |
216 | virtual Client &helper() const = 0; | |
217 | ||
e05a9d51 | 218 | /// dequeues and sends an Unknown answer to all queued requests |
0a958e42 | 219 | virtual void dropQueued(); |
a56fcf0b | 220 | |
48d54e4d | 221 | public: |
e237d339 AJ |
222 | /// Helper program identifier; does not change when contents do, |
223 | /// including during assignment | |
e05a9d51 EB |
224 | const InstanceId<SessionBase> index; |
225 | ||
895dba0a | 226 | int pid; |
b7ac5457 | 227 | Ip::Address addr; |
e0d28505 AJ |
228 | Comm::ConnectionPointer readPipe; |
229 | Comm::ConnectionPointer writePipe; | |
895dba0a | 230 | void *hIpc; |
48d54e4d | 231 | |
895dba0a CT |
232 | char *rbuf; |
233 | size_t rbuf_sz; | |
234 | size_t roffset; | |
aa839030 | 235 | |
236 | struct timeval dispatch_time; | |
aa839030 | 237 | struct timeval answer_time; |
238 | ||
239 | dlink_node link; | |
e0d28505 AJ |
240 | |
241 | struct _helper_flags { | |
895dba0a CT |
242 | bool writing; |
243 | bool closing; | |
244 | bool shutdown; | |
e0d28505 AJ |
245 | } flags; |
246 | ||
e05a9d51 | 247 | using Requests = std::list<Xaction *>; |
bf3e8d5a AJ |
248 | Requests requests; ///< requests in order of submission/expiration |
249 | ||
1f7ba0b4 | 250 | struct { |
895dba0a CT |
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 | |
1f7ba0b4 | 256 | } stats; |
895dba0a | 257 | void initStats(); |
48d54e4d AJ |
258 | }; |
259 | ||
e05a9d51 EB |
260 | /// represents a single "stateless helper" process; |
261 | /// supports concurrent helper requests | |
262 | class Session: public SessionBase | |
10044c9b | 263 | { |
e05a9d51 | 264 | CBDATA_CHILD(Session); |
5c2f68b7 | 265 | |
48d54e4d | 266 | public: |
895dba0a | 267 | uint64_t nextRequestId; |
017a2d1f | 268 | |
895dba0a CT |
269 | MemBuf *wqueue; |
270 | MemBuf *writebuf; | |
32fd6d8a | 271 | |
e05a9d51 | 272 | Client::Pointer parent; |
32fd6d8a | 273 | |
ddc77a2e CT |
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. | |
e05a9d51 | 278 | Xaction *replyXaction; |
ddc77a2e | 279 | |
1176e456 | 280 | /// Whether to ignore current message, because it is timed-out or other reason |
895dba0a | 281 | bool ignoreToEom; |
1176e456 | 282 | |
32fd6d8a CT |
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 | |
286 | ||
e05a9d51 EB |
287 | ~Session() override; |
288 | ||
ddc77a2e CT |
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. | |
e05a9d51 | 293 | Xaction *popRequest(int requestId); |
ddc77a2e | 294 | |
32fd6d8a CT |
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); | |
298 | ||
e05a9d51 | 299 | /* SessionBase API */ |
337b9aa4 | 300 | bool reserved() override {return false;} |
0a958e42 EB |
301 | void dropQueued() override; |
302 | Client &helper() const override { return *parent; } | |
a56fcf0b | 303 | |
f53969cc | 304 | /// Read timeout handler |
32fd6d8a | 305 | static void requestTimeout(const CommTimeoutCbParams &io); |
aa839030 | 306 | }; |
307 | ||
e05a9d51 EB |
308 | } // namespace Helper |
309 | ||
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 | |
10044c9b | 314 | { |
a56fcf0b | 315 | CBDATA_CHILD(helper_stateful_server); |
5c2f68b7 | 316 | |
48d54e4d | 317 | public: |
337b9aa4 | 318 | ~helper_stateful_server() override; |
a56fcf0b CT |
319 | void reserve(); |
320 | void clearReservation(); | |
321 | ||
0a958e42 | 322 | /* Helper::SessionBase API */ |
337b9aa4 | 323 | bool reserved() override {return reservationId.reserved();} |
0a958e42 | 324 | Helper::Client &helper() const override { return *parent; } |
a56fcf0b | 325 | |
3bd118d6 | 326 | statefulhelper::Pointer parent; |
a56fcf0b CT |
327 | |
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 | |
895dba0a | 333 | time_t reservationStart; ///< when the last `reservation` was made |
aa839030 | 334 | }; |
51ee7c82 | 335 | |
e05a9d51 | 336 | void helperSubmit(const Helper::Client::Pointer &, const char *buf, HLPCB *, void *cbData); |
3bd118d6 | 337 | void helperStatefulSubmit(const statefulhelper::Pointer &, const char *buf, HLPCB *, void *cbData, uint64_t reservation); |
e05a9d51 | 338 | void helperShutdown(const Helper::Client::Pointer &); |
3bd118d6 | 339 | void helperStatefulShutdown(const statefulhelper::Pointer &); |
aa839030 | 340 | |
ff9d9458 | 341 | #endif /* SQUID_SRC_HELPER_H */ |
70ac5b29 | 342 |