diff --git a/lib/jsonrpc.c b/lib/jsonrpc.c
index 926cbcb86e..66b7f9ef4c 100644
--- a/lib/jsonrpc.c
+++ b/lib/jsonrpc.c
@@ -1267,6 +1267,13 @@ jsonrpc_session_force_reconnect(struct jsonrpc_session *s)
     reconnect_force_reconnect(s->reconnect, time_msec());
 }
 
+/* Makes 's' gracefully drop its connection (if any) and reconnect. */
+void
+jsonrpc_session_graceful_reconnect(struct jsonrpc_session *s)
+{
+    reconnect_graceful_reconnect(s->reconnect, time_msec());
+}
+
 /* Sets 'max_backoff' as the maximum time, in milliseconds, to wait after a
  * connection attempt fails before attempting to connect again. */
 void
diff --git a/lib/jsonrpc.h b/lib/jsonrpc.h
index d75d66b863..3a34424e22 100644
--- a/lib/jsonrpc.h
+++ b/lib/jsonrpc.h
@@ -136,6 +136,7 @@ void jsonrpc_session_get_reconnect_stats(const struct jsonrpc_session *,
 
 void jsonrpc_session_enable_reconnect(struct jsonrpc_session *);
 void jsonrpc_session_force_reconnect(struct jsonrpc_session *);
+void jsonrpc_session_graceful_reconnect(struct jsonrpc_session *);
 
 void jsonrpc_session_set_max_backoff(struct jsonrpc_session *,
                                      int max_backoff);
diff --git a/lib/ovsdb-cs.c b/lib/ovsdb-cs.c
index 911b71dd4f..6f16d29f6d 100644
--- a/lib/ovsdb-cs.c
+++ b/lib/ovsdb-cs.c
@@ -230,8 +230,10 @@ static void ovsdb_cs_transition_at(struct ovsdb_cs *, enum ovsdb_cs_state,
 #define ovsdb_cs_transition(CS, STATE) \
     ovsdb_cs_transition_at(CS, STATE, OVS_SOURCE_LOCATOR)
 
-static void ovsdb_cs_retry_at(struct ovsdb_cs *, const char *where);
-#define ovsdb_cs_retry(CS) ovsdb_cs_retry_at(CS, OVS_SOURCE_LOCATOR)
+static void ovsdb_cs_retry_at(struct ovsdb_cs *, bool graceful,
+                              const char *where);
+#define ovsdb_cs_retry(CS, GRACEFUL) \
+    ovsdb_cs_retry_at((CS), (GRACEFUL), OVS_SOURCE_LOCATOR)
 
 static struct vlog_rate_limit syntax_rl = VLOG_RATE_LIMIT_INIT(1, 5);
 
@@ -400,9 +402,21 @@ ovsdb_cs_send_request(struct ovsdb_cs *cs, struct jsonrpc_msg *request)
 }
 
 static void
-ovsdb_cs_retry_at(struct ovsdb_cs *cs, const char *where)
+ovsdb_cs_reconnect(struct ovsdb_cs *cs, bool graceful)
 {
-    ovsdb_cs_force_reconnect(cs);
+    if (cs->session) {
+        if (graceful) {
+            jsonrpc_session_graceful_reconnect(cs->session);
+        } else {
+            jsonrpc_session_force_reconnect(cs->session);
+        }
+    }
+}
+
+static void
+ovsdb_cs_retry_at(struct ovsdb_cs *cs, bool graceful, const char *where)
+{
+    ovsdb_cs_reconnect(cs, graceful);
     ovsdb_cs_transition_at(cs, CS_S_RETRY, where);
 }
 
@@ -438,7 +452,7 @@ ovsdb_cs_process_response(struct ovsdb_cs *cs, struct jsonrpc_msg *msg)
                      ovsdb_cs_state_to_string(cs->state),
                      s);
         free(s);
-        ovsdb_cs_retry(cs);
+        ovsdb_cs_retry(cs, false);
         return;
     }
 
@@ -711,9 +725,7 @@ ovsdb_cs_enable_reconnect(struct ovsdb_cs *cs)
 void
 ovsdb_cs_force_reconnect(struct ovsdb_cs *cs)
 {
-    if (cs->session) {
-        jsonrpc_session_force_reconnect(cs->session);
-    }
+    ovsdb_cs_reconnect(cs, false);
 }
 
 /* Drops 'cs''s current connection and the cached session.  This is useful if
@@ -722,7 +734,7 @@ void
 ovsdb_cs_flag_inconsistency(struct ovsdb_cs *cs)
 {
     cs->data.last_id = UUID_ZERO;
-    ovsdb_cs_retry(cs);
+    ovsdb_cs_retry(cs, false);
 }
 
 /* Returns true if 'cs' is currently connected or will eventually try to
@@ -1951,7 +1963,7 @@ ovsdb_cs_check_server_db(struct ovsdb_cs *cs)
 {
     bool ok = ovsdb_cs_check_server_db__(cs);
     if (!ok) {
-        ovsdb_cs_retry(cs);
+        ovsdb_cs_retry(cs, true);
     }
     return ok;
 }
diff --git a/lib/reconnect.c b/lib/reconnect.c
index a929ddfd2d..633e77bdb2 100644
--- a/lib/reconnect.c
+++ b/lib/reconnect.c
@@ -77,6 +77,7 @@ 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 bool reconnect_reset_backoff__(struct reconnect *);
 
 static const char *
 reconnect_state_name__(enum state state)
@@ -330,6 +331,23 @@ reconnect_force_reconnect(struct reconnect *fsm, long long int now)
     }
 }
 
+/* If 'fsm' is enabled and currently connected (or attempting to connect),
+ * forces reconnect_run() for 'fsm' to return RECONNECT_DISCONNECT the next
+ * time it is called, which should cause the client to drop the connection (or
+ * attempt), back off, and then reconnect.
+ *
+ * Unlike reconnect_force_reconnect(), this also resets the backoff for
+ * connections that were active long enough.
+ * */
+void
+reconnect_graceful_reconnect(struct reconnect *fsm, long long int now)
+{
+    if (fsm->state & (S_CONNECTING | S_ACTIVE | S_IDLE)) {
+        reconnect_reset_backoff__(fsm);
+        reconnect_transition__(fsm, now, S_RECONNECT);
+    }
+}
+
 /* Tell 'fsm' that the connection dropped or that a connection attempt failed.
  * 'error' specifies the reason: a positive value represents an errno value,
  * EOF indicates that the connection was closed by the peer (e.g. read()
@@ -385,11 +403,7 @@ reconnect_disconnected(struct reconnect *fsm, long long int now, int error)
         if (fsm->backoff_free_tries > 1) {
             fsm->backoff_free_tries--;
             fsm->backoff = 0;
-        } else if (fsm->state & (S_ACTIVE | S_IDLE)
-                   && (fsm->last_activity - fsm->last_connected >= fsm->backoff
-                       || fsm->passive)) {
-            fsm->backoff = fsm->passive ? 0 : fsm->min_backoff;
-        } else {
+        } else if (!reconnect_reset_backoff__(fsm)) {
             if (fsm->backoff < fsm->min_backoff) {
                 fsm->backoff = fsm->min_backoff;
             } else if (fsm->backoff < fsm->max_backoff / 2) {
@@ -745,3 +759,15 @@ reconnect_may_retry(struct reconnect *fsm)
     }
     return may_retry;
 }
+
+static bool
+reconnect_reset_backoff__(struct reconnect *fsm)
+{
+    if (fsm->state & (S_ACTIVE | S_IDLE)
+        && (fsm->last_activity - fsm->last_connected >= fsm->backoff
+            || fsm->passive)) {
+        fsm->backoff = fsm->passive ? 0 : fsm->min_backoff;
+        return true;
+    }
+    return false;
+}
diff --git a/lib/reconnect.h b/lib/reconnect.h
index 40cc569c42..25bc627d66 100644
--- a/lib/reconnect.h
+++ b/lib/reconnect.h
@@ -67,6 +67,7 @@ void reconnect_enable(struct reconnect *, long long int now);
 void reconnect_disable(struct reconnect *, long long int now);
 
 void reconnect_force_reconnect(struct reconnect *, long long int now);
+void reconnect_graceful_reconnect(struct reconnect *, long long int now);
 void reconnect_skip_backoff(struct reconnect *);
 
 bool reconnect_is_connected(const struct reconnect *);
diff --git a/python/ovs/db/idl.py b/python/ovs/db/idl.py
index 889cf3431b..49bb964217 100644
--- a/python/ovs/db/idl.py
+++ b/python/ovs/db/idl.py
@@ -276,7 +276,7 @@ class Idl(object):
                                         tables=self.server_tables)
                     self.change_seqno = initial_change_seqno
                     if not self.__check_server_db():
-                        self.force_reconnect()
+                        self.graceful_reconnect()
                         break
                 else:
                     self.__parse_update(msg.params[1], OVSDB_UPDATE)
@@ -442,6 +442,12 @@ class Idl(object):
         In the meantime, the contents of the IDL will not change."""
         self._session.force_reconnect()
 
+    def graceful_reconnect(self):
+        """Makes the IDL to gracefully drop its connection to the database and
+        reconnect.  In the meantime, the contents of the IDL will not
+        change."""
+        self._session.graceful_reconnect()
+
     def session_name(self):
         return self._session.get_name()
 
diff --git a/python/ovs/jsonrpc.py b/python/ovs/jsonrpc.py
index bf32f8c87c..353a07fc49 100644
--- a/python/ovs/jsonrpc.py
+++ b/python/ovs/jsonrpc.py
@@ -612,5 +612,8 @@ class Session(object):
     def force_reconnect(self):
         self.reconnect.force_reconnect(ovs.timeval.msec())
 
+    def graceful_reconnect(self):
+        self.reconnect.graceful_reconnect(ovs.timeval.msec())
+
     def get_num_of_remotes(self):
         return len(self.remotes)
diff --git a/python/ovs/reconnect.py b/python/ovs/reconnect.py
index c4c6c87e9f..0f4a8e18da 100644
--- a/python/ovs/reconnect.py
+++ b/python/ovs/reconnect.py
@@ -325,6 +325,20 @@ class Reconnect(object):
                           Reconnect.Idle):
             self._transition(now, Reconnect.Reconnect)
 
+    def graceful_reconnect(self, now):
+        """If this FSM is enabled and currently connected (or attempting to
+        connect), forces self.run() to return ovs.reconnect.DISCONNECT the next
+        time it is called, which should cause the client to drop the connection
+        (or attempt), back off, and then reconnect.
+
+        Unlike self.force_reconnect(), this also updates backoff for long
+        lived sessions."""
+        if self.state in (Reconnect.ConnectInProgress,
+                          Reconnect.Active,
+                          Reconnect.Idle):
+            self._reset_backoff()
+            self._transition(now, Reconnect.Reconnect)
+
     def disconnected(self, now, error):
         """Tell this FSM that the connection dropped or that a connection
         attempt failed.  'error' specifies the reason: a positive value
@@ -377,14 +391,7 @@ class Reconnect(object):
             if self.backoff_free_tries > 1:
                 self.backoff_free_tries -= 1
                 self.backoff = 0
-            elif (self.state in (Reconnect.Active, Reconnect.Idle) and
-                (self.last_activity - self.last_connected >= self.backoff or
-                 self.passive)):
-                if self.passive:
-                    self.backoff = 0
-                else:
-                    self.backoff = self.min_backoff
-            else:
+            elif not self._reset_backoff():
                 if self.backoff < self.min_backoff:
                     self.backoff = self.min_backoff
                 elif self.backoff < self.max_backoff / 2:
@@ -623,3 +630,14 @@ class Reconnect(object):
             return True
         else:
             return False
+
+    def _reset_backoff(self):
+        if (self.state in (Reconnect.Active, Reconnect.Idle) and
+            (self.last_activity - self.last_connected >= self.backoff or
+             self.passive)):
+            if self.passive:
+                self.backoff = 0
+            else:
+                self.backoff = self.min_backoff
+            return True
+        return False
diff --git a/tests/reconnect.at b/tests/reconnect.at
index 0f74709f5a..884483df3d 100644
--- a/tests/reconnect.at
+++ b/tests/reconnect.at
@@ -1303,3 +1303,347 @@ run
 listening
   in LISTENING for 0 ms (0 ms backoff)
 ])
+
+######################################################################
+RECONNECT_CHECK([long connection doesn't reset backoff (force-reconnect)],
+  [enable
+
+# First connection attempt fails after 1000 ms.
+run
+connecting
+run
+timeout
+run
+connect-failed
+
+# Back off for 1000 ms.
+timeout
+run
+
+# Second connection attempt fails after 1000 ms.
+connecting
+timeout
+run
+connect-failed
+
+# Back off for 2000 ms.
+timeout
+run
+
+# Third connection attempt succeeds after 500 ms.
+connecting
+advance 500
+run
+connected
+
+# Connection receives 3 chunks of data spaced 2000 ms apart.
+advance 2000
+run
+activity
+advance 2000
+run
+activity
+advance 2000
+run
+activity
+
+# Forcefully reconnect.
+force-reconnect
+run
+connecting
+
+# Back off for 2000 ms.
+timeout
+
+# Connection succeeds.
+connected
+],
+  [### t=1000 ###
+enable
+  in BACKOFF for 0 ms (0 ms backoff)
+
+# First connection attempt fails after 1000 ms.
+run
+  should connect
+connecting
+  in CONNECTING for 0 ms (0 ms backoff)
+run
+timeout
+  advance 1000 ms
+
+### t=2000 ###
+  in CONNECTING for 1000 ms (0 ms backoff)
+run
+  should disconnect
+connect-failed
+  in BACKOFF for 0 ms (1000 ms backoff)
+  0 successful connections out of 1 attempts, seqno 0
+
+# Back off for 1000 ms.
+timeout
+  advance 1000 ms
+
+### t=3000 ###
+  in BACKOFF for 1000 ms (1000 ms backoff)
+run
+  should connect
+
+# Second connection attempt fails after 1000 ms.
+connecting
+  in CONNECTING for 0 ms (1000 ms backoff)
+timeout
+  advance 1000 ms
+
+### t=4000 ###
+  in CONNECTING for 1000 ms (1000 ms backoff)
+run
+  should disconnect
+connect-failed
+  in BACKOFF for 0 ms (2000 ms backoff)
+  0 successful connections out of 2 attempts, seqno 0
+
+# Back off for 2000 ms.
+timeout
+  advance 2000 ms
+
+### t=6000 ###
+  in BACKOFF for 2000 ms (2000 ms backoff)
+run
+  should connect
+
+# Third connection attempt succeeds after 500 ms.
+connecting
+  in CONNECTING for 0 ms (2000 ms backoff)
+advance 500
+
+### t=6500 ###
+  in CONNECTING for 500 ms (2000 ms backoff)
+run
+connected
+  in ACTIVE for 0 ms (2000 ms backoff)
+  created 1000, last activity 1000, last connected 6500
+  1 successful connections out of 3 attempts, seqno 1
+  connected
+  last connected 0 ms ago, connected 0 ms total
+
+# Connection receives 3 chunks of data spaced 2000 ms apart.
+advance 2000
+
+### t=8500 ###
+  in ACTIVE for 2000 ms (2000 ms backoff)
+run
+activity
+  created 1000, last activity 8500, last connected 6500
+advance 2000
+
+### t=10500 ###
+  in ACTIVE for 4000 ms (2000 ms backoff)
+run
+activity
+  created 1000, last activity 10500, last connected 6500
+advance 2000
+
+### t=12500 ###
+  in ACTIVE for 6000 ms (2000 ms backoff)
+run
+activity
+  created 1000, last activity 12500, last connected 6500
+
+# Forcefully reconnect.
+force-reconnect
+  in RECONNECT for 0 ms (2000 ms backoff)
+  1 successful connections out of 3 attempts, seqno 2
+  disconnected
+run
+  should disconnect
+connecting
+  in CONNECTING for 0 ms (2000 ms backoff)
+
+# Back off for 2000 ms.
+timeout
+  advance 2000 ms
+
+### t=14500 ###
+  in CONNECTING for 2000 ms (2000 ms backoff)
+  last connected 8000 ms ago, connected 6000 ms total
+
+# Connection succeeds.
+connected
+  in ACTIVE for 0 ms (2000 ms backoff)
+  created 1000, last activity 12500, last connected 14500
+  2 successful connections out of 4 attempts, seqno 3
+  connected
+  last connected 0 ms ago, connected 6000 ms total
+])
+
+######################################################################
+RECONNECT_CHECK([long connection resets backoff (graceful-reconnect)],
+  [enable
+
+# First connection attempt fails after 1000 ms.
+run
+connecting
+run
+timeout
+run
+connect-failed
+
+# Back off for 1000 ms.
+timeout
+run
+
+# Second connection attempt fails after 1000 ms.
+connecting
+timeout
+run
+connect-failed
+
+# Back off for 2000 ms.
+timeout
+run
+
+# Third connection attempt succeeds after 500 ms.
+connecting
+advance 500
+run
+connected
+
+# Connection receives 3 chunks of data spaced 2000 ms apart.
+advance 2000
+run
+activity
+advance 2000
+run
+activity
+advance 2000
+run
+activity
+
+# Gracefully reconnect.
+graceful-reconnect
+run
+connecting
+
+# Back off for 1000 ms.
+timeout
+
+# Connection succeeds.
+connected
+],
+  [### t=1000 ###
+enable
+  in BACKOFF for 0 ms (0 ms backoff)
+
+# First connection attempt fails after 1000 ms.
+run
+  should connect
+connecting
+  in CONNECTING for 0 ms (0 ms backoff)
+run
+timeout
+  advance 1000 ms
+
+### t=2000 ###
+  in CONNECTING for 1000 ms (0 ms backoff)
+run
+  should disconnect
+connect-failed
+  in BACKOFF for 0 ms (1000 ms backoff)
+  0 successful connections out of 1 attempts, seqno 0
+
+# Back off for 1000 ms.
+timeout
+  advance 1000 ms
+
+### t=3000 ###
+  in BACKOFF for 1000 ms (1000 ms backoff)
+run
+  should connect
+
+# Second connection attempt fails after 1000 ms.
+connecting
+  in CONNECTING for 0 ms (1000 ms backoff)
+timeout
+  advance 1000 ms
+
+### t=4000 ###
+  in CONNECTING for 1000 ms (1000 ms backoff)
+run
+  should disconnect
+connect-failed
+  in BACKOFF for 0 ms (2000 ms backoff)
+  0 successful connections out of 2 attempts, seqno 0
+
+# Back off for 2000 ms.
+timeout
+  advance 2000 ms
+
+### t=6000 ###
+  in BACKOFF for 2000 ms (2000 ms backoff)
+run
+  should connect
+
+# Third connection attempt succeeds after 500 ms.
+connecting
+  in CONNECTING for 0 ms (2000 ms backoff)
+advance 500
+
+### t=6500 ###
+  in CONNECTING for 500 ms (2000 ms backoff)
+run
+connected
+  in ACTIVE for 0 ms (2000 ms backoff)
+  created 1000, last activity 1000, last connected 6500
+  1 successful connections out of 3 attempts, seqno 1
+  connected
+  last connected 0 ms ago, connected 0 ms total
+
+# Connection receives 3 chunks of data spaced 2000 ms apart.
+advance 2000
+
+### t=8500 ###
+  in ACTIVE for 2000 ms (2000 ms backoff)
+run
+activity
+  created 1000, last activity 8500, last connected 6500
+advance 2000
+
+### t=10500 ###
+  in ACTIVE for 4000 ms (2000 ms backoff)
+run
+activity
+  created 1000, last activity 10500, last connected 6500
+advance 2000
+
+### t=12500 ###
+  in ACTIVE for 6000 ms (2000 ms backoff)
+run
+activity
+  created 1000, last activity 12500, last connected 6500
+
+# Gracefully reconnect.
+graceful-reconnect
+  in RECONNECT for 0 ms (1000 ms backoff)
+  1 successful connections out of 3 attempts, seqno 2
+  disconnected
+run
+  should disconnect
+connecting
+  in CONNECTING for 0 ms (1000 ms backoff)
+
+# Back off for 1000 ms.
+timeout
+  advance 1000 ms
+
+### t=13500 ###
+  in CONNECTING for 1000 ms (1000 ms backoff)
+  last connected 7000 ms ago, connected 6000 ms total
+
+# Connection succeeds.
+connected
+  in ACTIVE for 0 ms (1000 ms backoff)
+  created 1000, last activity 12500, last connected 13500
+  2 successful connections out of 4 attempts, seqno 3
+  connected
+  last connected 0 ms ago, connected 6000 ms total
+])
diff --git a/tests/test-reconnect.c b/tests/test-reconnect.c
index c84bb1cdbf..cabf228f84 100644
--- a/tests/test-reconnect.c
+++ b/tests/test-reconnect.c
@@ -108,6 +108,12 @@ do_force_reconnect(struct ovs_cmdl_context *ctx OVS_UNUSED)
     reconnect_force_reconnect(reconnect, now);
 }
 
+static void
+do_graceful_reconnect(struct ovs_cmdl_context *ctx OVS_UNUSED)
+{
+    reconnect_graceful_reconnect(reconnect, now);
+}
+
 static int
 error_from_string(const char *s)
 {
@@ -293,6 +299,7 @@ static const struct ovs_cmdl_command all_commands[] = {
     { "enable", NULL, 0, 0, do_enable, OVS_RO },
     { "disable", NULL, 0, 0, do_disable, OVS_RO },
     { "force-reconnect", NULL, 0, 0, do_force_reconnect, OVS_RO },
+    { "graceful-reconnect", NULL, 0, 0, do_graceful_reconnect, OVS_RO },
     { "disconnected", NULL, 0, 1, do_disconnected, OVS_RO },
     { "connecting", NULL, 0, 0, do_connecting, OVS_RO },
     { "connect-failed", NULL, 0, 1, do_connect_failed, OVS_RO },
diff --git a/tests/test-reconnect.py b/tests/test-reconnect.py
index cea48eb527..b397eac656 100644
--- a/tests/test-reconnect.py
+++ b/tests/test-reconnect.py
@@ -33,6 +33,10 @@ def do_force_reconnect(_):
     r.force_reconnect(now)
 
 
+def do_graceful_reconnect(_):
+    r.graceful_reconnect(now)
+
+
 def error_from_string(s):
     if not s:
         return 0
@@ -176,6 +180,7 @@ def main():
         "enable": do_enable,
         "disable": do_disable,
         "force-reconnect": do_force_reconnect,
+        "graceful-reconnect": do_graceful_reconnect,
         "disconnected": do_disconnected,
         "connecting": do_connecting,
         "connect-failed": do_connect_failed,
