]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Fix the trunk API docs to make them a bit easier to understand
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Tue, 7 Jan 2020 18:55:19 +0000 (12:55 -0600)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Tue, 7 Jan 2020 20:53:50 +0000 (14:53 -0600)
src/lib/server/trunk.c
src/lib/server/trunk.h

index b0abe9ab428af5239195e40fb07c4dfc4985cedd..6adf98faf8ae108ecfaae882523b2cef6c60e4c5 100644 (file)
@@ -295,7 +295,7 @@ struct fr_trunk_s {
 
        fr_time_t               last_connected;         //!< Last time a connection connected.
 
-       fr_time_t               last_open_failed;               //!< Last time a connection failed.
+       fr_time_t               last_open_failed;       //!< Last time a connection failed.
        /** @} */
 
        /** @name Statistics
index 344085a6459e47801066a9ac0cda4f5b32e63472..19bb5bf0d358fded7ed3e4d1d54b34d81c09b32a 100644 (file)
@@ -68,15 +68,17 @@ typedef struct {
                                                        ///< to close an existing connection.
 
 
-       fr_time_delta_t         req_cleanup_delay;      //!< How long must a request in the unassigned list
-                                                       ///< not have been used for, before it's cleaned up.
+       fr_time_delta_t         req_cleanup_delay;      //!< How long must a request in the unassigned (free)
+                                                       ///< list not have been used for before it's cleaned up
+                                                       ///< and actually freed.
 
        fr_time_delta_t         manage_interval;        //!< How often we run the management algorithm to
                                                        ///< open/close connections.
 
-       unsigned                req_pool_headers;       //!< How many chunk headers the pool should contain.
+       unsigned                req_pool_headers;       //!< How many chunk headers the talloc pool allocated
+                                                       ///< with the treq should contain.
 
-       size_t                  req_pool_size;          //!< How big the pool should be.
+       size_t                  req_pool_size;          //!< The size of the talloc pool allocated with the treq.
 
        bool                    always_writable;        //!< Set to true if our ability to write requests to
                                                        ///< a connection handle is not dependant on the state
@@ -129,60 +131,73 @@ extern CONF_PARSER const fr_trunk_config[];
 
 /** Allocate a new connection for the trunk
  *
- * The trunk code only interacts with the connections via the connection API.
- * Which means its shielded from all the implementation details of opening
+ * The trunk code only interacts with underlying connections via the connection API.
+ * As a result the trunk API is shielded from the implementation details of opening
  * and closing connections.
  *
- * When allocating a new connection, the trunk connection should specify an
- * open callback which installs read/write handlers.
+ * When creating new connections, this callback is used to allocate and configure
+ * a new #fr_connection_t, this #fr_connection_t and the fr_connection API is how the
+ * trunk signals the underlying connection that it should start, reconnect, and halt (stop).
  *
- * The trunk API must be informed when a connection is readable, and, if
- * `always_writable == false`, when the connection is writable.
+ * The trunk must be informed when the underlying connection is readable, and,
+ * if `always_writable == false`, when the connection is writable.
  *
- * When the connection is readable, a read I/O handler installed in the open()
- * callback of the fr_connection_t, must either call
- * `fr_trunk_connection_signal_readable(tconn)` immediately, or feed any incoming
- * data to the underlying library, and _then_ call #fr_trunk_connection_signal_readable.
+ * When the connection is readable, a read I/O handler installed by the open()
+ * callback of the #fr_connection_t must either:
  *
- * When the connection is writable, if `always_writable == true` then the
- * underlying library should be informed the connection is writable.
+ * - If there's no underlying I/O library, call `fr_trunk_connection_signal_readable(tconn)`
+ *   immediately, relying on the trunk demux callback to perform decoding and demuxing.
+ * - If there is an underlying I/O library, feed any incoming data to that library and
+ *   then call #fr_trunk_connection_signal_readable if the underlying I/O library
+ *   indicates complete responses are ready for processing.
  *
- * When the connection is writable, if `always_writable == false` then the
- * underlying library should be informed the connection is writable, with any
- * pending data written, then `fr_trunk_connection_signal_writable(tconn)` should be
- * called.
+ * When the connection is writable a write I/O handler installed by the open() callback
+ * of the #fr_connection_t must either:
+ *
+ * - If `always_writable == true` - Inform the underlying I/O library that the connection
+ *   is writable.  The trunk API does not need to be informed as it will immediately pass
+ *   through any enqueued requests to the I/O library.
+ * - If `always_writable == false` and there's an underlying I/O library,
+ *   call `fr_trunk_connection_signal_writable(tconn)` to allow the trunk mux callback
+ *   to pass requests to the underlying I/O library and (optionally) signal the I/O library
+ *   that the connection is writable.
+ * - If `always_writable == false` and there's no underlying I/O library,
+ *   call `fr_trunk_connection_signal_writable(tconn)` to allow the trunk mux callback
+ *   to encode and write requests to a socket.
  *
  * @param[in] tconn            The trunk connection this connection will be bound to.
- *                             Should be used as the context for any connections
+ *                             Should be used as the context for any #fr_connection_t
  *                             allocated.
  * @param[in] el               The event list to use for I/O and timer events.
- * @param[in] conf             Connection configuration.
+ * @param[in] conf             Configuration of the #fr_connection_t.
  * @param[in] log_prefix       What to prefix connection log messages with.
- * @param[in] uctx             User data to pass to the alloc callback.
+ * @param[in] uctx             User context data passed to #fr_trunk_alloc.
  * @return
- *     - A new fr_connection_t on success (should be in the halted state).
+ *     - A new fr_connection_t on success (should be in the halted state - the default).
  *     - NULL on error.
  */
 typedef fr_connection_t *(*fr_trunk_connection_alloc_t)(fr_trunk_connection_t *tconn, fr_event_list_t *el,
                                                        fr_connection_conf_t const *conf,
                                                        char const *log_prefix, void *uctx);
 
-/** Inform the API client which connections the trunk API wants to be notified of I/O events on
+/** Inform the trunk API client which I/O events the trunk wants to receive
  *
- * I/O handlers may call one or more of the following functions to signal
- * that an event has occurred.
+ * I/O handlers installed by this callback should call one or more of the following
+ * functions to signal that an I/O event has occurred:
  *
  * - fr_trunk_connection_signal_writable - Connection is now writable.
  * - fr_trunk_connection_signal_readable - Connection is now readable.
- * - fr_trunk_connection_signal_active - Connection is full or congested.
+ * - fr_trunk_connection_signal_inactive - Connection is full or congested.
  * - fr_trunk_connection_signal_active - Connection is no longer full or congested.
  * - fr_trunk_connection_signal_reconnect - Connection is inviable and should be reconnected.
  *
- * @param[in] tconn            That wants to be notified.
- * @param[in] conn             The underlying connection.
- * @param[in] el               to insert FD events into.
- * @param[in] notify_on                When to signal the trunk connection.
- * @param[in] uctx             User data to pass to the notify callback.
+ * @param[in] tconn            That should be notified of I/O events.
+ * @param[in] conn             The #fr_connection_t bound to the tconn.
+ *                             Use #fr_connection_get_handle to access the
+ *                             connection handle or file descriptor.
+ * @param[in] el               to insert I/O events into.
+ * @param[in] notify_on                I/O events to signal the trunk connection on.
+ * @param[in] uctx             User context data passed to #fr_trunk_alloc.
  */
 typedef void (*fr_trunk_connection_notify_t)(fr_trunk_connection_t *tconn, fr_connection_t *conn,
                                             fr_event_list_t *el,
@@ -190,44 +205,46 @@ typedef void (*fr_trunk_connection_notify_t)(fr_trunk_connection_t *tconn, fr_co
 
 /** Multiplex one or more requests into a single connection
  *
- * In most cases this function should pop requests from the pending
- * queue of the connection using #fr_trunk_connection_pop_request.
- *
- * This function should then serialize the protocol request data contained
- * within the request's rctx, writing it to the provided conn.
- * This function should then insert the treq (fr_trunk_request_t)
- * into a tracking structure stored in the conn (either in the handle or the
- * uctx).
- * This tracking structure will be used later in the demux function to match
- * protocol requests with protocol responses.
- *
- * If working at the socket level, and a write on a file descriptor indicates
- * less data was written than was needed, the API client should track the
- * amount of data written, and should call `fr_trunk_request_signal_partial(treq)`
- * this will move the request out of the pending queue, and store it in the
- * partial field of the connection.  The next time #fr_trunk_connection_pop_request
- * is called, the treq will be returned first.  The API client should continue
- * writing the partially written request to the socket.
- *
- * After calling #fr_trunk_request_signal_partial this function *MUST NOT*
+ * This callback should:
+ *
+ * - Pop one or more requests from the trunk connection's pending queue using
+ *   #fr_trunk_connection_pop_request.
+ * - Serialize the protocol request data contained within the trunk request's (treq's)
+ *   pctx, writing it to the provided #fr_connection_t (or underlying connection handle).
+ * - Insert the provided treq
+ *   into a tracking structure associated with the #fr_connection_t or uctx.
+ *   This tracking structure will be used later in the trunk demux callback to match
+ *   protocol requests with protocol responses.
+ *
+ * If working at the socket level and a write on a file descriptor indicates
+ * less data was written than was needed, the trunk API client should track the
+ * amount of data written in the protocol request (preq), and should call
+ * `fr_trunk_request_signal_partial(treq)`.
+ * #fr_trunk_request_signal_partial will move the request out of the pending
+ * queue, and store it in the partial slot of the trunk connection.
+ * The next time #fr_trunk_connection_pop_request is called, the partially written
+ * treq will be returned first.  The API client should continue writing the partially
+ * written request to the socket.
+ *
+ * After calling #fr_trunk_request_signal_partial this callback *MUST NOT*
  * call #fr_trunk_connection_pop_request again, and should immediately return.
  *
  * If the request can't be written to the connection because it the connection
- * has become unusable, this function should call
+ * has become unusable, this callback should call
  * `fr_connection_signal_reconnect(conn)` to notify the connection API that the
  * connection is unusable. The current request will either fail, or be
  * re-enqueued depending on the trunk configuration.
  *
- * After calling #fr_connection_signal_reconnect this function *MUST NOT*
+ * After calling #fr_connection_signal_reconnect this callback *MUST NOT*
  * call #fr_trunk_connection_pop_request again, and should immediately return.
  *
  * If the protocol request data can't be written to the connection because the
- * data is invalid, or because some other error occurred, this function should
- * call `fr_trunk_request_signal_fail(treq)`, this function may then continue
- * processing other requests.
+ * data is invalid or because some other error occurred, this callback should
+ * call `fr_trunk_request_signal_fail(treq)`, this callback may then continue
+ * popping/processing other requests.
  *
- * @param[in] tconn            The trunk connection used to dequeue trunk
- *                             requests.
+ * @param[in] tconn            The trunk connection to dequeue trunk
+ *                             requests from.
  * @param[in] conn             Connection to write the request to.
  *                             Use #fr_connection_get_handle to access the
  *                             connection handle or file descriptor.
@@ -237,28 +254,31 @@ typedef void (*fr_trunk_request_mux_t)(fr_trunk_connection_t *tconn, fr_connecti
 
 /** Demultiplex on or more responses, reading them from a connection, decoding them, and matching them with their requests
  *
- * This function should read responses from the connection, decode them,
- * and match them with a treq (fr_trunk_request_t) using a tracking structure
- * stored in the conn (either in the handle or the uctx).
+ * This callback should either:
+ *
+ * - If an underlying I/O library is used, request complete responses from
+ *   the I/O library, and match the responses with a treq (trunk request)
+ *   using a tracking structure associated with the #fr_connection_t or uctx.
+ * - If no underlying I/O library is used, read responses from the #fr_connection_t,
+ *   decode those responses, and match those responses with a treq using a tracking
+ *   structure associated with the #fr_connection_t or uctx.
  *
  * Once the treq has been retrieved from the tracking structure, the original
- * REQUEST * and rctx may be retrieved with
- * #fr_trunk_request_get_resumption_data.
+ * REQUEST * and rctx may be retrieved with #fr_trunk_request_get_resumption_data.
  *
  * The result (positive or negative), should be written to the rctx structure.
  *
- * `fr_trunk_request_signal_success(treq)` should be used to inform the trunk
+ * #fr_trunk_request_signal_success should be used to inform the trunk
  * that the request should be placed back into the worker thread's runnable
  * queue and should continue being processed.
  *
- * If a connection appears to have become unusable, this function should call
- * `fr_connection_signal_reconnect(conn)` and immediately return.  The current
- * request will either fail, or be re-enqueued depending on the trunk
- * configuration.
+ * If a connection appears to have become unusable, this callback should call
+ * #fr_connection_signal_reconnect and immediately return.  The current
+ * treq will either fail, or be re-enqueued depending on the trunk configuration.
  *
- * #tr_trunk_request_signal_fail should *NOT* be called in any circumstances
- * as this function is only used for reporting failures at an I/O layer level
- * not failures of queries or external services.
+ * #tr_trunk_request_signal_fail should *NOT* be called as this function is only
+ * used for reporting failures at an I/O layer level not failures of queries or
+ * external services.
  *
  * @param[in] tconn            The trunk connection.
  * @param[in] conn             Connection to read the request from.
@@ -268,20 +288,27 @@ typedef void (*fr_trunk_request_mux_t)(fr_trunk_connection_t *tconn, fr_connecti
  */
 typedef void (*fr_trunk_request_demux_t)(fr_trunk_connection_t *tconn, fr_connection_t *conn, void *uctx);
 
-/** Inform a remote service like a database that a request should be cancelled
+/** Inform a remote service like a datastore that a request should be cancelled
+ *
+ * This callback will be called any time there are one or more requests to be
+ * cancelled and a #fr_connection_t is writable, or as soon as a request is
+ * cancelled if `always_writable == true`.
  *
- * This function will be called any time there are one or more requests to be
- * cancelled and the conn is writable.  For efficiency, this function should
- * call #fr_trunk_connection_pop_cancellation multiple times, and process all
- * outstanding cancellation requests.
+ * For efficiency, this callback should call #fr_trunk_connection_pop_cancellation
+ * multiple times, and process all outstanding cancellation requests.
  *
- * If the response from the datastore needs to be tracked, then the treq should
- * be inserted into a tracking tree shared with the demuxer, and
- * #fr_trunk_request_signal_cancel_sent should be called to move the request into
+ * If the response (cancel ACK) from the remote service needs to be tracked,
+ * then the treq should be inserted into a tracking tree shared with the demuxer,
+ * and #fr_trunk_request_signal_cancel_sent should be called to move the treq into
  * the cancel_sent state.
  *
- * When the demuxer finds a matching response, it should remove the entry
- * from the tracking tree.
+ * As with the main mux callback, if a cancellation request is partially written
+ * #fr_trunk_request_signal_cancel_partial should be called, and the amount
+ * of data written should be tracked in the preq (protocol request).
+ *
+ * When the demuxer finds a matching (cancel ACK) response, the demuxer should
+ * remove the entry from the tracking tree and call
+ * #fr_trunk_request_signal_cancel_complete.
  *
  * @param[in] tconn            The trunk connection used to dequeue
  *                             cancellation requests.
@@ -292,18 +319,39 @@ typedef void (*fr_trunk_request_demux_t)(fr_trunk_connection_t *tconn, fr_connec
  */
 typedef void (*fr_trunk_request_cancel_mux_t)(fr_trunk_connection_t *tconn, fr_connection_t *conn, void *uctx);
 
-/** Remove an outstanding request from a matching structure
+/** Remove an outstanding request from a tracking/matching structure
+ *
+ * The treq (trunk request), and any associated resources should be
+ * removed from the the matching structure associated with the #fr_connection_t or uctx.
  *
- * The treq (fr_trunk_request_t), and any associated resources should be
- * removed from the the matching structure in the conn, and resources that do
- * were generated whilst encoding the protocol request should be freed.
+ * Which resources should be freed depends on the cancellation reason:
  *
- * Once this function has returned, if a cancel_sent callback has been
- * provided, the treq will enter the FR_TRUNK_REQUEST_CANCEL_EVENTED
- * state, and be placed on the connection's cancellation queue.
+ * - FR_TRUNK_CANCEL_REASON_MOVE - If an encoded request can be reused
+ *   it should be kept.  The trunk mux callback should be aware that
+ *   an encoded request may already be associated with a preq and use
+ *   that instead of re-encoding the preq.
+ *   If the encoded request cannot be reused it should be freed, and
+ *   any fields in the preq that were modified during the last mux call
+ *   (other than perhaps counters) should be reset to their initial values.
+ * - FR_TRUNK_CANCEL_REASON_SIGNAL - The encoded request and any I/O library
+ *   request handled may be freed or that may be left to another callback.
  *
- * The cancellation queue will be drained the next time the connection becomes
- * writable.
+ * After this callback is complete one of several actions will be taken:
+ *
+ * - If the cancellation reason was FR_TRUNK_CANCEL_REASON_MOVE, the treq
+ *   will move to the unassigned state, and then either be placed in the
+ *   trunk backlog, or immediately enqueued on another trunk connection.
+ * - If the reason was FR_TRUNK_CANCEL_SIGNAL
+ *   - ...and a request_cancel_mux callback was provided, the
+ *     the request_cancel_mux callback will be called when the connection
+ *     is next writable (or immediately if `always_writable == true`) and
+ *     the request_cancel_mux callback will send an explicit cancellation
+ *     request to terminate any outstanding queries on remote datastores.
+ *   - ...and no request_cancel_mux callback was provided, the
+ *     treq will enter the unassigned state and then be freed.
+ *
+ * @note FR_TRUNK_CANCEL_REASON_MOVE will only be set if the underlying connection
+ * is bad.  No cancellation requests will be sent for requests being moved.
  *
  * @param[in] conn             to remove request from.
  * @param[in] treq             Trunk request to cancel.
@@ -314,44 +362,47 @@ typedef void (*fr_trunk_request_cancel_mux_t)(fr_trunk_connection_t *tconn, fr_c
 typedef void (*fr_trunk_request_cancel_t)(fr_connection_t *conn, fr_trunk_request_t *treq, void *preq,
                                          fr_trunk_cancel_reason_t reason, void *uctx);
 
-/** Write a successful result to the rctx so that the API client is aware of the result
+/** Write a successful result to the rctx so that the trunk API client is aware of the result
  *
- * This function should free any memory not bound to the lifetime of the rctx
- * or request, or that was allocated explicitly to prepare for the REQUEST *
- * being used by a trunk, this may include library request handles and
- * encoded packets.
+ * The rctx should be modified in such a way that indicates to the trunk API client
+ * that the request was sent using the trunk and a response was received.
  *
- * The rctx should be modified in such a way that indicates to the API client
- * that the request could not be sent using the trunk.
+ * This callback should free any memory not bound to the lifetime of the rctx
+ * or request, or that was allocated explicitly to prepare for the REQUEST *
+ * being used by a trunk.  This may include I/O library request handles, raw
+ * responses, and decoded responses.
  *
- * After this function returns the REQUEST * will be marked as runnable.
+ * After this callback is complete, the request_free callback will be called if provided.
  */
 typedef void (*fr_trunk_request_complete_t)(REQUEST *request, void *preq, void *rctx, void *uctx);
 
-/** Write a failure result to the rctx so that the API client is aware that the request failed
+/** Write a failure result to the rctx so that the trunk API client is aware that the request failed
  *
- * This function should free any memory not bound to the lifetime of the rctx
- * or request, or that was allocated explicitly to prepare for the REQUEST *
- * being used by a trunk, this may include library request handles and
- * (partially-)encoded packets.
- *
- * The rctx should be modified in such a way that indicates to the API client
+ * The rctx should be modified in such a way that indicates to the trunk API client
  * that the request could not be sent using the trunk.
  *
- * After this function returns the REQUEST * will be marked as runnable.
+ * This callback should free any memory not bound to the lifetime of the rctx
+ * or request, or that was allocated explicitly to prepare for the REQUEST *
+ * being used by a trunk.
  *
- * @note If a cancel function is provided, this function should be used to remove
+ * @note If a cancel function is provided, the cancel function should be used to remove
  *       active requests from any request/response matching, not the fail function.
+ *      Both the cancel and fail functions will be called for a request that has been
+ *      sent or partially sent.
+ *
+ * After this callback is complete, the request_free callback will be called if provided.
  */
 typedef void (*fr_trunk_request_fail_t)(REQUEST *request, void *preq, void *rctx, void *uctx);
 
 /** Free resources associated with a trunk request
  *
  * The trunk request is complete.  If there's a request still associated with the
- * trunk request, that will be provided, so that i can be marked runnable if there's
- * no further processing required.
+ * trunk request, that will be provided so that it can be marked runnable, but
+ * be aware that the REQUEST * value will be NULL if the request was cancelled due
+ * to a signal.
  *
- * The preq *MUST* be explicitly freed by this function.
+ * The preq and any associated data such as encoded packets or I/O library request
+ * handled *MUST* be explicitly freed by this function.
  *
  * @param[in] request          to mark as runnable if no further processing is required.
  * @param[in] preq_to_free     As per the name.