From a85c0bbcfd19fcd88ee9966a86dd83107dfd6603 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Thu, 17 Dec 2009 13:46:16 -0800 Subject: [PATCH] reconnect: Add connection attempt limiting feature. Sometimes it is useful to limit the number of connection attempts, either from policy or because it is not possible to reconnect at all (e.g. because a connection was accepted from a listening socket instead of made with connect()). This commit adds that feature. --- lib/reconnect.c | 41 ++++++++++++++++++++-- lib/reconnect.h | 3 ++ tests/reconnect.at | 80 ++++++++++++++++++++++++++++++++++++++++++ tests/test-reconnect.c | 13 +++++++ 4 files changed, 134 insertions(+), 3 deletions(-) diff --git a/lib/reconnect.c b/lib/reconnect.c index fadeeb89..2ae65c68 100644 --- a/lib/reconnect.c +++ b/lib/reconnect.c @@ -57,6 +57,7 @@ struct reconnect { int backoff; long long int last_received; long long int last_connected; + unsigned int max_tries; /* These values are simply for statistics reporting, not otherwise used * directly by anything internal. */ @@ -69,6 +70,7 @@ struct reconnect { static void reconnect_transition__(struct reconnect *, long long int now, enum state state); static long long int reconnect_deadline__(const struct reconnect *); +static bool reconnect_may_retry(struct reconnect *); static const char * reconnect_state_name__(enum state state) @@ -99,6 +101,7 @@ reconnect_create(long long int now) fsm->backoff = 0; fsm->last_received = now; fsm->last_connected = now; + fsm->max_tries = UINT_MAX; fsm->creation_time = now; return fsm; @@ -160,6 +163,26 @@ reconnect_get_probe_interval(const struct reconnect *fsm) return fsm->probe_interval; } +/* Limits the maximum number of times that 'fsm' will ask the client to try to + * reconnect to 'max_tries'. UINT_MAX (the default) means an unlimited number + * of tries. + * + * After the number of tries has expired, the 'fsm' will disable itself + * instead of backing off and retrying. */ +void +reconnect_set_max_tries(struct reconnect *fsm, unsigned int max_tries) +{ + fsm->max_tries = max_tries; +} + +/* Returns the current remaining number of connection attempts, UINT_MAX if + * the number is unlimited. */ +unsigned int +reconnect_get_max_tries(struct reconnect *fsm) +{ + return fsm->max_tries; +} + /* Configures the backoff parameters for 'fsm'. 'min_backoff' is the minimum * number of milliseconds, and 'max_backoff' is the maximum, between connection * attempts. @@ -213,7 +236,7 @@ reconnect_is_enabled(const struct reconnect *fsm) void reconnect_enable(struct reconnect *fsm, long long int now) { - if (fsm->state == S_VOID) { + if (fsm->state == S_VOID && reconnect_may_retry(fsm)) { reconnect_transition__(fsm, now, S_BACKOFF); fsm->backoff = 0; } @@ -250,7 +273,7 @@ reconnect_force_reconnect(struct reconnect *fsm, long long int now) void reconnect_disconnected(struct reconnect *fsm, long long int now, int error) { - if (fsm->state != S_BACKOFF) { + if (!(fsm->state & (S_BACKOFF | S_VOID))) { /* Report what happened. */ if (fsm->state & (S_ACTIVE | S_IDLE)) { if (error > 0) { @@ -285,7 +308,9 @@ reconnect_disconnected(struct reconnect *fsm, long long int now, int error) VLOG_INFO("%s: waiting %.3g seconds before reconnect\n", fsm->name, fsm->backoff / 1000.0); } - reconnect_transition__(fsm, now, S_BACKOFF); + + reconnect_transition__(fsm, now, + reconnect_may_retry(fsm) ? S_BACKOFF : S_VOID); } } @@ -521,3 +546,13 @@ reconnect_get_stats(const struct reconnect *fsm, long long int now, stats->state = reconnect_state_name__(fsm->state); stats->state_elapsed = now - fsm->state_entered; } + +static bool +reconnect_may_retry(struct reconnect *fsm) +{ + bool may_retry = fsm->max_tries > 0; + if (may_retry && fsm->max_tries != UINT_MAX) { + fsm->max_tries--; + } + return may_retry; +} diff --git a/lib/reconnect.h b/lib/reconnect.h index 3442c07a..76c7f78e 100644 --- a/lib/reconnect.h +++ b/lib/reconnect.h @@ -42,6 +42,9 @@ int reconnect_get_min_backoff(const struct reconnect *); int reconnect_get_max_backoff(const struct reconnect *); int reconnect_get_probe_interval(const struct reconnect *); +void reconnect_set_max_tries(struct reconnect *, unsigned int max_tries); +unsigned int reconnect_get_max_tries(struct reconnect *); + void reconnect_set_backoff(struct reconnect *, int min_backoff, int max_backoff); void reconnect_set_probe_interval(struct reconnect *, int probe_interval); diff --git a/tests/reconnect.at b/tests/reconnect.at index 33e4b951..225da0d4 100644 --- a/tests/reconnect.at +++ b/tests/reconnect.at @@ -1037,3 +1037,83 @@ timeout ]) AT_CLEANUP +###################################################################### +AT_SETUP([max-tries of 1 honored]) +AT_KEYWORDS([reconnect]) +AT_DATA([input], [set-max-tries 1 +enable + +# Connection succeeds. +run +connected + +# Send inactivity probe. +timeout +run + +# Idle timeout kills connection. +timeout +run +disconnected +]) +OVS_CHECK_LCOV([test-reconnect < input], [0], + [### t=1000 ### +set-max-tries 1 + 1 tries left +enable + in BACKOFF for 0 ms (0 ms backoff) + 0 tries left + +# Connection succeeds. +run + should connect +connected + in ACTIVE for 0 ms (0 ms backoff) + 1 successful connections out of 1 attempts, seqno 1 + connected (0 ms), total 0 ms connected + +# Send inactivity probe. +timeout + advance 5000 ms + +### t=6000 ### + in ACTIVE for 5000 ms (0 ms backoff) + connected (5000 ms), total 5000 ms connected +run + should send probe + in IDLE for 0 ms (0 ms backoff) + +# Idle timeout kills connection. +timeout + advance 5000 ms + +### t=11000 ### + in IDLE for 5000 ms (0 ms backoff) + connected (10000 ms), total 10000 ms connected +run + should disconnect +disconnected + in VOID for 0 ms (1000 ms backoff) + 1 successful connections out of 1 attempts, seqno 2 + not connected (0 ms), total 10000 ms connected +]) +AT_CLEANUP + +###################################################################### +AT_SETUP([max-tries of 0 honored]) +AT_KEYWORDS([reconnect]) +AT_DATA([input], [set-max-tries 0 +enable +run +timeout +]) +OVS_CHECK_LCOV([test-reconnect < input], [0], + [### t=1000 ### +set-max-tries 0 + 0 tries left +enable +run +timeout + no timeout +]) +AT_CLEANUP diff --git a/tests/test-reconnect.c b/tests/test-reconnect.c index a8784fc8..8441fadd 100644 --- a/tests/test-reconnect.c +++ b/tests/test-reconnect.c @@ -40,6 +40,7 @@ int main(void) { struct reconnect_stats prev; + unsigned int old_max_tries; int old_time; char line[128]; @@ -49,6 +50,7 @@ main(void) reconnect_get_stats(reconnect, now, &prev); printf("### t=%d ###\n", now); old_time = now; + old_max_tries = reconnect_get_max_tries(reconnect); while (fgets(line, sizeof line, stdin)) { struct reconnect_stats cur; struct svec args; @@ -74,6 +76,10 @@ main(void) reconnect_get_stats(reconnect, now, &cur); diff_stats(&prev, &cur); prev = cur; + if (reconnect_get_max_tries(reconnect) != old_max_tries) { + old_max_tries = reconnect_get_max_tries(reconnect); + printf(" %u tries left\n", old_max_tries); + } } return 0; @@ -190,6 +196,12 @@ do_timeout(int argc UNUSED, char *argv[] UNUSED) } } +static void +do_set_max_tries(int argc UNUSED, char *argv[]) +{ + reconnect_set_max_tries(reconnect, atoi(argv[1])); +} + static void diff_stats(const struct reconnect_stats *old, const struct reconnect_stats *new) @@ -235,6 +247,7 @@ static const struct command commands[] = { { "run", 0, 1, do_run }, { "advance", 1, 1, do_advance }, { "timeout", 0, 0, do_timeout }, + { "set-max-tries", 1, 1, do_set_max_tries }, { NULL, 0, 0, NULL }, }; -- 2.30.2