9219d1
From ddbdb15a4b6050d9c667ca2bc546a118e208a342 Mon Sep 17 00:00:00 2001
9219d1
From: Han Zhou <hzhou@ovn.org>
9219d1
Date: Thu, 30 Jul 2020 23:18:58 -0700
9219d1
Subject: [PATCH 22/22] Avoid nb_cfg update notification flooding
9219d1
9219d1
nb_cfg as a mechanism to "ping" OVN control plane is very useful
9219d1
in many ways. However, the current implementation will trigger
9219d1
update notifications flooding in the whole control plane. Each
9219d1
HV updates to SB the nb_cfg number and all these updates are
9219d1
notified to all the other HVs, which is O(n^2). Although updates
9219d1
are batched in fewers notifications than n^2, it still generates
9219d1
significant load on SB DB and ovn-controllers.
9219d1
9219d1
To solve this problem and make the mechanism more useful in large
9219d1
scale producation deployment, this patch separates the per HV
9219d1
*private* data (write only by the owning chassis and not
9219d1
interesting to any other HVs) from the Chassis table to a separate
9219d1
table, so that each HV can conditionally monitor and get updates
9219d1
only for its own record.
9219d1
9219d1
Test result shows great improvement:
9219d1
In a test environment with 1200 sandbox HVs, and 12K ports created
9219d1
on 80 lswitches and 1 lrouter, do the sync test when the system
9219d1
is idle, with command:
9219d1
9219d1
    time ovn-nbctl --wait=hv sync
9219d1
9219d1
Original result:
9219d1
real    0m13.724s
9219d1
user    0m0.295s
9219d1
sys     0m0.012s
9219d1
9219d1
With this patch:
9219d1
real    0m3.255s
9219d1
user    0m0.248s
9219d1
sys     0m0.020s
9219d1
9219d1
Also, regarding backwards compatibility note that the nb_cfg from the
9219d1
Chassis table is no longer updated. If any system is relying on this
9219d1
mechanism they should start using the nb_cfg from the Chassis_Private
9219d1
table from now on.
9219d1
9219d1
Change-Id: I9be2449f3317ff6b91d9afc8f53a9caa8e14c062
9219d1
Co-authored-by: Lucas Alvares Gomes <lucasagomes@gmail.com>
9219d1
Signed-off-by: Lucas Alvares Gomes <lucasagomes@gmail.com>
9219d1
Signed-off-by: Han Zhou <hzhou@ovn.org>
9219d1
Acked-by: Dumitru Ceara <dceara@redhat.com>
9219d1
9219d1
(cherry-picked from upstream master commit 4adc10f58127e45b5883f2e7cb1c702720b95043)
9219d1
---
9219d1
 controller/chassis.c        | 30 ++++++++++++++++++++----
9219d1
 controller/chassis.h        |  8 +++++--
9219d1
 controller/ovn-controller.c | 42 ++++++++++++++++++++++++++++-----
9219d1
 lib/chassis-index.c         | 26 +++++++++++++++++++++
9219d1
 lib/chassis-index.h         |  6 +++++
9219d1
 northd/ovn-northd.c         | 46 +++++++++++++++++++++++++++++++------
9219d1
 ovn-sb.ovsschema            | 17 ++++++++++++--
9219d1
 ovn-sb.xml                  | 42 +++++++++++++++++++++++++++++----
9219d1
 tests/ovn-controller.at     | 26 +++++++++++++++++++++
9219d1
 9 files changed, 218 insertions(+), 25 deletions(-)
9219d1
9219d1
diff --git a/controller/chassis.c b/controller/chassis.c
9219d1
index bdf3fb950..6ac591e02 100644
9219d1
--- a/controller/chassis.c
9219d1
+++ b/controller/chassis.c
9219d1
@@ -621,14 +621,18 @@ chassis_update(const struct sbrec_chassis *chassis_rec,
9219d1
 const struct sbrec_chassis *
9219d1
 chassis_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
9219d1
             struct ovsdb_idl_index *sbrec_chassis_by_name,
9219d1
+            struct ovsdb_idl_index *sbrec_chassis_private_by_name,
9219d1
             const struct ovsrec_open_vswitch_table *ovs_table,
9219d1
             const struct sbrec_chassis_table *chassis_table,
9219d1
             const char *chassis_id,
9219d1
             const struct ovsrec_bridge *br_int,
9219d1
-            const struct sset *transport_zones)
9219d1
+            const struct sset *transport_zones,
9219d1
+            const struct sbrec_chassis_private **chassis_private)
9219d1
 {
9219d1
     struct ovs_chassis_cfg ovs_cfg;
9219d1
 
9219d1
+    *chassis_private = NULL;
9219d1
+
9219d1
     /* Get the chassis config from the ovs table. */
9219d1
     ovs_chassis_cfg_init(&ovs_cfg);
9219d1
     if (!chassis_parse_ovs_config(ovs_table, br_int, &ovs_cfg)) {
9219d1
@@ -655,6 +659,18 @@ chassis_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
9219d1
                                       !existed ? "registering" : "updating",
9219d1
                                       chassis_id);
9219d1
         }
9219d1
+
9219d1
+        const struct sbrec_chassis_private *chassis_private_rec =
9219d1
+            chassis_private_lookup_by_name(sbrec_chassis_private_by_name,
9219d1
+                                           chassis_id);
9219d1
+        if (!chassis_private_rec && ovnsb_idl_txn) {
9219d1
+            chassis_private_rec = sbrec_chassis_private_insert(ovnsb_idl_txn);
9219d1
+            sbrec_chassis_private_set_name(chassis_private_rec,
9219d1
+                                           chassis_id);
9219d1
+            sbrec_chassis_private_set_chassis(chassis_private_rec,
9219d1
+                                              chassis_rec);
9219d1
+        }
9219d1
+        *chassis_private = chassis_private_rec;
9219d1
     }
9219d1
 
9219d1
     ovs_chassis_cfg_destroy(&ovs_cfg);
9219d1
@@ -710,16 +726,22 @@ chassis_get_mac(const struct sbrec_chassis *chassis_rec,
9219d1
  * required. */
9219d1
 bool
9219d1
 chassis_cleanup(struct ovsdb_idl_txn *ovnsb_idl_txn,
9219d1
-                const struct sbrec_chassis *chassis_rec)
9219d1
+                const struct sbrec_chassis *chassis_rec,
9219d1
+                const struct sbrec_chassis_private *chassis_private_rec)
9219d1
 {
9219d1
-    if (!chassis_rec) {
9219d1
+    if (!chassis_rec && !chassis_private_rec) {
9219d1
         return true;
9219d1
     }
9219d1
     if (ovnsb_idl_txn) {
9219d1
         ovsdb_idl_txn_add_comment(ovnsb_idl_txn,
9219d1
                                   "ovn-controller: unregistering chassis '%s'",
9219d1
                                   chassis_rec->name);
9219d1
-        sbrec_chassis_delete(chassis_rec);
9219d1
+        if (chassis_rec) {
9219d1
+            sbrec_chassis_delete(chassis_rec);
9219d1
+        }
9219d1
+        if (chassis_private_rec) {
9219d1
+            sbrec_chassis_private_delete(chassis_private_rec);
9219d1
+        }
9219d1
     }
9219d1
     return false;
9219d1
 }
9219d1
diff --git a/controller/chassis.h b/controller/chassis.h
9219d1
index 178d2957e..81055b403 100644
9219d1
--- a/controller/chassis.h
9219d1
+++ b/controller/chassis.h
9219d1
@@ -17,6 +17,7 @@
9219d1
 #define OVN_CHASSIS_H 1
9219d1
 
9219d1
 #include <stdbool.h>
9219d1
+#include "lib/ovn-sb-idl.h"
9219d1
 
9219d1
 struct ovsdb_idl;
9219d1
 struct ovsdb_idl_index;
9219d1
@@ -33,12 +34,15 @@ void chassis_register_ovs_idl(struct ovsdb_idl *);
9219d1
 const struct sbrec_chassis *chassis_run(
9219d1
     struct ovsdb_idl_txn *ovnsb_idl_txn,
9219d1
     struct ovsdb_idl_index *sbrec_chassis_by_name,
9219d1
+    struct ovsdb_idl_index *sbrec_chassis_private_by_name,
9219d1
     const struct ovsrec_open_vswitch_table *,
9219d1
     const struct sbrec_chassis_table *,
9219d1
     const char *chassis_id, const struct ovsrec_bridge *br_int,
9219d1
-    const struct sset *transport_zones);
9219d1
+    const struct sset *transport_zones,
9219d1
+    const struct sbrec_chassis_private **chassis_private);
9219d1
 bool chassis_cleanup(struct ovsdb_idl_txn *ovnsb_idl_txn,
9219d1
-                     const struct sbrec_chassis *);
9219d1
+                     const struct sbrec_chassis *,
9219d1
+                     const struct sbrec_chassis_private *);
9219d1
 bool chassis_get_mac(const struct sbrec_chassis *chassis,
9219d1
                      const char *bridge_mapping,
9219d1
                      struct eth_addr *chassis_mac);
9219d1
diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
9219d1
index 67b3cd989..933acf676 100644
9219d1
--- a/controller/ovn-controller.c
9219d1
+++ b/controller/ovn-controller.c
9219d1
@@ -155,6 +155,7 @@ update_sb_monitors(struct ovsdb_idl *ovnsb_idl,
9219d1
     struct ovsdb_idl_condition ce =  OVSDB_IDL_CONDITION_INIT(&ce);
9219d1
     struct ovsdb_idl_condition ip_mcast = OVSDB_IDL_CONDITION_INIT(&ip_mcast);
9219d1
     struct ovsdb_idl_condition igmp = OVSDB_IDL_CONDITION_INIT(&igmp);
9219d1
+    struct ovsdb_idl_condition chprv = OVSDB_IDL_CONDITION_INIT(&chprv);
9219d1
 
9219d1
     if (monitor_all) {
9219d1
         ovsdb_idl_condition_add_clause_true(&pb;;
9219d1
@@ -165,6 +166,7 @@ update_sb_monitors(struct ovsdb_idl *ovnsb_idl,
9219d1
         ovsdb_idl_condition_add_clause_true(&ce);
9219d1
         ovsdb_idl_condition_add_clause_true(&ip_mcast);
9219d1
         ovsdb_idl_condition_add_clause_true(&igmp);
9219d1
+        ovsdb_idl_condition_add_clause_true(&chprv);
9219d1
         goto out;
9219d1
     }
9219d1
 
9219d1
@@ -196,7 +198,16 @@ update_sb_monitors(struct ovsdb_idl *ovnsb_idl,
9219d1
                                                   &chassis->header_.uuid);
9219d1
         sbrec_igmp_group_add_clause_chassis(&igmp, OVSDB_F_EQ,
9219d1
                                             &chassis->header_.uuid);
9219d1
+
9219d1
+        /* Monitors Chassis_Private record for current chassis only */
9219d1
+        sbrec_chassis_private_add_clause_name(&chprv, OVSDB_F_EQ,
9219d1
+                                              chassis->name);
9219d1
+    } else {
9219d1
+        /* During initialization, we monitor all records in Chassis_Private so
9219d1
+         * that we don't try to recreate existing ones. */
9219d1
+        ovsdb_idl_condition_add_clause_true(&chprv);
9219d1
     }
9219d1
+
9219d1
     if (local_ifaces) {
9219d1
         const char *name;
9219d1
         SSET_FOR_EACH (name, local_ifaces) {
9219d1
@@ -229,6 +240,7 @@ out:
9219d1
     sbrec_controller_event_set_condition(ovnsb_idl, &ce);
9219d1
     sbrec_ip_multicast_set_condition(ovnsb_idl, &ip_mcast);
9219d1
     sbrec_igmp_group_set_condition(ovnsb_idl, &igmp);
9219d1
+    sbrec_chassis_private_set_condition(ovnsb_idl, &chprv);
9219d1
     ovsdb_idl_condition_destroy(&pb;;
9219d1
     ovsdb_idl_condition_destroy(&lf);
9219d1
     ovsdb_idl_condition_destroy(&mb;;
9219d1
@@ -237,6 +249,7 @@ out:
9219d1
     ovsdb_idl_condition_destroy(&ce);
9219d1
     ovsdb_idl_condition_destroy(&ip_mcast);
9219d1
     ovsdb_idl_condition_destroy(&igmp);
9219d1
+    ovsdb_idl_condition_destroy(&chprv);
9219d1
 }
9219d1
 
9219d1
 static const char *
9219d1
@@ -2090,6 +2103,8 @@ main(int argc, char *argv[])
9219d1
 
9219d1
     struct ovsdb_idl_index *sbrec_chassis_by_name
9219d1
         = chassis_index_create(ovnsb_idl_loop.idl);
9219d1
+    struct ovsdb_idl_index *sbrec_chassis_private_by_name
9219d1
+        = chassis_private_index_create(ovnsb_idl_loop.idl);
9219d1
     struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath
9219d1
         = mcast_group_index_create(ovnsb_idl_loop.idl);
9219d1
     struct ovsdb_idl_index *sbrec_logical_flow_by_logical_datapath
9219d1
@@ -2118,7 +2133,8 @@ main(int argc, char *argv[])
9219d1
         = igmp_group_index_create(ovnsb_idl_loop.idl);
9219d1
 
9219d1
     ovsdb_idl_track_add_all(ovnsb_idl_loop.idl);
9219d1
-    ovsdb_idl_omit_alert(ovnsb_idl_loop.idl, &sbrec_chassis_col_nb_cfg);
9219d1
+    ovsdb_idl_omit_alert(ovnsb_idl_loop.idl,
9219d1
+                         &sbrec_chassis_private_col_nb_cfg);
9219d1
 
9219d1
     /* Omit the external_ids column of all the tables except for -
9219d1
      *  - DNS. pinctrl.c uses the external_ids column of DNS,
9219d1
@@ -2155,6 +2171,10 @@ main(int argc, char *argv[])
9219d1
      * other_config column so we no longer need to monitor it */
9219d1
     ovsdb_idl_omit_alert(ovnsb_idl_loop.idl, &sbrec_chassis_col_external_ids);
9219d1
 
9219d1
+    /* Do not monitor Chassis_Private external_ids */
9219d1
+    ovsdb_idl_omit(ovnsb_idl_loop.idl,
9219d1
+                   &sbrec_chassis_private_col_external_ids);
9219d1
+
9219d1
     update_sb_monitors(ovnsb_idl_loop.idl, NULL, NULL, NULL, false);
9219d1
 
9219d1
     stopwatch_create(CONTROLLER_LOOP_STOPWATCH_NAME, SW_MS);
9219d1
@@ -2361,10 +2381,13 @@ main(int argc, char *argv[])
9219d1
                 process_br_int(ovs_idl_txn, bridge_table, ovs_table);
9219d1
             const char *chassis_id = get_ovs_chassis_id(ovs_table);
9219d1
             const struct sbrec_chassis *chassis = NULL;
9219d1
+            const struct sbrec_chassis_private *chassis_private = NULL;
9219d1
             if (chassis_id) {
9219d1
                 chassis = chassis_run(ovnsb_idl_txn, sbrec_chassis_by_name,
9219d1
+                                      sbrec_chassis_private_by_name,
9219d1
                                       ovs_table, chassis_table, chassis_id,
9219d1
-                                      br_int, &transport_zones);
9219d1
+                                      br_int, &transport_zones,
9219d1
+                                      &chassis_private);
9219d1
             }
9219d1
 
9219d1
             if (br_int) {
9219d1
@@ -2489,10 +2512,10 @@ main(int argc, char *argv[])
9219d1
                 engine_set_force_recompute(false);
9219d1
             }
9219d1
 
9219d1
-            if (ovnsb_idl_txn && chassis) {
9219d1
+            if (ovnsb_idl_txn && chassis_private) {
9219d1
                 int64_t cur_cfg = ofctrl_get_cur_cfg();
9219d1
-                if (cur_cfg && cur_cfg != chassis->nb_cfg) {
9219d1
-                    sbrec_chassis_set_nb_cfg(chassis, cur_cfg);
9219d1
+                if (cur_cfg && cur_cfg != chassis_private->nb_cfg) {
9219d1
+                    sbrec_chassis_private_set_nb_cfg(chassis_private, cur_cfg);
9219d1
                 }
9219d1
             }
9219d1
 
9219d1
@@ -2595,10 +2618,17 @@ main(int argc, char *argv[])
9219d1
                    ? chassis_lookup_by_name(sbrec_chassis_by_name, chassis_id)
9219d1
                    : NULL);
9219d1
 
9219d1
+            const struct sbrec_chassis_private *chassis_private
9219d1
+                = (chassis_id
9219d1
+                   ? chassis_private_lookup_by_name(
9219d1
+                         sbrec_chassis_private_by_name, chassis_id)
9219d1
+                   : NULL);
9219d1
+
9219d1
             /* Run all of the cleanup functions, even if one of them returns
9219d1
              * false. We're done if all of them return true. */
9219d1
             done = binding_cleanup(ovnsb_idl_txn, port_binding_table, chassis);
9219d1
-            done = chassis_cleanup(ovnsb_idl_txn, chassis) && done;
9219d1
+            done = chassis_cleanup(ovnsb_idl_txn,
9219d1
+                                   chassis, chassis_private) && done;
9219d1
             done = encaps_cleanup(ovs_idl_txn, br_int) && done;
9219d1
             done = igmp_group_cleanup(ovnsb_idl_txn, sbrec_igmp_group) && done;
9219d1
             if (done) {
9219d1
diff --git a/lib/chassis-index.c b/lib/chassis-index.c
9219d1
index 39066f4cc..13120fe3e 100644
9219d1
--- a/lib/chassis-index.c
9219d1
+++ b/lib/chassis-index.c
9219d1
@@ -40,6 +40,32 @@ chassis_lookup_by_name(struct ovsdb_idl_index *sbrec_chassis_by_name,
9219d1
     return retval;
9219d1
 }
9219d1
 
9219d1
+struct ovsdb_idl_index *
9219d1
+chassis_private_index_create(struct ovsdb_idl *idl)
9219d1
+{
9219d1
+    return ovsdb_idl_index_create1(idl,
9219d1
+                                   &sbrec_chassis_private_col_name);
9219d1
+}
9219d1
+
9219d1
+/* Finds and returns the chassis with the given 'name', or NULL if no such
9219d1
+ * chassis exists. */
9219d1
+const struct sbrec_chassis_private *
9219d1
+chassis_private_lookup_by_name(
9219d1
+    struct ovsdb_idl_index *sbrec_chassis_private_by_name,
9219d1
+    const char *name)
9219d1
+{
9219d1
+    struct sbrec_chassis_private *target =
9219d1
+        sbrec_chassis_private_index_init_row(sbrec_chassis_private_by_name);
9219d1
+    sbrec_chassis_private_index_set_name(target, name);
9219d1
+
9219d1
+    struct sbrec_chassis_private *retval = sbrec_chassis_private_index_find(
9219d1
+        sbrec_chassis_private_by_name, target);
9219d1
+
9219d1
+    sbrec_chassis_private_index_destroy_row(target);
9219d1
+
9219d1
+    return retval;
9219d1
+}
9219d1
+
9219d1
 struct ovsdb_idl_index *
9219d1
 ha_chassis_group_index_create(struct ovsdb_idl *idl)
9219d1
 {
9219d1
diff --git a/lib/chassis-index.h b/lib/chassis-index.h
9219d1
index 302e5f0fd..b9b331f34 100644
9219d1
--- a/lib/chassis-index.h
9219d1
+++ b/lib/chassis-index.h
9219d1
@@ -23,6 +23,12 @@ struct ovsdb_idl_index *chassis_index_create(struct ovsdb_idl *);
9219d1
 const struct sbrec_chassis *chassis_lookup_by_name(
9219d1
     struct ovsdb_idl_index *sbrec_chassis_by_name, const char *name);
9219d1
 
9219d1
+struct ovsdb_idl_index *chassis_private_index_create(struct ovsdb_idl *);
9219d1
+
9219d1
+const struct sbrec_chassis_private *
9219d1
+chassis_private_lookup_by_name(
9219d1
+    struct ovsdb_idl_index *sbrec_chassis_private_by_name, const char *name);
9219d1
+
9219d1
 struct ovsdb_idl_index *ha_chassis_group_index_create(struct ovsdb_idl *idl);
9219d1
 const struct sbrec_ha_chassis_group *ha_chassis_group_lookup_by_name(
9219d1
     struct ovsdb_idl_index *sbrec_ha_chassis_grp_by_name, const char *name);
9219d1
diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
9219d1
index fc05accde..c83f9d5c2 100644
9219d1
--- a/northd/ovn-northd.c
9219d1
+++ b/northd/ovn-northd.c
9219d1
@@ -12024,6 +12024,11 @@ static const char *rbac_chassis_update[] =
9219d1
     {"nb_cfg", "external_ids", "encaps", "vtep_logical_switches",
9219d1
      "other_config"};
9219d1
 
9219d1
+static const char *rbac_chassis_private_auth[] =
9219d1
+    {"name"};
9219d1
+static const char *rbac_chassis_private_update[] =
9219d1
+    {"nb_cfg", "chassis"};
9219d1
+
9219d1
 static const char *rbac_encap_auth[] =
9219d1
     {"chassis_name"};
9219d1
 static const char *rbac_encap_update[] =
9219d1
@@ -12061,6 +12066,14 @@ static struct rbac_perm_cfg {
9219d1
         .update = rbac_chassis_update,
9219d1
         .n_update = ARRAY_SIZE(rbac_chassis_update),
9219d1
         .row = NULL
9219d1
+    },{
9219d1
+        .table = "Chassis_Private",
9219d1
+        .auth = rbac_chassis_private_auth,
9219d1
+        .n_auth = ARRAY_SIZE(rbac_chassis_private_auth),
9219d1
+        .insdel = true,
9219d1
+        .update = rbac_chassis_private_update,
9219d1
+        .n_update = ARRAY_SIZE(rbac_chassis_private_update),
9219d1
+        .row = NULL
9219d1
     },{
9219d1
         .table = "Encap",
9219d1
         .auth = rbac_encap_auth,
9219d1
@@ -12230,12 +12243,23 @@ update_northbound_cfg(struct northd_context *ctx,
9219d1
     /* Update northbound hv_cfg if appropriate. */
9219d1
     if (nbg) {
9219d1
         /* Find minimum nb_cfg among all chassis. */
9219d1
-        const struct sbrec_chassis *chassis;
9219d1
+        const struct sbrec_chassis_private *chassis_priv;
9219d1
         int64_t hv_cfg = nbg->nb_cfg;
9219d1
-        SBREC_CHASSIS_FOR_EACH (chassis, ctx->ovnsb_idl) {
9219d1
-            if (!smap_get_bool(&chassis->other_config, "is-remote", false) &&
9219d1
-                chassis->nb_cfg < hv_cfg) {
9219d1
-                hv_cfg = chassis->nb_cfg;
9219d1
+        SBREC_CHASSIS_PRIVATE_FOR_EACH (chassis_priv, ctx->ovnsb_idl) {
9219d1
+            const struct sbrec_chassis *chassis = chassis_priv->chassis;
9219d1
+            if (chassis) {
9219d1
+                if (smap_get_bool(&chassis->other_config,
9219d1
+                                  "is-remote", false)) {
9219d1
+                    /* Skip remote chassises. */
9219d1
+                    continue;
9219d1
+                }
9219d1
+            } else {
9219d1
+                VLOG_WARN("Chassis not exist for Chassis_Private record, "
9219d1
+                          "name: %s", chassis_priv->name);
9219d1
+            }
9219d1
+
9219d1
+            if (chassis_priv->nb_cfg < hv_cfg) {
9219d1
+                hv_cfg = chassis_priv->nb_cfg;
9219d1
             }
9219d1
         }
9219d1
 
9219d1
@@ -12248,7 +12272,8 @@ update_northbound_cfg(struct northd_context *ctx,
9219d1
 
9219d1
 /* Handle a fairly small set of changes in the southbound database. */
9219d1
 static void
9219d1
-ovnsb_db_run(struct northd_context *ctx, struct ovsdb_idl_loop *sb_loop,
9219d1
+ovnsb_db_run(struct northd_context *ctx,
9219d1
+             struct ovsdb_idl_loop *sb_loop,
9219d1
              struct hmap *ports)
9219d1
 {
9219d1
     if (!ctx->ovnnb_txn || !ovsdb_idl_has_ever_connected(ctx->ovnsb_idl)) {
9219d1
@@ -12529,10 +12554,17 @@ main(int argc, char *argv[])
9219d1
     ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_meter_band_col_burst_size);
9219d1
 
9219d1
     ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_chassis);
9219d1
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_chassis_col_nb_cfg);
9219d1
     ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_chassis_col_name);
9219d1
     ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_chassis_col_other_config);
9219d1
 
9219d1
+    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_chassis_private);
9219d1
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
9219d1
+                         &sbrec_chassis_private_col_name);
9219d1
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
9219d1
+                         &sbrec_chassis_private_col_chassis);
9219d1
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
9219d1
+                         &sbrec_chassis_private_col_nb_cfg);
9219d1
+
9219d1
     ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_ha_chassis);
9219d1
     add_column_noalert(ovnsb_idl_loop.idl,
9219d1
                        &sbrec_ha_chassis_col_chassis);
9219d1
diff --git a/ovn-sb.ovsschema b/ovn-sb.ovsschema
9219d1
index 99c5de822..3af76540a 100644
9219d1
--- a/ovn-sb.ovsschema
9219d1
+++ b/ovn-sb.ovsschema
9219d1
@@ -1,7 +1,7 @@
9219d1
 {
9219d1
     "name": "OVN_Southbound",
9219d1
-    "version": "2.8.2",
9219d1
-    "cksum": "464326363 21916",
9219d1
+    "version": "2.9.0",
9219d1
+    "cksum": "223619766 22548",
9219d1
     "tables": {
9219d1
         "SB_Global": {
9219d1
             "columns": {
9219d1
@@ -46,6 +46,19 @@
9219d1
                                               "max": "unlimited"}}},
9219d1
             "isRoot": true,
9219d1
             "indexes": [["name"]]},
9219d1
+        "Chassis_Private": {
9219d1
+            "columns": {
9219d1
+                "name": {"type": "string"},
9219d1
+                "chassis": {"type": {"key": {"type": "uuid",
9219d1
+                                             "refTable": "Chassis",
9219d1
+                                             "refType": "weak"},
9219d1
+                                     "min": 0, "max": 1}},
9219d1
+                "nb_cfg": {"type": {"key": "integer"}},
9219d1
+                "external_ids": {
9219d1
+                    "type": {"key": "string", "value": "string",
9219d1
+                             "min": 0, "max": "unlimited"}}},
9219d1
+            "isRoot": true,
9219d1
+            "indexes": [["name"]]},
9219d1
         "Encap": {
9219d1
             "columns": {
9219d1
                 "type": {"type": {"key": {
9219d1
diff --git a/ovn-sb.xml b/ovn-sb.xml
9219d1
index a74d9c3ea..59b21711b 100644
9219d1
--- a/ovn-sb.xml
9219d1
+++ b/ovn-sb.xml
9219d1
@@ -256,10 +256,8 @@
9219d1
     </column>
9219d1
 
9219d1
     <column name="nb_cfg">
9219d1
-      Sequence number for the configuration.  When ovn-controller
9219d1
-      updates the configuration of a chassis from the contents of the
9219d1
-      southbound database, it copies <ref table="SB_Global" column="nb_cfg"/>
9219d1
-      from the <ref table="SB_Global"/> table into this column.
9219d1
+      Deprecated. This column is replaced by the 
9219d1
+      column="nb_cfg"/> column of the <ref table="Chassis_Private"/> table.
9219d1
     </column>
9219d1
 
9219d1
     <column name="other_config" key="ovn-bridge-mappings">
9219d1
@@ -366,6 +364,42 @@
9219d1
     </group>
9219d1
   
9219d1
 
9219d1
+  
9219d1
+    

9219d1
+      Each row in this table maintains per chassis private data that are
9219d1
+      accessed only by the owning chassis (write only) and ovn-northd, not by
9219d1
+      any other chassis.  These data are stored in this separate table instead
9219d1
+      of the <ref table="Chassis"/> table for performance considerations:
9219d1
+      the rows in this table can be conditionally monitored by chassises so
9219d1
+      that each chassis only get update notifications for its own row, to avoid
9219d1
+      unnecessary chassis private data update flooding in a large scale
9219d1
+      deployment.
9219d1
+    

9219d1
+
9219d1
+    <column name="name">
9219d1
+      The name of the chassis that owns these chassis-private data.
9219d1
+    </column>
9219d1
+
9219d1
+    <column name="chassis">
9219d1
+      The reference to <ref table="Chassis"/> table for the chassis that owns
9219d1
+      these chassis-private data.
9219d1
+    </column>
9219d1
+
9219d1
+    <column name="nb_cfg">
9219d1
+      Sequence number for the configuration.  When ovn-controller
9219d1
+      updates the configuration of a chassis from the contents of the
9219d1
+      southbound database, it copies <ref table="SB_Global" column="nb_cfg"/>
9219d1
+      from the <ref table="SB_Global"/> table into this column.
9219d1
+    </column>
9219d1
+
9219d1
+    <group title="Common Columns">
9219d1
+      The overall purpose of these columns is described under Common
9219d1
+      Columns at the beginning of this document.
9219d1
+
9219d1
+      <column name="external_ids"/>
9219d1
+    </group>
9219d1
+  
9219d1
+
9219d1
   
9219d1
     

9219d1
       The <ref column="encaps" table="Chassis"/> column in the 
9219d1
diff --git a/tests/ovn-controller.at b/tests/ovn-controller.at
9219d1
index 77936c776..1b96934b1 100644
9219d1
--- a/tests/ovn-controller.at
9219d1
+++ b/tests/ovn-controller.at
9219d1
@@ -321,3 +321,29 @@ as ovn-sb
9219d1
 OVS_APP_EXIT_AND_WAIT([ovsdb-server])
9219d1
 
9219d1
 AT_CLEANUP
9219d1
+
9219d1
+# Checks that ovn-controller increments the nb_cfg value in the Chassis_Private table
9219d1
+AT_SETUP([ovn-controller - Bump Chassis_Private nb_cfg value])
9219d1
+AT_KEYWORDS([ovn])
9219d1
+ovn_start
9219d1
+
9219d1
+net_add n1
9219d1
+sim_add hv
9219d1
+as hv
9219d1
+ovs-vsctl add-br br-phys
9219d1
+ovn_attach n1 br-phys 192.168.0.1
9219d1
+
9219d1
+OVS_WAIT_UNTIL([test xhv = x`ovn-sbctl --columns name --bare find chassis`])
9219d1
+
9219d1
+# Bump the NB_Global nb_cfg value
9219d1
+nb_global_id=$(ovn-nbctl --columns _uuid --bare find nb_global)
9219d1
+ovn-nbctl set NB_Global ${nb_global_id} nb_cfg=999
9219d1
+
9219d1
+# ovn-controller should bump the nb_cfg in the chassis_private table
9219d1
+OVS_WAIT_UNTIL([test x999 = x`ovn-sbctl --columns nb_cfg --bare find chassis_private`])
9219d1
+
9219d1
+# Assert that the the nb_cfg from the Chassis table was not incremented
9219d1
+OVS_WAIT_UNTIL([test x0 = x`ovn-sbctl --columns nb_cfg --bare find chassis`])
9219d1
+
9219d1
+OVN_CLEANUP([hv])
9219d1
+AT_CLEANUP
9219d1
-- 
9219d1
2.26.2
9219d1