{"id":815896,"url":"http://patchwork.ozlabs.org/api/patches/815896/?format=json","web_url":"http://patchwork.ozlabs.org/project/openvswitch/patch/20170919220125.32535-45-blp@ovn.org/","project":{"id":47,"url":"http://patchwork.ozlabs.org/api/projects/47/?format=json","name":"Open vSwitch","link_name":"openvswitch","list_id":"ovs-dev.openvswitch.org","list_email":"ovs-dev@openvswitch.org","web_url":"http://openvswitch.org/","scm_url":"git@github.com:openvswitch/ovs.git","webscm_url":"https://github.com/openvswitch/ovs","list_archive_url":"","list_archive_url_format":"","commit_url_format":""},"msgid":"<20170919220125.32535-45-blp@ovn.org>","list_archive_url":null,"date":"2017-09-19T22:01:17","name":"[ovs-dev,RFC,44/52] ovsdb-server: Add new RPC \"set_db_change_aware\".","commit_ref":null,"pull_url":null,"state":"rfc","archived":false,"hash":"9141afea368bd52c8205fc1180a09556efa2883e","submitter":{"id":67603,"url":"http://patchwork.ozlabs.org/api/people/67603/?format=json","name":"Ben Pfaff","email":"blp@ovn.org"},"delegate":null,"mbox":"http://patchwork.ozlabs.org/project/openvswitch/patch/20170919220125.32535-45-blp@ovn.org/mbox/","series":[{"id":3975,"url":"http://patchwork.ozlabs.org/api/series/3975/?format=json","web_url":"http://patchwork.ozlabs.org/project/openvswitch/list/?series=3975","date":"2017-09-19T22:00:34","name":"clustering implementation","version":1,"mbox":"http://patchwork.ozlabs.org/series/3975/mbox/"}],"comments":"http://patchwork.ozlabs.org/api/patches/815896/comments/","check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/815896/checks/","tags":{},"related":[],"headers":{"Return-Path":"<ovs-dev-bounces@openvswitch.org>","X-Original-To":["incoming@patchwork.ozlabs.org","dev@openvswitch.org"],"Delivered-To":["patchwork-incoming@bilbo.ozlabs.org","ovs-dev@mail.linuxfoundation.org"],"Authentication-Results":"ozlabs.org;\n\tspf=pass (mailfrom) smtp.mailfrom=openvswitch.org\n\t(client-ip=140.211.169.12; helo=mail.linuxfoundation.org;\n\tenvelope-from=ovs-dev-bounces@openvswitch.org;\n\treceiver=<UNKNOWN>)","Received":["from mail.linuxfoundation.org (mail.linuxfoundation.org\n\t[140.211.169.12])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256\n\tbits)) (No client certificate requested)\n\tby ozlabs.org (Postfix) with ESMTPS id 3xxcmg5Wmmz9sMN\n\tfor <incoming@patchwork.ozlabs.org>;\n\tWed, 20 Sep 2017 08:21:55 +1000 (AEST)","from mail.linux-foundation.org (localhost [127.0.0.1])\n\tby mail.linuxfoundation.org (Postfix) with ESMTP id 10AD2D10;\n\tTue, 19 Sep 2017 22:02:50 +0000 (UTC)","from smtp1.linuxfoundation.org (smtp1.linux-foundation.org\n\t[172.17.192.35])\n\tby mail.linuxfoundation.org (Postfix) with ESMTPS id C2396AF7\n\tfor <dev@openvswitch.org>; Tue, 19 Sep 2017 22:02:48 +0000 (UTC)","from relay4-d.mail.gandi.net (relay4-d.mail.gandi.net\n\t[217.70.183.196])\n\tby smtp1.linuxfoundation.org (Postfix) with ESMTPS id 337B5174\n\tfor <dev@openvswitch.org>; Tue, 19 Sep 2017 22:02:47 +0000 (UTC)","from sigabrt.benpfaff.org (unknown [208.91.2.3])\n\t(Authenticated sender: blp@ovn.org)\n\tby relay4-d.mail.gandi.net (Postfix) with ESMTPSA id E7F17172094;\n\tWed, 20 Sep 2017 00:02:44 +0200 (CEST)"],"X-Greylist":"domain auto-whitelisted by SQLgrey-1.7.6","X-Originating-IP":"208.91.2.3","From":"Ben Pfaff <blp@ovn.org>","To":"dev@openvswitch.org","Date":"Tue, 19 Sep 2017 15:01:17 -0700","Message-Id":"<20170919220125.32535-45-blp@ovn.org>","X-Mailer":"git-send-email 2.10.2","In-Reply-To":"<20170919220125.32535-1-blp@ovn.org>","References":"<20170919220125.32535-1-blp@ovn.org>","X-Spam-Status":"No, score=-0.7 required=5.0 tests=RCVD_IN_DNSWL_LOW\n\tautolearn=disabled version=3.3.1","X-Spam-Checker-Version":"SpamAssassin 3.3.1 (2010-03-16) on\n\tsmtp1.linux-foundation.org","Cc":"Ben Pfaff <blp@ovn.org>","Subject":"[ovs-dev] [PATCH RFC 44/52] ovsdb-server: Add new RPC\n\t\"set_db_change_aware\".","X-BeenThere":"ovs-dev@openvswitch.org","X-Mailman-Version":"2.1.12","Precedence":"list","List-Id":"<ovs-dev.openvswitch.org>","List-Unsubscribe":"<https://mail.openvswitch.org/mailman/options/ovs-dev>,\n\t<mailto:ovs-dev-request@openvswitch.org?subject=unsubscribe>","List-Archive":"<http://mail.openvswitch.org/pipermail/ovs-dev/>","List-Post":"<mailto:ovs-dev@openvswitch.org>","List-Help":"<mailto:ovs-dev-request@openvswitch.org?subject=help>","List-Subscribe":"<https://mail.openvswitch.org/mailman/listinfo/ovs-dev>,\n\t<mailto:ovs-dev-request@openvswitch.org?subject=subscribe>","MIME-Version":"1.0","Content-Type":"text/plain; charset=\"us-ascii\"","Content-Transfer-Encoding":"7bit","Sender":"ovs-dev-bounces@openvswitch.org","Errors-To":"ovs-dev-bounces@openvswitch.org"},"content":"The _Server database recently added to ovsdb-server can be used to dump out\ninformation about databases, but monitoring updates to _Server is not yet\nvery useful because for historical reasons ovsdb-server drops all of its\nOVSDB connections whenever databases are added or removed or otherwise\nchange in some major way.  It is not a good idea to change this behavior\nfor all clients, because some of them rely on it, but this commit\nintroduces a new RPC that allows clients that understand _Server to\nsuppress the connection-closing behavior.\n\nSigned-off-by: Ben Pfaff <blp@ovn.org>\n---\n ovsdb/_server.xml       |  34 +++++++++\n ovsdb/jsonrpc-server.c  | 195 +++++++++++++++++++++++++++++++++++++-----------\n ovsdb/jsonrpc-server.h  |   4 +-\n ovsdb/ovsdb-client.c    |  31 +++++++-\n ovsdb/ovsdb-server.1.in |  57 ++++++++++++++\n ovsdb/ovsdb-server.c    |  11 +--\n tests/ovsdb-server.at   |  34 ++++++++-\n 7 files changed, 311 insertions(+), 55 deletions(-)","diff":"diff --git a/ovsdb/_server.xml b/ovsdb/_server.xml\nindex a55beb9bd6de..8ef782fb97b2 100644\n--- a/ovsdb/_server.xml\n+++ b/ovsdb/_server.xml\n@@ -13,6 +13,40 @@\n       one row per database.  As its database configuration and status changes,\n       the server automatically and immediately updates the table to match.\n     </p>\n+\n+    <p>\n+      The OVSDB protocol specified in RFC 7047 does not provide a way for an\n+      OVSDB client to find out about some kinds of configuration changes, such\n+      as about databases added or removed while a client is connected to the\n+      server, or databases changing between read/write and read-only due to a\n+      transition between active and backup roles.  This table provides a\n+      solution: clients can monitor the table's contents to find out about\n+      important changes.\n+    </p>\n+\n+    <p>\n+      Traditionally, <code>ovsdb-server</code> disconnects all of its clients\n+      when a significant configuration change occurs, because this prompts a\n+      well-written client to reassess what is available from the server when it\n+      reconnects.  Because this table provides an alternative and more\n+      efficient way to find out about those changes, OVS 2.9 also introduces\n+      the <code>set_db_change_aware</code> RPC, documented in\n+      <code>ovsdb-server</code>(1), to allow clients to suppress this\n+      disconnection behavior.\n+    </p>\n+\n+    <p>\n+      When a database is removed from the server, in addition to\n+      <code>Database</code> table updates, the server sends <code>cancel</code>\n+      messages, as described in RFC 7047 section 4.1.4, in reply to outstanding\n+      transactions for the removed database.  The server also cancels any\n+      outstanding monitoring initiated by <code>monitor</code> or\n+      <code>monitor_cond</code> requested on the removed database, sending the\n+      <code>monitor_canceled</code> RPC described in\n+      <code>ovsdb-server</code>(5).  Only clients that disable disconnection\n+      with <code>set_db_change_aware</code> receive these messages.\n+    </p>\n+\n     <p>\n       Clients can use the <code>_uuid</code> column in this table as a\n       generation number.  The server generates a fresh <code>_uuid</code> every\ndiff --git a/ovsdb/jsonrpc-server.c b/ovsdb/jsonrpc-server.c\nindex 6e5f75498fc1..a667dbe67f5f 100644\n--- a/ovsdb/jsonrpc-server.c\n+++ b/ovsdb/jsonrpc-server.c\n@@ -57,12 +57,15 @@ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);\n /* Sessions. */\n static struct ovsdb_jsonrpc_session *ovsdb_jsonrpc_session_create(\n     struct ovsdb_jsonrpc_remote *, struct jsonrpc_session *, bool);\n+static void ovsdb_jsonrpc_session_preremove_db(struct ovsdb_jsonrpc_remote *,\n+                                               struct ovsdb *);\n static void ovsdb_jsonrpc_session_run_all(struct ovsdb_jsonrpc_remote *);\n static void ovsdb_jsonrpc_session_wait_all(struct ovsdb_jsonrpc_remote *);\n static void ovsdb_jsonrpc_session_get_memory_usage_all(\n     const struct ovsdb_jsonrpc_remote *, struct simap *usage);\n static void ovsdb_jsonrpc_session_close_all(struct ovsdb_jsonrpc_remote *);\n-static void ovsdb_jsonrpc_session_reconnect_all(struct ovsdb_jsonrpc_remote *);\n+static void ovsdb_jsonrpc_session_reconnect_all(struct ovsdb_jsonrpc_remote *,\n+                                                bool force);\n static void ovsdb_jsonrpc_session_set_all_options(\n     struct ovsdb_jsonrpc_remote *, const struct ovsdb_jsonrpc_options *);\n static bool ovsdb_jsonrpc_active_session_get_status(\n@@ -83,6 +86,8 @@ static void ovsdb_jsonrpc_trigger_create(struct ovsdb_jsonrpc_session *,\n static struct ovsdb_jsonrpc_trigger *ovsdb_jsonrpc_trigger_find(\n     struct ovsdb_jsonrpc_session *, const struct json *id, size_t hash);\n static void ovsdb_jsonrpc_trigger_complete(struct ovsdb_jsonrpc_trigger *);\n+static void ovsdb_jsonrpc_trigger_preremove_db(struct ovsdb_jsonrpc_session *,\n+                                               struct ovsdb *);\n static void ovsdb_jsonrpc_trigger_complete_all(struct ovsdb_jsonrpc_session *);\n static void ovsdb_jsonrpc_trigger_complete_done(\n     struct ovsdb_jsonrpc_session *);\n@@ -99,6 +104,8 @@ static struct jsonrpc_msg *ovsdb_jsonrpc_monitor_cancel(\n     struct ovsdb_jsonrpc_session *,\n     struct json_array *params,\n     const struct json *request_id);\n+static void ovsdb_jsonrpc_monitor_preremove_db(struct ovsdb_jsonrpc_session *,\n+                                               struct ovsdb *);\n static void ovsdb_jsonrpc_monitor_remove_all(struct ovsdb_jsonrpc_session *);\n static void ovsdb_jsonrpc_monitor_flush_all(struct ovsdb_jsonrpc_session *);\n static bool ovsdb_jsonrpc_monitor_needs_flush(struct ovsdb_jsonrpc_session *);\n@@ -157,34 +164,25 @@ ovsdb_jsonrpc_server_create(bool read_only)\n bool\n ovsdb_jsonrpc_server_add_db(struct ovsdb_jsonrpc_server *svr, struct ovsdb *db)\n {\n-    /* The OVSDB protocol doesn't have a way to notify a client that a\n-     * database has been added.  If some client tried to use the database\n-     * that we're adding and failed, then forcing it to reconnect seems like\n-     * a reasonable way to make it try again.\n-     *\n-     * If this is too big of a hammer in practice, we could be more selective,\n-     * e.g. disconnect only connections that actually tried to use a database\n-     * with 'db''s name. */\n-    ovsdb_jsonrpc_server_reconnect(svr);\n-\n+    ovsdb_jsonrpc_server_reconnect(svr, false);\n     return ovsdb_server_add_db(&svr->up, db);\n }\n \n-/* Removes 'db' from the set of databases served out by 'svr'.  Returns\n- * true if successful, false if there is no database associated with 'db'. */\n-bool\n+/* Removes 'db' from the set of databases served out by 'svr'. */\n+void\n ovsdb_jsonrpc_server_remove_db(struct ovsdb_jsonrpc_server *svr,\n                                struct ovsdb *db)\n {\n-    /* There might be pointers to 'db' from 'svr', such as monitors or\n-     * outstanding transactions.  Disconnect all JSON-RPC connections to avoid\n-     * accesses to freed memory.\n-     *\n-     * If this is too big of a hammer in practice, we could be more selective,\n-     * e.g. disconnect only connections that actually reference 'db'. */\n-    ovsdb_jsonrpc_server_reconnect(svr);\n+    struct shash_node *node;\n+    SHASH_FOR_EACH (node, &svr->remotes) {\n+        struct ovsdb_jsonrpc_remote *remote = node->data;\n+\n+        ovsdb_jsonrpc_session_preremove_db(remote, db);\n+    }\n+\n+    ovsdb_jsonrpc_server_reconnect(svr, false);\n \n-    return ovsdb_server_remove_db(&svr->up, db);\n+    ovsdb_server_remove_db(&svr->up, db);\n }\n \n void\n@@ -333,17 +331,20 @@ ovsdb_jsonrpc_server_free_remote_status(\n     free(status->locks_lost);\n }\n \n-/* Forces all of the JSON-RPC sessions managed by 'svr' to disconnect and\n- * reconnect. */\n+/* Makes all of the JSON-RPC sessions managed by 'svr' to disconnect.  (They\n+ * will then generally reconnect.).\n+ *\n+ * If 'force' is true, disconnects all sessions.  Otherwise, disconnects only\n+ * sesions that aren't database change aware. */\n void\n-ovsdb_jsonrpc_server_reconnect(struct ovsdb_jsonrpc_server *svr)\n+ovsdb_jsonrpc_server_reconnect(struct ovsdb_jsonrpc_server *svr, bool force)\n {\n     struct shash_node *node;\n \n     SHASH_FOR_EACH (node, &svr->remotes) {\n         struct ovsdb_jsonrpc_remote *remote = node->data;\n \n-        ovsdb_jsonrpc_session_reconnect_all(remote);\n+        ovsdb_jsonrpc_session_reconnect_all(remote, force);\n     }\n }\n \n@@ -359,7 +360,7 @@ ovsdb_jsonrpc_server_set_read_only(struct ovsdb_jsonrpc_server *svr,\n {\n     if (svr->read_only != read_only) {\n         svr->read_only = read_only;\n-        ovsdb_jsonrpc_server_reconnect(svr);\n+        ovsdb_jsonrpc_server_reconnect(svr, false);\n     }\n }\n \n@@ -432,6 +433,20 @@ struct ovsdb_jsonrpc_session {\n     struct ovsdb_session up;\n     struct ovsdb_jsonrpc_remote *remote;\n \n+    /* RFC 7047 does not contemplate how to alert clients to changes to the set\n+     * of databases, e.g. databases that are added or removed while the\n+     * database server is running.  Traditionally, ovsdb-server disconnects all\n+     * of its clients when this happens; a well-written client will reassess\n+     * what is available from the server upon reconnection.\n+     *\n+     * OVS 2.9 introduces a way for clients to monitor changes to the databases\n+     * being served, through the Database table in the _Server database that\n+     * OVSDB adds in this version.  ovsdb-server suppresses the connection\n+     * close for clients that identify themselves as taking advantage of this\n+     * mechanism.\n+     */\n+    bool db_change_aware;\n+\n     /* Triggers. */\n     struct hmap triggers;       /* Hmap of \"struct ovsdb_jsonrpc_trigger\"s. */\n \n@@ -478,6 +493,20 @@ ovsdb_jsonrpc_session_create(struct ovsdb_jsonrpc_remote *remote,\n     return s;\n }\n \n+/* Database 'db' is about to be removed from the database server.  To prepare,\n+ * this function removes all references to 'db' from session 's'. */\n+static void\n+ovsdb_jsonrpc_session_preremove_db(struct ovsdb_jsonrpc_remote *remote,\n+                                   struct ovsdb *db)\n+{\n+    struct ovsdb_jsonrpc_session *s;\n+\n+    LIST_FOR_EACH (s, node, &remote->sessions) {\n+        ovsdb_jsonrpc_monitor_preremove_db(s, db);\n+        ovsdb_jsonrpc_trigger_preremove_db(s, db);\n+    }\n+}\n+\n static void\n ovsdb_jsonrpc_session_close(struct ovsdb_jsonrpc_session *s)\n {\n@@ -606,17 +635,23 @@ ovsdb_jsonrpc_session_close_all(struct ovsdb_jsonrpc_remote *remote)\n     }\n }\n \n-/* Forces all of the JSON-RPC sessions managed by 'remote' to disconnect and\n- * reconnect. */\n+/* Makes all of the JSON-RPC sessions managed by 'remove' to disconnect.  (They\n+ * will then generally reconnect.).\n+ *\n+ * If 'force' is true, disconnects all sessions.  Otherwise, disconnects only\n+ * sesions that aren't database change aware. */\n static void\n-ovsdb_jsonrpc_session_reconnect_all(struct ovsdb_jsonrpc_remote *remote)\n+ovsdb_jsonrpc_session_reconnect_all(struct ovsdb_jsonrpc_remote *remote,\n+                                    bool force)\n {\n     struct ovsdb_jsonrpc_session *s, *next;\n \n     LIST_FOR_EACH_SAFE (s, next, node, &remote->sessions) {\n-        jsonrpc_session_force_reconnect(s->js);\n-        if (!jsonrpc_session_is_alive(s->js)) {\n-            ovsdb_jsonrpc_session_close(s);\n+        if (force || !s->db_change_aware) {\n+            jsonrpc_session_force_reconnect(s->js);\n+            if (!jsonrpc_session_is_alive(s->js)) {\n+                ovsdb_jsonrpc_session_close(s);\n+            }\n         }\n     }\n }\n@@ -859,6 +894,17 @@ ovsdb_jsonrpc_session_unlock__(struct ovsdb_lock_waiter *waiter)\n }\n \n static struct jsonrpc_msg *\n+syntax_error_reply(const struct jsonrpc_msg *request, const char *details)\n+{\n+    struct ovsdb_error *error = ovsdb_syntax_error(\n+        request->params, NULL, \"%s: %s\", request->method, details);\n+    struct jsonrpc_msg *msg = jsonrpc_create_error(ovsdb_error_to_json(error),\n+                                                   request->id);\n+    ovsdb_error_destroy(error);\n+    return msg;\n+}\n+\n+static struct jsonrpc_msg *\n ovsdb_jsonrpc_session_unlock(struct ovsdb_jsonrpc_session *s,\n                              struct jsonrpc_msg *request)\n {\n@@ -872,24 +918,21 @@ ovsdb_jsonrpc_session_unlock(struct ovsdb_jsonrpc_session *s,\n \n     error = ovsdb_jsonrpc_session_parse_lock_name(request, &lock_name);\n     if (error) {\n-        goto error;\n+        return jsonrpc_create_error(ovsdb_error_to_json_free(error),\n+                                    request->id);\n     }\n \n     /* Report error if this session has not issued a \"lock\" or \"steal\" for this\n      * lock. */\n     waiter = ovsdb_session_get_lock_waiter(&s->up, lock_name);\n     if (!waiter) {\n-        error = ovsdb_syntax_error(\n-            request->params, NULL, \"\\\"unlock\\\" without \\\"lock\\\" or \\\"steal\\\"\");\n-        goto error;\n+        return syntax_error_reply(request,\n+                                  \"\\\"unlock\\\" without \\\"lock\\\" or \\\"steal\\\"\");\n     }\n \n     ovsdb_jsonrpc_session_unlock__(waiter);\n \n     return jsonrpc_create_reply(json_object_create(), request->id);\n-\n-error:\n-    return jsonrpc_create_error(ovsdb_error_to_json_free(error), request->id);\n }\n \n static struct jsonrpc_msg *\n@@ -903,6 +946,21 @@ execute_transaction(struct ovsdb_jsonrpc_session *s, struct ovsdb *db,\n     return NULL;\n }\n \n+static struct jsonrpc_msg *\n+ovsdb_jsonrpc_session_set_db_change_aware(struct ovsdb_jsonrpc_session *s,\n+                                          const struct jsonrpc_msg *request)\n+{\n+    const struct json_array *params = json_array(request->params);\n+    if (params->n != 1\n+        || (params->elems[0]->type != JSON_TRUE &&\n+            params->elems[0]->type != JSON_FALSE)) {\n+        return syntax_error_reply(request, \"true or false parameter expected\");\n+    }\n+\n+    s->db_change_aware = json_boolean(params->elems[0]);\n+    return jsonrpc_create_reply(json_object_create(), request->id);\n+}\n+\n static void\n ovsdb_jsonrpc_session_got_request(struct ovsdb_jsonrpc_session *s,\n                                   struct jsonrpc_msg *request)\n@@ -963,6 +1021,8 @@ ovsdb_jsonrpc_session_got_request(struct ovsdb_jsonrpc_session *s,\n         reply = ovsdb_jsonrpc_session_lock(s, request, OVSDB_LOCK_STEAL);\n     } else if (!strcmp(request->method, \"unlock\")) {\n         reply = ovsdb_jsonrpc_session_unlock(s, request);\n+    } else if (!strcmp(request->method, \"set_db_change_aware\")) {\n+        reply = ovsdb_jsonrpc_session_set_db_change_aware(s, request);\n     } else if (!strcmp(request->method, \"echo\")) {\n         reply = jsonrpc_create_reply(json_clone(request->params), request->id);\n     } else {\n@@ -1098,14 +1158,34 @@ ovsdb_jsonrpc_trigger_complete(struct ovsdb_jsonrpc_trigger *t)\n }\n \n static void\n-ovsdb_jsonrpc_trigger_complete_all(struct ovsdb_jsonrpc_session *s)\n+ovsdb_jsonrpc_trigger_remove__(struct ovsdb_jsonrpc_session *s,\n+                                   struct ovsdb *db)\n {\n     struct ovsdb_jsonrpc_trigger *t, *next;\n     HMAP_FOR_EACH_SAFE (t, next, hmap_node, &s->triggers) {\n-        ovsdb_jsonrpc_trigger_complete(t);\n+        if (!db || t->trigger.db == db) {\n+            ovsdb_jsonrpc_trigger_complete(t);\n+        }\n     }\n }\n \n+/* Database 'db' is about to be removed from the database server.  To prepare,\n+ * this function removes all references from triggers in 's' to 'db'. */\n+static void\n+ovsdb_jsonrpc_trigger_preremove_db(struct ovsdb_jsonrpc_session *s,\n+                                   struct ovsdb *db)\n+{\n+    ovs_assert(db);\n+    ovsdb_jsonrpc_trigger_remove__(s, db);\n+}\n+\n+/* Removes all triggers from 's'. */\n+static void\n+ovsdb_jsonrpc_trigger_complete_all(struct ovsdb_jsonrpc_session *s)\n+{\n+    ovsdb_jsonrpc_trigger_remove__(s, NULL);\n+}\n+\n static void\n ovsdb_jsonrpc_trigger_complete_done(struct ovsdb_jsonrpc_session *s)\n {\n@@ -1526,15 +1606,42 @@ ovsdb_jsonrpc_monitor_cancel(struct ovsdb_jsonrpc_session *s,\n }\n \n static void\n-ovsdb_jsonrpc_monitor_remove_all(struct ovsdb_jsonrpc_session *s)\n+ovsdb_jsonrpc_monitor_remove__(struct ovsdb_jsonrpc_session *s,\n+                               struct ovsdb *db)\n {\n     struct ovsdb_jsonrpc_monitor *m, *next;\n \n     HMAP_FOR_EACH_SAFE (m, next, node, &s->monitors) {\n-        ovsdb_jsonrpc_monitor_destroy(m);\n+        if (!db || m->db == db) {\n+            if (db && jsonrpc_session_is_connected(s->js)\n+                && s->db_change_aware) {\n+                struct jsonrpc_msg *notify = jsonrpc_create_notify(\n+                    \"monitor_canceled\",\n+                    json_array_create_1(json_clone(m->monitor_id)));\n+                ovsdb_jsonrpc_session_send(s, notify);\n+            }\n+            ovsdb_jsonrpc_monitor_destroy(m);\n+        }\n     }\n }\n \n+/* Database 'db' is about to be removed from the database server.  To prepare,\n+ * this function removes all references from monitors in 's' to 'db'. */\n+static void\n+ovsdb_jsonrpc_monitor_preremove_db(struct ovsdb_jsonrpc_session *s,\n+                                   struct ovsdb *db)\n+{\n+    ovs_assert(db);\n+    ovsdb_jsonrpc_monitor_remove__(s, db);\n+}\n+\n+/* Cancels all monitors in 's'. */\n+static void\n+ovsdb_jsonrpc_monitor_remove_all(struct ovsdb_jsonrpc_session *s)\n+{\n+    ovsdb_jsonrpc_monitor_remove__(s, NULL);\n+}\n+\n static struct json *\n ovsdb_jsonrpc_monitor_compose_update(struct ovsdb_jsonrpc_monitor *m,\n                                      bool initial)\ndiff --git a/ovsdb/jsonrpc-server.h b/ovsdb/jsonrpc-server.h\nindex a3acc75f8d4f..50a8b879c5a9 100644\n--- a/ovsdb/jsonrpc-server.h\n+++ b/ovsdb/jsonrpc-server.h\n@@ -27,7 +27,7 @@ struct uuid;\n struct ovsdb_jsonrpc_server *ovsdb_jsonrpc_server_create(bool read_only);\n bool ovsdb_jsonrpc_server_add_db(struct ovsdb_jsonrpc_server *,\n                                  struct ovsdb *);\n-bool ovsdb_jsonrpc_server_remove_db(struct ovsdb_jsonrpc_server *,\n+void ovsdb_jsonrpc_server_remove_db(struct ovsdb_jsonrpc_server *,\n                                      struct ovsdb *);\n void ovsdb_jsonrpc_server_destroy(struct ovsdb_jsonrpc_server *);\n \n@@ -64,7 +64,7 @@ bool ovsdb_jsonrpc_server_get_remote_status(\n void ovsdb_jsonrpc_server_free_remote_status(\n     struct ovsdb_jsonrpc_remote_status *);\n \n-void ovsdb_jsonrpc_server_reconnect(struct ovsdb_jsonrpc_server *);\n+void ovsdb_jsonrpc_server_reconnect(struct ovsdb_jsonrpc_server *, bool force);\n \n void ovsdb_jsonrpc_server_run(struct ovsdb_jsonrpc_server *);\n void ovsdb_jsonrpc_server_wait(struct ovsdb_jsonrpc_server *);\ndiff --git a/ovsdb/ovsdb-client.c b/ovsdb/ovsdb-client.c\nindex 194ff47593f3..cecc0346eda1 100644\n--- a/ovsdb/ovsdb-client.c\n+++ b/ovsdb/ovsdb-client.c\n@@ -71,6 +71,12 @@ struct ovsdb_client_command {\n /* --timestamp: Print a timestamp before each update on \"monitor\" command? */\n static bool timestamp;\n \n+/* --db-change-aware: Enable db_change_aware feature for \"monitor\" command?\n+ *\n+ * (This option is undocumented because it is expected to be useful only for\n+ * testing that the db_change_aware feature actually works.) */\n+static bool db_change_aware;\n+\n /* Format for table output. */\n static struct table_style table_style = TABLE_STYLE_DEFAULT;\n \n@@ -182,6 +188,7 @@ parse_options(int argc, char *argv[])\n     enum {\n         OPT_BOOTSTRAP_CA_CERT = UCHAR_MAX + 1,\n         OPT_TIMESTAMP,\n+        OPT_DB_CHANGE_AWARE,\n         VLOG_OPTION_ENUMS,\n         DAEMON_OPTION_ENUMS,\n         TABLE_OPTION_ENUMS,\n@@ -191,6 +198,7 @@ parse_options(int argc, char *argv[])\n         {\"help\", no_argument, NULL, 'h'},\n         {\"version\", no_argument, NULL, 'V'},\n         {\"timestamp\", no_argument, NULL, OPT_TIMESTAMP},\n+        {\"db-change-aware\", no_argument, NULL, OPT_DB_CHANGE_AWARE},\n         VLOG_LONG_OPTIONS,\n         DAEMON_LONG_OPTIONS,\n #ifdef HAVE_OPENSSL\n@@ -233,6 +241,10 @@ parse_options(int argc, char *argv[])\n             timestamp = true;\n             break;\n \n+        case OPT_DB_CHANGE_AWARE:\n+            db_change_aware = true;\n+            break;\n+\n         case '?':\n             exit(EXIT_FAILURE);\n \n@@ -949,7 +961,6 @@ do_monitor__(struct jsonrpc *rpc, const char *database,\n     const char *table_name = argv[0];\n     struct unixctl_server *unixctl;\n     struct ovsdb_schema *schema;\n-    struct jsonrpc_msg *request;\n     struct json *monitor, *monitor_requests, *request_id;\n     bool exiting = false;\n     bool blocked = false;\n@@ -1017,11 +1028,29 @@ do_monitor__(struct jsonrpc *rpc, const char *database,\n         free(nodes);\n     }\n \n+    if (db_change_aware) {\n+        struct jsonrpc_msg *request = jsonrpc_create_request(\n+            \"set_db_change_aware\",\n+            json_array_create_1(json_boolean_create(true)),\n+            NULL);\n+        struct jsonrpc_msg *reply;\n+        int error = jsonrpc_transact_block(rpc, request, &reply);\n+        if (error) {\n+            ovs_fatal(error, \"%s: error setting db_change_aware\", server);\n+        }\n+        if (reply->type == JSONRPC_ERROR) {\n+            ovs_fatal(0, \"%s: set_db_change_aware failed (%s)\",\n+                      server, json_to_string(reply->error, 0));\n+        }\n+        jsonrpc_msg_destroy(reply);\n+    }\n+\n     monitor = json_array_create_3(json_string_create(database),\n                                   json_null_create(), monitor_requests);\n     const char *method = version == OVSDB_MONITOR_V2 ? \"monitor_cond\"\n                                                      : \"monitor\";\n \n+    struct jsonrpc_msg *request;\n     request = jsonrpc_create_request(method, monitor, NULL);\n     request_id = json_clone(request->id);\n     jsonrpc_send(rpc, request);\ndiff --git a/ovsdb/ovsdb-server.1.in b/ovsdb/ovsdb-server.1.in\nindex 02144125e67c..9bfd36edd351 100644\n--- a/ovsdb/ovsdb-server.1.in\n+++ b/ovsdb/ovsdb-server.1.in\n@@ -406,6 +406,24 @@ notifications (see below) to the request, it must be unique among all\n active monitors.  \\fBovsdb\\-server\\fR rejects attempt to create two\n monitors with the same identifier.\n .\n+.IP \"4.1.7. Monitor Cancellation\"\n+.IP\n+When a database monitored by a session is removed, and database change\n+awareness is enabled for the session (see Section 4.1.16), the\n+database server spontaneously cancels all monitors (including\n+conditional monitors described in Section 4.1.12) for the removed\n+database.  For each canceled monitor, it issues a notification in the\n+following form:\n+.\n+.PP\n+.RS\n+.nf\n+\"method\": \"monitor_canceled\"\n+\"params\": [<json-value>]\n+\"id\": null\n+.fi\n+.RE\n+.\n .IP \"4.1.12. Monitor_cond\"\n A new monitor method added in Open vSwitch version 2.6. The monitor_cond\n request enables a client to replicate subsets of tables within an OVSDB\n@@ -704,6 +722,45 @@ The response object contains the following members:\n the running OVSDB server process. A fresh UUID is generated when the\n process restarts.\n .\n+.IP \"4.1.16. Database Change Awareness\"\n+.IP\n+RFC 7047 does not provide a way for a client to find out about some\n+kinds of configuration changes, such as about databases added or\n+removed while a client is connected to the server, or databases\n+changing between read/write and read-only due to a transition between\n+active and backup roles.  Traditionally, \\fBovsdb\\-server\\fR\n+disconnects all of its clients when this happens, because this prompts\n+a well-written client to reassess what is available from the server\n+when it reconnects.\n+.IP\n+OVS 2.9 provides a way for clients to keep track of these kinds of\n+changes, by monitoring the \\fBDatabase\\fR table in the \\fB_Server\\fR\n+database introduced in this release.  See \\fBovsdb-server\\fR(5) for\n+details.  By itself, this does not suppress \\fBovsdb\\-server\\fR\n+disconnection behavior, because a client might monitor this database\n+without understanding its special semantics.  Instead,\n+\\fBovsdb\\-server\\fR provides a special request:\n+.PP\n+.RS\n+.nf\n+\"method\": \"set_db_change_aware\"\n+\"params\": [<boolean>]\n+\"id\": <nonnull-json-value>\n+.fi\n+.RE\n+.IP\n+If the boolean in the request is true, it suppresses the\n+connection-closing behavior for the current connection, and false\n+restores the default behavior.  The reply is always the same:\n+.PP\n+.RS\n+.nf\n+\"result\": {}\n+\"error\": null\n+\"id\": same \"id\" as request\n+.fi\n+.RE\n+.\n .IP \"5.1. Notation\"\n For <condition>, RFC 7047 only allows the use of \\fB!=\\fR, \\fB==\\fR,\n \\fBincludes\\fR, and \\fBexcludes\\fR operators with set types.  Open\ndiff --git a/ovsdb/ovsdb-server.c b/ovsdb/ovsdb-server.c\nindex 52eb21f89aa0..e28fca5989fc 100644\n--- a/ovsdb/ovsdb-server.c\n+++ b/ovsdb/ovsdb-server.c\n@@ -1227,7 +1227,7 @@ ovsdb_server_disable_monitor_cond(struct unixctl_conn *conn,\n     struct ovsdb_jsonrpc_server *jsonrpc = jsonrpc_;\n \n     ovsdb_jsonrpc_disable_monitor_cond();\n-    ovsdb_jsonrpc_server_reconnect(jsonrpc);\n+    ovsdb_jsonrpc_server_reconnect(jsonrpc, true);\n     unixctl_command_reply(conn, NULL);\n }\n \n@@ -1284,7 +1284,7 @@ ovsdb_server_reconnect(struct unixctl_conn *conn, int argc OVS_UNUSED,\n                        const char *argv[] OVS_UNUSED, void *jsonrpc_)\n {\n     struct ovsdb_jsonrpc_server *jsonrpc = jsonrpc_;\n-    ovsdb_jsonrpc_server_reconnect(jsonrpc);\n+    ovsdb_jsonrpc_server_reconnect(jsonrpc, true);\n     unixctl_command_reply(conn, NULL);\n }\n \n@@ -1386,12 +1386,9 @@ ovsdb_server_add_database(struct unixctl_conn *conn, int argc OVS_UNUSED,\n static void\n remove_db(struct server_config *config, struct shash_node *node)\n {\n-    struct db *db;\n-    bool ok;\n+    struct db *db = node->data;\n \n-    db = node->data;\n-    ok = ovsdb_jsonrpc_server_remove_db(config->jsonrpc, db->db);\n-    ovs_assert(ok);\n+    ovsdb_jsonrpc_server_remove_db(config->jsonrpc, db->db);\n \n     close_db(db);\n     shash_delete(config->all_dbs, node);\ndiff --git a/tests/ovsdb-server.at b/tests/ovsdb-server.at\nindex ccbc1ac0b717..1ad49e64139f 100644\n--- a/tests/ovsdb-server.at\n+++ b/tests/ovsdb-server.at\n@@ -179,7 +179,7 @@ AT_CLEANUP\n \n AT_SETUP([ovsdb-server/add-db and remove-db])\n AT_KEYWORDS([ovsdb server positive])\n-on_exit 'kill `cat ovsdb-server.pid`'\n+on_exit 'kill `cat *.pid`'\n ordinal_schema > schema1\n constraint_schema > schema2\n AT_CHECK([ovsdb-tool create db1 schema1], [0], [ignore], [ignore])\n@@ -190,6 +190,19 @@ AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --remote=punix:db.sock db1]\n CHECK_DBS([ordinals\n ])\n \n+# Remove the database.\n+AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/remove-db ordinals], [0])\n+CHECK_DBS([])\n+\n+# Start monitoring processes.\n+AT_CHECK([ovsdb-client --detach --pidfile=ovsdb-client-1.pid --no-headings monitor _Server Database name > db-changes-unaware])\n+AT_CHECK([ovsdb-client --detach --pidfile=ovsdb-client-2.pid --db-change-aware --no-headings monitor _Server Database name > db-changes-aware])\n+\n+# Add the first database back.\n+AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/add-db db1], [0])\n+CHECK_DBS([ordinals\n+])\n+\n # Add the second database.\n AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/add-db db2], [0])\n CHECK_DBS([constraints\n@@ -253,6 +266,25 @@ AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/add-db db2], [0])\n CHECK_DBS([constraints\n ])\n AT_CHECK([ovsdb-client list-tables unix:db.sock constraints], [0], [ignore], [ignore])\n+\n+# Check the monitoring results.\n+AT_CHECK([${PERL} $srcdir/uuidfilt.pl db-changes-aware], [0], [dnl\n+<0> initial _Server\n+\n+<1> insert ordinals\n+\n+<2> insert constraints\n+\n+<1> delete ordinals\n+\n+<2> delete constraints\n+\n+<3> insert constraints\n+])\n+AT_CHECK([${PERL} $srcdir/uuidfilt.pl db-changes-unaware], [0], [dnl\n+<0> initial _Server\n+])\n+\n OVS_APP_EXIT_AND_WAIT([ovsdb-server])\n AT_CLEANUP\n \n","prefixes":["ovs-dev","RFC","44/52"]}