/*
- * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
+ * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
*
* Squid software is distributed under GPLv2+ license and includes
* contributions from numerous individuals and organizations.
#include "dlink.h"
#include "helper/ChildConfig.h"
#include "helper/forward.h"
+#include "helper/Reply.h"
+#include "helper/Request.h"
+#include "helper/ReservationId.h"
#include "ip/Address.h"
-#include "SBuf.h"
+#include "sbuf/SBuf.h"
#include <list>
#include <map>
+#include <queue>
+#include <unordered_map>
class Packable;
+class wordlist;
+namespace Helper
+{
+/// Holds the required data to serve a helper request.
+class Xaction {
+ MEMPROXY_CLASS(Helper::Xaction);
+public:
+ Xaction(HLPCB *c, void *d, const char *b): request(c, d, b) {}
+ Helper::Request request;
+ Helper::Reply reply;
+};
+}
+
+class HelperServerBase;
/**
* Managers a set of individual helper processes with a common queue of requests.
*
* idle: no processes are working on requests (and no requests are queued);
* normal: some, but not all processes are working (and no requests are queued);
* busy: all processes are working (and some requests are possibly queued);
- * full: all processes are working and at least 2*#processes requests are queued.
+ * overloaded: a busy helper with more than queue-size requests in the queue.
*
- * A "busy" helper queues new requests and issues a WARNING every 10 minutes or so.
- * A "full" helper either drops new requests or keeps queuing them, depending on
+ * A busy helper queues new requests and issues a WARNING every 10 minutes or so.
+ * An overloaded helper either drops new requests or keeps queuing them, depending on
* whether the caller can handle dropped requests (trySubmit vs helperSubmit APIs).
- * An attempt to use a "full" helper that has been "full" for 3+ minutes kills worker.
- * Given enough load, all helpers except for external ACL will make such attempts.
+ * If an overloaded helper has been overloaded for 3+ minutes, an attempt to use
+ * it results in on-persistent-overload action, which may kill worker.
*/
class helper
{
cmdline(NULL),
id_name(name),
ipc_type(0),
- full_time(0),
+ droppedRequests(0),
+ overloadStart(0),
last_queue_warn(0),
last_restart(0),
timeout(0),
}
~helper();
- ///< whether at least one more request can be successfully submitted
- bool queueFull() const;
+ /// \returns next request in the queue, or nil.
+ Helper::Xaction *nextRequest();
- ///< If not full, submit request. Otherwise, either kill Squid or return false.
+ /// If possible, submit request. Otherwise, either kill Squid or return false.
bool trySubmit(const char *buf, HLPCB * callback, void *data);
/// Submits a request to the helper or add it to the queue if none of
/// the servers is available.
- void submitRequest(Helper::Request *r);
+ void submitRequest(Helper::Xaction *r);
/// Dump some stats about the helper state to a Packable object
void packStatsInto(Packable *p, const char *label = NULL) const;
+ /// whether the helper will be in "overloaded" state after one more request
+ /// already overloaded helpers return true
+ bool willOverload() const;
+
+ /// Updates interall statistics and start new helper server processes after
+ /// an unexpected server exit
+ /// \param needsNewServers true if new servers must started, false otherwise
+ void handleKilledServer(HelperServerBase *srv, bool &needsNewServers);
public:
wordlist *cmdline;
dlink_list servers;
- dlink_list queue;
+ std::queue<Helper::Xaction *> queue;
const char *id_name;
Helper::ChildConfig childs; ///< Configuration settings for number running.
int ipc_type;
Ip::Address addr;
- time_t full_time; ///< when a full helper became full (zero for non-full helpers)
+ unsigned int droppedRequests; ///< requests not sent during helper overload
+ time_t overloadStart; ///< when the helper became overloaded (zero if it is not)
time_t last_queue_warn;
time_t last_restart;
time_t timeout; ///< Requests timeout
protected:
friend void helperSubmit(helper * hlp, const char *buf, HLPCB * callback, void *data);
- void prepSubmit();
+ bool queueFull() const;
+ bool overloaded() const;
+ void syncQueueStats();
+ bool prepSubmit();
void submit(const char *buf, HLPCB * callback, void *data);
};
CBDATA_CLASS(statefulhelper);
public:
- inline statefulhelper(const char *name) : helper(name), datapool(NULL), IsAvailable(NULL), OnEmptyQueue(NULL) {}
+ typedef std::unordered_map<Helper::ReservationId, helper_stateful_server *> Reservations;
+
+ inline statefulhelper(const char *name) : helper(name) {}
inline ~statefulhelper() {}
public:
- MemAllocator *datapool;
- HLPSAVAIL *IsAvailable;
- HLPSONEQ *OnEmptyQueue;
+ /// reserve the given server
+ void reserveServer(helper_stateful_server * srv);
+
+ /// undo reserveServer(), clear the reservation and kick the queue
+ void cancelReservation(const Helper::ReservationId reservation);
private:
- friend void helperStatefulSubmit(statefulhelper * hlp, const char *buf, HLPCB * callback, void *data, helper_stateful_server * lastserver);
- void submit(const char *buf, HLPCB * callback, void *data, helper_stateful_server *lastserver);
+ friend void helperStatefulSubmit(statefulhelper * hlp, const char *buf, HLPCB * callback, void *data, const Helper::ReservationId & reservation);
+
+ /// \return the previously reserved server (if the reservation is still valid) or nil
+ helper_stateful_server *findServer(const Helper::ReservationId & reservation);
+
+ void submit(const char *buf, HLPCB * callback, void *data, const Helper::ReservationId & reservation);
+ bool trySubmit(const char *buf, HLPCB * callback, void *data, const Helper::ReservationId & reservation);
+
+ ///< reserved servers indexed by reservation IDs
+ Reservations reservations;
};
-/**
- * Fields shared between stateless and stateful helper servers.
- */
-class HelperServerBase
+/// represents a single helper process abstraction
+class HelperServerBase: public CbdataParent
{
public:
+ virtual ~HelperServerBase();
/** Closes pipes to the helper safely.
* Handles the case where the read and write pipes are the same FD.
*
*/
void closeWritePipeSafely(const char *name);
+ // TODO: Teach each child to report its child-specific state instead.
+ /// whether the server is locked for exclusive use by a client
+ virtual bool reserved() = 0;
+
+ /// dequeues and sends a Helper::Unknown answer to all queued requests
+ virtual void dropQueued();
+
+ /// the helper object that created this server
+ virtual helper *getParent() const = 0;
+
public:
/// Helper program identifier; does not change when contents do,
/// including during assignment
bool writing;
bool closing;
bool shutdown;
- bool reserved;
} flags;
- typedef std::list<Helper::Request *> Requests;
+ typedef std::list<Helper::Xaction *> Requests;
Requests requests; ///< requests in order of submission/expiration
struct {
class MemBuf;
class CommTimeoutCbParams;
+// TODO: Rename to StatelessHelperServer and rename HelperServerBase to HelperServer.
+/// represents a single "stateless helper" process
class helper_server : public HelperServerBase
{
- CBDATA_CLASS(helper_server);
+ CBDATA_CHILD(helper_server);
public:
uint64_t nextRequestId;
helper *parent;
+ /// The helper request Xaction object for the current reply .
+ /// A helper reply may be distributed to more than one of the retrieved
+ /// packets from helper. This member stores the Xaction object as long as
+ /// the end-of-message for current reply is not retrieved.
+ Helper::Xaction *replyXaction;
+
+ /// Whether to ignore current message, because it is timed-out or other reason
+ bool ignoreToEom;
+
// STL says storing std::list iterators is safe when changing the list
typedef std::map<uint64_t, Requests::iterator> RequestIndex;
RequestIndex requestsIndex; ///< maps request IDs to requests
+ virtual ~helper_server();
+ /// Search in queue for the request with requestId, return the related
+ /// Xaction object and remove it from queue.
+ /// If concurrency is disabled then the requestId is ignored and the
+ /// Xaction of the next request in queue is retrieved.
+ Helper::Xaction *popRequest(int requestId);
+
/// Run over the active requests lists and forces a retry, or timedout reply
/// or the configured "on timeout response" for timedout requests.
void checkForTimedOutRequests(bool const retry);
+ /*HelperServerBase API*/
+ virtual bool reserved() override {return false;}
+ virtual void dropQueued() override;
+ virtual helper *getParent() const override {return parent;}
+
/// Read timeout handler
static void requestTimeout(const CommTimeoutCbParams &io);
+
+ /// close handler to handle exited server processes
+ static void HelperServerClosed(helper_server *srv);
};
+// TODO: Rename to StatefulHelperServer and rename HelperServerBase to HelperServer.
+/// represents a single "stateful helper" process
class helper_stateful_server : public HelperServerBase
{
- CBDATA_CLASS(helper_stateful_server);
+ CBDATA_CHILD(helper_stateful_server);
public:
- /* MemBuf wqueue; */
- /* MemBuf writebuf; */
+ virtual ~helper_stateful_server();
+ void reserve();
+ void clearReservation();
+
+ /* HelperServerBase API */
+ virtual bool reserved() override {return reservationId.reserved();}
+ virtual helper *getParent() const override {return parent;}
+
+ /// close handler to handle exited server processes
+ static void HelperServerClosed(helper_stateful_server *srv);
statefulhelper *parent;
- void *data; /* State data used by the calling routines */
+ // Reservations temporary lock the server for an exclusive "client" use. The
+ // client keeps the reservation ID as a proof of her reservation. If a
+ // reservation expires, and the server is reserved for another client, then
+ // the reservation ID presented by the late client will not match ours.
+ Helper::ReservationId reservationId; ///< "confirmation ID" of the last
+ time_t reservationStart; ///< when the last `reservation` was made
};
/* helper.c */
void helperOpenServers(helper * hlp);
void helperStatefulOpenServers(statefulhelper * hlp);
void helperSubmit(helper * hlp, const char *buf, HLPCB * callback, void *data);
-void helperStatefulSubmit(statefulhelper * hlp, const char *buf, HLPCB * callback, void *data, helper_stateful_server * lastserver);
+void helperStatefulSubmit(statefulhelper * hlp, const char *buf, HLPCB * callback, void *data, uint64_t reservation);
void helperShutdown(helper * hlp);
void helperStatefulShutdown(statefulhelper * hlp);
-void helperStatefulReleaseServer(helper_stateful_server * srv);
-void *helperStatefulServerGetData(helper_stateful_server * srv);
#endif /* SQUID_HELPER_H */