/*
- * DEBUG: section 84 Helper process maintenance
- * AUTHOR: Harvest Derived?
- *
- * SQUID Web Proxy Cache http://www.squid-cache.org/
- * ----------------------------------------------------------
- *
- * Squid is the result of efforts by numerous individuals from
- * the Internet community; see the CONTRIBUTORS file for full
- * details. Many organizations have provided support for Squid's
- * development; see the SPONSORS file for full details. Squid is
- * Copyrighted (C) 2001 by the Regents of the University of
- * California; see the COPYRIGHT file for full details. Squid
- * incorporates software developed and/or copyrighted by other
- * sources; see the CREDITS file for full details.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ * 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.
+ * Please see the COPYING and CONTRIBUTORS files for details.
*/
+/* DEBUG: section 84 Helper process maintenance */
+
#ifndef SQUID_HELPER_H
#define SQUID_HELPER_H
#include "base/AsyncCall.h"
+#include "base/InstanceId.h"
#include "cbdata.h"
#include "comm/forward.h"
#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 "HelperChildConfig.h"
-#include "HelperReply.h"
+#include "sbuf/SBuf.h"
-class helper_request;
+#include <list>
+#include <map>
+#include <queue>
+#include <unordered_map>
-typedef void HLPCB(void *, const HelperReply &reply);
+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.
+ *
+ * With respect to load, a helper goes through these states (roughly):
+ * 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);
+ * 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.
+ * An overloaded helper either drops new requests or keeps queuing them, depending on
+ * whether the caller can handle dropped requests (trySubmit vs helperSubmit APIs).
+ * 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
{
+ CBDATA_CLASS(helper);
+
public:
inline helper(const char *name) :
- cmdline(NULL),
- id_name(name),
- ipc_type(0),
- last_queue_warn(0),
- last_restart(0),
- eom('\n') {
+ cmdline(NULL),
+ id_name(name),
+ ipc_type(0),
+ droppedRequests(0),
+ overloadStart(0),
+ last_queue_warn(0),
+ last_restart(0),
+ timeout(0),
+ retryTimedOut(false),
+ retryBrokenHelper(false),
+ eom('\n') {
memset(&stats, 0, sizeof(stats));
}
~helper();
+ /// \returns next request in the queue, or nil.
+ Helper::Xaction *nextRequest();
+
+ /// 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::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;
- HelperChildConfig childs; ///< Configuration settings for number running.
+ Helper::ChildConfig childs; ///< Configuration settings for number running.
int ipc_type;
Ip::Address addr;
+ 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
+ bool retryTimedOut; ///< Whether the timed-out requests must retried
+ bool retryBrokenHelper; ///< Whether the requests must retried on BH replies
+ SBuf onTimedOutResponse; ///< The response to use when helper response timedout
char eom; ///< The char which marks the end of (response) message, normally '\n'
struct _stats {
int requests;
int replies;
+ int timedout;
int queue_size;
int avg_svc_time;
} stats;
-private:
- CBDATA_CLASS2(helper);
+protected:
+ friend void helperSubmit(helper * hlp, const char *buf, HLPCB * callback, void *data);
+ bool queueFull() const;
+ bool overloaded() const;
+ void syncQueueStats();
+ bool prepSubmit();
+ void submit(const char *buf, HLPCB * callback, void *data);
};
class statefulhelper : public helper
{
+ CBDATA_CLASS(statefulhelper);
+
public:
- inline statefulhelper(const char *name) : helper(name), datapool(NULL), IsAvailable(NULL), OnEmptyQueue(NULL) {};
- inline ~statefulhelper() {};
+ 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:
- CBDATA_CLASS2(statefulhelper);
+ 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.
+ *
+ * \param name displayed for the helper being shutdown if logging an error
*/
- void closePipesSafely();
+ void closePipesSafely(const char *name);
/** Closes the reading pipe.
* If the read and write sockets are the same the write pipe will
* also be closed. Otherwise its left open for later handling.
+ *
+ * \param name displayed for the helper being shutdown if logging an error
*/
- void closeWritePipeSafely();
+ 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:
- int index;
+ /// Helper program identifier; does not change when contents do,
+ /// including during assignment
+ const InstanceId<HelperServerBase> index;
int pid;
Ip::Address addr;
Comm::ConnectionPointer readPipe;
dlink_node link;
struct _helper_flags {
- bool busy;
bool writing;
bool closing;
bool shutdown;
- bool reserved;
} flags;
+ typedef std::list<Helper::Xaction *> Requests;
+ Requests requests; ///< requests in order of submission/expiration
+
struct {
uint64_t uses; //< requests sent to this helper
uint64_t replies; //< replies received from this helper
uint64_t pending; //< queued lookups waiting to be sent to this helper
uint64_t releases; //< times release() has been called on this helper (if stateful)
+ uint64_t timedout; //< requests which timed-out
} stats;
void initStats();
};
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_CHILD(helper_server);
+
public:
+ uint64_t nextRequestId;
+
MemBuf *wqueue;
MemBuf *writebuf;
helper *parent;
- helper_request **requests;
-private:
- CBDATA_CLASS2(helper_server);
-};
+ /// 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;
-class helper_stateful_request;
+ /// Whether to ignore current message, because it is timed-out or other reason
+ bool ignoreToEom;
-class helper_stateful_server : public HelperServerBase
-{
-public:
- /* MemBuf wqueue; */
- /* MemBuf writebuf; */
+ // 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
- statefulhelper *parent;
- helper_stateful_request *request;
+ 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);
- void *data; /* State data used by the calling routines */
+ /// 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);
-private:
- CBDATA_CLASS2(helper_stateful_server);
+ /*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);
};
-class helper_request
+// TODO: Rename to StatefulHelperServer and rename HelperServerBase to HelperServer.
+/// represents a single "stateful helper" process
+class helper_stateful_server : public HelperServerBase
{
+ CBDATA_CHILD(helper_stateful_server);
public:
- MEMPROXY_CLASS(helper_request);
- char *buf;
- HLPCB *callback;
- void *data;
+ virtual ~helper_stateful_server();
+ void reserve();
+ void clearReservation();
- struct timeval dispatch_time;
-};
+ /* HelperServerBase API */
+ virtual bool reserved() override {return reservationId.reserved();}
+ virtual helper *getParent() const override {return parent;}
-MEMPROXY_CLASS_INLINE(helper_request);
+ /// close handler to handle exited server processes
+ static void HelperServerClosed(helper_stateful_server *srv);
-class helper_stateful_request
-{
+ statefulhelper *parent;
-public:
- MEMPROXY_CLASS(helper_stateful_request);
- char *buf;
- HLPCB *callback;
- int placeholder; /* if 1, this is a dummy request waiting for a stateful helper to become available */
- void *data;
+ // 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
};
-MEMPROXY_CLASS_INLINE(helper_stateful_request);
-
/* 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 helperStats(StoreEntry * sentry, helper * hlp, const char *label = NULL);
-void helperStatefulStats(StoreEntry * sentry, statefulhelper * hlp, const char *label = NULL);
+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 */
+