reconnect: Add connection attempt limiting feature.
authorBen Pfaff <blp@nicira.com>
Thu, 17 Dec 2009 21:46:16 +0000 (13:46 -0800)
committerBen Pfaff <blp@nicira.com>
Mon, 4 Jan 2010 17:47:01 +0000 (09:47 -0800)
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
lib/reconnect.h
tests/reconnect.at
tests/test-reconnect.c

index fadeeb89b20a834522093786698cc45ef5d6a65c..2ae65c6809958b630eac1a9d082fcd135fef6df8 100644 (file)
@@ -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;
+}
index 3442c07ab2a26645d976343e71f186acf0a8be63..76c7f78ec9bead193108d84b4447457f5eee9b66 100644 (file)
@@ -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);
index 33e4b951889767e16ff3d490a91215878ffcef3c..225da0d495448e48bb889396d33a44bc078d5176 100644 (file)
@@ -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
index a8784fc884dcbbafec50f9e3dd047e7f75a27e24..8441fadd3bcacffd62ea71d07733818409e17c5c 100644 (file)
@@ -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 },
 };