The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
trunk_tests.c
Go to the documentation of this file.
1#include <freeradius-devel/util/acutest.h>
2#include <freeradius-devel/util/acutest_helpers.h>
3#include <freeradius-devel/util/syserror.h>
4#include <sys/socket.h>
5
6#define TRUNK_TESTS 1
7#include "trunk.c"
8
9//#include <gperftools/profiler.h>
10typedef struct {
11 trunk_request_t *treq; //!< Trunk request.
12 bool cancelled; //!< Seen by the cancelled callback.
13 bool completed; //!< Seen by the complete callback.
14 bool failed; //!< Seen by the failed callback.
15 bool freed; //!< Seen by the free callback.
16 bool signal_partial; //!< Muxer should signal that this request is partially written.
17 bool signal_cancel_partial; //!< Muxer should signal that this request is partially cancelled.
18 int priority; //!< Priority of request
20
21typedef struct {
22 uint64_t cancelled; //!< Count of tests in this run that were cancelled.
23 uint64_t completed; //!< Count of tests in this run that completed.
24 uint64_t failed; //!< Count of tests in this run that failed.
25 uint64_t freed; //!< Count of tests in this run that were freed.
27
28#define DEBUG_LVL_SET if (acutest_verbose_level_ >= 3) fr_debug_lvl = L_DBG_LVL_4 + 1
29
30static void test_mux(UNUSED fr_event_list_t *el, trunk_connection_t *tconn, connection_t *conn, UNUSED void *uctx)
31{
32 trunk_request_t *treq;
33 size_t count = 0;
34 int fd = *(talloc_get_type_abort(conn->h, int));
35 ssize_t slen;
36
37 while (trunk_connection_pop_request(&treq, tconn) == 0) {
38 test_proto_request_t *preq = treq->pub.preq;
39 count++;
40
41 /*
42 * Simulate a partial write
43 */
44 if (preq && preq->signal_partial) {
46 preq->signal_partial = false;
47 break;
48 }
49
50 if (acutest_verbose_level_ >= 3) printf("%s - Wrote %p\n", __FUNCTION__, preq);
51
52 slen = write(fd, &preq, sizeof(preq));
53 if (slen < 0) return;
54 if (slen == 0) return;
55 if (slen < (ssize_t)sizeof(preq)) abort();
56
58 }
59 TEST_CHECK(count > 0);
60}
61
63{
64 trunk_request_t *treq;
65 size_t count = 0;
66 int fd = *(talloc_get_type_abort(conn->h, int));
67 ssize_t slen;
68
69 /*
70 * For cancellation we just do
71 */
72 while ((trunk_connection_pop_cancellation(&treq, tconn) == 0)) {
73 test_proto_request_t *preq = treq->pub.preq;
74 count++;
75
76 /*
77 * Simulate a partial cancel write
78 */
79 if (preq && preq->signal_cancel_partial) {
81 preq->signal_cancel_partial = false;
82 break;
83 }
84
85 if (acutest_verbose_level_ >= 3) printf("%s - Wrote %p\n", __FUNCTION__, preq);
86 slen = write(fd, &preq, sizeof(preq));
87 if (slen < 0) {
88 fr_perror("%s - %s", __FUNCTION__, fr_syserror(errno));
89 return;
90 }
91 if (slen == 0) return;
92 if (slen < (ssize_t)sizeof(preq)) abort();
93
95 }
96 TEST_CHECK(count > 0);
97}
98
100{
101 int fd = *(talloc_get_type_abort(conn->h, int));
103 ssize_t slen;
104
105 for (;;) {
106 slen = read(fd, &preq, sizeof(preq));
107 if (slen <= 0) break;
108
109 if (acutest_verbose_level_ >= 3) printf("%s - Read %p (%zu)\n", __FUNCTION__, preq, (size_t)slen);
110
111 /*
112 * Coverity considers data read from a file to be tainted,
113 * and considers its use to be a defect--but almost all the
114 * rest of the loop validates the pointer to the extent
115 * possible--all of the pointer should be read, its talloc
116 * "dynamic type" had better be right, and it should either
117 * be freed or have a statethe demuxer can handle or ignore.
118 * This isn't like a range check on a numeric value;
119 * Coverity doesn't recognize it as validation.
120 */
121 TEST_CHECK(slen == sizeof(preq));
122 talloc_get_type_abort(preq, test_proto_request_t);
123
124 if (preq->freed) continue;
125
126 /*
127 * Demuxer can handle both normal requests and cancelled ones
128 */
129 switch (preq->treq->pub.state) {
131 break; /* Hack - just ignore it */
132
134 /* coverity[tainted_data] */
136 break;
137
139 /* coverity[tainted_data] */
141 break;
142
143 default:
144 fr_assert(0);
145 break;
146 }
147 }
148}
149
150static void _conn_io_error(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags,
151 UNUSED int fd_errno, void *uctx)
152{
153
154 trunk_connection_t *tconn = talloc_get_type_abort(uctx, trunk_connection_t);
155
157}
158
159static void _conn_io_read(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, void *uctx)
160{
161 trunk_connection_t *tconn = talloc_get_type_abort(uctx, trunk_connection_t);
163}
164
165static void _conn_io_write(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, void *uctx)
166{
167 trunk_connection_t *tconn = talloc_get_type_abort(uctx, trunk_connection_t);
169}
170
173 trunk_connection_event_t notify_on, UNUSED void *uctx)
174{
175 int fd = *(talloc_get_type_abort(conn->h, int));
176
177 switch (notify_on) {
180 break;
181
183 TEST_CHECK(fr_event_fd_insert(conn, NULL, el, fd, _conn_io_read, NULL, _conn_io_error, tconn) == 0);
184 break;
185
187 TEST_CHECK(fr_event_fd_insert(conn, NULL, el, fd, NULL, _conn_io_write, _conn_io_error, tconn) == 0);
188 break;
189
192 break;
193
194 default:
195 fr_assert(0);
196 }
197}
198
199static void test_request_cancel(UNUSED connection_t *conn, void *preq,
200 UNUSED trunk_cancel_reason_t reason, void *uctx)
201{
202 test_proto_stats_t *stats = uctx;
203 test_proto_request_t *our_preq;
204
205 if (!preq) return;
206
207 our_preq = talloc_get_type_abort(preq, test_proto_request_t);
208 our_preq->cancelled = true;
209 if (stats) stats->cancelled++;
210}
211
212static void test_request_complete(UNUSED request_t *request, void *preq, UNUSED void *rctx, void *uctx)
213{
214 test_proto_stats_t *stats = uctx;
215 test_proto_request_t *our_preq;
216
217 if (!preq) return;
218
219 our_preq = talloc_get_type_abort(preq, test_proto_request_t);
220 our_preq->completed = true;
221 if (stats) stats->completed++;
222}
223
224static void test_request_fail(UNUSED request_t *request, void *preq, UNUSED void *rctx, UNUSED trunk_request_state_t state, void *uctx)
225{
226 test_proto_stats_t *stats = uctx;
227 test_proto_request_t *our_preq;
228
229 if (!preq) return;
230
231 our_preq = talloc_get_type_abort(preq, test_proto_request_t);
232 our_preq->failed = true;
233 if (stats) stats->failed++;
234}
235
236static void test_request_free(UNUSED request_t *request, void *preq, void *uctx)
237{
238 test_proto_stats_t *stats = uctx;
239 test_proto_request_t *our_preq;
240
241 if (!preq) return;
242
243 our_preq = talloc_get_type_abort(preq, test_proto_request_t);
244 our_preq->freed = true;
245 if (stats) stats->freed++;
246}
247
248/** Whenever the second socket in a socket pair is readable, read all pending data, and write it back
249 *
250 */
251static void _conn_io_loopback(UNUSED fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
252{
253 int *our_h = talloc_get_type_abort(uctx, int);
254 static uint8_t buff[1024];
255 static size_t to_write;
256 ssize_t slen;
257
258 fr_assert(fd == our_h[1]);
259
260 while (true) {
261 slen = read(fd, buff, sizeof(buff));
262 if (slen <= 0) return;
263
264 to_write = (size_t)slen;
265
266 if (acutest_verbose_level_ >= 3) printf("%s - Read %zu bytes of data\n", __FUNCTION__, slen);
267 slen = write(our_h[1], buff, (size_t)to_write);
268 if (slen < 0) return;
269
270 if (slen < (ssize_t)to_write) {
271 to_write -= slen;
272 if (acutest_verbose_level_ >= 3) {
273 printf("%s - Partial write %zu bytes left\n", __FUNCTION__, to_write);
274 }
275 return;
276 } else {
277 if (acutest_verbose_level_ >= 3) printf("%s - Wrote %zu bytes of data\n", __FUNCTION__, slen);
278 }
279 }
280}
281
282static void _conn_close(UNUSED fr_event_list_t *el, void *h, UNUSED void *uctx)
283{
284 int *our_h = talloc_get_type_abort(h, int);
285
286 talloc_free_children(our_h); /* Clear the IO handlers */
287
288 close(our_h[0]);
289 close(our_h[1]);
290
291 talloc_free(our_h);
292}
293
294/** Insert I/O handlers that loop any data back round
295 *
296 */
298{
299 int *our_h = talloc_get_type_abort(h, int);
300
301 /*
302 * This always needs to be inserted
303 */
304 TEST_CHECK(fr_event_fd_insert(our_h, NULL, el, our_h[1], _conn_io_loopback, NULL, NULL, our_h) == 0);
305
307}
308
309/** Allocate a basic socket pair
310 *
311 */
312CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
313static connection_state_t _conn_init(void **h_out, connection_t *conn, UNUSED void *uctx)
314{
315 int *h;
316
317 h = talloc_array(conn, int, 2);
318 socketpair(AF_UNIX, SOCK_STREAM, 0, h);
319
320 fr_nonblock(h[0]);
321 fr_nonblock(h[1]);
322 connection_signal_on_fd(conn, h[0]);
323 *h_out = h;
324
326}
327
330 connection_conf_t const *conn_conf,
331 char const *log_prefix, UNUSED void *uctx)
332{
333 connection_conf_t cstat;
334
335 if (!conn_conf) {
336 memset(&cstat, 0, sizeof(cstat));
337 conn_conf = &cstat;
338 }
339 return connection_alloc(tconn, el,
341 .init = _conn_init,
342 .open = _conn_open,
343 .close = _conn_close
344 },
345 conn_conf,
346 log_prefix, tconn);
347}
348
349static int8_t test_preq_cmp(void const *a, void const *b)
350{
351 test_proto_request_t const *preq_a = a;
352 test_proto_request_t const *preq_b = b;
353 return CMP(preq_a->priority, preq_b->priority);
354}
355
356static trunk_t *test_setup_trunk(TALLOC_CTX *ctx, fr_event_list_t *el, trunk_conf_t *conf, bool with_cancel_mux, void *uctx)
357{
360 .connection_notify = _conn_notify,
361 .request_prioritise = test_preq_cmp,
362 .request_mux = test_mux,
363 .request_demux = test_demux,
364 .request_cancel = test_request_cancel,
365 .request_complete = test_request_complete,
366 .request_fail = test_request_fail,
367 .request_free = test_request_free
368 };
369
370 /*
371 * Function list is copied, so this is OK.
372 */
373 if (with_cancel_mux) io_funcs.request_cancel_mux = test_cancel_mux;
374
375 return trunk_alloc(ctx, el, &io_funcs, conf, "test_socket_pair", uctx, false);
376}
377
379{
380 TALLOC_CTX *ctx = talloc_init_const("test");
381 trunk_t *trunk;
383 int events;
384
386 .start = 2,
387 .min = 2
388 };
391 .request_prioritise = fr_pointer_cmp,
392 };
393
395
396 el = fr_event_list_alloc(ctx, NULL, NULL);
397
399
400 trunk = trunk_alloc(ctx, el, &io_funcs, &conf, "test_socket_pair", NULL, false);
401 TEST_CHECK(trunk != NULL);
402 if (!trunk) return;
403
406 TEST_CHECK(events == 2); /* Two I/O write events, no timers */
409
411 TEST_CHECK(events == 0); /* I/O events should have been cleared */
412
413 talloc_free(trunk);
414 talloc_free(ctx);
415}
416
418{
419 TALLOC_CTX *ctx = talloc_init_const("test");
420 trunk_t *trunk;
422 int events;
424 .start = 2,
425 .min = 2,
426 .conn_conf = &(connection_conf_t){
427 .reconnection_delay = fr_time_delta_from_nsec(NSEC / 2)
428 }
429 };
432 .request_prioritise = fr_pointer_cmp,
433 };
435
436 el = fr_event_list_alloc(ctx, NULL, NULL);
437
438 if (!el) return;
439
441
442 trunk = trunk_alloc(ctx, el, &io_funcs, &conf, "test_socket_pair", NULL, false);
443 TEST_CHECK(trunk != NULL);
444 if (!trunk) return;
445
447 TEST_CHECK(events == 2); /* Two I/O write events, no timers */
451
453 TEST_CHECK(events == 0); /* I/O events should have been cleared */
454 TEST_MSG("Got %u events", events);
455
457
460 TEST_CHECK(events == 1); /* Two timer events but event loops only adds one to the total*/
461 TEST_MSG("Got %u events", events);
463
465
467 TEST_CHECK(events == 2); /* Two I/O write events, no timers */
469
472 TEST_CHECK(events == 0); /* I/O events should have been cleared */
473
474 talloc_free(trunk);
475 talloc_free(ctx);
476}
477
478CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function*/
479static connection_state_t _conn_init_no_signal(void **h_out, connection_t *conn, UNUSED void *uctx)
480{
481 int *h;
482
483 h = talloc_array(conn, int, 2);
484 socketpair(AF_UNIX, SOCK_STREAM, 0, h);
485 *h_out = h;
486
488}
489
493 char const *log_prefix, void *uctx)
494{
495 return connection_alloc(tconn, el,
497 .init = _conn_init_no_signal,
498 .open = _conn_open,
499 .close = _conn_close
500 },
502 .connection_timeout = fr_time_delta_from_sec(1),
503 .reconnection_delay = fr_time_delta_from_sec(1)
504 },
505 log_prefix, uctx);
506}
507
509{
510 TALLOC_CTX *ctx = talloc_init_const("test");
511 trunk_t *trunk;
513 int events;
514 trunk_connection_t *tconn;
516 .start = 1,
517 .min = 1
518 };
521 .request_prioritise = fr_pointer_cmp,
522 };
523
525
526 el = fr_event_list_alloc(ctx, NULL, NULL);
527
529
530
531 trunk = trunk_alloc(ctx, el, &io_funcs, &conf, "test_socket_pair", NULL, false);
532 TEST_CHECK(trunk != NULL);
533 if (!trunk) return;
534
535 /*
536 * Trigger connection timeout
537 */
539 TEST_CHECK(fr_event_list_num_timers(el) == 1); /* One timer event for the connection timeout */
541 TEST_CHECK(events == 1); /* We didn't install the I/O events */
542
543 tconn = fr_dlist_head(&trunk->connecting);
544 TEST_CHECK(tconn != NULL);
545 if (tconn == NULL) return;
546
549
550 /*
551 * Timeout should now fire
552 */
554
555 /*
556 * Connection delay not implemented for timed out connections
557 */
560
562 TEST_CHECK(events == 0); /* I/O events should have been cleared */
563
564 talloc_free(trunk);
565 talloc_free(ctx);
566}
567
570 UNUSED connection_conf_t const *conn_conf,
571 char const *log_prefix, void *uctx)
572{
573 return connection_alloc(tconn, el,
575 .init = _conn_init,
576 .open = _conn_open,
577 .close = _conn_close
578 },
580 .connection_timeout = fr_time_delta_from_sec(1),
581 .reconnection_delay = fr_time_delta_from_sec(1)
582 },
583 log_prefix, uctx);
584}
585
587{
588 TALLOC_CTX *ctx = talloc_init_const("test");
589 trunk_t *trunk;
591 int events;
592 trunk_connection_t *tconn;
594 .start = 1,
595 .min = 1,
596 .conn_conf = &(connection_conf_t){
597 .reconnection_delay = fr_time_delta_from_sec(1),
598 .connection_timeout = fr_time_delta_from_sec(1)
599 }
600 };
603 .request_prioritise = fr_pointer_cmp,
604 };
605
607
608 el = fr_event_list_alloc(ctx, NULL, NULL);
610
611 trunk = trunk_alloc(ctx, el, &io_funcs, &conf, "test_socket_pair", NULL, false);
612 TEST_CHECK(trunk != NULL);
613 if (!trunk) return;
614
615 /*
616 * Trigger connection timeout
617 */
619 TEST_CHECK(fr_event_list_num_timers(el) == 1); /* One timer event for the connection timeout */
621 TEST_CHECK(events == 2); /* We didn't install the I/O events */
623
624 tconn = fr_minmax_heap_min_peek(trunk->active);
625 TEST_CHECK(tconn != NULL);
626 if (tconn == NULL) return;
627
630
631 /*
632 * Trigger reconnection
633 */
636
638 TEST_CHECK(events == 0); /* Reconnect delay not ready to fire yet, no I/O handlers installed */
639 TEST_CHECK(fr_event_list_num_timers(el) == 1); /* One timer event for reconnect delay */
640
643 TEST_CHECK(events == 1); /* Reconnect delay should now be ready to fire */
644
645 fr_event_service(el); /* Services the timer, which then triggers init */
646
649
651 TEST_CHECK(events == 1); /* Should have a pending I/O event and a timer */
652
653 talloc_free(trunk);
654 talloc_free(ctx);
655}
656
657/*
658 * Test basic enqueue and dequeue
659 */
660static void test_enqueue_basic(void)
661{
662 TALLOC_CTX *ctx = talloc_init_const("test");
663 trunk_t *trunk;
666 .start = 1,
667 .min = 1,
668 .manage_interval = fr_time_delta_from_nsec(NSEC * 0.5)
669 };
671 trunk_request_t *treq = NULL;
672 trunk_enqueue_t rcode;
673
675
676 el = fr_event_list_alloc(ctx, NULL, NULL);
678
679 trunk = test_setup_trunk(ctx, el, &conf, true, NULL);
680
681 /*
682 * Our preq is a pointer to the trunk
683 * request so we don't have to manage
684 * a tree of requests and responses.
685 */
686 preq = talloc_zero(NULL, test_proto_request_t);
687
688 /*
689 * The trunk is active, but there's no
690 * connections.
691 *
692 * We're under the current request limit
693 * so the request should enter the
694 * backlog.
695 */
696 rcode = trunk_request_enqueue(&treq, trunk, NULL, preq, NULL);
697 preq->treq = treq;
699
701
702 /*
703 * Allow the connection to establish
704 */
707
710
711 /*
712 * Should now be active and have a write event
713 * inserted into the event loop.
714 */
716
717 /*
718 * Trunk should be signalled the connection is
719 * writable.
720 *
721 * We should then:
722 * - Pop a request from the pending queue.
723 * - Write the request to the socket pair
724 */
727
729
730 /*
731 * Gives the loopback function a chance
732 * to read the data, and write it back.
733 */
736
737 /*
738 * Trunk should be signalled the connection is
739 * readable.
740 *
741 * We should then:
742 * - Read the (looped back) response.
743 * - Signal the trunk that the connection is readable.
744 */
747
748 TEST_CHECK(preq->completed == true);
749 TEST_CHECK(preq->failed == false);
750 TEST_CHECK(preq->cancelled == false);
751 TEST_CHECK(preq->freed == true);
752 talloc_free(preq);
753
754 talloc_free(trunk);
755 talloc_free(ctx);
756}
757
758/*
759 * Test request cancellations when the connection is in various states
760 */
762{
763 TALLOC_CTX *ctx = talloc_init_const("test");
764 trunk_t *trunk;
767 .start = 1,
768 .min = 1,
769 .manage_interval = fr_time_delta_from_nsec(NSEC * 0.5)
770 };
772 trunk_request_t *treq = NULL;
773
775
776 el = fr_event_list_alloc(ctx, NULL, NULL);
778
779 trunk = test_setup_trunk(ctx, el, &conf, false, NULL);
780 preq = talloc_zero(NULL, test_proto_request_t);
781 trunk_request_enqueue(&treq, trunk, NULL, preq, NULL);
782
783 TEST_CASE("cancellation via trunk free - TRUNK_REQUEST_STATE_BACKLOG");
784 talloc_free(trunk);
785 TEST_CHECK(preq->completed == false);
786 TEST_CHECK(preq->failed == true);
787 TEST_CHECK(preq->cancelled == false);
788 TEST_CHECK(preq->freed == true);
789 talloc_free(preq);
790
791 TEST_CASE("cancellation via signal - TRUNK_REQUEST_STATE_BACKLOG");
792 trunk = test_setup_trunk(ctx, el, &conf, false, NULL);
793 preq = talloc_zero(NULL, test_proto_request_t);
794 treq = NULL;
795 trunk_request_enqueue(&treq, trunk, NULL, preq, NULL);
796 preq->treq = treq;
799
800 TEST_CHECK(preq->completed == false);
801 TEST_CHECK(preq->failed == false); /* Request/rctx not guaranteed after signal, so can't call fail */
802 TEST_CHECK(preq->cancelled == false);
803 TEST_CHECK(preq->freed == true);
804 talloc_free(preq);
805 talloc_free(trunk);
806
807 TEST_CASE("cancellation via trunk free - TRUNK_REQUEST_STATE_PARTIAL");
808 trunk = test_setup_trunk(ctx, el, &conf, false, NULL);
809 preq = talloc_zero(NULL, test_proto_request_t);
810 preq->signal_partial = true;
811 treq = NULL;
812 trunk_request_enqueue(&treq, trunk, NULL, preq, NULL);
813 preq->treq = treq;
814
815 fr_event_corral(el, test_time_base, false); /* Connect the connection */
817
818 fr_event_corral(el, test_time_base, false); /* Send the request */
820
822
823 talloc_free(trunk);
824
825 TEST_CHECK(preq->completed == false);
826 TEST_CHECK(preq->failed == true);
827 TEST_CHECK(preq->cancelled == true);
828 TEST_CHECK(preq->freed == true);
829 talloc_free(preq);
830
831 TEST_CASE("cancellation via signal - TRUNK_REQUEST_STATE_PARTIAL");
832 trunk = test_setup_trunk(ctx, el, &conf, false, NULL);
833 preq = talloc_zero(NULL, test_proto_request_t);
834 preq->signal_partial = true;
835 treq = NULL;
836 trunk_request_enqueue(&treq, trunk, NULL, preq, NULL);
837 preq->treq = treq;
838
839 fr_event_corral(el, test_time_base, false); /* Connect the connection */
841
842 fr_event_corral(el, test_time_base, false); /* Send the request */
844
848
849 TEST_CHECK(preq->completed == false);
850 TEST_CHECK(preq->failed == false); /* Request/rctx not guaranteed after signal, so can't call fail */
851 TEST_CHECK(preq->cancelled == true);
852 TEST_CHECK(preq->freed == true);
853 talloc_free(preq);
854 talloc_free(trunk);
855
856 TEST_CASE("cancellation via trunk free - TRUNK_REQUEST_STATE_SENT");
857 trunk = test_setup_trunk(ctx, el, &conf, false, NULL);
858 preq = talloc_zero(NULL, test_proto_request_t);
859 treq = NULL;
860 trunk_request_enqueue(&treq, trunk, NULL, preq, NULL);
861 preq->treq = treq;
862
863 fr_event_corral(el, test_time_base, false); /* Connect the connection */
865
866 fr_event_corral(el, test_time_base, false); /* Send the request */
868
870 talloc_free(trunk);
871
872 TEST_CHECK(preq->completed == false);
873 TEST_CHECK(preq->failed == true);
874 TEST_CHECK(preq->cancelled == true);
875 TEST_CHECK(preq->freed == true);
876 talloc_free(preq);
877
878 TEST_CASE("cancellation via signal - TRUNK_REQUEST_STATE_SENT");
879 trunk = test_setup_trunk(ctx, el, &conf, false, NULL);
880 preq = talloc_zero(NULL, test_proto_request_t);
881 treq = NULL;
882 trunk_request_enqueue(&treq, trunk, NULL, preq, NULL);
883 preq->treq = treq;
884
885 fr_event_corral(el, test_time_base, false); /* Connect the connection */
887
888 fr_event_corral(el, test_time_base, false); /* Send the request */
890
894
895 TEST_CHECK(preq->completed == false);
896 TEST_CHECK(preq->failed == false); /* Request/rctx not guaranteed after signal, so can't call fail */
897 TEST_CHECK(preq->cancelled == true);
898 TEST_CHECK(preq->freed == true);
899 talloc_free(preq);
900 talloc_free(trunk);
901
902 TEST_CASE("cancellation via trunk free - TRUNK_REQUEST_STATE_CANCEL_PARTIAL");
903 trunk = test_setup_trunk(ctx, el, &conf, true, NULL);
904 preq = talloc_zero(NULL, test_proto_request_t);
905 preq->signal_cancel_partial = true;
906 treq = NULL;
907 trunk_request_enqueue(&treq, trunk, NULL, preq, NULL);
908 preq->treq = treq;
909
910 fr_event_corral(el, test_time_base, false); /* Connect the connection */
912
913 fr_event_corral(el, test_time_base, false); /* Send the request */
915
919
920 fr_event_corral(el, test_time_base, false); /* Send the cancellation request */
922
924
925 talloc_free(trunk);
926
927 TEST_CHECK(preq->completed == false);
928 TEST_CHECK(preq->failed == false);
929 TEST_CHECK(preq->cancelled == true);
930 TEST_CHECK(preq->freed == true);
931 talloc_free(preq);
932
933 TEST_CASE("cancellation via trunk free - TRUNK_REQUEST_STATE_CANCEL_SENT");
934 trunk = test_setup_trunk(ctx, el, &conf, true, NULL);
935 preq = talloc_zero(NULL, test_proto_request_t);
936 treq = NULL;
937 trunk_request_enqueue(&treq, trunk, NULL, preq, NULL);
938 preq->treq = treq;
939
940 fr_event_corral(el, test_time_base, false); /* Connect the connection */
942
943 fr_event_corral(el, test_time_base, false); /* Send the request */
945
949
950 fr_event_corral(el, test_time_base, false); /* Send the cancellation request */
952
954
955 talloc_free(trunk);
956
957 TEST_CHECK(preq->completed == false);
958 TEST_CHECK(preq->failed == false);
959 TEST_CHECK(preq->cancelled == true);
960 TEST_CHECK(preq->freed == true);
961 talloc_free(preq);
962
963 TEST_CASE("trunk free after TRUNK_REQUEST_STATE_CANCEL_COMPLETE");
964 trunk = test_setup_trunk(ctx, el, &conf, true, NULL);
965 preq = talloc_zero(NULL, test_proto_request_t);
966 treq = NULL;
967 trunk_request_enqueue(&treq, trunk, NULL, preq, NULL);
968 preq->treq = treq;
969
970 fr_event_corral(el, test_time_base, false); /* Connect the connection */
972
973 fr_event_corral(el, test_time_base, false); /* Send the request */
975
979
980 fr_event_corral(el, test_time_base, false); /* Send the cancellation request */
982
984
985 fr_event_corral(el, test_time_base, false); /* Loop the cancel request back round */
987
988 fr_event_corral(el, test_time_base, false); /* Read the cancel ACK (such that it is) */
990
992
993 talloc_free(trunk);
994
995 TEST_CHECK(preq->completed == false);
996 TEST_CHECK(preq->failed == false);
997 TEST_CHECK(preq->cancelled == true);
998 TEST_CHECK(preq->freed == true);
999 talloc_free(preq);
1000
1001
1002 talloc_free(ctx);
1003}
1004
1005/*
1006 * Test PARTIAL -> SENT and CANCEL-PARTIAL -> CANCEL-SENT
1007 */
1009{
1010 TALLOC_CTX *ctx = talloc_init_const("test");
1011 trunk_t *trunk;
1013 trunk_conf_t conf = {
1014 .start = 1,
1015 .min = 1,
1016 .manage_interval = fr_time_delta_from_nsec(NSEC * 0.5)
1017 };
1019 trunk_request_t *treq = NULL;
1020
1022
1023 el = fr_event_list_alloc(ctx, NULL, NULL);
1025
1026 trunk = test_setup_trunk(ctx, el, &conf, true, NULL);
1027 preq = talloc_zero(NULL, test_proto_request_t);
1028 preq->signal_partial = true;
1029 preq->signal_cancel_partial = true;
1030
1031 TEST_CASE("TRUNK_REQUEST_STATE_PARTIAL -> TRUNK_REQUEST_STATE_SENT");
1032
1033 trunk_request_enqueue(&treq, trunk, NULL, preq, NULL);
1034 preq->treq = treq;
1035
1036 fr_event_corral(el, test_time_base, false); /* Connect the connection */
1038
1039 fr_event_corral(el, test_time_base, false); /* Send the request */
1041
1043
1044 fr_event_corral(el, test_time_base, false); /* Complete the partial request */
1046
1048
1051
1052 TEST_CASE("TRUNK_REQUEST_STATE_CANCEL_PARTIAL -> TRUNK_REQUEST_STATE_CANCEL_SENT");
1053
1054 fr_event_corral(el, test_time_base, false); /* Send partial cancel request */
1056
1058
1059 fr_event_corral(el, test_time_base, false); /* Complete the partial cancellation */
1061
1063
1064 fr_event_corral(el, test_time_base, false); /* Loop the cancellation request back */
1066
1068
1069 talloc_free(trunk);
1070
1071 TEST_CHECK(preq->completed == false);
1072 TEST_CHECK(preq->failed == false);
1073 TEST_CHECK(preq->cancelled == true);
1074 TEST_CHECK(preq->freed == true);
1075 talloc_free(preq);
1076
1077 talloc_free(ctx);
1078}
1079
1080/*
1081 * Test calling reconnect with requests in each different state
1082 */
1084{
1085 TALLOC_CTX *ctx = talloc_init_const("test");
1086 trunk_t *trunk;
1088 trunk_conf_t conf = {
1089 .start = 2,
1090 .min = 2,
1091 .manage_interval = fr_time_delta_from_nsec(NSEC * 0.5),
1092 .conn_conf = &(connection_conf_t){
1093 .reconnection_delay = fr_time_delta_from_nsec(NSEC / 10)
1094 },
1095 .backlog_on_failed_conn = true
1096 };
1098 trunk_request_t *treq = NULL;
1099 trunk_connection_t *tconn;
1100
1103
1104 el = fr_event_list_alloc(ctx, NULL, NULL);
1106
1107 trunk = test_setup_trunk(ctx, el, &conf, true, NULL);
1108 preq = talloc_zero(ctx, test_proto_request_t);
1109 preq->signal_partial = true;
1110 preq->signal_cancel_partial = true;
1111
1112 fr_event_corral(el, test_time_base, false); /* Connect the connection(s) */
1114
1115 TEST_CASE("dequeue on reconnect - TRUNK_REQUEST_STATE_PENDING");
1116
1118
1119 trunk_request_enqueue(&treq, trunk, NULL, preq, NULL);
1120 preq->treq = treq;
1121
1122 tconn = treq->pub.tconn; /* Store the conn the request was assigned to */
1124
1126
1127 /*
1128 * Should be reassigned to the other connection
1129 */
1130 TEST_CHECK(tconn != treq->pub.tconn);
1132
1133 /*
1134 * Should be reassigned to the backlog
1135 */
1138 TEST_CHECK(!treq->pub.tconn);
1139
1140 TEST_CASE("cancel on reconnect - TRUNK_REQUEST_STATE_PARTIAL");
1141
1142 /*
1143 * Allow the connections to reconnect
1144 */
1147 fr_event_service(el); /* run management function */
1149 fr_event_service(el); /* service any I/O callbacks */
1150
1151 /*
1152 * Request should now be assigned back to one of the reconnected
1153 * connections.
1154 */
1156 TEST_CHECK(treq->pub.tconn != NULL);
1157
1159 fr_event_corral(el, test_time_base, false); /* Send the request (partially) */
1161
1163
1164 /*
1165 * Reconnect the connection.
1166 *
1167 * preq should pass through the cancel function,
1168 * then be re-assigned.
1169 */
1170 tconn = treq->pub.tconn;
1172
1173 TEST_CHECK(preq->completed == false);
1174 TEST_CHECK(preq->failed == false);
1175 TEST_CHECK(preq->cancelled == true);
1176 TEST_CHECK(preq->freed == false);
1177
1178 preq->cancelled = false; /* Reset */
1179
1181 TEST_CHECK(tconn != treq->pub.tconn); /* Ensure it moved */
1182
1183 TEST_CASE("cancel on reconnect - TRUNK_REQUEST_STATE_SENT");
1184
1185 /*
1186 * Sent the request (fully)
1187 */
1188 fr_event_corral(el, test_time_base, false); /* Send the request (partially) */
1190
1191 /*
1192 * The above indeed appears to send the request partially;
1193 * this appears to be required to send it fully, judging by
1194 * the following check, which fails without it.
1195 */
1196 fr_event_corral(el, test_time_base, false); /* Send the request (partially) */
1199
1200 tconn = treq->pub.tconn;
1202
1204
1205 /*
1206 * Allow the connections to reconnect
1207 * and send the request.
1208 */
1212 TEST_CHECK(tconn != treq->pub.tconn); /* Ensure it moved */
1213
1214 TEST_CHECK(preq->completed == false);
1215 TEST_CHECK(preq->failed == false);
1216 TEST_CHECK(preq->cancelled == true);
1217 TEST_CHECK(preq->freed == false);
1218
1219 preq->cancelled = false; /* Reset */
1220
1221 TEST_CASE("free on reconnect - TRUNK_REQUEST_STATE_CANCEL");
1222
1223 /*
1224 * Signal the request should be cancelled
1225 */
1228
1229 /*
1230 * Requests in the cancel state, are
1231 * freed instead of being moved between
1232 * connections.
1233 */
1234 trunk_connection_signal_reconnect(tconn, CONNECTION_FAILED); /* treq->pub.tconn, now invalid due to cancel */
1235
1239
1243
1247
1248 TEST_CHECK(preq->completed == false);
1249 TEST_CHECK(preq->failed == false);
1250 TEST_CHECK(preq->cancelled == true);
1251 TEST_CHECK(preq->freed == true);
1252
1253 /*
1254 * Allow the connection we just reconnected
1255 * to open so it doesn't interfere with
1256 * the next test.
1257 */
1261
1262 TEST_CASE("free on reconnect - TRUNK_REQUEST_STATE_CANCEL_PARTIAL");
1263
1264 /*
1265 * Queue up a new request, and get it to the cancel-partial state.
1266 */
1267 preq = talloc_zero(ctx, test_proto_request_t);
1268 preq->signal_cancel_partial = true;
1269 treq = NULL;
1270 trunk_request_enqueue(&treq, trunk, NULL, preq, NULL);
1271 preq->treq = treq;
1272
1274
1275 /*
1276 * Sent the request (fully)
1277 */
1279 fr_event_corral(el, test_time_base, false); /* Send the request (fully) */
1281
1283 trunk_request_signal_cancel(treq); /* Cancel the request */
1284
1286
1287 /*
1288 * Transition to cancel partial
1289 */
1293
1295
1296 /*
1297 * Trigger a reconnection
1298 */
1300
1304
1305 TEST_CHECK(preq->completed == false);
1306 TEST_CHECK(preq->failed == false);
1307 TEST_CHECK(preq->cancelled == true);
1308 TEST_CHECK(preq->freed == true);
1309
1310 /*
1311 * Allow the connection we just reconnected
1312 * top open so it doesn't interfere with
1313 * the next test.
1314 */
1318
1319 TEST_CASE("free on reconnect - TRUNK_REQUEST_STATE_CANCEL_SENT");
1320
1321 /*
1322 * Queue up a new request, and get it to the cancel-sent state.
1323 */
1324 preq = talloc_zero(NULL, test_proto_request_t);
1325 treq = NULL;
1326 trunk_request_enqueue(&treq, trunk, NULL, preq, NULL);
1327 preq->treq = treq;
1328
1330
1331 /*
1332 * Sent the request (fully)
1333 */
1335 fr_event_corral(el, test_time_base, false); /* Send the request (fully) */
1337
1339 trunk_request_signal_cancel(treq); /* Cancel the request */
1340
1342
1343 /*
1344 * Transition to cancel
1345 */
1349
1351
1352 /*
1353 * Trigger a reconnection
1354 */
1356
1360
1361 TEST_CHECK(preq->completed == false);
1362 TEST_CHECK(preq->failed == false);
1363 TEST_CHECK(preq->cancelled == true);
1364 TEST_CHECK(preq->freed == true);
1365
1366 talloc_free(preq);
1367
1368 talloc_free(ctx);
1369}
1370
1372{
1373 TALLOC_CTX *ctx = talloc_init_const("test");
1374 trunk_t *trunk;
1376 trunk_conf_t conf = {
1377 .start = 0,
1378 .min = 0, /* No connections on start */
1379 .manage_interval = fr_time_delta_from_nsec(NSEC * 0.5)
1380 };
1382 trunk_request_t *treq_a = NULL, *treq_b = NULL, *treq_c = NULL;
1383
1385
1386 el = fr_event_list_alloc(ctx, NULL, NULL);
1388
1389 /* Need to provide a timer starting value above zero */
1391
1392 trunk = test_setup_trunk(ctx, el, &conf, true, NULL);
1393 preq = talloc_zero(NULL, test_proto_request_t);
1394
1395 TEST_CASE("C0 - Enqueue should spawn");
1396 trunk_request_enqueue(&treq_a, trunk, NULL, preq, NULL);
1397
1398 /*
1399 * This causes the event associated with the request left on
1400 * the backlog queue to be handled, which (along with the other
1401 * corral; service sequence, makes the checks all pass.
1402 */
1405
1407
1408 TEST_CASE("C1 connecting, !max_req_per_conn - Enqueue MUST NOT spawn");
1409 trunk_request_enqueue(&treq_b, trunk, NULL, preq, NULL);
1410
1412
1413 /*
1414 * Allow the connections to open
1415 */
1418
1420
1421 TEST_CASE("C1 active, !max_req_per_conn - Enqueue MUST NOT spawn");
1422 trunk_request_enqueue(&treq_c, trunk, NULL, preq, NULL);
1423
1426
1427 talloc_free(ctx);
1428 talloc_free(preq);
1429}
1430
1432{
1433 TALLOC_CTX *ctx = talloc_init_const("test");
1434 trunk_t *trunk;
1436 trunk_conf_t conf = {
1437 .start = 2,
1438 .min = 2, /* No connections on start */
1439 .manage_interval = fr_time_delta_from_nsec(NSEC * 0.5)
1440 };
1442 trunk_connection_t *tconn;
1443 trunk_request_t *treq_a = NULL, *treq_b = NULL, *treq_c = NULL;
1444
1446
1447 el = fr_event_list_alloc(ctx, NULL, NULL);
1449
1450 trunk = test_setup_trunk(ctx, el, &conf, true, NULL);
1451 preq = talloc_zero(NULL, test_proto_request_t);
1452 printf("Rebalance %p\n", preq);
1453
1454 /*
1455 * Allow the connections to open
1456 */
1459
1460 /*
1461 * Mark one of the connections as full, and
1462 * enqueue three requests on the other.
1463 */
1464 tconn = fr_minmax_heap_min_peek(trunk->active);
1465
1466 TEST_CASE("C2 connected, R0 - Signal inactive");
1468
1469
1470 trunk_request_enqueue(&treq_a, trunk, NULL, preq, NULL);
1471 trunk_request_enqueue(&treq_b, trunk, NULL, preq, NULL);
1472 trunk_request_enqueue(&treq_c, trunk, NULL, preq, NULL);
1473
1474 TEST_CASE("C1 connected, C2 inactive, R3 - Enqueued");
1477
1478 /*
1479 * Now mark the previous connection as
1480 * active. It should receive at least
1481 * one of the requests.
1482 */
1483 TEST_CASE("C2 active, R3 - Signal active, should balance");
1485
1488
1489 talloc_free(ctx);
1490 talloc_free(preq);
1491}
1492
1493#define ALLOC_REQ(_id) \
1494do { \
1495 treq_##_id = trunk_request_alloc(trunk, NULL); \
1496 preq_##_id = talloc_zero(ctx, test_proto_request_t); \
1497 preq_##_id->treq = treq_##_id; \
1498 preq_##_id->priority = next_prio++; \
1499} while (0)
1500
1502{
1503 TALLOC_CTX *ctx = talloc_init_const("test");
1504 trunk_t *trunk;
1506 trunk_conf_t conf = {
1507 .start = 0, /* No connections on start */
1508 .min = 0,
1509 .max = 2,
1510 .max_req_per_conn = 2,
1511 .target_req_per_conn = 2, /* One request per connection */
1512 .manage_interval = fr_time_delta_from_nsec(NSEC * 0.5)
1513 };
1514 test_proto_request_t *preq_a, *preq_b, *preq_c, *preq_d, *preq_e;
1515 trunk_request_t *treq_a = NULL, *treq_b = NULL, *treq_c = NULL, *treq_d = NULL, *treq_e = NULL;
1516 int next_prio = 0;
1517
1519
1520 el = fr_event_list_alloc(ctx, NULL, NULL);
1522
1523 /* Need to provide a timer starting value above zero */
1525
1526 trunk = test_setup_trunk(ctx, el, &conf, true, NULL);
1527 TRUNK_VERIFY(trunk);
1528
1529 /*
1530 * Queuing a request should start a connection.
1531 */
1532 TEST_CASE("C0, R1 - Enqueue should spawn");
1533 ALLOC_REQ(a);
1534 TEST_CHECK(trunk_request_enqueue(&treq_a, trunk, NULL, preq_a, NULL) == TRUNK_ENQUEUE_IN_BACKLOG);
1535 TRUNK_VERIFY(trunk);
1536
1537 /*
1538 * Like test_connection_start_on_enqueue(), you have to process the backlog
1539 * to start the chain of events.
1540 */
1544
1546 TRUNK_VERIFY(trunk);
1547
1548 /*
1549 * Queuing another request should *NOT* start another connection
1550 */
1551 TEST_CASE("C1 connecting, R2 - MUST NOT spawn");
1552 ALLOC_REQ(b);
1553 TEST_CHECK(trunk_request_enqueue(&treq_b, trunk, NULL, preq_b, NULL) == TRUNK_ENQUEUE_IN_BACKLOG);
1555 TRUNK_VERIFY(trunk);
1556
1557 TEST_CASE("C1 connecting, R3 - MUST NOT spawn");
1558 ALLOC_REQ(c);
1559 TEST_CHECK(trunk_request_enqueue(&treq_c, trunk, NULL, preq_c, NULL) == TRUNK_ENQUEUE_IN_BACKLOG);
1561 TRUNK_VERIFY(trunk);
1562
1563 TEST_CASE("C1 connecting, R4 - MUST NOT spawn");
1564 ALLOC_REQ(d);
1565 TEST_CHECK(trunk_request_enqueue(&treq_d, trunk, NULL, preq_d, NULL) == TRUNK_ENQUEUE_IN_BACKLOG);
1567 TRUNK_VERIFY(trunk);
1568
1569 TEST_CASE("C1 connecting, R5 - MUST NOT spawn, NO CAPACITY");
1570 ALLOC_REQ(e);
1571 TEST_CHECK(trunk_request_enqueue(&treq_e, trunk, NULL, preq_e, NULL) == TRUNK_ENQUEUE_NO_CAPACITY);
1573 TRUNK_VERIFY(trunk);
1574
1575 /*
1576 * Allowing connection to open
1577 */
1580
1581 TEST_CASE("C1 active, R4 - Check pending 2");
1584 TRUNK_VERIFY(trunk);
1585
1586 /*
1587 * Sending requests
1588 */
1591
1592 TEST_CASE("C1 active, R4 - Check sent 2");
1594 TRUNK_VERIFY(trunk);
1595
1596 /*
1597 * Looping I/O
1598 */
1602
1603 /*
1604 * Receiving responses
1605 */
1609
1610 TEST_CHECK(preq_a->completed == true);
1611 TEST_CHECK(preq_a->failed == false);
1612 TEST_CHECK(preq_a->cancelled == false);
1613 TEST_CHECK(preq_a->freed == true);
1614
1615 TEST_CHECK(preq_b->completed == true);
1616 TEST_CHECK(preq_b->failed == false);
1617 TEST_CHECK(preq_b->cancelled == false);
1618 TEST_CHECK(preq_b->freed == true);
1619
1622 TRUNK_VERIFY(trunk);
1623
1624 TEST_CASE("C1 active, R0 - Check complete 2, pending 0");
1625
1626 /*
1627 * Sending requests
1628 */
1631
1632 /*
1633 * Looping I/O
1634 */
1637
1638 /*
1639 * Receiving responses
1640 */
1643
1644 TEST_CHECK(preq_c->completed == true);
1645 TEST_CHECK(preq_c->failed == false);
1646 TEST_CHECK(preq_c->cancelled == false);
1647 TEST_CHECK(preq_c->freed == true);
1648
1649 TEST_CHECK(preq_d->completed == true);
1650 TEST_CHECK(preq_d->failed == false);
1651 TEST_CHECK(preq_d->cancelled == false);
1652 TEST_CHECK(preq_d->freed == true);
1653
1655 TRUNK_VERIFY(trunk);
1656
1657 talloc_free(trunk);
1658 talloc_free(ctx);
1659}
1660
1662{
1663 TALLOC_CTX *ctx = talloc_init_const("test");
1664 trunk_t *trunk;
1666 trunk_conf_t conf = {
1667 .start = 0, /* No connections on start */
1668 .min = 0,
1669 .max = 0,
1670 .max_req_per_conn = 0,
1671 .target_req_per_conn = 2, /* One request per connection */
1672 .manage_interval = fr_time_delta_from_nsec(NSEC / 10)
1673 };
1674
1675 test_proto_request_t *preq_a, *preq_b, *preq_c;
1676 trunk_request_t *treq_a = NULL, *treq_b = NULL, *treq_c = NULL;
1677 test_proto_stats_t stats;
1678 int next_prio = 0;
1679
1681
1682 el = fr_event_list_alloc(ctx, NULL, NULL);
1684
1685 /* Need to provide a timer starting value above zero */
1687
1688 memset(&stats, 0, sizeof(stats));
1689 trunk = test_setup_trunk(ctx, el, &conf, true, &stats);
1690
1691 /*
1692 * Queuing a request should start a connection.
1693 */
1694 TEST_CASE("C0, R1 - Enqueue should spawn");
1695 ALLOC_REQ(a);
1696 TEST_CHECK(trunk_request_enqueue(&treq_a, trunk, NULL, preq_a, NULL) == TRUNK_ENQUEUE_IN_BACKLOG);
1697
1698 /*
1699 * Processing the event associated with the backlog creates
1700 * the connection in connecting state..
1701 */
1704
1706
1707 TEST_CASE("C1 connecting, R2 - MUST NOT spawn");
1708 ALLOC_REQ(b);
1709 TEST_CHECK(trunk_request_enqueue(&treq_b, trunk, NULL, preq_b, NULL) == TRUNK_ENQUEUE_IN_BACKLOG);
1712
1713 /*
1714 * Open connection
1715 */
1718
1720
1721 TEST_CASE("C1 connected, R3 - should spawn");
1722 ALLOC_REQ(c);
1723 TEST_CHECK(trunk_request_enqueue(&treq_c, trunk, NULL, preq_c, NULL) == TRUNK_ENQUEUE_OK);
1725
1728
1732
1733 /*
1734 * Complete requests
1735 */
1737
1740
1742
1743 TEST_CASE("C1 connected, C2 connecting, R2 - MUST NOT spawn");
1746
1747 /*
1748 * Finish the last request, should close one connection
1749 */
1752
1754
1755 TEST_CASE("C1 connected, R0");
1758
1759 /*
1760 * Requests now done, should close another connection
1761 */
1764
1766
1767 TEST_CASE("C0, R0");
1769
1770 TEST_CHECK(stats.completed == 3);
1771 TEST_CHECK(stats.failed == 0);
1772 TEST_CHECK(stats.cancelled == 0);
1773 TEST_CHECK(stats.freed == 3);
1774
1775 /*
1776 * Queuing a request should start a connection.
1777 */
1778 TEST_CASE("C0, R1 - Enqueue should spawn");
1779 ALLOC_REQ(a);
1780 TEST_CHECK(trunk_request_enqueue(&treq_a, trunk, NULL, preq_a, NULL) == TRUNK_ENQUEUE_IN_BACKLOG);
1781
1782 /*
1783 * ...once the event associated with the backlogged request is handled.
1784 */
1787
1789
1790 TEST_CASE("C1 connecting, R2 - MUST NOT spawn");
1791 ALLOC_REQ(b);
1792 TEST_CHECK(trunk_request_enqueue(&treq_b, trunk, NULL, preq_b, NULL) == TRUNK_ENQUEUE_IN_BACKLOG);
1795
1796 /*
1797 * Open connection
1798 */
1801
1803
1804 TEST_CASE("C1 connected, R3 - should spawn");
1805 ALLOC_REQ(c);
1806 TEST_CHECK(trunk_request_enqueue(&treq_c, trunk, NULL, preq_c, NULL) == TRUNK_ENQUEUE_OK);
1808
1811
1815
1816 talloc_free(trunk);
1817 talloc_free(ctx);
1818}
1819
1820#undef fr_time /* Need to the real time */
1822{
1823 TALLOC_CTX *ctx = talloc_init_const("test");
1824 trunk_t *trunk;
1826 int events;
1827 trunk_conf_t conf = {
1828 .start = 1,
1829 .min = 1,
1830 .max = 0,
1831 .max_req_per_conn = 0,
1832 .target_req_per_conn = 0, /* One request per connection */
1833 .req_pool_headers = 1,
1834 .req_pool_size = sizeof(test_proto_request_t),
1835 .manage_interval = fr_time_delta_from_nsec(NSEC * 0.5)
1836 };
1837 size_t i = 0, requests = 100000;
1838 fr_time_t enqueue_start, enqueue_stop, io_start, io_stop;
1839 fr_time_delta_t enqueue_time, io_time, total_time;
1840 trunk_request_t **treq_array;
1841 test_proto_request_t **preq_array;
1842 test_proto_stats_t stats;
1843
1845
1846 el = fr_event_list_alloc(ctx, NULL, NULL);
1848
1849 /* Need to provide a timer starting value above zero */
1851
1852 memset(&stats, 0, sizeof(stats));
1853 trunk = test_setup_trunk(ctx, el, &conf, true, &stats);
1854
1855 /*
1856 * Open the connections
1857 */
1860
1861 /*
1862 * Build up a cache of requests
1863 * This prevents all mallocs on request enqueue.
1864 *
1865 * When the server's running, this does represent
1866 * close to what we'd have as a steady state.
1867 */
1868 MEM(treq_array = talloc_array(ctx, trunk_request_t *, requests));
1869 for (i = 0; i < requests; i++) treq_array[i] = trunk_request_alloc(trunk, NULL);
1870 for (i = 0; i < requests; i++) trunk_request_free(&treq_array[i]);
1871
1872 MEM(preq_array = talloc_array(ctx, test_proto_request_t *, requests));
1873
1875
1876 TEST_CASE("Enqueue requests");
1877 enqueue_start = fr_time();
1878// ProfilerStart(getenv("FR_PROFILE"));
1879 for (i = 0; i < requests; i++) {
1880 trunk_request_t *treq;
1881 test_proto_request_t *preq = NULL;
1882
1883 treq = trunk_request_alloc(trunk, NULL);
1884 preq = talloc_zero(treq, test_proto_request_t);
1885 preq->treq = treq;
1886 trunk_request_enqueue(&treq, trunk, NULL, preq, NULL);
1887 }
1888 enqueue_stop = fr_time();
1889 enqueue_time = fr_time_sub(enqueue_stop, enqueue_start);
1890 if (acutest_verbose_level_ >= 1) {
1891 INFO("Enqueue time %pV (%u rps) (%"PRIu64"/%"PRIu64")",
1892 fr_box_time_delta(enqueue_time),
1893 (uint32_t)(requests / ((float)(fr_time_delta_unwrap(enqueue_time)) / NSEC)),
1894 trunk->pub.req_alloc_new, trunk->pub.req_alloc_reused);
1895 }
1896
1897 TEST_CASE("Perform I/O operations");
1898 io_start = fr_time();
1899 while (true) {
1901 if (!events) break;
1904 }
1905 io_stop = fr_time();
1906 io_time = fr_time_sub(io_stop, io_start);
1907
1908 if (acutest_verbose_level_ >= 1) {
1909 INFO("I/O time %pV (%u rps)",
1910 fr_box_time_delta(io_time),
1911 (uint32_t)(requests / ((float)(fr_time_delta_unwrap(io_time)) / NSEC)));
1912 }
1913
1914 if (acutest_verbose_level_ >= 1) {
1915 total_time = fr_time_sub(io_stop, enqueue_start);
1916 INFO("Total time %pV (%u rps)",
1917 fr_box_time_delta(total_time),
1918 (uint32_t)(requests / ((float)(fr_time_delta_unwrap(total_time)) / NSEC)));
1919 }
1920
1921 TEST_CHECK_LEN(stats.completed, requests);
1922 TEST_CHECK_LEN(stats.failed, 0);
1923 TEST_CHECK_LEN(stats.cancelled, 0);
1924 TEST_CHECK_LEN(stats.freed, requests);
1925
1926// ProfilerStop();
1927
1928 talloc_free(ctx);
1929}
1930
1931/*
1932 * Connection spawning
1933 */
1935 /*
1936 * Basic tests
1937 */
1938 { "Basic - Alloc then free", test_socket_pair_alloc_then_free },
1939 { "Basic - Alloc then reconnect then free", test_socket_pair_alloc_then_reconnect_then_free },
1940
1941 /*
1942 * Connection timeout
1943 */
1944 { "Timeouts - Connection", test_socket_pair_alloc_then_connect_timeout },
1945 { "Timeouts - Reconnect delay", test_socket_pair_alloc_then_reconnect_check_delay },
1946
1947 /*
1948 * Basic enqueue/dequeue
1949 */
1950 { "Enqueue - Basic", test_enqueue_basic },
1951 { "Enqueue - Cancellation points", test_enqueue_cancellation_points },
1952 { "Enqueue - Partial state transitions", test_partial_to_complete_states },
1953 { "Requeue - On reconnect", test_requeue_on_reconnect },
1954
1955 /*
1956 * Rebalance
1957 */
1958 { "Rebalance - Connection rebalance", test_connection_rebalance_requests },
1959
1960 /*
1961 * Connection spawning tests
1962 */
1963 { "Spawn - Test connection start on enqueue", test_connection_start_on_enqueue },
1964 { "Spawn - Connection levels max", test_connection_levels_max },
1965 { "Spawn - Connection levels alternating edges",test_connection_levels_alternating_edges },
1966
1967 /*
1968 * Performance tests
1969 */
1970 { "Speed Test - Enqueue, and I/O", test_enqueue_and_io_speed },
1971 { NULL }
1972};
#define TEST_CHECK(cond)
Definition acutest.h:85
static int acutest_verbose_level_
Definition acutest.h:418
#define TEST_CASE(name)
Definition acutest.h:184
#define TEST_MSG(...)
Definition acutest.h:215
#define TEST_CHECK_LEN(_got, _exp)
#define CC_NO_UBSAN(_sanitize)
Definition build.h:428
#define CMP(_a, _b)
Same as CMP_PREFER_SMALLER use when you don't really care about ordering, you just want an ordering.
Definition build.h:112
#define UNUSED
Definition build.h:317
connection_state_t
Definition connection.h:45
@ CONNECTION_STATE_CONNECTED
File descriptor is open (ready for writing).
Definition connection.h:52
@ CONNECTION_STATE_CONNECTING
Waiting for connection to establish.
Definition connection.h:50
@ CONNECTION_FAILED
Connection is being reconnected because it failed.
Definition connection.h:84
Holds a complete set of functions for a connection.
Definition connection.h:186
void fr_talloc_fault_setup(void)
Register talloc fault handlers.
Definition debug.c:1223
#define MEM(x)
Definition debug.h:36
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:486
#define fr_event_fd_insert(...)
Definition event.h:248
@ FR_EVENT_FILTER_IO
Combined filter for read/write functions/.
Definition event.h:84
void fr_event_service(fr_event_list_t *el)
Service any outstanding timer or file descriptor events.
Definition event.c:2194
int fr_event_corral(fr_event_list_t *el, fr_time_t now, bool wait)
Gather outstanding timer and file descriptor events.
Definition event.c:2059
uint64_t fr_event_list_num_timers(fr_event_list_t *el)
Return the number of timer events currently scheduled.
Definition event.c:560
talloc_free(reap)
fr_event_list_t * fr_event_list_alloc(TALLOC_CTX *ctx, fr_event_status_cb_t status, void *status_uctx)
Initialise a new event list.
Definition event.c:2523
int fr_event_fd_delete(fr_event_list_t *el, int fd, fr_event_filter_t filter)
Remove a file descriptor from the event loop.
Definition event.c:1203
Stores all information relating to an event list.
Definition event.c:377
unsigned int uint32_t
long int ssize_t
unsigned char uint8_t
unsigned long int size_t
void * fr_minmax_heap_min_peek(fr_minmax_heap_t *hp)
int fr_nonblock(UNUSED int fd)
Definition misc.c:293
int8_t fr_pointer_cmp(void const *a, void const *b)
Compares two pointers.
Definition misc.c:417
static const trunk_io_funcs_t io_funcs
Definition bio.c:2397
#define fr_assert(_expr)
Definition rad_assert.h:38
#define INFO(fmt,...)
Definition radict.c:54
static fr_event_list_t * events
Definition radsniff.c:59
static rs_t * conf
Definition radsniff.c:53
uint64_t connection_get_num_timed_out(connection_t const *conn)
Return the number of times this connection has timed out whilst connecting.
Definition connection.c:613
uint64_t connection_get_num_reconnected(connection_t const *conn)
Return the number of times we've attempted to establish or re-establish this connection.
Definition connection.c:601
void connection_signal_reconnect(connection_t *conn, connection_reason_t reason)
Asynchronously signal the connection should be reconnected.
int connection_signal_on_fd(connection_t *conn, int fd)
Setup the connection to change states to connected or failed based on I/O events.
connection_t * connection_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, connection_funcs_t const *funcs, connection_conf_t const *conf, char const *log_prefix, void const *uctx)
Allocate a new connection.
static char buff[sizeof("18446744073709551615")+3]
Definition size_tests.c:41
static fr_time_t test_time_base
Definition slab_tests.c:42
return count
Definition module.c:155
fr_time_t test_time
Definition state_test.c:3
#define fr_time()
Allow us to arbitrarily manipulate time.
Definition state_test.c:8
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
static TALLOC_CTX * talloc_init_const(char const *name)
Allocate a top level chunk with a constant name.
Definition talloc.h:112
static fr_time_t fr_time_add_time_delta(fr_time_t a, fr_time_delta_t b)
Definition time.h:173
static int64_t fr_time_delta_unwrap(fr_time_delta_t time)
Definition time.h:154
static fr_time_delta_t fr_time_delta_from_sec(int64_t sec)
Definition time.h:590
#define NSEC
Definition time.h:379
static fr_time_delta_t fr_time_delta_from_nsec(int64_t nsec)
Definition time.h:563
#define fr_time_sub(_a, _b)
Subtract one time from another.
Definition time.h:229
A time delta, a difference in time measured in nanoseconds.
Definition time.h:80
"server local" time.
Definition time.h:69
void fr_timer_list_set_time_func(fr_timer_list_t *tl, fr_event_time_source_t func)
Override event list time source.
Definition timer.c:1008
A management API for bonding multiple connections together.
int trunk_connection_pop_cancellation(trunk_request_t **treq_out, trunk_connection_t *tconn)
Pop a cancellation request off a connection's cancellation queue.
Definition trunk.c:3834
void trunk_reconnect(trunk_t *trunk, int states, connection_reason_t reason)
Force the trunk to re-establish its connections.
Definition trunk.c:4724
void trunk_request_signal_partial(trunk_request_t *treq)
Signal a partial write.
Definition trunk.c:2028
void trunk_request_signal_cancel_sent(trunk_request_t *treq)
Signal that a remote server has been notified of the cancellation.
Definition trunk.c:2259
void trunk_connection_signal_readable(trunk_connection_t *tconn)
Signal that a trunk connection is readable.
Definition trunk.c:3920
trunk_request_t * trunk_request_alloc(trunk_t *trunk, request_t *request)
(Pre-)Allocate a new trunk request
Definition trunk.c:2473
uint32_t trunk_request_count_by_connection(trunk_connection_t const *tconn, int req_state)
Return the count number of requests associated with a trunk connection.
Definition trunk.c:2878
struct trunk_request_pub_s pub
Public fields in the trunk request.
Definition trunk.c:100
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)
Allocate a new collection of connections.
Definition trunk.c:4945
struct trunk_pub_s pub
Public fields in the trunk connection.
Definition trunk.c:198
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:2586
void trunk_request_signal_cancel_complete(trunk_request_t *treq)
Signal that a remote server acked our cancellation.
Definition trunk.c:2283
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:3882
fr_dlist_head_t connecting
Connections which are not yet in the open state.
Definition trunk.c:224
void trunk_request_signal_cancel(trunk_request_t *treq)
Cancel a trunk request.
Definition trunk.c:2151
struct trunk_connection_pub_s pub
Public fields in the trunk connection.
Definition trunk.c:134
uint16_t trunk_connection_count_by_state(trunk_t *trunk, int conn_state)
Return the count number of connections in the specified states.
Definition trunk.c:2854
fr_minmax_heap_t * active
Connections which can service requests.
Definition trunk.c:226
void trunk_request_free(trunk_request_t **treq_to_free)
If the trunk request is freed then update the target requests.
Definition trunk.c:2321
void trunk_connection_signal_active(trunk_connection_t *tconn)
Signal a trunk connection is no longer full.
Definition trunk.c:3959
void trunk_connection_signal_inactive(trunk_connection_t *tconn)
Signal a trunk connection cannot accept more requests.
Definition trunk.c:3936
uint64_t trunk_request_count_by_state(trunk_t *trunk, int conn_state, int req_state)
Return a count of requests on a connection in a specific state.
Definition trunk.c:4521
void trunk_request_signal_cancel_partial(trunk_request_t *treq)
Signal a partial cancel write.
Definition trunk.c:2235
void trunk_request_signal_sent(trunk_request_t *treq)
Signal that the request was written to a connection successfully.
Definition trunk.c:2049
void trunk_request_signal_complete(trunk_request_t *treq)
Signal that a trunk request is complete.
Definition trunk.c:2093
void trunk_connection_signal_reconnect(trunk_connection_t *tconn, connection_reason_t reason)
Signal a trunk connection is no longer viable.
Definition trunk.c:3998
void trunk_connection_signal_writable(trunk_connection_t *tconn)
Signal that a trunk connection is writable.
Definition trunk.c:3902
Associates request queues with a connection.
Definition trunk.c:133
Wraps a normal request.
Definition trunk.c:99
Main trunk management handle.
Definition trunk.c:197
#define TRUNK_VERIFY(_trunk)
Definition trunk.h:929
#define TRUNK_REQUEST_STATE_ALL
All request states.
Definition trunk.h:195
@ TRUNK_CONN_CONNECTING
Connection is connecting.
Definition trunk.h:90
@ TRUNK_CONN_ACTIVE
Connection is connected and ready to service requests.
Definition trunk.h:91
uint64_t _CONST req_alloc_reused
How many requests were reused.
Definition trunk.h:328
trunk_request_state_t _CONST state
Which list the request is now located in.
Definition trunk.h:345
trunk_connection_t *_CONST tconn
Connection this request belongs to.
Definition trunk.h:349
trunk_connection_alloc_t connection_alloc
Allocate a new connection_t.
Definition trunk.h:733
trunk_connection_event_t
What type of I/O events the trunk connection is currently interested in receiving.
Definition trunk.h:72
@ TRUNK_CONN_EVENT_BOTH
Trunk should be notified if a connection is readable or writable.
Definition trunk.h:79
@ TRUNK_CONN_EVENT_WRITE
Trunk should be notified if a connection is writable.
Definition trunk.h:77
@ TRUNK_CONN_EVENT_NONE
Don't notify the trunk on connection state changes.
Definition trunk.h:73
@ TRUNK_CONN_EVENT_READ
Trunk should be notified if a connection is readable.
Definition trunk.h:75
#define TRUNK_CONN_ALL
All connection states.
Definition trunk.h:111
trunk_cancel_reason_t
Reasons for a request being cancelled.
Definition trunk.h:55
uint64_t _CONST req_alloc_new
How many requests we've allocated.
Definition trunk.h:326
connection_t *_CONST conn
The underlying connection.
Definition trunk.h:369
trunk_enqueue_t
Definition trunk.h:148
@ TRUNK_ENQUEUE_OK
Operation was successful.
Definition trunk.h:150
@ TRUNK_ENQUEUE_NO_CAPACITY
At maximum number of connections, and no connection has capacity.
Definition trunk.h:151
@ TRUNK_ENQUEUE_IN_BACKLOG
Request should be enqueued in backlog.
Definition trunk.h:149
void *_CONST preq
Data for the muxer to write to the connection.
Definition trunk.h:351
trunk_request_cancel_mux_t request_cancel_mux
!< Read one or more requests from a connection.
Definition trunk.h:746
trunk_request_state_t
Used for sanity checks and to simplify freeing.
Definition trunk.h:161
@ TRUNK_REQUEST_STATE_PARTIAL
Some of the request was written to the socket, more of it should be written later.
Definition trunk.h:170
@ TRUNK_REQUEST_STATE_CANCEL_SENT
We've informed the remote server that the request has been cancelled.
Definition trunk.h:185
@ TRUNK_REQUEST_STATE_CANCEL
A request on a particular socket was cancel.
Definition trunk.h:184
@ TRUNK_REQUEST_STATE_CANCEL_PARTIAL
We partially wrote a cancellation request.
Definition trunk.h:187
@ TRUNK_REQUEST_STATE_BACKLOG
In the backlog.
Definition trunk.h:167
@ TRUNK_REQUEST_STATE_PENDING
In the queue of a connection and is pending writing.
Definition trunk.h:168
@ TRUNK_REQUEST_STATE_SENT
Was written to a socket. Waiting for a response.
Definition trunk.h:172
Common configuration parameters for a trunk.
Definition trunk.h:224
I/O functions to pass to trunk_alloc.
Definition trunk.h:732
trunk_request_t * treq
Trunk request.
Definition trunk_tests.c:11
#define DEBUG_LVL_SET
Definition trunk_tests.c:28
TEST_LIST
static void test_demux(UNUSED fr_event_list_t *el, UNUSED trunk_connection_t *tconn, connection_t *conn, UNUSED void *uctx)
Definition trunk_tests.c:99
uint64_t cancelled
Count of tests in this run that were cancelled.
Definition trunk_tests.c:22
uint64_t freed
Count of tests in this run that were freed.
Definition trunk_tests.c:25
static connection_t * test_setup_socket_pair_connection_alloc(trunk_connection_t *tconn, fr_event_list_t *el, connection_conf_t const *conn_conf, char const *log_prefix, UNUSED void *uctx)
static void test_enqueue_and_io_speed(void)
bool cancelled
Seen by the cancelled callback.
Definition trunk_tests.c:12
static void _conn_io_error(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, UNUSED int fd_errno, void *uctx)
static void _conn_io_loopback(UNUSED fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
Whenever the second socket in a socket pair is readable, read all pending data, and write it back.
static void test_mux(UNUSED fr_event_list_t *el, trunk_connection_t *tconn, connection_t *conn, UNUSED void *uctx)
Definition trunk_tests.c:30
static void test_socket_pair_alloc_then_connect_timeout(void)
static void test_cancel_mux(UNUSED fr_event_list_t *el, trunk_connection_t *tconn, connection_t *conn, UNUSED void *uctx)
Definition trunk_tests.c:62
static void _conn_io_write(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, void *uctx)
static void test_partial_to_complete_states(void)
static connection_t * test_setup_socket_pair_1s_timeout_connection_alloc(trunk_connection_t *tconn, fr_event_list_t *el, UNUSED connection_conf_t const *conf, char const *log_prefix, void *uctx)
static void test_request_free(UNUSED request_t *request, void *preq, void *uctx)
static void test_socket_pair_alloc_then_free(void)
bool failed
Seen by the failed callback.
Definition trunk_tests.c:14
static void test_request_fail(UNUSED request_t *request, void *preq, UNUSED void *rctx, UNUSED trunk_request_state_t state, void *uctx)
static void test_connection_rebalance_requests(void)
static connection_state_t _conn_init_no_signal(void **h_out, connection_t *conn, UNUSED void *uctx)
static connection_t * test_setup_socket_pair_1s_reconnection_delay_alloc(trunk_connection_t *tconn, fr_event_list_t *el, UNUSED connection_conf_t const *conn_conf, char const *log_prefix, void *uctx)
static void test_connection_levels_max(void)
static void test_socket_pair_alloc_then_reconnect_check_delay(void)
bool freed
Seen by the free callback.
Definition trunk_tests.c:15
uint64_t failed
Count of tests in this run that failed.
Definition trunk_tests.c:24
static void _conn_io_read(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, void *uctx)
bool completed
Seen by the complete callback.
Definition trunk_tests.c:13
static void _conn_notify(trunk_connection_t *tconn, connection_t *conn, fr_event_list_t *el, trunk_connection_event_t notify_on, UNUSED void *uctx)
#define ALLOC_REQ(_id)
int priority
Priority of request.
Definition trunk_tests.c:18
static void test_socket_pair_alloc_then_reconnect_then_free(void)
static trunk_t * test_setup_trunk(TALLOC_CTX *ctx, fr_event_list_t *el, trunk_conf_t *conf, bool with_cancel_mux, void *uctx)
static connection_state_t _conn_open(fr_event_list_t *el, void *h, UNUSED void *uctx)
Insert I/O handlers that loop any data back round.
static void test_enqueue_cancellation_points(void)
static void test_requeue_on_reconnect(void)
static void test_connection_start_on_enqueue(void)
static int8_t test_preq_cmp(void const *a, void const *b)
bool signal_partial
Muxer should signal that this request is partially written.
Definition trunk_tests.c:16
static void test_request_complete(UNUSED request_t *request, void *preq, UNUSED void *rctx, void *uctx)
static void test_request_cancel(UNUSED connection_t *conn, void *preq, UNUSED trunk_cancel_reason_t reason, void *uctx)
static void test_enqueue_basic(void)
uint64_t completed
Count of tests in this run that completed.
Definition trunk_tests.c:23
static void test_connection_levels_alternating_edges(void)
static void _conn_close(UNUSED fr_event_list_t *el, void *h, UNUSED void *uctx)
static connection_state_t _conn_init(void **h_out, connection_t *conn, UNUSED void *uctx)
Allocate a basic socket pair.
bool signal_cancel_partial
Muxer should signal that this request is partially cancelled.
Definition trunk_tests.c:17
close(uq->fd)
static fr_event_list_t * el
void fr_perror(char const *fmt,...)
Print the current error to stderr with a prefix.
Definition strerror.c:732
#define fr_box_time_delta(_val)
Definition value.h:354