The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
pipeline.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or (at
5 * your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: 2d832f1b7b0ddd54163f0220fda93e30f9eac7c3 $
19 * @file lib/redis/pipeline.c
20 * @brief Functions for pipelining commands.
21 *
22 * @copyright 2019 The FreeRADIUS server project
23 * @copyright 2019 Network RADIUS SAS (legal@networkradius.com)
24 *
25 * @author Arran Cudbard-Bell (a.cudbardb@freeradius.org)
26 */
27
28#include <freeradius-devel/server/connection.h>
29#include <freeradius-devel/server/trunk.h>
30
31#include "pipeline.h"
32#include "cluster_async.h"
33#include "io.h"
34
35
36/** The thread local free list
37 *
38 * Any entries remaining in the list will be freed when the thread is joined
39 */
41
42typedef enum {
43 FR_REDIS_COMMAND_NORMAL = 0, //!< A normal, non-transactional command.
44 FR_REDIS_COMMAND_TRANSACTION_START, //!< Start of a transaction block. Either WATCH or MULTI.
45 ///< if a transaction is started with WATCH, then multi
46 ///< is not marked up as a transaction start.
47 FR_REDIS_COMMAND_TRANSACTION_END //!< End of a transaction block. Either EXEC or DISCARD.
48 ///< If this command fails with
49 ///< MOVED or ASK, all commands back to the previous
50 ///< MULTI command must be requeued.
52
53typedef enum {
54 FR_REDIS_COMMAND_FMT_EXPANDED = 0, //!< A command as a single string
55 FR_REDIS_COMMAND_FMT_ARGV, //!< A command as an argv array
56 FR_REDIS_COMMAND_FMT_PREFORMATTED //!< A command preformatted with redisCommandFormat
58
59/** Represents a single command
60 *
61 */
63 fr_redis_command_set_t *cmds; //!< Command set this entry belongs to.
64 fr_dlist_t entry; //!< Entry in the command buffer.
65
66 fr_redis_command_type_t type; //!< Redis command type.
67 fr_redis_command_fmt_t fmt; //!< Redis command format.
68
69 union {
70 struct{
71 char const *str; //!< The command string.
72 size_t str_len; //!< Length of the command string.
73 };
74 struct {
75 size_t argc; //!< Number of argv arguments.
76 char const **argv; //!< Arguments for the redis command.
77 size_t *argv_len; //!< Lengths of the arguments.
78 };
79 };
80
81 uint64_t sqn; //!< The sequence number of the command. This is only
82 ///< valid for a specific handle, and is unique within
83 ///< the handle.
84
85 fr_redis_command_complete_t complete; //!< Callback to process result from this command.
86
87 void *rctx; //!< To be passed to the callback.
88};
89
90/** Represents a collection of pipelined commands
91 *
92 * Commands MUST map to the same cluster node if using clustering.
93 */
96
97 fr_redis_async_rcode_t rcode; //!< Code from last error returned.
98 bool autofree; //!< Should the command set be freed when it is complete
99
100 char *next_node_ip; //!< IP address of node from MOVED / ASK reply
101 uint16_t next_node_port; //!< Port of node from MOVED / ASK reply
102
103 /** @name Command state lists
104 * @{
105 */
106 fr_dlist_head_t pending; //!< Commands yet to be sent.
107 fr_dlist_head_t sent; //!< Commands sent.
108 fr_dlist_head_t completed; //!< Commands complete with replies.
109 /** @} */
110
111 uint8_t redirected; //!< How many times this command set was redirected.
112
113 /** @name Request state
114 *
115 * treq and request are duplicated here with the trunk code.
116 * The reason for this, is because a fr_command_set_t, may need to be transferred
117 * between trunks when redirects are being followed, and so we need this information
118 * encapsulated within the command set, not just within the trunk.
119 * @{
120 */
121 trunk_request_t *treq; //!< Trunk request this command set is associated with.
122 request_t *request; //!< Request this commands set is associated with (if any).
123 void *rctx; //!< Resume context to write results to.
124 /** @} */
125
126 /** @name Callback functions
127 * @{
128 */
129 fr_redis_command_set_complete_t complete; //!< Notify the creator of the command set
130 ///< that the command set has executed to
131 ///< to completion. We have results for
132 ///< all commands.
133
134 fr_redis_command_set_fail_t fail; //!< Notify the creator of the command set
135 ///< that the command set failed to execute
136 ///< to completion. Partial results will
137 ///< be available.
138 /** @} */
139
140 /** @name Command set transaction stats
141 *
142 * We do these checks as REDIS commands from a great number of requests may pipeline
143 * requests on the same connection and leaving a transaction open would be fairly
144 * catastrophic, potentially causing errors across all future command sets set to
145 * the connection.
146 * @{
147 */
148 bool txn_watch; //!< Transaction was started with a watch statement.
149 uint16_t txn_start; //!< Number of times a transaction block was started
150 ///< in this command set.
151 uint16_t txn_end; //!< The number of times a transaction block ended
152 ///< in this command set.
153
154 /** @} */
155
156 bool blocking; //!< This command set contains one or more commands
157 ///< which block the client (e.g. WAIT)
158};
159
161 fr_redis_io_conf_t const *io_conf; //!< Redis I/O configuration. Specifies how to connect
162 ///< to the host this trunk is used to communicate with.
163 trunk_t *trunk; //!< Trunk containing all the connections to a specific
164 ///< host.
165 fr_redis_ct_t *rtcluster; //!< Cluster this trunk belongs to.
166
167 fr_redis_trunk_active_t active; //!< Callback to run when the trunk becomes active.
168 void *active_uctx; //!< Uctx to pass to active callback.
169};
170
171/** Free any free requests when the thread is joined
172 *
173 */
175{
176 fr_dlist_head_t *list = talloc_get_type_abort(arg, fr_dlist_head_t);
178
179 /*
180 * See the destructor for why this works
181 */
182 while ((cmds = fr_dlist_head(list))) if (talloc_free(cmds) < 0) return -1;
183 return talloc_free(list);
184}
185
186/** Free a command set
187 *
188 */
190{
192 (likely(!fr_dlist_entry_in_list(&cmds->entry)))) return 0; /* Keep a buffer of 1024 */
193
194 /*
195 * Freed from the free list....
196 */
198 fr_dlist_entry_unlink(&cmds->entry); /* Don't trust the list head to be available */
199 return 0;
200 }
201
202 /*
203 * It is possible for a command set to be freed while its trunk request
204 * is still inflight.
205 * This is an edge case such as shutting down the server when scripts
206 * are still being loaded, since the script loading done on redis trunk
207 * startup are not run through requests, so there isn't a cancellation
208 * path.
209 */
210 if (cmds->treq) {
211 switch (cmds->treq->state) {
216 break;
217
218 default:
219 break;
220
221 }
222 }
223
224 talloc_free_children(cmds);
225 memset(cmds, 0, sizeof(*cmds));
226
228
229 return -1; /* Prevent the free */
230}
231
232/** Allocate a new command set
233 *
234 * This is a set of commands that the calling module wants to execute
235 * on the redis server in sequence.
236 *
237 * Control will be returned to the caller via the registered complete
238 * and fail functions.
239 *
240 * @param[in] ctx to bind the command set's lifetime to.
241 * @param[in] request to pass to places that need it.
242 * @param[in] complete Function to call when all commands have been processed.
243 * @param[in] fail Function to call if the command set was not executed
244 * or was partially executed.
245 * @param[in] rctx Resume context to pass to complete and fail functions.
246 * @param[in] autofree Should the command set be freed when completed.
247 * @return A new or refurbished command set.
248 */
250 request_t *request,
253 void *rctx, bool autofree)
254
255{
257 fr_dlist_head_t *free_list;
258
259#define COMMAND_PRE_ALLOC_COUNT 8 //!< How much room we pre-allocate for commands.
260#define COMMAND_PRE_ALLOC_LEN 64 //!< How much we allocate for each command string.
261
262 /*
263 * Initialise the free list
264 */
266 MEM(free_list = talloc(NULL, fr_dlist_head_t));
267 fr_dlist_init(free_list, fr_redis_command_set_t, entry);
269 } else {
270 free_list = command_set_free_list;
271 }
272
273 /*
274 * Pull an element out of the free list
275 * or allocate a new one.
276 */
277 cmds = fr_dlist_pop_head(free_list);
278 if (!cmds) {
283 talloc_set_destructor(cmds, _redis_command_set_free);
285 }
286
290 cmds->request = request;
291 cmds->complete = complete;
292 cmds->fail = fail;
293 cmds->rctx = rctx;
294 cmds->autofree = autofree;
295
296 if (ctx) talloc_link_ctx(ctx, cmds);
297
298 return cmds;
299}
300
302 fr_redis_command_set_t *cmds, char const *cmd)
303{
304 /*
305 * Transaction sanity checks.
306 *
307 * Because commands from many different requests share the same connection
308 * we need to ensure that transaction blocks aren't left dangling and
309 * that the commands are all in the right order.
310 *
311 * We try very hard to do this without incurring a performance penalty
312 * for non-transactional commands.
313 */
314 switch (tolower(cmd[0])) {
315 case 'm':
316 if (tolower(cmd[1]) != 'u') break;
317 if (strncasecmp(cmd, "multi", sizeof("multi") - 1) != 0) break;
318 /*
319 * There should only ever be a difference of
320 * 1 between txn starts and txn ends.
321 */
322 if ((cmds->txn_end < cmds->txn_start) && ((cmds->txn_start - cmds->txn_end) > 1)) {
323 ROPTIONAL(REDEBUG, ERROR, "Too many consecutive \"MULTI\" commands");
325 }
326 /*
327 * If we have a watch before the MULTI,
328 * that's marked as the start of the transaction
329 * block.
330 */
332 cmds->txn_start++; /* Yes MULTI increments start, not WATCH */
333 break;
334
335 case 'e':
336 if (tolower(cmd[1]) != 'x') break;
337 if (strncasecmp(cmd, "exec", sizeof("exec") - 1) != 0) break;
338 goto txn_end;
339
340 /*
341 * It's useful to allow discard as it allows command syntax checks
342 * to be performed against the REDIS server without actually
343 * executing the commands.
344 */
345 case 'd':
346 if (tolower(cmd[1]) != 'i') break;
347 if (strncasecmp(cmd, "discard", sizeof("discard") - 1) != 0) break;
348 txn_end:
349 if (cmds->txn_start <= cmds->txn_end) {
350 ROPTIONAL(REDEBUG, ERROR, "Transaction not started, missing \"MULTI\" command");
352 }
354 cmds->txn_end++;
355 break;
356
357 case 'w':
358 if (tolower(cmd[1]) != 'a') break;
359
360 if (strncasecmp(cmd, "wait", sizeof("wait") - 1) == 0) {
361 cmds->blocking = true;
362 break;
363 }
364
365 if (strncasecmp(cmd, "watch", sizeof("watch") - 1) != 0) break;
366 if (cmds->txn_watch) {
367 ROPTIONAL(REDEBUG, ERROR, "Too many consecutive \"WATCH\" commands");
369 }
370 if (cmds->txn_start > cmds->txn_end) {
371 ROPTIONAL(REDEBUG, ERROR, "\"WATCH\" can only be used before \"MULTI\"");
373 }
375
376 default:
377 break;
378 }
379
381}
382
383/** Add a literal command to the command set
384 *
385 * The command must either be entirely static, or parented by the command set.
386 *
387 * @note Caller should disallow "SUBSCRIBE" et al, if they're not appropriate.
388 * As subscribing to a stream where we're not expecting it would break
389 * things, badly.
390 *
391 * @param[in] cmds Command set to add command to.
392 * @param[in] cmd_str A fully expanded/formatted command to send to redis.
393 * Must be static, or have the same lifetime as the
394 * command set (allocated with the command set as the parent).
395 * @param[in] complete Callback to run when this command completes
396 * @param[in] rctx to pass to `complete`
397 * @return
398 * - FR_REDIS_PIPELINE_BAD_CMDS if a bad command sequence is enqueued.
399 * - FR_REDIS_PIPELINE_OK if command was enqueued successfully.
400 */
402 fr_redis_command_complete_t complete, void *rctx)
403{
404 request_t *request = cmds->request;
407
409
410 MEM(cmd = talloc_zero(cmds, fr_redis_command_t));
411 cmd->cmds = cmds;
412 cmd->type = type;
413 cmd->str = cmd_str;
414 cmd->complete = complete;
415 cmd->rctx = rctx;
417 fr_dlist_insert_tail(&cmds->pending, cmd);
418
420}
421
422/** Add a command with arguments to the command set
423 *
424 * The command and arguments must either be entirely static, or parented by the command set.
425 *
426 * @param[in] cmds Command set to add command to.
427 * @param[in] argc Number of arguments.
428 * @param[in] argv Redis command arguments.
429 * @param[in] argv_len Length of the command arguments.
430 * @param[in] complete Callback to run when this command completes
431 * @param[in] rctx to pass to `complete`
432 * @return
433 * - FR_REDIS_PIPELINE_BAD_CMDS if a bad command sequence is enqueued.
434 * - FR_REDIS_PIPELINE_OK if command was enqueued successfully.
435 */
437 char const **argv, size_t *argv_len,
438 fr_redis_command_complete_t complete, void *rctx)
439{
440 request_t *request = cmds->request;
443
445
446 MEM(cmd = talloc_zero(cmds, fr_redis_command_t));
447 cmd->cmds = cmds;
448 cmd->type = type;
449 cmd->argc = argc;
450 cmd->argv = argv;
451 cmd->argv_len = argv_len;
452 cmd->complete = complete;
453 cmd->rctx = rctx;
455 fr_dlist_insert_tail(&cmds->pending, cmd);
456
458}
459
460/** Add an preformatted command to the command set as formatted by redisCommandFormat or it's variants
461 *
462 * The command must either be entirely static, or parented by the command set.
463 *
464 * @note Caller should disallow "SUBSCRIBE" et al, if they're not appropriate.
465 * As subscribing to a stream where we're not expecting it would break
466 * things, badly.
467 *
468 * @param[in] cmds Command set to add command to.
469 * @param[in] cmd_str A fully formatted command to send to redis.
470 * Must be static, or have the same lifetime as the
471 * command set (allocated with the command set as the parent).
472 * @param[in] cmd_len The length of cmd_str (as returned by redisCommandForamt)
473 * @param[in] complete Callback to run when this command completes
474 * @param[in] rctx to pass to `complete`
475 * @return
476 * - FR_REDIS_PIPELINE_BAD_CMDS if a bad command sequence is enqueued.
477 * - FR_REDIS_PIPELINE_OK if command was enqueued successfully.
478 */
480 size_t cmd_len,
481 fr_redis_command_complete_t complete, void *rctx)
482{
483 request_t *request = cmds->request;
486 char const *p = cmd_str, *end;
487
488 /*
489 * Preformatted Redis commands start *<n>\r\n$<n>\r\n<cmd>. Verify that is what we have.
490 */
491 end = p + cmd_len;
492 if (*p++ != '*') {
493 error:
494 ERROR("Incorrect Redis command format");
496 }
497 while (isdigit(*p) && (p < end)) p++;
498 if (*p++ != '\r') goto error;
499 if (*p++ != '\n') goto error;
500 if (*p++ != '$') goto error;
501 while (isdigit(*p) && (p < end)) p++;
502 if (*p++ != '\r') goto error;
503 if (*p++ != '\n') goto error;
504
506
507 MEM(cmd = talloc_zero(cmds, fr_redis_command_t));
508 cmd->cmds = cmds;
509 cmd->type = type;
510 cmd->str = cmd_str;
511 cmd->str_len = cmd_len;
512 cmd->complete = complete;
513 cmd->rctx = rctx;
515 fr_dlist_insert_tail(&cmds->pending, cmd);
516
518}
519
520/** Enqueue a command set on a specific trunk
521 *
522 * The command set may be passed around several trunks before it is complete.
523 * This is to allow it to follow MOVED and ASK responses.
524 *
525 * @param[in] rtrunk to enqueue command set on.
526 * @param[in] cmds Command set to enqueue.
527 * @return
528 * - FR_REDIS_PIPELINE_OK if commands were immediately enqueued or placed in the backlog.
529 * - FR_REDIS_PIPELINE_DST_UNAVAILABLE if the REDIS host is unreachable.
530 * - FR_REDIS_PIPELINE_FAIL any other general error.
531 */
533{
534 if (cmds->txn_start != cmds->txn_end) {
535 ERROR("Refusing to enqueue - Unbalanced transaction start/stop commands");
537 }
538
539 switch (trunk_request_enqueue(&cmds->treq, rtrunk->trunk, cmds->request, cmds, cmds->rctx)) {
540 case TRUNK_ENQUEUE_OK:
542 if (cmds->blocking) trunk_request_mark_blocking(cmds->treq);
544
547
548 default:
550 }
551}
552
553/** Cancel a command set
554 *
555 * @param[in] cmds Command set to cancel.
556 */
562
563/** Convert a MOVED / ASK reply into an address and port
564 *
565 */
566static int redis_addr_from_redirect(TALLOC_CTX *ctx, char **addr, uint16_t *port, redisReply *redirect)
567{
568 unsigned long key;
569 fr_sbuff_t sbuff;
570 fr_ipaddr_t ipaddr;
572
573 if (!redirect || (redirect->type != REDIS_REPLY_ERROR)) return -1;
574
575 fr_sbuff_init_in(&sbuff, redirect->str, redirect->len);
576
579 fr_strerror_const("No '-MOVED' or '-ASK' log_prefix");
580 return -1;
581 }
582
583 if (fr_sbuff_out(NULL, &key, &sbuff) < 0) {
584 fr_strerror_const("Failed to parse key slot from MOVED / ASK reply");
585 return -1;
586 };
587 if (key >= KEY_SLOTS) {
588 fr_strerror_printf("Key %lu outside of redis slot range", key);
589 return -1;
590 }
591
592 if (!fr_sbuff_next_if_char(&sbuff, ' ')) {
593 fr_strerror_const("Missing key/host separator");
594 return -1;
595 }
596
597 if (fr_inet_pton_port(&ipaddr, port, fr_sbuff_current(&sbuff), fr_sbuff_remaining(&sbuff),
598 AF_UNSPEC, true, true) < 0) {
599 return -1;
600 }
601 fr_assert(ipaddr.af);
602
603 *addr = talloc_strdup(ctx, fr_inet_ntop(buff, sizeof(buff), &ipaddr));
604
605 return 0;
606}
607
608/** Callback for for receiving Redis replies
609 *
610 * This is called by hiredis for each response is receives. privData is set to the
611 * fr_command_set
612 *
613 * @note Called only from hiredis, not the trunk itself.
614 *
615 * @param[in] ac The async context the command was enqueued on.
616 * @param[in] vreply redisReply containing the result of the command.
617 * @param[in] privdata fr_redis_command_t that was sent to the Redis server.
618 * The fr_redis_command_t contains a pointer to the
619 * fr_redis_command_set_t which holds the treq which
620 * we use to signal that we have responses for all
621 * commands.
622 */
623static void _redis_pipeline_demux(struct redisAsyncContext *ac, void *vreply, void *privdata)
624{
627 connection_t *conn = talloc_get_type_abort(ac->ev.data, connection_t);
628 fr_redis_handle_t *h = talloc_get_type_abort(conn->h, fr_redis_handle_t);
629 redisReply *reply = vreply;
630
631 /*
632 * If we're already disconnecting, then ignore the response.
633 * Testing has shown this callback can be called by hiredis after the connection
634 * has errored. At that point privdata is no longer valid so there's nothing
635 * that can be done.
636 */
637 if (h->freeing) return;
638
639 /*
640 * First check if we should ignore the response
641 */
643 DEBUG4("Ignoring response with SQN %"PRIu64, (h->rsp_sqn - 1)); /* Already incremented */
644 return;
645 }
646
647 cmd = talloc_get_type_abort(privdata, fr_redis_command_t);
648 cmds = cmd->cmds;
649
650 /*
651 * The trunk request has already failed, nothing more to do.
652 */
653 if (cmds->rcode == REDIS_ASYNC_RCODE_FAIL) return;
654
655 fr_dlist_remove(&cmds->sent, cmd);
656 fr_dlist_insert_tail(&cmds->completed, cmd);
657
658 if (!reply) {
660 error:
661 /*
662 * Mark remaining sent commands to be ignored and fail the treq
663 */
664 fr_dlist_foreach(&cmds->sent, fr_redis_command_t, sent_cmd) {
665 fr_redis_connection_ignore_response(h, sent_cmd->sqn);
666 }
667
668 /*
669 * Only REDIS_ASYNC_RCODE_ERROR is really a failure.
670 */
671 if (cmds->rcode == REDIS_ASYNC_RCODE_ERROR) {
673 } else {
675 }
676 cmds->treq = NULL;
677 return;
678 }
679
680 /*
681 * If the reply was an error, look for known types.
682 */
683 if (reply->type == REDIS_REPLY_ERROR) {
684 request_t *request = cmds->request;
685
686 fr_assert_msg(reply->str, "Error response contained no error string");
687
688 if (strncmp(REDIS_ERROR_MOVED_STR, reply->str, sizeof(REDIS_ERROR_MOVED_STR) - 1) == 0) {
689 ROPTIONAL(RWARN, WARN, "Server returned %s", reply->str);
691 goto redirect;
692 } else if (strncmp(REDIS_ERROR_ASK_STR, reply->str, sizeof(REDIS_ERROR_ASK_STR) - 1) == 0) {
693 ROPTIONAL(RWARN, WARN, "Server returned %s", reply->str);
695 redirect:
696 if (redis_addr_from_redirect(cmds, &cmds->next_node_ip, &cmds->next_node_port, reply) < 0) {
698 }
699 cmds->redirected++;
700 } else if (strncmp(REDIS_ERROR_TRY_AGAIN_STR, reply->str, sizeof(REDIS_ERROR_TRY_AGAIN_STR) - 1) == 0) {
701 ROPTIONAL(RWARN, WARN, "Server returned %s", reply->str);
703 } else if (strncmp(REDIS_ERROR_NO_SCRIPT_STR, reply->str, sizeof(REDIS_ERROR_NO_SCRIPT_STR) - 1) == 0) {
704 ROPTIONAL(RWARN, WARN, "Server returned %s", reply->str);
706 } else {
707 fr_strerror_printf("Server error: %s", reply->str);
709 }
710 goto error;
711 }
712
713 if (cmd->complete) cmd->complete(cmds->request, cmd, reply, cmd->rctx);
715
716 /*
717 * Check is the command set is complete,
718 * and if it is, tell the trunk the treq
719 * is complete.
720 */
721 if ((fr_dlist_num_elements(&cmds->pending) == 0) &&
722 (fr_dlist_num_elements(&cmds->sent) == 0)) {
724 cmds->treq = NULL;
725 }
726}
727
728CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function */
730 connection_conf_t const *conf,
731 char const *log_prefix, void *uctx)
732{
733 fr_redis_trunk_t *rtrunk = talloc_get_type_abort(uctx, fr_redis_trunk_t);
734
735 return fr_redis_connection_alloc(tconn, el, conf, rtrunk->io_conf,
736#ifdef HAVE_REDIS_SSL
737 fr_redis_ct_ssl_ctx(rtrunk->rtcluster),
738#endif
739 log_prefix);
740}
741
742/** Enqueue one or more command sets onto a redis handle
743 *
744 * Because the trunk is in always writable mode, _redis_pipeline_mux
745 * will be called any time trunk_request_enqueue is called, so there'll only
746 * ever be one command to dequeue.
747 *
748 * @param[in] el Event list for trunk events. Unused.
749 * @param[in] tconn Trunk connection holding the commands to enqueue.
750 * @param[in] conn Connection handle containing the fr_redis_handle_t.
751 * @param[in] uctx fr_redis_cluster_t. Unused.
752 */
753CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function */
755 connection_t *conn, UNUSED void *uctx)
756{
757 trunk_request_t *treq;
760 fr_redis_handle_t *h = talloc_get_type_abort(conn->h, fr_redis_handle_t);
761 request_t *request;
762 int ret;
763
764 while (trunk_connection_pop_request(&treq, tconn) == 0) {
765 cmds = talloc_get_type_abort(treq->preq, fr_redis_command_set_t);
766 request = treq->request;
767 while ((cmd = fr_dlist_head(&cmds->pending))) {
768 /*
769 * If this fails it probably means the connection
770 * is disconnecting, but if that's happening then
771 * we shouldn't be enqueueing new requests?
772 */
773 switch (cmd->fmt) {
775 if (DEBUG_ENABLED3) {
776 size_t i;
777 ROPTIONAL(RDEBUG3, DEBUG3, "Sending Redis argv command");
778 for (i = 0; i < cmd->argc; i++) {
779 ROPTIONAL(RDEBUG3, DEBUG3, " %pV",
780 fr_box_strvalue_len(cmd->argv[i], cmd->argv_len[i]));
781 }
782
783 }
784 ret = redisAsyncCommandArgv(h->ac, _redis_pipeline_demux, cmd, cmd->argc,
785 cmd->argv, cmd->argv_len);
786 break;
787
789 ROPTIONAL(RDEBUG3, DEBUG3, "Sending Redis command %s", cmd->str);
790 ret = redisAsyncCommand(h->ac, _redis_pipeline_demux, cmd, cmd->str);
791 break;
792
794 ROPTIONAL(RDEBUG3, DEBUG3, "Sending Redis formatted command %s", cmd->str);
795 ret = redisAsyncFormattedCommand(h->ac, _redis_pipeline_demux, cmd,
796 cmd->str, cmd->str_len);
797 break;
798 }
799
800 if (unlikely(ret != REDIS_OK)) {
801 ROPTIONAL(REDEBUG, ERROR, "Unexpected error queueing REDIS command");
802
803 while ((cmd = fr_dlist_head(&cmds->sent))) {
805 fr_dlist_remove(&cmds->sent, cmd);
806 fr_dlist_insert_tail(&cmds->pending, cmd);
807 }
809 return;
810 }
812 fr_dlist_remove(&cmds->pending, cmd);
813 fr_dlist_insert_tail(&cmds->sent, cmd);
814 }
816 }
817}
818
819/** Deal with cancellation of sent requests
820 *
821 * We can't actually signal redis to not process the request, so depending
822 * on why the commands were cancelled, we either tell the handle to ignore
823 * them, or move them back into the pending list.
824 */
826 trunk_cancel_reason_t reason, UNUSED void *uctx)
827{
828 fr_redis_command_set_t *cmds = talloc_get_type_abort(preq, fr_redis_command_set_t);
829 fr_redis_handle_t *h = conn->h;
830
831 /*
832 * How we cancel is very different depending
833 * on _WHY_ we're cancelling.
834 */
835 switch (reason) {
836 /*
837 * Cancel is only called for requests that
838 * have been sent, and only when the connection
839 * is about to be closed for some reason.
840 *
841 * We don't need to tell the handle to ignore
842 * the responses, we just need to get the
843 * command set back into the correct state for
844 * execution by another handle.
845 */
848 fr_dlist_move(&cmds->pending, &cmds->sent);
849 return;
850
851 /*
852 * If the request was cancelled due to a signal
853 * we'll have a response coming back for a
854 * request, pctx and rctx that no longer exist.
855 * Tell the handle to signal that the response
856 * should be ignored when it's received.
857 *
858 * Free will take care of cleaning up the
859 * pending commands.
860 */
862 {
864
865 /*
866 * Only connected connections will get replies that
867 * need to be ignored.
868 */
869 if (conn->state != CONNECTION_STATE_CONNECTED) return;
870
871 for (cmd = fr_dlist_head(&cmds->sent);
872 cmd;
873 cmd = fr_dlist_next(&cmds->sent, cmd)) {
875 }
876 }
877 return;
878
880 fr_assert(0);
881 return;
882 }
883}
884
885/** Signal the API client that we got a complete set of responses to a command set
886 *
887 */
889 UNUSED void *rctx, UNUSED void *uctx)
890{
891 fr_redis_command_set_t *cmds = talloc_get_type_abort(preq, fr_redis_command_set_t);
892
893 if (cmds->complete) cmds->complete(cmds->request, &cmds->completed, cmds->rctx);
895}
896
897/** Signal the API client that we failed enqueuing the commands
898 *
899 */
900static void _redis_pipeline_command_set_fail(UNUSED request_t *request, void *preq, UNUSED void *rctx,
902{
903 fr_redis_command_set_t *cmds = talloc_get_type_abort(preq, fr_redis_command_set_t);
904
906 if (cmds->fail) cmds->fail(cmds->request, &cmds->completed, cmds->rctx);
908}
909
910/** Free the command set
911 *
912 */
913static void _redis_pipeline_command_set_free(UNUSED request_t *request, void *preq,
914 UNUSED void *uctx)
915{
916 fr_redis_command_set_t *cmds = talloc_get_type_abort(preq, fr_redis_command_set_t);
917
918 if (cmds->autofree) talloc_free(cmds);
919}
920
921CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private trunk_t trips --fsanitize=function */
923{
924 fr_redis_trunk_t *rtcluster = talloc_get_type_abort(uctx, fr_redis_trunk_t);
925
926 rtcluster->active(rtcluster, rtcluster->active_uctx);
927}
928
929/** Allocate a new trunk
930 *
931 * @param[in] rtcluster to allocate the trunk for.
932 * @param[in] io_conf Describing the connection to a single REDIS host.
933 * @param[in] trigger_args Pairs to pass to trigger requests, if triggers are enabled.
934 * @param[in] active Callback to run when the trunk becomes active.
935 * @param[in] active_uctx Uctx to pass to active callback.
936 * @param[in] active_oneshot Should the call back be run just once.
937 * @return
938 * - On success, a new fr_redis_trunk_t which can be used for pipelining commands.
939 * - NULL on failure.
940 */
942 fr_pair_list_t *trigger_args, fr_redis_trunk_active_t active,
943 void *active_uctx, bool active_oneshot)
944{
945 fr_redis_trunk_t *rtrunk;
948 .request_mux = _redis_pipeline_mux,
949 /* demux called directly by hiredis */
950 .request_cancel = _redis_pipeline_command_set_cancel,
951 .request_complete = _redis_pipeline_command_set_complete,
952 .request_fail = _redis_pipeline_command_set_fail,
954 };
955
956 MEM(rtrunk = talloc(rtcluster, fr_redis_trunk_t));
957 *rtrunk = (fr_redis_trunk_t) {
958 .io_conf = io_conf,
959 .rtcluster = rtcluster,
960 .active = active,
961 .active_uctx = active_uctx,
962 };
963 rtrunk->trunk = trunk_alloc(rtrunk, fr_redis_ct_el(rtcluster), &io_funcs, fr_redis_ct_trunk_conf(rtcluster),
964 io_conf->log_prefix, rtrunk, false, trigger_args);
965 if (!rtrunk->trunk) {
966 talloc_free(rtrunk);
967 return NULL;
968 }
969
970 if (active) trunk_add_watch(rtrunk->trunk, TRUNK_STATE_ACTIVE, _redis_trunk_active, active_oneshot, rtrunk);
971
972 return rtrunk;
973}
974
976{
977 switch(cmd->type) {
980 return cmd->str;
981
983 return cmd->argv[0];
984 }
985 return NULL;
986}
987
988/** Extract the rcode from a command set
989 */
994
995/** Extract the next node address and port from a command set
996 */
998{
999 ioconf->hostname = cmds->next_node_ip;
1000 ioconf->port = cmds->next_node_port;
1001}
1002
1003/** Reset a command set to it's state before enqueuing
1004 *
1005 * For use when handling MOVED / ASK where the command set needs to be sent
1006 * to another node.
1007 */
1009{
1010 fr_redis_command_t *cmd;
1011
1012 /*
1013 * Move sent and completed commands back to the pending list
1014 * Popping from the tail of sent, then completed and inserting
1015 * into the head of pending ensures pending is back in the
1016 * original sequence.
1017 */
1018 while ((cmd = fr_dlist_pop_tail(&cmds->sent))) {
1019 fr_dlist_insert_head(&cmds->pending, cmd);
1020 }
1021 while ((cmd = fr_dlist_pop_tail(&cmds->completed))) {
1022 fr_dlist_insert_head(&cmds->pending, cmd);
1023 }
1024
1025 TALLOC_FREE(cmds->next_node_ip);
1026 cmds->next_node_port = 0;
1027 cmds->treq = NULL;
1028
1029 return 0;
1030}
1031
1033{
1034 if (fr_dlist_num_elements(&cmds->pending) > 0) return -1;
1035 if (fr_dlist_num_elements(&cmds->sent) > 0) return -1;
1036 fr_dlist_clear(&cmds->completed);
1037 TALLOC_FREE(cmds->next_node_ip);
1038 cmds->next_node_port = 0;
1039 cmds->treq = NULL;
1040 return 0;
1041}
#define _Thread_local
Definition atexit.h:213
#define fr_atexit_thread_local(_name, _free, _uctx)
Definition atexit.h:224
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:391
#define CC_NO_UBSAN(_sanitize)
Definition build.h:503
#define unlikely(_x)
Definition build.h:455
#define UNUSED
Definition build.h:384
trunk_conf_t const * fr_redis_ct_trunk_conf(fr_redis_ct_t *rtcluster)
fr_event_list_t * fr_redis_ct_el(fr_redis_ct_t *rtcluster)
fr_redis_trunk_active_t active
Callback to run when the trunk becomes active.
Thread local state for a cluster.
Redis asynchronous cluster management.
#define KEY_SLOTS
Maximum number of keyslots (should not change).
TALLOC_CTX * autofree
Definition common.c:29
@ CONNECTION_STATE_CONNECTED
File descriptor is open (ready for writing).
Definition connection.h:54
#define fr_assert_msg(_x, _msg,...)
Calls panic_action ifndef NDEBUG, else logs error and causes the server to exit immediately with code...
Definition debug.h:243
#define MEM(x)
Definition debug.h:38
#define ERROR(fmt,...)
Definition dhcpclient.c:40
#define fr_dlist_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition dlist.h:242
static void * fr_dlist_head(fr_dlist_head_t const *list_head)
Return the HEAD item of a list or NULL if the list is empty.
Definition dlist.h:468
#define fr_dlist_foreach(_list_head, _type, _iter)
Iterate over the contents of a list.
Definition dlist.h:98
static void * fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
Definition dlist.h:620
static bool fr_dlist_entry_in_list(fr_dlist_t const *entry)
Check if a list entry is part of a list.
Definition dlist.h:145
static void fr_dlist_entry_unlink(fr_dlist_t *entry)
Remove an item from the dlist when we don't have access to the head.
Definition dlist.h:128
static unsigned int fr_dlist_num_elements(fr_dlist_head_t const *head)
Return the number of elements in the dlist.
Definition dlist.h:921
static void * fr_dlist_pop_tail(fr_dlist_head_t *list_head)
Remove the tail item in a list.
Definition dlist.h:670
static void * fr_dlist_pop_head(fr_dlist_head_t *list_head)
Remove the head item in a list.
Definition dlist.h:654
static int fr_dlist_insert_tail(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the tail of a list.
Definition dlist.h:360
static int fr_dlist_move(fr_dlist_head_t *list_dst, fr_dlist_head_t *list_src)
Merge two lists, inserting the source at the tail of the destination.
Definition dlist.h:745
#define fr_dlist_talloc_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition dlist.h:257
static int fr_dlist_insert_head(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the head of a list.
Definition dlist.h:320
static void fr_dlist_entry_init(fr_dlist_t *entry)
Initialise a linked list without metadata.
Definition dlist.h:120
static void * fr_dlist_next(fr_dlist_head_t const *list_head, void const *ptr)
Get the next item in a list.
Definition dlist.h:537
static void fr_dlist_clear(fr_dlist_head_t *list_head)
Efficiently remove all elements in a dlist.
Definition dlist.h:277
Head of a doubly linked list.
Definition dlist.h:51
Entry in a doubly linked list.
Definition dlist.h:41
talloc_free(hp)
int fr_inet_pton_port(fr_ipaddr_t *out, uint16_t *port_out, char const *value, ssize_t inlen, int af, bool resolve, bool mask)
Parses IPv4/6 address + port, to fr_ipaddr_t and integer (port)
Definition inet.c:944
char * fr_inet_ntop(char out[static FR_IPADDR_STRLEN], size_t outlen, fr_ipaddr_t const *addr)
Print the address portion of a fr_ipaddr_t.
Definition inet.c:1025
#define FR_IPADDR_STRLEN
Like INET6_ADDRSTRLEN but includes space for the textual Zone ID.
Definition inet.h:89
int af
Address family.
Definition inet.h:64
IPv4/6 prefix.
void unlang_interpret_mark_runnable(request_t *request)
Mark a request as resumable.
Definition interpret.c:2008
connection_t * fr_redis_connection_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, connection_conf_t const *conn_conf, fr_redis_io_conf_t const *io_conf, char const *log_prefix)
Allocate an async redis I/O connection.
Definition io.c:610
fr_redis_sqn_t rsp_sqn
Current redis response number.
Definition io.h:95
redisAsyncContext * ac
Async handle for hiredis.
Definition io.h:84
static void fr_redis_connection_ignore_response(fr_redis_handle_t *h, fr_redis_sqn_t sqn)
Ignore a response with a specific sequence number.
Definition io.h:116
char const * hostname
Definition io.h:51
char const * log_prefix
Definition io.h:57
static bool fr_redis_connection_process_response(fr_redis_handle_t *h)
Update the response sequence number and check if we should ignore the response.
Definition io.h:134
uint16_t port
Definition io.h:52
bool freeing
Ensure that redisAsyncFree doesn't cause a callback loop.
Definition io.h:78
static fr_redis_sqn_t fr_redis_connection_sent_request(fr_redis_handle_t *h)
Tell the handle we sent a command, and get the SQN that command was assigned.
Definition io.h:106
Store I/O state.
Definition io.h:75
#define DEBUG3(_fmt,...)
Definition log.h:271
#define ROPTIONAL(_l_request, _l_global, _fmt,...)
Use different logging functions depending on whether request is NULL or not.
Definition log.h:545
#define RDEBUG3(fmt,...)
Definition log.h:360
#define RWARN(fmt,...)
Definition log.h:314
#define DEBUG4(_fmt,...)
Definition log.h:272
#define DEBUG_ENABLED3
True if global debug level 1-3 messages are enabled.
Definition log.h:264
Stores all information relating to an event list.
Definition event.c:377
unsigned short uint16_t
unsigned char uint8_t
int strncasecmp(char *s1, char *s2, int n)
Definition missing.c:35
static const trunk_io_funcs_t io_funcs
Definition bio.c:2719
Function prototypes and datatypes for the REST (HTTP) transport.
fr_redis_async_rcode_t rcode
Code from last error returned.
Definition pipeline.c:97
char const * fr_redis_command_get_cmd(fr_redis_command_t *cmd)
Definition pipeline.c:975
request_t * request
Request this commands set is associated with (if any).
Definition pipeline.c:122
static int _command_set_free_list_free_on_exit(void *arg)
Free any free requests when the thread is joined.
Definition pipeline.c:174
fr_redis_command_complete_t complete
Callback to process result from this command.
Definition pipeline.c:85
fr_dlist_head_t sent
Commands sent.
Definition pipeline.c:107
fr_redis_async_rcode_t fr_redis_command_set_rcode(fr_redis_command_set_t *cmds)
Extract the rcode from a command set.
Definition pipeline.c:990
uint16_t txn_start
Number of times a transaction block was started in this command set.
Definition pipeline.c:149
fr_redis_pipeline_status_t fr_redis_command_preformatted_add(fr_redis_command_set_t *cmds, char const *cmd_str, size_t cmd_len, fr_redis_command_complete_t complete, void *rctx)
Add an preformatted command to the command set as formatted by redisCommandFormat or it's variants.
Definition pipeline.c:479
fr_redis_io_conf_t const * io_conf
Redis I/O configuration.
Definition pipeline.c:161
static void _redis_pipeline_command_set_cancel(connection_t *conn, void *preq, trunk_cancel_reason_t reason, UNUSED void *uctx)
Deal with cancellation of sent requests.
Definition pipeline.c:825
trunk_t * trunk
Trunk containing all the connections to a specific host.
Definition pipeline.c:163
fr_dlist_t entry
Entry in the command buffer.
Definition pipeline.c:64
static connection_t * _redis_pipeline_connection_alloc(trunk_connection_t *tconn, fr_event_list_t *el, connection_conf_t const *conf, char const *log_prefix, void *uctx)
Definition pipeline.c:729
void * active_uctx
Uctx to pass to active callback.
Definition pipeline.c:168
char * next_node_ip
IP address of node from MOVED / ASK reply.
Definition pipeline.c:100
#define COMMAND_PRE_ALLOC_COUNT
void * rctx
Resume context to write results to.
Definition pipeline.c:123
static void _redis_pipeline_command_set_fail(UNUSED request_t *request, void *preq, UNUSED void *rctx, UNUSED trunk_request_state_t state, UNUSED void *uctx)
Signal the API client that we failed enqueuing the commands.
Definition pipeline.c:900
uint64_t sqn
The sequence number of the command.
Definition pipeline.c:81
fr_redis_command_set_complete_t complete
Notify the creator of the command set that the command set has executed to to completion.
Definition pipeline.c:129
fr_redis_command_fmt_t fmt
Redis command format.
Definition pipeline.c:67
fr_redis_command_set_fail_t fail
Notify the creator of the command set that the command set failed to execute to completion.
Definition pipeline.c:134
fr_redis_trunk_active_t active
Callback to run when the trunk becomes active.
Definition pipeline.c:167
uint16_t txn_end
The number of times a transaction block ended in this command set.
Definition pipeline.c:151
fr_redis_pipeline_status_t redis_command_set_enqueue(fr_redis_trunk_t *rtrunk, fr_redis_command_set_t *cmds)
Enqueue a command set on a specific trunk.
Definition pipeline.c:532
fr_redis_ct_t * rtcluster
Cluster this trunk belongs to.
Definition pipeline.c:165
static void _redis_trunk_active(UNUSED trunk_t *trunk, UNUSED trunk_state_t prev, UNUSED trunk_state_t state, void *uctx)
Definition pipeline.c:922
int fr_redis_command_set_reset(fr_redis_command_set_t *cmds)
Reset a command set to it's state before enqueuing.
Definition pipeline.c:1008
static void _redis_pipeline_demux(struct redisAsyncContext *ac, void *vreply, void *privdata)
Callback for for receiving Redis replies.
Definition pipeline.c:623
bool autofree
Should the command set be freed when it is complete.
Definition pipeline.c:98
#define COMMAND_PRE_ALLOC_LEN
fr_redis_command_set_t * fr_redis_command_set_alloc(TALLOC_CTX *ctx, request_t *request, fr_redis_command_set_complete_t complete, fr_redis_command_set_fail_t fail, void *rctx, bool autofree)
Allocate a new command set.
Definition pipeline.c:249
static int redis_addr_from_redirect(TALLOC_CTX *ctx, char **addr, uint16_t *port, redisReply *redirect)
Convert a MOVED / ASK reply into an address and port.
Definition pipeline.c:566
void fr_redis_command_set_next_node(fr_redis_command_set_t *cmds, fr_redis_io_conf_t *ioconf)
Extract the next node address and port from a command set.
Definition pipeline.c:997
static fr_redis_pipeline_status_t redis_command_transaction_check(request_t *request, fr_redis_command_type_t *type, fr_redis_command_set_t *cmds, char const *cmd)
Definition pipeline.c:301
static void _redis_pipeline_command_set_free(UNUSED request_t *request, void *preq, UNUSED void *uctx)
Free the command set.
Definition pipeline.c:913
static _Thread_local fr_dlist_head_t * command_set_free_list
The thread local free list.
Definition pipeline.c:40
uint16_t next_node_port
Port of node from MOVED / ASK reply.
Definition pipeline.c:101
void fr_redis_command_set_cancel(fr_redis_command_set_t *cmds)
Cancel a command set.
Definition pipeline.c:557
fr_redis_pipeline_status_t fr_redis_command_argv_add(fr_redis_command_set_t *cmds, size_t argc, char const **argv, size_t *argv_len, fr_redis_command_complete_t complete, void *rctx)
Add a command with arguments to the command set.
Definition pipeline.c:436
void * rctx
To be passed to the callback.
Definition pipeline.c:87
fr_redis_command_type_t type
Redis command type.
Definition pipeline.c:66
static int _redis_command_set_free(fr_redis_command_set_t *cmds)
Free a command set.
Definition pipeline.c:189
static void _redis_pipeline_command_set_complete(UNUSED request_t *request, void *preq, UNUSED void *rctx, UNUSED void *uctx)
Signal the API client that we got a complete set of responses to a command set.
Definition pipeline.c:888
bool txn_watch
Transaction was started with a watch statement.
Definition pipeline.c:148
fr_dlist_head_t completed
Commands complete with replies.
Definition pipeline.c:108
fr_redis_command_type_t
Definition pipeline.c:42
@ FR_REDIS_COMMAND_TRANSACTION_START
Start of a transaction block.
Definition pipeline.c:44
@ FR_REDIS_COMMAND_NORMAL
A normal, non-transactional command.
Definition pipeline.c:43
@ FR_REDIS_COMMAND_TRANSACTION_END
End of a transaction block.
Definition pipeline.c:47
uint8_t redirected
How many times this command set was redirected.
Definition pipeline.c:111
fr_redis_pipeline_status_t fr_redis_command_literal_add(fr_redis_command_set_t *cmds, char const *cmd_str, fr_redis_command_complete_t complete, void *rctx)
Add a literal command to the command set.
Definition pipeline.c:401
bool blocking
This command set contains one or more commands which block the client (e.g.
Definition pipeline.c:156
fr_redis_trunk_t * fr_redis_trunk_alloc(fr_redis_ct_t *rtcluster, fr_redis_io_conf_t const *io_conf, fr_pair_list_t *trigger_args, fr_redis_trunk_active_t active, void *active_uctx, bool active_oneshot)
Allocate a new trunk.
Definition pipeline.c:941
trunk_request_t * treq
Trunk request this command set is associated with.
Definition pipeline.c:121
int fr_redis_command_set_clear(fr_redis_command_set_t *cmds)
Definition pipeline.c:1032
static void _redis_pipeline_mux(UNUSED fr_event_list_t *el, trunk_connection_t *tconn, connection_t *conn, UNUSED void *uctx)
Enqueue one or more command sets onto a redis handle.
Definition pipeline.c:754
fr_dlist_head_t pending
Commands yet to be sent.
Definition pipeline.c:106
fr_redis_command_set_t * cmds
Command set this entry belongs to.
Definition pipeline.c:63
fr_redis_command_fmt_t
Definition pipeline.c:53
@ FR_REDIS_COMMAND_FMT_ARGV
A command as an argv array.
Definition pipeline.c:55
@ FR_REDIS_COMMAND_FMT_PREFORMATTED
A command preformatted with redisCommandFormat.
Definition pipeline.c:56
@ FR_REDIS_COMMAND_FMT_EXPANDED
A command as a single string.
Definition pipeline.c:54
Represents a single command.
Definition pipeline.c:62
Represents a collection of pipelined commands.
Definition pipeline.c:94
Redis asynchronous command pipelining.
void(* fr_redis_command_set_complete_t)(request_t *request, fr_dlist_head_t *completed, void *rctx)
Do something meaningful with the replies to the commands previously issued.
Definition pipeline.h:65
void(* fr_redis_command_complete_t)(request_t *request, fr_redis_command_t *cmd, redisReply *reply, void *rctx)
Process the reply from a single command.
Definition pipeline.h:60
void(* fr_redis_command_set_fail_t)(request_t *request, fr_dlist_head_t *completed, void *rctx)
Write a failure result to the rctx so that the module is aware that the request failed.
Definition pipeline.h:70
struct fr_redis_trunk_s fr_redis_trunk_t
Definition pipeline.h:53
fr_redis_pipeline_status_t
Definition pipeline.h:43
@ FR_REDIS_PIPELINE_OK
No failure.
Definition pipeline.h:44
@ FR_REDIS_PIPELINE_BAD_CMDS
Malformed command set.
Definition pipeline.h:45
@ FR_REDIS_PIPELINE_DST_UNAVAILABLE
Cluster or host is down.
Definition pipeline.h:46
@ FR_REDIS_PIPELINE_FAIL
Generic failure.
Definition pipeline.h:48
void(* fr_redis_trunk_active_t)(fr_redis_trunk_t *rtrunk, void *uctx)
Definition pipeline.h:55
#define fr_assert(_expr)
Definition rad_assert.h:37
#define REDEBUG(fmt,...)
#define WARN(fmt,...)
static rs_t * conf
Definition radsniff.c:52
#define REDIS_ERROR_TRY_AGAIN_STR
Definition base.h:49
fr_redis_async_rcode_t
Definition base.h:80
@ REDIS_ASYNC_RCODE_MOVE
Attempt operation on an alternative node with remap.
Definition base.h:88
@ REDIS_ASYNC_RCODE_ERROR
Unrecoverable error.
Definition base.h:82
@ REDIS_ASYNC_RCODE_ASK
Attempt operation on an alternative node.
Definition base.h:87
@ REDIS_ASYNC_RCODE_FAIL
The command set trunk request has been failed.
Definition base.h:90
@ REDIS_ASYNC_RCODE_TRY_AGAIN
Try the operation again.
Definition base.h:86
@ REDIS_ASYNC_RCODE_NO_SCRIPT
Script doesn't exist.
Definition base.h:89
@ REDIS_ASYNC_RCODE_SUCCESS
Operation was successful.
Definition base.h:81
#define REDIS_ERROR_MOVED_STR
Definition base.h:47
#define REDIS_ERROR_ASK_STR
Definition base.h:48
#define REDIS_ERROR_NO_SCRIPT_STR
Definition base.h:50
bool fr_sbuff_next_if_char(fr_sbuff_t *sbuff, char c)
Return true if the current char matches, and if it does, advance.
Definition sbuff.c:2178
#define fr_sbuff_adv_past_str_literal(_sbuff, _needle)
#define fr_sbuff_current(_sbuff_or_marker)
#define fr_sbuff_out(_err, _out, _in)
#define fr_sbuff_init_in(_out, _start, _len_or_end)
#define fr_sbuff_remaining(_sbuff_or_marker)
static char buff[sizeof("18446744073709551615")+3]
Definition size_tests.c:37
fr_aka_sim_id_type_t type
int talloc_link_ctx(TALLOC_CTX *parent, TALLOC_CTX *child)
Link two different parent and child contexts, so the child is freed before the parent.
Definition talloc.c:168
#define talloc_zero_pooled_object(_ctx, _type, _num_subobjects, _total_subobjects_size)
Definition talloc.h:208
#define talloc_strdup(_ctx, _str)
Definition talloc.h:149
void * state
Definition testlib.c:46
void trunk_request_signal_fail(trunk_request_t *treq)
Signal that a trunk request failed.
Definition trunk.c:2196
trunk_watch_entry_t * trunk_add_watch(trunk_t *trunk, trunk_state_t state, trunk_watch_t watch, bool oneshot, void const *uctx)
Add a watch entry to the trunk state list.
Definition trunk.c:915
trunk_enqueue_t trunk_request_enqueue(trunk_request_t **treq_out, trunk_t *trunk, request_t *request, void *preq, void *rctx)
Enqueue a request that needs data written to the trunk.
Definition trunk.c:2657
int trunk_connection_pop_request(trunk_request_t **treq_out, trunk_connection_t *tconn)
Pop a request off a connection's pending queue.
Definition trunk.c:3979
void trunk_request_signal_cancel(trunk_request_t *treq)
Cancel a trunk request.
Definition trunk.c:2216
trunk_t * trunk_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, trunk_io_funcs_t const *funcs, trunk_conf_t const *conf, char const *log_prefix, void const *uctx, bool delay_start, fr_pair_list_t *trigger_args)
Allocate a new collection of connections.
Definition trunk.c:5124
void trunk_request_mark_blocking(trunk_request_t *treq)
Mark a trunk request as one which will block the connection until it is completed.
Definition trunk.c:2859
void trunk_request_signal_sent(trunk_request_t *treq)
Signal that the request was written to a connection successfully.
Definition trunk.c:2114
void trunk_request_signal_complete(trunk_request_t *treq)
Signal that a trunk request is complete.
Definition trunk.c:2158
Associates request queues with a connection.
Definition trunk.c:137
Wraps a normal request.
Definition trunk.c:99
Main trunk management handle.
Definition trunk.c:219
trunk_connection_alloc_t connection_alloc
Allocate a new connection_t.
Definition trunk.h:747
trunk_cancel_reason_t
Reasons for a request being cancelled.
Definition trunk.h:55
@ TRUNK_CANCEL_REASON_NONE
Request has not been cancelled.
Definition trunk.h:56
@ TRUNK_CANCEL_REASON_SIGNAL
Request cancelled due to a signal.
Definition trunk.h:57
@ TRUNK_CANCEL_REASON_REQUEUE
A previously sent request is being requeued.
Definition trunk.h:59
@ TRUNK_CANCEL_REASON_MOVE
Request cancelled because it's being moved.
Definition trunk.h:58
trunk_state_t
Definition trunk.h:62
@ TRUNK_STATE_ACTIVE
Trunk has at least one active connection which can service requests.
Definition trunk.h:64
@ TRUNK_ENQUEUE_DST_UNAVAILABLE
Destination is down.
Definition trunk.h:163
@ TRUNK_ENQUEUE_OK
Operation was successful.
Definition trunk.h:160
@ TRUNK_ENQUEUE_IN_BACKLOG
Request should be enqueued in backlog.
Definition trunk.h:159
trunk_request_state_t
Used for sanity checks and to simplify freeing.
Definition trunk.h:171
@ TRUNK_REQUEST_STATE_BACKLOG
In the backlog.
Definition trunk.h:177
@ TRUNK_REQUEST_STATE_PENDING
In the queue of a connection and is pending writing.
Definition trunk.h:178
@ TRUNK_REQUEST_STATE_SENT
Was written to a socket. Waiting for a response.
Definition trunk.h:182
I/O functions to pass to trunk_alloc.
Definition trunk.h:746
static fr_event_list_t * el
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_const(_msg)
Definition strerror.h:223
#define fr_box_strvalue_len(_val, _len)
Definition value.h:309