diff --git a/lib/reconnect.c b/lib/reconnect.c index fadeeb89b..2ae65c680 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 3442c07ab..76c7f78ec 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 33e4b9518..225da0d49 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 a8784fc88..8441fadd3 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 }, };