7f7b2c
From 5b1c8c21d5620bf3c7e8d3899fe7dbdd1c65b10e Mon Sep 17 00:00:00 2001
7f7b2c
From: Ilya Maximets <i.maximets@ovn.org>
7f7b2c
Date: Fri, 11 Dec 2020 11:59:16 +0100
7f7b2c
Subject: [PATCH 3/7] nbctl: Use partial set updates instead of re-setting the
7f7b2c
 whole column.
7f7b2c
7f7b2c
Northbound database has many tables that could contain columns with
7f7b2c
very large sets.  For example 'ports' column of the 'Logical_Switch'
7f7b2c
table could contain thousands of ports in a set.
7f7b2c
7f7b2c
Current strategy of nbctl while adding a new port to the set is to
7f7b2c
copy all existing ports, add one and set the new set of ports.
7f7b2c
Similar behavior is for deletion too.
7f7b2c
7f7b2c
If we have 1000 ports and want to add one more, resulted transaction
7f7b2c
in current code will look like this:
7f7b2c
7f7b2c
  {transact,
7f7b2c
        < wait operation >
7f7b2c
        < create new lsp in Logical_Switch_Port table >
7f7b2c
7f7b2c
        where: _uuid  == 'logical switch row uuid'
7f7b2c
        op   : update
7f7b2c
        rows : ports ['< list of current 1000 ports + 1 new >']
7f7b2c
  }
7f7b2c
7f7b2c
The code was written before support for partial updates for sets was
7f7b2c
implemented.  Now we have it and can replace the old strategy.
7f7b2c
With this change resulted transaction will be:
7f7b2c
7f7b2c
  {transact,
7f7b2c
        < wait operation >
7f7b2c
        < create new lsp in Logical_Switch_Port table >
7f7b2c
7f7b2c
        where: _uuid  == 'logical switch row uuid'
7f7b2c
        op   :  mutate
7f7b2c
        mutations : ports insert ['< 1 uuid of a new lsp >']
7f7b2c
  }
7f7b2c
7f7b2c
Unfortunately, for now, this only decreases transaction size in half,
7f7b2c
because '<wait operation>', that is still in the transaction, contains
7f7b2c
the full current list of ports:
7f7b2c
7f7b2c
        where: _uuid  == 'logical switch row uuid'
7f7b2c
        op   : wait
7f7b2c
        until: ==
7f7b2c
        rows : ports ['< list of current 1000 ports >']
7f7b2c
7f7b2c
But anyway, beside the overall code cleanup, this reduces transaction
7f7b2c
size in half and should reduce pressure on the ovsdb-server since it
7f7b2c
will not need to parse and process all 1000 ports twice.
7f7b2c
7f7b2c
This change doesn't affect 'append_request' messages within the raft
7f7b2c
cluster, because ovsdb-server is not smart enough to use mutations
7f7b2c
there, and this will also not affect 'update' messages from the
7f7b2c
ovsdb-server to its clients, because it is smart enough to construct
7f7b2c
'modify' updates regardless of the original transaction.
7f7b2c
7f7b2c
One difference between full updates and partial is that partial
7f7b2c
changes are not visible for the idl client until transaction is
7f7b2c
committed and the update received from the server.  New switch ports
7f7b2c
are not visible while iterating over 'ports' of the logical switch and
7f7b2c
removed ports are still there.  For this reason, we have to maintain
7f7b2c
runtime cache of the mapping between ports and routers/switches they
7f7b2c
attached to.
7f7b2c
7f7b2c
Acked-by: Mark Michelson <mmichels@redhat.com>
7f7b2c
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
7f7b2c
Signed-off-by: Numan Siddique <numans@ovn.org>
7f7b2c
---
7f7b2c
 utilities/ovn-nbctl.c | 350 +++++++++++-------------------------------
7f7b2c
 1 file changed, 87 insertions(+), 263 deletions(-)
7f7b2c
7f7b2c
diff --git a/utilities/ovn-nbctl.c b/utilities/ovn-nbctl.c
7f7b2c
index cbf2cb1f1..7c4dce12a 100644
7f7b2c
--- a/utilities/ovn-nbctl.c
7f7b2c
+++ b/utilities/ovn-nbctl.c
7f7b2c
@@ -126,7 +126,11 @@ static char * OVS_WARN_UNUSED_RESULT main_loop(const char *args,
7f7b2c
 static void server_loop(struct ovsdb_idl *idl, int argc, char *argv[]);
7f7b2c
 
7f7b2c
 /* A context for keeping track of which switch/router certain ports are
7f7b2c
- * connected to. */
7f7b2c
+ * connected to.
7f7b2c
+ *
7f7b2c
+ * It is required to track changes that we did within current set of commands
7f7b2c
+ * because partial updates of sets in database are not reflected in the idl
7f7b2c
+ * until transaction is committed and updates received from the server. */
7f7b2c
 struct nbctl_context {
7f7b2c
     struct ctl_context base;
7f7b2c
     struct shash lsp_to_ls_map;
7f7b2c
@@ -1508,21 +1512,15 @@ nbctl_lsp_add(struct ctl_context *ctx)
7f7b2c
 
7f7b2c
     /* Insert the logical port into the logical switch. */
7f7b2c
     nbrec_logical_switch_verify_ports(ls);
7f7b2c
-    struct nbrec_logical_switch_port **new_ports = xmalloc(sizeof *new_ports *
7f7b2c
-                                                    (ls->n_ports + 1));
7f7b2c
-    nullable_memcpy(new_ports, ls->ports, sizeof *new_ports * ls->n_ports);
7f7b2c
-    new_ports[ls->n_ports] = CONST_CAST(struct nbrec_logical_switch_port *,
7f7b2c
-                                             lsp);
7f7b2c
-    nbrec_logical_switch_set_ports(ls, new_ports, ls->n_ports + 1);
7f7b2c
-    free(new_ports);
7f7b2c
+    nbrec_logical_switch_update_ports_addvalue(ls, lsp);
7f7b2c
 
7f7b2c
     /* Updating runtime cache. */
7f7b2c
     shash_add(&nbctx->lsp_to_ls_map, lsp_name, ls);
7f7b2c
 }
7f7b2c
 
7f7b2c
-/* Removes logical switch port 'ls->ports[idx]'. */
7f7b2c
+/* Removes logical switch port 'lsp' from the logical switch 'ls'. */
7f7b2c
 static void
7f7b2c
-remove_lsp(struct ctl_context *ctx, size_t idx,
7f7b2c
+remove_lsp(struct ctl_context *ctx,
7f7b2c
            const struct nbrec_logical_switch *ls,
7f7b2c
            const struct nbrec_logical_switch_port *lsp)
7f7b2c
 {
7f7b2c
@@ -1534,12 +1532,8 @@ remove_lsp(struct ctl_context *ctx, size_t idx,
7f7b2c
     /* First remove 'lsp' from the array of ports.  This is what will
7f7b2c
      * actually cause the logical port to be deleted when the transaction is
7f7b2c
      * sent to the database server (due to garbage collection). */
7f7b2c
-    struct nbrec_logical_switch_port **new_ports
7f7b2c
-        = xmemdup(ls->ports, sizeof *new_ports * ls->n_ports);
7f7b2c
-    new_ports[idx] = new_ports[ls->n_ports - 1];
7f7b2c
     nbrec_logical_switch_verify_ports(ls);
7f7b2c
-    nbrec_logical_switch_set_ports(ls, new_ports, ls->n_ports - 1);
7f7b2c
-    free(new_ports);
7f7b2c
+    nbrec_logical_switch_update_ports_delvalue(ls, lsp);
7f7b2c
 
7f7b2c
     /* Delete 'lsp' from the IDL.  This won't have a real effect on the
7f7b2c
      * database server (the IDL will suppress it in fact) but it means that it
7f7b2c
@@ -1571,12 +1565,7 @@ nbctl_lsp_del(struct ctl_context *ctx)
7f7b2c
         ctx->error = error;
7f7b2c
         return;
7f7b2c
     }
7f7b2c
-    for (size_t i = 0; i < ls->n_ports; i++) {
7f7b2c
-        if (ls->ports[i] == lsp) {
7f7b2c
-            remove_lsp(ctx, i, ls, lsp);
7f7b2c
-            break;
7f7b2c
-        }
7f7b2c
-    }
7f7b2c
+    remove_lsp(ctx, ls, lsp);
7f7b2c
 }
7f7b2c
 
7f7b2c
 static void
7f7b2c
@@ -2366,17 +2355,13 @@ nbctl_acl_add(struct ctl_context *ctx)
7f7b2c
     }
7f7b2c
 
7f7b2c
     /* Insert the acl into the logical switch/port group. */
7f7b2c
-    struct nbrec_acl **new_acls = xmalloc(sizeof *new_acls * (n_acls + 1));
7f7b2c
-    nullable_memcpy(new_acls, acls, sizeof *new_acls * n_acls);
7f7b2c
-    new_acls[n_acls] = acl;
7f7b2c
     if (pg) {
7f7b2c
         nbrec_port_group_verify_acls(pg);
7f7b2c
-        nbrec_port_group_set_acls(pg, new_acls, n_acls + 1);
7f7b2c
+        nbrec_port_group_update_acls_addvalue(pg, acl);
7f7b2c
     } else {
7f7b2c
         nbrec_logical_switch_verify_acls(ls);
7f7b2c
-        nbrec_logical_switch_set_acls(ls, new_acls, n_acls + 1);
7f7b2c
+        nbrec_logical_switch_update_acls_addvalue(ls, acl);
7f7b2c
     }
7f7b2c
-    free(new_acls);
7f7b2c
 }
7f7b2c
 
7f7b2c
 static void
7f7b2c
@@ -2416,23 +2401,21 @@ nbctl_acl_del(struct ctl_context *ctx)
7f7b2c
     /* If priority and match are not specified, delete all ACLs with the
7f7b2c
      * specified direction. */
7f7b2c
     if (ctx->argc == 3) {
7f7b2c
-        struct nbrec_acl **new_acls = xmalloc(sizeof *new_acls * n_acls);
7f7b2c
-
7f7b2c
-        int n_new_acls = 0;
7f7b2c
-        for (size_t i = 0; i < n_acls; i++) {
7f7b2c
-            if (strcmp(direction, acls[i]->direction)) {
7f7b2c
-                new_acls[n_new_acls++] = acls[i];
7f7b2c
-            }
7f7b2c
-        }
7f7b2c
-
7f7b2c
         if (pg) {
7f7b2c
             nbrec_port_group_verify_acls(pg);
7f7b2c
-            nbrec_port_group_set_acls(pg, new_acls, n_new_acls);
7f7b2c
         } else {
7f7b2c
             nbrec_logical_switch_verify_acls(ls);
7f7b2c
-            nbrec_logical_switch_set_acls(ls, new_acls, n_new_acls);
7f7b2c
         }
7f7b2c
-        free(new_acls);
7f7b2c
+
7f7b2c
+        for (size_t i = 0; i < n_acls; i++) {
7f7b2c
+            if (!strcmp(direction, acls[i]->direction)) {
7f7b2c
+                if (pg) {
7f7b2c
+                    nbrec_port_group_update_acls_delvalue(pg, acls[i]);
7f7b2c
+                } else {
7f7b2c
+                    nbrec_logical_switch_update_acls_delvalue(ls, acls[i]);
7f7b2c
+                }
7f7b2c
+            }
7f7b2c
+        }
7f7b2c
         return;
7f7b2c
     }
7f7b2c
 
7f7b2c
@@ -2454,19 +2437,13 @@ nbctl_acl_del(struct ctl_context *ctx)
7f7b2c
 
7f7b2c
         if (priority == acl->priority && !strcmp(ctx->argv[4], acl->match) &&
7f7b2c
              !strcmp(direction, acl->direction)) {
7f7b2c
-            struct nbrec_acl **new_acls
7f7b2c
-                = xmemdup(acls, sizeof *new_acls * n_acls);
7f7b2c
-            new_acls[i] = acls[n_acls - 1];
7f7b2c
             if (pg) {
7f7b2c
                 nbrec_port_group_verify_acls(pg);
7f7b2c
-                nbrec_port_group_set_acls(pg, new_acls,
7f7b2c
-                                          n_acls - 1);
7f7b2c
+                nbrec_port_group_update_acls_delvalue(pg, acl);
7f7b2c
             } else {
7f7b2c
                 nbrec_logical_switch_verify_acls(ls);
7f7b2c
-                nbrec_logical_switch_set_acls(ls, new_acls,
7f7b2c
-                                              n_acls - 1);
7f7b2c
+                nbrec_logical_switch_update_acls_delvalue(ls, acl);
7f7b2c
             }
7f7b2c
-            free(new_acls);
7f7b2c
             return;
7f7b2c
         }
7f7b2c
     }
7f7b2c
@@ -2620,14 +2597,7 @@ nbctl_qos_add(struct ctl_context *ctx)
7f7b2c
 
7f7b2c
     /* Insert the qos rule the logical switch. */
7f7b2c
     nbrec_logical_switch_verify_qos_rules(ls);
7f7b2c
-    struct nbrec_qos **new_qos_rules
7f7b2c
-        = xmalloc(sizeof *new_qos_rules * (ls->n_qos_rules + 1));
7f7b2c
-    nullable_memcpy(new_qos_rules,
7f7b2c
-                    ls->qos_rules, sizeof *new_qos_rules * ls->n_qos_rules);
7f7b2c
-    new_qos_rules[ls->n_qos_rules] = qos;
7f7b2c
-    nbrec_logical_switch_set_qos_rules(ls, new_qos_rules,
7f7b2c
-                                       ls->n_qos_rules + 1);
7f7b2c
-    free(new_qos_rules);
7f7b2c
+    nbrec_logical_switch_update_qos_rules_addvalue(ls, qos);
7f7b2c
 }
7f7b2c
 
7f7b2c
 static void
7f7b2c
@@ -2664,34 +2634,32 @@ nbctl_qos_del(struct ctl_context *ctx)
7f7b2c
     /* If uuid was specified, delete qos_rule with the
7f7b2c
      * specified uuid. */
7f7b2c
     if (ctx->argc == 3) {
7f7b2c
-        struct nbrec_qos **new_qos_rules
7f7b2c
-            = xmalloc(sizeof *new_qos_rules * ls->n_qos_rules);
7f7b2c
+        size_t i;
7f7b2c
 
7f7b2c
-        int n_qos_rules = 0;
7f7b2c
+        nbrec_logical_switch_verify_qos_rules(ls);
7f7b2c
         if (qos_rule_uuid) {
7f7b2c
-            for (size_t i = 0; i < ls->n_qos_rules; i++) {
7f7b2c
-                if (!uuid_equals(qos_rule_uuid,
7f7b2c
-                                 &(ls->qos_rules[i]->header_.uuid))) {
7f7b2c
-                    new_qos_rules[n_qos_rules++] = ls->qos_rules[i];
7f7b2c
+            for (i = 0; i < ls->n_qos_rules; i++) {
7f7b2c
+                if (uuid_equals(qos_rule_uuid,
7f7b2c
+                                &(ls->qos_rules[i]->header_.uuid))) {
7f7b2c
+                    nbrec_logical_switch_update_qos_rules_delvalue(
7f7b2c
+                        ls, ls->qos_rules[i]);
7f7b2c
+                    break;
7f7b2c
                 }
7f7b2c
             }
7f7b2c
-            if (n_qos_rules == ls->n_qos_rules) {
7f7b2c
+            if (i == ls->n_qos_rules) {
7f7b2c
                 ctl_error(ctx, "uuid is not found");
7f7b2c
             }
7f7b2c
 
7f7b2c
         /* If priority and match are not specified, delete all qos_rules
7f7b2c
          * with the specified direction. */
7f7b2c
         } else {
7f7b2c
-            for (size_t i = 0; i < ls->n_qos_rules; i++) {
7f7b2c
-                if (strcmp(direction, ls->qos_rules[i]->direction)) {
7f7b2c
-                    new_qos_rules[n_qos_rules++] = ls->qos_rules[i];
7f7b2c
+            for (i = 0; i < ls->n_qos_rules; i++) {
7f7b2c
+                if (!strcmp(direction, ls->qos_rules[i]->direction)) {
7f7b2c
+                    nbrec_logical_switch_update_qos_rules_delvalue(
7f7b2c
+                        ls, ls->qos_rules[i]);
7f7b2c
                 }
7f7b2c
             }
7f7b2c
         }
7f7b2c
-
7f7b2c
-        nbrec_logical_switch_verify_qos_rules(ls);
7f7b2c
-        nbrec_logical_switch_set_qos_rules(ls, new_qos_rules, n_qos_rules);
7f7b2c
-        free(new_qos_rules);
7f7b2c
         return;
7f7b2c
     }
7f7b2c
 
7f7b2c
@@ -2718,14 +2686,8 @@ nbctl_qos_del(struct ctl_context *ctx)
7f7b2c
 
7f7b2c
         if (priority == qos->priority && !strcmp(ctx->argv[4], qos->match) &&
7f7b2c
              !strcmp(direction, qos->direction)) {
7f7b2c
-            struct nbrec_qos **new_qos_rules
7f7b2c
-                = xmemdup(ls->qos_rules,
7f7b2c
-                          sizeof *new_qos_rules * ls->n_qos_rules);
7f7b2c
-            new_qos_rules[i] = ls->qos_rules[ls->n_qos_rules - 1];
7f7b2c
             nbrec_logical_switch_verify_qos_rules(ls);
7f7b2c
-            nbrec_logical_switch_set_qos_rules(ls, new_qos_rules,
7f7b2c
-                                          ls->n_qos_rules - 1);
7f7b2c
-            free(new_qos_rules);
7f7b2c
+            nbrec_logical_switch_update_qos_rules_delvalue(ls, qos);
7f7b2c
             return;
7f7b2c
         }
7f7b2c
     }
7f7b2c
@@ -3188,16 +3150,7 @@ nbctl_lr_lb_add(struct ctl_context *ctx)
7f7b2c
 
7f7b2c
     /* Insert the load balancer into the logical router. */
7f7b2c
     nbrec_logical_router_verify_load_balancer(lr);
7f7b2c
-    struct nbrec_load_balancer **new_lbs
7f7b2c
-        = xmalloc(sizeof *new_lbs * (lr->n_load_balancer + 1));
7f7b2c
-
7f7b2c
-    nullable_memcpy(new_lbs, lr->load_balancer,
7f7b2c
-                    sizeof *new_lbs * lr->n_load_balancer);
7f7b2c
-    new_lbs[lr->n_load_balancer] = CONST_CAST(struct nbrec_load_balancer *,
7f7b2c
-            new_lb);
7f7b2c
-    nbrec_logical_router_set_load_balancer(lr, new_lbs,
7f7b2c
-            lr->n_load_balancer + 1);
7f7b2c
-    free(new_lbs);
7f7b2c
+    nbrec_logical_router_update_load_balancer_addvalue(lr, new_lb);
7f7b2c
 }
7f7b2c
 
7f7b2c
 static void
7f7b2c
@@ -3231,14 +3184,7 @@ nbctl_lr_lb_del(struct ctl_context *ctx)
7f7b2c
         if (uuid_equals(&del_lb->header_.uuid, &lb->header_.uuid)) {
7f7b2c
             /* Remove the matching rule. */
7f7b2c
             nbrec_logical_router_verify_load_balancer(lr);
7f7b2c
-
7f7b2c
-            struct nbrec_load_balancer **new_lbs
7f7b2c
-                = xmemdup(lr->load_balancer,
7f7b2c
-                    sizeof *new_lbs * lr->n_load_balancer);
7f7b2c
-            new_lbs[i] = lr->load_balancer[lr->n_load_balancer - 1];
7f7b2c
-            nbrec_logical_router_set_load_balancer(lr, new_lbs,
7f7b2c
-                                          lr->n_load_balancer - 1);
7f7b2c
-            free(new_lbs);
7f7b2c
+            nbrec_logical_router_update_load_balancer_delvalue(lr, lb);
7f7b2c
             return;
7f7b2c
         }
7f7b2c
     }
7f7b2c
@@ -3313,16 +3259,7 @@ nbctl_ls_lb_add(struct ctl_context *ctx)
7f7b2c
 
7f7b2c
     /* Insert the load balancer into the logical switch. */
7f7b2c
     nbrec_logical_switch_verify_load_balancer(ls);
7f7b2c
-    struct nbrec_load_balancer **new_lbs
7f7b2c
-        = xmalloc(sizeof *new_lbs * (ls->n_load_balancer + 1));
7f7b2c
-
7f7b2c
-    nullable_memcpy(new_lbs, ls->load_balancer,
7f7b2c
-                    sizeof *new_lbs * ls->n_load_balancer);
7f7b2c
-    new_lbs[ls->n_load_balancer] = CONST_CAST(struct nbrec_load_balancer *,
7f7b2c
-            new_lb);
7f7b2c
-    nbrec_logical_switch_set_load_balancer(ls, new_lbs,
7f7b2c
-            ls->n_load_balancer + 1);
7f7b2c
-    free(new_lbs);
7f7b2c
+    nbrec_logical_switch_update_load_balancer_addvalue(ls, new_lb);
7f7b2c
 }
7f7b2c
 
7f7b2c
 static void
7f7b2c
@@ -3356,14 +3293,7 @@ nbctl_ls_lb_del(struct ctl_context *ctx)
7f7b2c
         if (uuid_equals(&del_lb->header_.uuid, &lb->header_.uuid)) {
7f7b2c
             /* Remove the matching rule. */
7f7b2c
             nbrec_logical_switch_verify_load_balancer(ls);
7f7b2c
-
7f7b2c
-            struct nbrec_load_balancer **new_lbs
7f7b2c
-                = xmemdup(ls->load_balancer,
7f7b2c
-                        sizeof *new_lbs * ls->n_load_balancer);
7f7b2c
-            new_lbs[i] = ls->load_balancer[ls->n_load_balancer - 1];
7f7b2c
-            nbrec_logical_switch_set_load_balancer(ls, new_lbs,
7f7b2c
-                                          ls->n_load_balancer - 1);
7f7b2c
-            free(new_lbs);
7f7b2c
+            nbrec_logical_switch_update_load_balancer_delvalue(ls, lb);
7f7b2c
             return;
7f7b2c
         }
7f7b2c
     }
7f7b2c
@@ -3792,14 +3722,7 @@ nbctl_lr_policy_add(struct ctl_context *ctx)
7f7b2c
     smap_destroy(&options);
7f7b2c
 
7f7b2c
     nbrec_logical_router_verify_policies(lr);
7f7b2c
-    struct nbrec_logical_router_policy **new_policies
7f7b2c
-        = xmalloc(sizeof *new_policies * (lr->n_policies + 1));
7f7b2c
-    memcpy(new_policies, lr->policies,
7f7b2c
-           sizeof *new_policies * lr->n_policies);
7f7b2c
-    new_policies[lr->n_policies] = policy;
7f7b2c
-    nbrec_logical_router_set_policies(lr, new_policies,
7f7b2c
-                                      lr->n_policies + 1);
7f7b2c
-    free(new_policies);
7f7b2c
+    nbrec_logical_router_update_policies_addvalue(lr, policy);
7f7b2c
     if (next_hop != NULL) {
7f7b2c
         free(next_hop);
7f7b2c
     }
7f7b2c
@@ -3836,38 +3759,35 @@ nbctl_lr_policy_del(struct ctl_context *ctx)
7f7b2c
     /* If uuid was specified, delete routing policy with the
7f7b2c
      * specified uuid. */
7f7b2c
     if (ctx->argc == 3) {
7f7b2c
-        struct nbrec_logical_router_policy **new_policies
7f7b2c
-            = xmemdup(lr->policies,
7f7b2c
-                      sizeof *new_policies * lr->n_policies);
7f7b2c
-        int n_policies = 0;
7f7b2c
+        size_t i;
7f7b2c
 
7f7b2c
+        nbrec_logical_router_verify_policies(lr);
7f7b2c
         if (lr_policy_uuid) {
7f7b2c
-            for (size_t i = 0; i < lr->n_policies; i++) {
7f7b2c
-                if (!uuid_equals(lr_policy_uuid,
7f7b2c
-                                 &(lr->policies[i]->header_.uuid))) {
7f7b2c
-                    new_policies[n_policies++] = lr->policies[i];
7f7b2c
+            for (i = 0; i < lr->n_policies; i++) {
7f7b2c
+                if (uuid_equals(lr_policy_uuid,
7f7b2c
+                                &(lr->policies[i]->header_.uuid))) {
7f7b2c
+                    nbrec_logical_router_update_policies_delvalue(
7f7b2c
+                        lr, lr->policies[i]);
7f7b2c
+                    break;
7f7b2c
                 }
7f7b2c
             }
7f7b2c
-            if (n_policies == lr->n_policies) {
7f7b2c
+            if (i == lr->n_policies) {
7f7b2c
                 if (!shash_find(&ctx->options, "--if-exists")) {
7f7b2c
                     ctl_error(ctx, "Logical router policy uuid is not found.");
7f7b2c
                 }
7f7b2c
-                free(new_policies);
7f7b2c
                 return;
7f7b2c
             }
7f7b2c
 
7f7b2c
-    /* If match is not specified, delete all routing policies with the
7f7b2c
-     * specified priority. */
7f7b2c
+        /* If match is not specified, delete all routing policies with the
7f7b2c
+         * specified priority. */
7f7b2c
         } else {
7f7b2c
-            for (int i = 0; i < lr->n_policies; i++) {
7f7b2c
-                if (priority != lr->policies[i]->priority) {
7f7b2c
-                    new_policies[n_policies++] = lr->policies[i];
7f7b2c
+            for (i = 0; i < lr->n_policies; i++) {
7f7b2c
+                if (priority == lr->policies[i]->priority) {
7f7b2c
+                    nbrec_logical_router_update_policies_delvalue(
7f7b2c
+                        lr, lr->policies[i]);
7f7b2c
                 }
7f7b2c
             }
7f7b2c
         }
7f7b2c
-        nbrec_logical_router_verify_policies(lr);
7f7b2c
-        nbrec_logical_router_set_policies(lr, new_policies, n_policies);
7f7b2c
-        free(new_policies);
7f7b2c
         return;
7f7b2c
     }
7f7b2c
 
7f7b2c
@@ -3876,14 +3796,8 @@ nbctl_lr_policy_del(struct ctl_context *ctx)
7f7b2c
         struct nbrec_logical_router_policy *routing_policy = lr->policies[i];
7f7b2c
         if (priority == routing_policy->priority &&
7f7b2c
             !strcmp(ctx->argv[3], routing_policy->match)) {
7f7b2c
-            struct nbrec_logical_router_policy **new_policies
7f7b2c
-                = xmemdup(lr->policies,
7f7b2c
-                          sizeof *new_policies * lr->n_policies);
7f7b2c
-            new_policies[i] = lr->policies[lr->n_policies - 1];
7f7b2c
             nbrec_logical_router_verify_policies(lr);
7f7b2c
-            nbrec_logical_router_set_policies(lr, new_policies,
7f7b2c
-                                              lr->n_policies - 1);
7f7b2c
-            free(new_policies);
7f7b2c
+            nbrec_logical_router_update_policies_delvalue(lr, routing_policy);
7f7b2c
             return;
7f7b2c
         }
7f7b2c
     }
7f7b2c
@@ -4083,14 +3997,7 @@ nbctl_lr_route_add(struct ctl_context *ctx)
7f7b2c
     }
7f7b2c
 
7f7b2c
     nbrec_logical_router_verify_static_routes(lr);
7f7b2c
-    struct nbrec_logical_router_static_route **new_routes
7f7b2c
-        = xmalloc(sizeof *new_routes * (lr->n_static_routes + 1));
7f7b2c
-    nullable_memcpy(new_routes, lr->static_routes,
7f7b2c
-               sizeof *new_routes * lr->n_static_routes);
7f7b2c
-    new_routes[lr->n_static_routes] = route;
7f7b2c
-    nbrec_logical_router_set_static_routes(lr, new_routes,
7f7b2c
-                                           lr->n_static_routes + 1);
7f7b2c
-    free(new_routes);
7f7b2c
+    nbrec_logical_router_update_static_routes_addvalue(lr, route);
7f7b2c
 
7f7b2c
 cleanup:
7f7b2c
     free(next_hop);
7f7b2c
@@ -4147,11 +4054,9 @@ nbctl_lr_route_del(struct ctl_context *ctx)
7f7b2c
         output_port = ctx->argv[4];
7f7b2c
     }
7f7b2c
 
7f7b2c
-    struct nbrec_logical_router_static_route **new_routes
7f7b2c
-        = xmemdup(lr->static_routes,
7f7b2c
-                  sizeof *new_routes * lr->n_static_routes);
7f7b2c
-    size_t n_new = 0;
7f7b2c
-    for (int i = 0; i < lr->n_static_routes; i++) {
7f7b2c
+    size_t n_removed = 0;
7f7b2c
+    nbrec_logical_router_verify_static_routes(lr);
7f7b2c
+    for (size_t i = 0; i < lr->n_static_routes; i++) {
7f7b2c
         /* Compare route policy, if specified. */
7f7b2c
         if (policy) {
7f7b2c
             char *nb_policy = lr->static_routes[i]->policy;
7f7b2c
@@ -4160,7 +4065,6 @@ nbctl_lr_route_del(struct ctl_context *ctx)
7f7b2c
                     nb_is_src_route = true;
7f7b2c
             }
7f7b2c
             if (is_src_route != nb_is_src_route) {
7f7b2c
-                new_routes[n_new++] = lr->static_routes[i];
7f7b2c
                 continue;
7f7b2c
             }
7f7b2c
         }
7f7b2c
@@ -4171,14 +4075,12 @@ nbctl_lr_route_del(struct ctl_context *ctx)
7f7b2c
                 normalize_prefix_str(lr->static_routes[i]->ip_prefix);
7f7b2c
             if (!rt_prefix) {
7f7b2c
                 /* Ignore existing prefix we couldn't parse. */
7f7b2c
-                new_routes[n_new++] = lr->static_routes[i];
7f7b2c
                 continue;
7f7b2c
             }
7f7b2c
 
7f7b2c
             int ret = strcmp(prefix, rt_prefix);
7f7b2c
             free(rt_prefix);
7f7b2c
             if (ret) {
7f7b2c
-                new_routes[n_new++] = lr->static_routes[i];
7f7b2c
                 continue;
7f7b2c
             }
7f7b2c
         }
7f7b2c
@@ -4189,13 +4091,11 @@ nbctl_lr_route_del(struct ctl_context *ctx)
7f7b2c
                 normalize_prefix_str(lr->static_routes[i]->nexthop);
7f7b2c
             if (!rt_nexthop) {
7f7b2c
                 /* Ignore existing nexthop we couldn't parse. */
7f7b2c
-                new_routes[n_new++] = lr->static_routes[i];
7f7b2c
                 continue;
7f7b2c
             }
7f7b2c
             int ret = strcmp(nexthop, rt_nexthop);
7f7b2c
             free(rt_nexthop);
7f7b2c
             if (ret) {
7f7b2c
-                new_routes[n_new++] = lr->static_routes[i];
7f7b2c
                 continue;
7f7b2c
             }
7f7b2c
         }
7f7b2c
@@ -4204,18 +4104,17 @@ nbctl_lr_route_del(struct ctl_context *ctx)
7f7b2c
         if (output_port) {
7f7b2c
             char *rt_output_port = lr->static_routes[i]->output_port;
7f7b2c
             if (!rt_output_port || strcmp(output_port, rt_output_port)) {
7f7b2c
-                new_routes[n_new++] = lr->static_routes[i];
7f7b2c
+                continue;
7f7b2c
             }
7f7b2c
         }
7f7b2c
-    }
7f7b2c
 
7f7b2c
-    if (n_new < lr->n_static_routes) {
7f7b2c
-        nbrec_logical_router_verify_static_routes(lr);
7f7b2c
-        nbrec_logical_router_set_static_routes(lr, new_routes, n_new);
7f7b2c
-        goto out;
7f7b2c
+        /* Everything matched. Removing. */
7f7b2c
+        nbrec_logical_router_update_static_routes_delvalue(
7f7b2c
+            lr, lr->static_routes[i]);
7f7b2c
+        n_removed++;
7f7b2c
     }
7f7b2c
 
7f7b2c
-    if (!shash_find(&ctx->options, "--if-exists")) {
7f7b2c
+    if (!n_removed && !shash_find(&ctx->options, "--if-exists")) {
7f7b2c
         ctl_error(ctx, "no matching route: policy '%s', prefix '%s', nexthop "
7f7b2c
                   "'%s', output_port '%s'.",
7f7b2c
                   policy ? policy : "any",
7f7b2c
@@ -4224,8 +4123,6 @@ nbctl_lr_route_del(struct ctl_context *ctx)
7f7b2c
                   output_port ? output_port : "any");
7f7b2c
     }
7f7b2c
 
7f7b2c
-out:
7f7b2c
-    free(new_routes);
7f7b2c
     free(prefix);
7f7b2c
     free(nexthop);
7f7b2c
 }
7f7b2c
@@ -4497,11 +4394,7 @@ nbctl_lr_nat_add(struct ctl_context *ctx)
7f7b2c
 
7f7b2c
     /* Insert the NAT into the logical router. */
7f7b2c
     nbrec_logical_router_verify_nat(lr);
7f7b2c
-    struct nbrec_nat **new_nats = xmalloc(sizeof *new_nats * (lr->n_nat + 1));
7f7b2c
-    nullable_memcpy(new_nats, lr->nat, sizeof *new_nats * lr->n_nat);
7f7b2c
-    new_nats[lr->n_nat] = nat;
7f7b2c
-    nbrec_logical_router_set_nat(lr, new_nats, lr->n_nat + 1);
7f7b2c
-    free(new_nats);
7f7b2c
+    nbrec_logical_router_update_nat_addvalue(lr, nat);
7f7b2c
 
7f7b2c
 cleanup:
7f7b2c
     free(new_logical_ip);
7f7b2c
@@ -4537,17 +4430,12 @@ nbctl_lr_nat_del(struct ctl_context *ctx)
7f7b2c
 
7f7b2c
     if (ctx->argc == 3) {
7f7b2c
         /*Deletes all NATs with the specified type. */
7f7b2c
-        struct nbrec_nat **new_nats = xmalloc(sizeof *new_nats * lr->n_nat);
7f7b2c
-        int n_nat = 0;
7f7b2c
+        nbrec_logical_router_verify_nat(lr);
7f7b2c
         for (size_t i = 0; i < lr->n_nat; i++) {
7f7b2c
-            if (strcmp(nat_type, lr->nat[i]->type)) {
7f7b2c
-                new_nats[n_nat++] = lr->nat[i];
7f7b2c
+            if (!strcmp(nat_type, lr->nat[i]->type)) {
7f7b2c
+                nbrec_logical_router_update_nat_delvalue(lr, lr->nat[i]);
7f7b2c
             }
7f7b2c
         }
7f7b2c
-
7f7b2c
-        nbrec_logical_router_verify_nat(lr);
7f7b2c
-        nbrec_logical_router_set_nat(lr, new_nats, n_nat);
7f7b2c
-        free(new_nats);
7f7b2c
         return;
7f7b2c
     }
7f7b2c
 
7f7b2c
@@ -4569,13 +4457,8 @@ nbctl_lr_nat_del(struct ctl_context *ctx)
7f7b2c
             continue;
7f7b2c
         }
7f7b2c
         if (!strcmp(nat_type, nat->type) && !strcmp(nat_ip, old_ip)) {
7f7b2c
-            struct nbrec_nat **new_nats
7f7b2c
-                = xmemdup(lr->nat, sizeof *new_nats * lr->n_nat);
7f7b2c
-            new_nats[i] = lr->nat[lr->n_nat - 1];
7f7b2c
             nbrec_logical_router_verify_nat(lr);
7f7b2c
-            nbrec_logical_router_set_nat(lr, new_nats,
7f7b2c
-                                          lr->n_nat - 1);
7f7b2c
-            free(new_nats);
7f7b2c
+            nbrec_logical_router_update_nat_delvalue(lr, nat);
7f7b2c
             should_return = true;
7f7b2c
         }
7f7b2c
         free(old_ip);
7f7b2c
@@ -4854,14 +4737,7 @@ nbctl_lrp_set_gateway_chassis(struct ctl_context *ctx)
7f7b2c
 
7f7b2c
     /* Insert the logical gateway chassis into the logical router port. */
7f7b2c
     nbrec_logical_router_port_verify_gateway_chassis(lrp);
7f7b2c
-    struct nbrec_gateway_chassis **new_gc = xmalloc(
7f7b2c
-        sizeof *new_gc * (lrp->n_gateway_chassis + 1));
7f7b2c
-    nullable_memcpy(new_gc, lrp->gateway_chassis,
7f7b2c
-                    sizeof *new_gc * lrp->n_gateway_chassis);
7f7b2c
-    new_gc[lrp->n_gateway_chassis] = gc;
7f7b2c
-    nbrec_logical_router_port_set_gateway_chassis(
7f7b2c
-        lrp, new_gc, lrp->n_gateway_chassis + 1);
7f7b2c
-    free(new_gc);
7f7b2c
+    nbrec_logical_router_port_update_gateway_chassis_addvalue(lrp, gc);
7f7b2c
     free(gc_name);
7f7b2c
 }
7f7b2c
 
7f7b2c
@@ -4878,14 +4754,8 @@ remove_gc(const struct nbrec_logical_router_port *lrp, size_t idx)
7f7b2c
          * will actually cause the gateway chassis to be deleted when the
7f7b2c
          * transaction is sent to the database server (due to garbage
7f7b2c
          * collection). */
7f7b2c
-        struct nbrec_gateway_chassis **new_gc
7f7b2c
-            = xmemdup(lrp->gateway_chassis,
7f7b2c
-                      sizeof *new_gc * lrp->n_gateway_chassis);
7f7b2c
-        new_gc[idx] = new_gc[lrp->n_gateway_chassis - 1];
7f7b2c
         nbrec_logical_router_port_verify_gateway_chassis(lrp);
7f7b2c
-        nbrec_logical_router_port_set_gateway_chassis(
7f7b2c
-            lrp, new_gc, lrp->n_gateway_chassis - 1);
7f7b2c
-        free(new_gc);
7f7b2c
+        nbrec_logical_router_port_update_gateway_chassis_delvalue(lrp, gc);
7f7b2c
     }
7f7b2c
 
7f7b2c
     /* Delete 'gc' from the IDL.  This won't have a real effect on
7f7b2c
@@ -5118,21 +4988,15 @@ nbctl_lrp_add(struct ctl_context *ctx)
7f7b2c
 
7f7b2c
     /* Insert the logical port into the logical router. */
7f7b2c
     nbrec_logical_router_verify_ports(lr);
7f7b2c
-    struct nbrec_logical_router_port **new_ports = xmalloc(sizeof *new_ports *
7f7b2c
-                                                        (lr->n_ports + 1));
7f7b2c
-    nullable_memcpy(new_ports, lr->ports, sizeof *new_ports * lr->n_ports);
7f7b2c
-    new_ports[lr->n_ports] = CONST_CAST(struct nbrec_logical_router_port *,
7f7b2c
-                                             lrp);
7f7b2c
-    nbrec_logical_router_set_ports(lr, new_ports, lr->n_ports + 1);
7f7b2c
-    free(new_ports);
7f7b2c
+    nbrec_logical_router_update_ports_addvalue(lr, lrp);
7f7b2c
 
7f7b2c
     /* Updating runtime cache. */
7f7b2c
     shash_add(&nbctx->lrp_to_lr_map, lrp->name, lr);
7f7b2c
 }
7f7b2c
 
7f7b2c
-/* Removes logical router port 'lr->ports[idx]'. */
7f7b2c
+/* Removes logical router port 'lrp' from logical router 'lr'. */
7f7b2c
 static void
7f7b2c
-remove_lrp(struct ctl_context *ctx, size_t idx,
7f7b2c
+remove_lrp(struct ctl_context *ctx,
7f7b2c
            const struct nbrec_logical_router *lr,
7f7b2c
            const struct nbrec_logical_router_port *lrp)
7f7b2c
 {
7f7b2c
@@ -5144,12 +5008,8 @@ remove_lrp(struct ctl_context *ctx, size_t idx,
7f7b2c
     /* First remove 'lrp' from the array of ports.  This is what will
7f7b2c
      * actually cause the logical port to be deleted when the transaction is
7f7b2c
      * sent to the database server (due to garbage collection). */
7f7b2c
-    struct nbrec_logical_router_port **new_ports
7f7b2c
-        = xmemdup(lr->ports, sizeof *new_ports * lr->n_ports);
7f7b2c
-    new_ports[idx] = new_ports[lr->n_ports - 1];
7f7b2c
     nbrec_logical_router_verify_ports(lr);
7f7b2c
-    nbrec_logical_router_set_ports(lr, new_ports, lr->n_ports - 1);
7f7b2c
-    free(new_ports);
7f7b2c
+    nbrec_logical_router_update_ports_delvalue(lr, lrp);
7f7b2c
 
7f7b2c
     /* Delete 'lrp' from the IDL.  This won't have a real effect on
7f7b2c
      * the database server (the IDL will suppress it in fact) but it
7f7b2c
@@ -5181,12 +5041,7 @@ nbctl_lrp_del(struct ctl_context *ctx)
7f7b2c
         ctx->error = error;
7f7b2c
         return;
7f7b2c
     }
7f7b2c
-    for (size_t i = 0; i < lr->n_ports; i++) {
7f7b2c
-        if (lr->ports[i] == lrp) {
7f7b2c
-            remove_lrp(ctx, i, lr, lrp);
7f7b2c
-            break;
7f7b2c
-        }
7f7b2c
-    }
7f7b2c
+    remove_lrp(ctx, lr, lrp);
7f7b2c
 }
7f7b2c
 
7f7b2c
 /* Print a list of logical router ports. */
7f7b2c
@@ -5458,15 +5313,7 @@ nbctl_fwd_group_add(struct ctl_context *ctx)
7f7b2c
       nbrec_forwarding_group_set_liveness(fwd_group, true);
7f7b2c
     }
7f7b2c
 
7f7b2c
-    struct nbrec_forwarding_group **new_fwd_groups =
7f7b2c
-            xmalloc(sizeof(*new_fwd_groups) * (ls->n_forwarding_groups + 1));
7f7b2c
-    memcpy(new_fwd_groups, ls->forwarding_groups,
7f7b2c
-           sizeof *new_fwd_groups * ls->n_forwarding_groups);
7f7b2c
-    new_fwd_groups[ls->n_forwarding_groups] = fwd_group;
7f7b2c
-    nbrec_logical_switch_set_forwarding_groups(ls, new_fwd_groups,
7f7b2c
-                                               (ls->n_forwarding_groups + 1));
7f7b2c
-    free(new_fwd_groups);
7f7b2c
-
7f7b2c
+    nbrec_logical_switch_update_forwarding_groups_addvalue(ls, fwd_group);
7f7b2c
 }
7f7b2c
 
7f7b2c
 static void
7f7b2c
@@ -5488,14 +5335,8 @@ nbctl_fwd_group_del(struct ctl_context *ctx)
7f7b2c
 
7f7b2c
     for (int i = 0; i < ls->n_forwarding_groups; ++i) {
7f7b2c
         if (!strcmp(ls->forwarding_groups[i]->name, fwd_group->name)) {
7f7b2c
-            struct nbrec_forwarding_group **new_fwd_groups =
7f7b2c
-                xmemdup(ls->forwarding_groups,
7f7b2c
-                        sizeof *new_fwd_groups * ls->n_forwarding_groups);
7f7b2c
-            new_fwd_groups[i] =
7f7b2c
-                ls->forwarding_groups[ls->n_forwarding_groups - 1];
7f7b2c
-            nbrec_logical_switch_set_forwarding_groups(ls, new_fwd_groups,
7f7b2c
-                (ls->n_forwarding_groups - 1));
7f7b2c
-            free(new_fwd_groups);
7f7b2c
+            nbrec_logical_switch_update_forwarding_groups_delvalue(
7f7b2c
+                ls, ls->forwarding_groups[i]);
7f7b2c
             nbrec_forwarding_group_delete(fwd_group);
7f7b2c
             return;
7f7b2c
         }
7f7b2c
@@ -6093,16 +5934,7 @@ cmd_ha_ch_grp_add_chassis(struct ctl_context *ctx)
7f7b2c
     nbrec_ha_chassis_set_priority(ha_chassis, priority);
7f7b2c
 
7f7b2c
     nbrec_ha_chassis_group_verify_ha_chassis(ha_ch_grp);
7f7b2c
-
7f7b2c
-    struct nbrec_ha_chassis **new_ha_chs =
7f7b2c
-        xmalloc(sizeof *new_ha_chs * (ha_ch_grp->n_ha_chassis + 1));
7f7b2c
-    nullable_memcpy(new_ha_chs, ha_ch_grp->ha_chassis,
7f7b2c
-                    sizeof *new_ha_chs * ha_ch_grp->n_ha_chassis);
7f7b2c
-    new_ha_chs[ha_ch_grp->n_ha_chassis] =
7f7b2c
-        CONST_CAST(struct nbrec_ha_chassis *, ha_chassis);
7f7b2c
-    nbrec_ha_chassis_group_set_ha_chassis(ha_ch_grp, new_ha_chs,
7f7b2c
-                                          ha_ch_grp->n_ha_chassis + 1);
7f7b2c
-    free(new_ha_chs);
7f7b2c
+    nbrec_ha_chassis_group_update_ha_chassis_addvalue(ha_ch_grp, ha_chassis);
7f7b2c
 }
7f7b2c
 
7f7b2c
 static void
7f7b2c
@@ -6117,11 +5949,9 @@ cmd_ha_ch_grp_remove_chassis(struct ctl_context *ctx)
7f7b2c
 
7f7b2c
     const char *chassis_name = ctx->argv[2];
7f7b2c
     struct nbrec_ha_chassis *ha_chassis = NULL;
7f7b2c
-    size_t idx = 0;
7f7b2c
     for (size_t i = 0; i < ha_ch_grp->n_ha_chassis; i++) {
7f7b2c
         if (!strcmp(ha_ch_grp->ha_chassis[i]->chassis_name, chassis_name)) {
7f7b2c
             ha_chassis = ha_ch_grp->ha_chassis[i];
7f7b2c
-            idx = i;
7f7b2c
             break;
7f7b2c
         }
7f7b2c
     }
7f7b2c
@@ -6132,14 +5962,8 @@ cmd_ha_ch_grp_remove_chassis(struct ctl_context *ctx)
7f7b2c
         return;
7f7b2c
     }
7f7b2c
 
7f7b2c
-    struct nbrec_ha_chassis **new_ha_ch
7f7b2c
-        = xmemdup(ha_ch_grp->ha_chassis,
7f7b2c
-                  sizeof *new_ha_ch * ha_ch_grp->n_ha_chassis);
7f7b2c
-    new_ha_ch[idx] = new_ha_ch[ha_ch_grp->n_ha_chassis - 1];
7f7b2c
     nbrec_ha_chassis_group_verify_ha_chassis(ha_ch_grp);
7f7b2c
-    nbrec_ha_chassis_group_set_ha_chassis(ha_ch_grp, new_ha_ch,
7f7b2c
-                                          ha_ch_grp->n_ha_chassis - 1);
7f7b2c
-    free(new_ha_ch);
7f7b2c
+    nbrec_ha_chassis_group_update_ha_chassis_delvalue(ha_ch_grp, ha_chassis);
7f7b2c
     nbrec_ha_chassis_delete(ha_chassis);
7f7b2c
 }
7f7b2c
 
7f7b2c
-- 
7f7b2c
2.28.0
7f7b2c