]>
| Commit | Line | Data |
|---|---|---|
| 1 | /* | |
| 2 | * Copyright (C) 1996-2025 The Squid Software Foundation and contributors | |
| 3 | * | |
| 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. | |
| 7 | */ | |
| 8 | ||
| 9 | /* DEBUG: section 84 Helper process maintenance */ | |
| 10 | ||
| 11 | #ifndef SQUID_SRC_HELPER_H | |
| 12 | #define SQUID_SRC_HELPER_H | |
| 13 | ||
| 14 | #include "base/AsyncCall.h" | |
| 15 | #include "base/InstanceId.h" | |
| 16 | #include "base/RefCount.h" | |
| 17 | #include "cbdata.h" | |
| 18 | #include "comm/forward.h" | |
| 19 | #include "dlink.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" | |
| 27 | ||
| 28 | #include <list> | |
| 29 | #include <map> | |
| 30 | #include <queue> | |
| 31 | #include <unordered_map> | |
| 32 | ||
| 33 | class CommTimeoutCbParams; | |
| 34 | class MemBuf; | |
| 35 | class Packable; | |
| 36 | class wordlist; | |
| 37 | ||
| 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 | }; | |
| 48 | ||
| 49 | class SessionBase; | |
| 50 | ||
| 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); | |
| 58 | * overloaded: a busy helper with more than queue-size requests in the queue. | |
| 59 | * | |
| 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. | |
| 65 | */ | |
| 66 | class Client: public RefCountable | |
| 67 | { | |
| 68 | public: | |
| 69 | using Pointer = RefCount<Client>; | |
| 70 | ||
| 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); | |
| 74 | ||
| 75 | virtual ~Client(); | |
| 76 | ||
| 77 | /// \returns next request in the queue, or nil. | |
| 78 | Xaction *nextRequest(); | |
| 79 | ||
| 80 | /// If possible, submit request. Otherwise, either kill Squid or return false. | |
| 81 | bool trySubmit(const char *buf, HLPCB * callback, void *data); | |
| 82 | ||
| 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 *); | |
| 86 | ||
| 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; | |
| 92 | ||
| 93 | /// Updates internal statistics and starts new helper processes after | |
| 94 | /// an unexpected server exit | |
| 95 | void handleKilledServer(SessionBase *); | |
| 96 | ||
| 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); | |
| 101 | ||
| 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 | ||
| 107 | /// sends transaction response to the transaction initiator | |
| 108 | void callBack(Xaction &); | |
| 109 | ||
| 110 | /// Starts required helper process(es). | |
| 111 | /// The caller is responsible for checking that new processes are needed. | |
| 112 | virtual void openSessions(); | |
| 113 | ||
| 114 | public: | |
| 115 | wordlist *cmdline = nullptr; | |
| 116 | dlink_list servers; | |
| 117 | std::queue<Xaction *> queue; | |
| 118 | const char *id_name = nullptr; | |
| 119 | ChildConfig childs; ///< Configuration settings for number running. | |
| 120 | int ipc_type = 0; | |
| 121 | Ip::Address addr; | |
| 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' | |
| 131 | ||
| 132 | struct _stats { | |
| 133 | int requests = 0; | |
| 134 | int replies = 0; | |
| 135 | int timedout = 0; | |
| 136 | int queue_size = 0; | |
| 137 | int avg_svc_time = 0; | |
| 138 | } stats; | |
| 139 | ||
| 140 | protected: | |
| 141 | /// \param name admin-visible helper category (with this process lifetime) | |
| 142 | explicit Client(const char * const name): id_name(name) {} | |
| 143 | ||
| 144 | bool queueFull() const; | |
| 145 | bool overloaded() const; | |
| 146 | void syncQueueStats(); | |
| 147 | bool prepSubmit(); | |
| 148 | void submit(const char *buf, HLPCB * callback, void *data); | |
| 149 | }; | |
| 150 | ||
| 151 | } // namespace Helper | |
| 152 | ||
| 153 | // TODO: Rename to a *Client. | |
| 154 | class statefulhelper: public Helper::Client | |
| 155 | { | |
| 156 | public: | |
| 157 | using Pointer = RefCount<statefulhelper>; | |
| 158 | typedef std::unordered_map<Helper::ReservationId, helper_stateful_server *> Reservations; | |
| 159 | ||
| 160 | ~statefulhelper() override = default; | |
| 161 | ||
| 162 | static Pointer Make(const char *name); | |
| 163 | ||
| 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 | ||
| 170 | /* Helper::Client API */ | |
| 171 | void openSessions() override; | |
| 172 | ||
| 173 | private: | |
| 174 | friend void helperStatefulSubmit(const statefulhelper::Pointer &, const char *buf, HLPCB *, void *cbData, const Helper::ReservationId &); | |
| 175 | ||
| 176 | explicit statefulhelper(const char * const name): Helper::Client(name) {} | |
| 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 | ||
| 184 | ///< reserved servers indexed by reservation IDs | |
| 185 | Reservations reservations; | |
| 186 | }; | |
| 187 | ||
| 188 | namespace Helper | |
| 189 | { | |
| 190 | ||
| 191 | /// represents a single helper process | |
| 192 | class SessionBase: public CbdataParent | |
| 193 | { | |
| 194 | public: | |
| 195 | ~SessionBase() override; | |
| 196 | ||
| 197 | /// close handler to handle exited server processes | |
| 198 | static void HelperServerClosed(SessionBase *); | |
| 199 | ||
| 200 | /** Closes pipes to the helper safely. | |
| 201 | * Handles the case where the read and write pipes are the same FD. | |
| 202 | */ | |
| 203 | void closePipesSafely(); | |
| 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 | */ | |
| 209 | void closeWritePipeSafely(); | |
| 210 | ||
| 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 | ||
| 215 | /// our creator (parent) object | |
| 216 | virtual Client &helper() const = 0; | |
| 217 | ||
| 218 | /// dequeues and sends an Unknown answer to all queued requests | |
| 219 | virtual void dropQueued(); | |
| 220 | ||
| 221 | public: | |
| 222 | /// Helper program identifier; does not change when contents do, | |
| 223 | /// including during assignment | |
| 224 | const InstanceId<SessionBase> index; | |
| 225 | ||
| 226 | int pid; | |
| 227 | Ip::Address addr; | |
| 228 | Comm::ConnectionPointer readPipe; | |
| 229 | Comm::ConnectionPointer writePipe; | |
| 230 | void *hIpc; | |
| 231 | ||
| 232 | char *rbuf; | |
| 233 | size_t rbuf_sz; | |
| 234 | size_t roffset; | |
| 235 | ||
| 236 | struct timeval dispatch_time; | |
| 237 | struct timeval answer_time; | |
| 238 | ||
| 239 | dlink_node link; | |
| 240 | ||
| 241 | struct _helper_flags { | |
| 242 | bool writing; | |
| 243 | bool closing; | |
| 244 | bool shutdown; | |
| 245 | } flags; | |
| 246 | ||
| 247 | using Requests = std::list<Xaction *>; | |
| 248 | Requests requests; ///< requests in order of submission/expiration | |
| 249 | ||
| 250 | struct { | |
| 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 | |
| 256 | } stats; | |
| 257 | void initStats(); | |
| 258 | }; | |
| 259 | ||
| 260 | /// represents a single "stateless helper" process; | |
| 261 | /// supports concurrent helper requests | |
| 262 | class Session: public SessionBase | |
| 263 | { | |
| 264 | CBDATA_CHILD(Session); | |
| 265 | ||
| 266 | public: | |
| 267 | uint64_t nextRequestId; | |
| 268 | ||
| 269 | MemBuf *wqueue; | |
| 270 | MemBuf *writebuf; | |
| 271 | ||
| 272 | Client::Pointer parent; | |
| 273 | ||
| 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; | |
| 279 | ||
| 280 | /// Whether to ignore current message, because it is timed-out or other reason | |
| 281 | bool ignoreToEom; | |
| 282 | ||
| 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 | ||
| 287 | ~Session() override; | |
| 288 | ||
| 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); | |
| 294 | ||
| 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 | ||
| 299 | /* SessionBase API */ | |
| 300 | bool reserved() override {return false;} | |
| 301 | void dropQueued() override; | |
| 302 | Client &helper() const override { return *parent; } | |
| 303 | ||
| 304 | /// Read timeout handler | |
| 305 | static void requestTimeout(const CommTimeoutCbParams &io); | |
| 306 | }; | |
| 307 | ||
| 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 | |
| 314 | { | |
| 315 | CBDATA_CHILD(helper_stateful_server); | |
| 316 | ||
| 317 | public: | |
| 318 | ~helper_stateful_server() override; | |
| 319 | void reserve(); | |
| 320 | void clearReservation(); | |
| 321 | ||
| 322 | /* Helper::SessionBase API */ | |
| 323 | bool reserved() override {return reservationId.reserved();} | |
| 324 | Helper::Client &helper() const override { return *parent; } | |
| 325 | ||
| 326 | statefulhelper::Pointer parent; | |
| 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 | |
| 333 | time_t reservationStart; ///< when the last `reservation` was made | |
| 334 | }; | |
| 335 | ||
| 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 &); | |
| 340 | ||
| 341 | #endif /* SQUID_SRC_HELPER_H */ | |
| 342 |