[ovs-dev] [PATCH 3/7] replication: Allow replication of _Server database.

Ilya Maximets i.maximets at ovn.org
Sat May 1 00:55:44 UTC 2021


If replication server is connected to a cluster member, clients
would like to know the state of the cluster from that server.
This will allow them to make a decision about re-connection.

Marking 'Database' table with a 'copyForReplication' flag to
have a new '_synced_Database' table where records from the
active server will be stored.  This way client will see state
of local databases in 'Database' table and state of databases
of the active server in '_synced_Database' table.

Signed-off-by: Ilya Maximets <i.maximets at ovn.org>
---
 Documentation/ref/ovsdb-server.7.rst |  3 +-
 NEWS                                 |  4 ++
 ovsdb/_server.ovsschema              |  5 ++-
 ovsdb/_server.xml                    | 10 +++++
 ovsdb/ovsdb-server.c                 |  2 +-
 ovsdb/replication.c                  | 66 +++++++++++++++++++++++++---
 tests/ovsdb-server.at                |  5 +++
 7 files changed, 86 insertions(+), 9 deletions(-)

diff --git a/Documentation/ref/ovsdb-server.7.rst b/Documentation/ref/ovsdb-server.7.rst
index 717f62d81..28686eb44 100644
--- a/Documentation/ref/ovsdb-server.7.rst
+++ b/Documentation/ref/ovsdb-server.7.rst
@@ -109,7 +109,8 @@ by setting ``copyForReplication`` flag to ``true``.  For each table marked
 with this flag, ``ovsdb-server`` will create one more table with the same
 name and ``_synced_`` prefix (e.g., ``_synced_<table-name>``).  Server in a
 backup role will keep its own content in the original table and will put
-data, received from the active server, to this special table.
+data, received from the active server, to this special table.  Currently
+used to replicate ``Database`` table from a ``_Server`` database.
 
 4 Wire Protocol
 ---------------
diff --git a/NEWS b/NEWS
index 790f93af9..d2b12b23c 100644
--- a/NEWS
+++ b/NEWS
@@ -4,6 +4,10 @@ Post-v2.15.0
      * New command line argument '--enable-txn-forward' for ovsdb-server in
        replication mode that allows to forward transactions that includes
        database modifications to the primary database server.
+     * Replication (backup) server now replicates a _Server database from the
+       replication source (active server).  Data from the 'Database' table of
+       the active server stored in '_synced_Database' table on backup and
+       available for monitoring.
    - In ovs-vsctl and vtep-ctl, the "find" command now accept new
      operators {in} and {not-in}.
    - Userspace datapath:
diff --git a/ovsdb/_server.ovsschema b/ovsdb/_server.ovsschema
index a867e5cbf..ad31a7f7e 100644
--- a/ovsdb/_server.ovsschema
+++ b/ovsdb/_server.ovsschema
@@ -1,6 +1,6 @@
 {"name": "_Server",
- "version": "1.1.0",
- "cksum": "3236486585 698",
+ "version": "1.2.0",
+ "cksum": "88700080 731",
  "tables": {
    "Database": {
      "columns": {
@@ -18,4 +18,5 @@
          "type": {"key": {"type": "uuid"}, "min": 0, "max": 1}},
        "index": {
          "type": {"key": {"type": "integer"}, "min": 0, "max": 1}}},
+     "copyForReplication": true,
      "isRoot": true}}}
diff --git a/ovsdb/_server.xml b/ovsdb/_server.xml
index 70cd22db7..2e8b3393d 100644
--- a/ovsdb/_server.xml
+++ b/ovsdb/_server.xml
@@ -54,6 +54,16 @@
       to the server causes its row <code>_uuid</code> to change.
     </p>
 
+    <p>
+      This table is marked with <code>copyForReplication</code> flag.  This
+      means that <code>ovsdb-server</code> will create one more table with
+      name <code>_synced_Database</code> for replication purposes.  Backup
+      database in active-backup model will replicate content of this table
+      from the active database and store it in <code>_synced_Database</code>
+      table.  This way clients are able to monitor the state of the active
+      server, e.g., check the status of clustered databases.
+    </p>
+
     <column name="name">
       The database's name, as specified in its schema.
     </column>
diff --git a/ovsdb/ovsdb-server.c b/ovsdb/ovsdb-server.c
index c9d21af1c..89780dac8 100644
--- a/ovsdb/ovsdb-server.c
+++ b/ovsdb/ovsdb-server.c
@@ -160,7 +160,7 @@ ovsdb_replication_init(const char *sync_from, const char *exclude,
     struct shash_node *node;
     SHASH_FOR_EACH (node, all_dbs) {
         struct db *db = node->data;
-        if (node->name[0] != '_' && db->db) {
+        if (db->db) {
             replication_add_local_db(node->name, db->db);
         }
     }
diff --git a/ovsdb/replication.c b/ovsdb/replication.c
index 543881843..5aac325f2 100644
--- a/ovsdb/replication.c
+++ b/ovsdb/replication.c
@@ -49,6 +49,7 @@ static void add_monitored_table(struct ovsdb_table_schema *table,
                                 struct json *monitor_requests);
 
 static struct ovsdb_error *reset_database(struct ovsdb *db);
+static void reset_internal_databases(void);
 
 static struct ovsdb_error *process_notification(struct json *, struct ovsdb *);
 static struct ovsdb_error *process_table_update(struct json *table_update,
@@ -104,6 +105,7 @@ static enum ovsdb_replication_state state;
 struct replication_db {
     struct ovsdb *db;
     bool schema_version_higher;
+    bool internal;  /* True if the database name starts with '_'. */
      /* Points to the schema received from the active server if
       * the local db schema version is higher. NULL otherwise. */
     struct ovsdb_schema *active_db_schema;
@@ -118,6 +120,10 @@ static bool is_replication_possible(struct ovsdb_schema *local_db_schema,
 static struct shash local_dbs = SHASH_INITIALIZER(&local_dbs);
 static struct shash *replication_dbs;
 
+/* Number of internal databases in 'replication_dbs', i.e. databases which
+ * name starts with '_'. */
+static int n_internal_dbs = 0;
+
 static struct shash *replication_dbs_create(void);
 static void replication_dbs_destroy(void);
 /* Find 'struct ovsdb' by name within 'replication_dbs' */
@@ -134,6 +140,7 @@ replication_init(const char *sync_from_, const char *exclude_tables,
      * parseable. An error here is unexpected. */
     ovs_assert(!set_excluded_tables(exclude_tables, false));
 
+    reset_internal_databases();
     replication_dbs_destroy();
 
     shash_clear(&local_dbs);
@@ -337,6 +344,9 @@ replication_run(void)
                         if (r->active_db_schema) {
                             ovsdb_schema_destroy(r->active_db_schema);
                         }
+                        if (r->internal) {
+                            n_internal_dbs--;
+                        }
                         free(r);
                         ovsdb_schema_destroy(schema);
                     }
@@ -349,8 +359,10 @@ replication_run(void)
                 if (hmap_is_empty(&request_ids)) {
                     struct shash_node *node;
 
-                    if (shash_is_empty(replication_dbs)) {
+                    if (shash_is_empty(replication_dbs) ||
+                        n_internal_dbs == shash_count(replication_dbs)) {
                         VLOG_WARN("Nothing to replicate.");
+                        replication_dbs_destroy();
                         state = RPL_S_ERR;
                     } else {
                         SHASH_FOR_EACH (node, replication_dbs) {
@@ -552,6 +564,8 @@ disconnect_active_server(void)
     session = NULL;
     /* Cancel all pending transactions. */
     ovsdb_txn_forward_cancel_all(false);
+    /* Clear data from '_synced_' tables. */
+    reset_internal_databases();
 }
 
 void
@@ -586,9 +600,12 @@ reset_database(struct ovsdb *db)
     struct shash_node *table_node;
 
     SHASH_FOR_EACH (table_node, &db->tables) {
-        /* Delete all rows if the table is not excluded. */
-        if (!excluded_tables_find(db->schema->name, table_node->name)) {
-            struct ovsdb_table *table = table_node->data;
+        struct ovsdb_table *table = table_node->data;
+
+        /* Delete all rows if the table is not excluded and if it has no
+         * special '_synced_' copy. */
+        if (!excluded_tables_find(db->schema->name, table_node->name)
+            && !table->schema->copy_for_replication) {
             struct ovsdb_row *row, *next;
             HMAP_FOR_EACH_SAFE (row, next, hmap_node, &table->rows) {
                 ovsdb_txn_row_delete(txn, row);
@@ -599,6 +616,26 @@ reset_database(struct ovsdb *db)
     return ovsdb_txn_propose_commit_block(txn, false);
 }
 
+static void
+reset_internal_databases(void)
+{
+    if (!replication_dbs) {
+        return;
+    }
+
+    struct shash_node *node;
+
+    SHASH_FOR_EACH (node, replication_dbs) {
+        struct replication_db *rdb = node->data;
+
+        if (rdb->internal) {
+            /* Cleaning up data from '_synced_' tables of internal DBs. */
+            VLOG_INFO("Resetting internal database %s", node->name);
+            ovsdb_error_assert(reset_database(rdb->db));
+        }
+    }
+}
+
 /* Create a monitor request for 'db'. The monitor request will include
  * any tables from 'excluded_tables'
  *
@@ -617,9 +654,11 @@ create_monitor_request(struct ovsdb_schema *schema)
 
     for (int j = 0; j < n; j++) {
         struct ovsdb_table_schema *table = nodes[j]->data;
+        /* Not monitoring data from replicated copies of tables. */
+        bool skip = !strncmp(table->name, "_synced_", 8);
 
         /* Monitor all tables not excluded. */
-        if (!excluded_tables_find(db_name, table->name)) {
+        if (!excluded_tables_find(db_name, table->name) && !skip) {
             add_monitored_table(table, monitor_request);
         }
     }
@@ -690,6 +729,18 @@ process_table_update(struct json *table_update, const char *table_name,
         return ovsdb_error("unknown table", "unknown table %s", table_name);
     }
 
+    if (table->schema->copy_for_replication) {
+        /* Data from this table should go to special '_synced_<name>' table. */
+        char *name = xasprintf("_synced_%s", table_name);
+
+        table = ovsdb_get_table(database, name);
+        free(name);
+        if (!table) {
+            return ovsdb_error("unknown table",
+                               "unknown table _synced_%s", table_name);
+        }
+    }
+
     if (table_update->type != JSON_OBJECT) {
         return ovsdb_error("Not a JSON object",
                            "<table-update> for table is not object");
@@ -853,7 +904,11 @@ replication_dbs_create(void)
         repl_db->db = node->data;
         repl_db->schema_version_higher = false;
         repl_db->active_db_schema = NULL;
+        repl_db->internal = (node->name[0] == '_');
         shash_add(new, node->name, repl_db);
+        if (repl_db->internal) {
+            n_internal_dbs++;
+        }
     }
 
     return new;
@@ -882,6 +937,7 @@ replication_dbs_destroy(void)
     hmap_destroy(&replication_dbs->map);
     free(replication_dbs);
     replication_dbs = NULL;
+    n_internal_dbs = 0;
 }
 
 /* Return true if replication just started or is ongoing.
diff --git a/tests/ovsdb-server.at b/tests/ovsdb-server.at
index c718f7e2a..378e07372 100644
--- a/tests/ovsdb-server.at
+++ b/tests/ovsdb-server.at
@@ -1502,6 +1502,11 @@ m4_define([OVSDB_CHECK_EXECUTION],
    AT_CHECK([ovsdb-client dump], [0], [stdout], [ignore])
    OVS_WAIT_UNTIL([ ovsdb-client dump unix:db2.sock > dump2; diff stdout dump2])
 
+   AT_CHECK([ovsdb-client dump unix:db2.sock _Server _synced_Database | dnl
+                          sed 's/_synced_Database/Database/' > synced_Server ])
+   AT_CHECK([ovsdb-client dump unix:db.sock  _Server Database > Server])
+   AT_CHECK([diff synced_Server Server])
+
    OVSDB_SERVER_SHUTDOWN
    OVSDB_SERVER_SHUTDOWN2
    AT_CLEANUP])
-- 
2.26.3



More information about the dev mailing list