diff mbox series

[ovs-dev] northd: Don't set SB.Load_Balancer columns if not needed.

Message ID 20220325150845.4759-1-dceara@redhat.com
State Changes Requested
Headers show
Series [ovs-dev] northd: Don't set SB.Load_Balancer columns if not needed. | expand

Checks

Context Check Description
ovsrobot/apply-robot success apply and check: success
ovsrobot/github-robot-_Build_and_Test fail github build: failed
ovsrobot/github-robot-_ovn-kubernetes fail github build: failed

Commit Message

Dumitru Ceara March 25, 2022, 3:08 p.m. UTC
OVS commit 1cc618c32524 ("ovsdb-idl: Fix atomicity of writes that
don't change a column's value.") [0] explains why writes that don't
change a column's value cannot be optimized out early if the column is
read/write.

In northd, most tables have change tracking enabled, making all their
columns read/write.  That means that a write to a column (even if it
doesn't change the value) will add the row to the current transaction.
Validation is eventually performed before sending the transaction to the
server but if there are lots of such records this becomes costly.

Profiling what happens in northd when running with a NB database taken
from an ovn-k8s-like scale test (16K load balancers applied to 120
logical switches and routers) we notice that ovn-northd was always
writing to the SB.Load_Balancer columns even if nothing changed.

This commit changes that behavior and only writes to the
SB.Load_Balancer columns if needed.

Without this change, in our test server, with ovn-northd running
against NB database mentioned above, processing loop intervals were
~13 seconds.

With this change applied, loop intervals go down to ~7 seconds.

[0] https://github.com/openvswitch/ovs/commit/1cc618c32524

Signed-off-by: Dumitru Ceara <dceara@redhat.com>
---
 northd/northd.c | 105 ++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 83 insertions(+), 22 deletions(-)

Comments

Dumitru Ceara March 29, 2022, 9:28 a.m. UTC | #1
On 3/25/22 16:08, Dumitru Ceara wrote:
> OVS commit 1cc618c32524 ("ovsdb-idl: Fix atomicity of writes that
> don't change a column's value.") [0] explains why writes that don't
> change a column's value cannot be optimized out early if the column is
> read/write.
> 
> In northd, most tables have change tracking enabled, making all their
> columns read/write.  That means that a write to a column (even if it
> doesn't change the value) will add the row to the current transaction.
> Validation is eventually performed before sending the transaction to the
> server but if there are lots of such records this becomes costly.
> 
> Profiling what happens in northd when running with a NB database taken
> from an ovn-k8s-like scale test (16K load balancers applied to 120
> logical switches and routers) we notice that ovn-northd was always
> writing to the SB.Load_Balancer columns even if nothing changed.
> 
> This commit changes that behavior and only writes to the
> SB.Load_Balancer columns if needed.
> 
> Without this change, in our test server, with ovn-northd running
> against NB database mentioned above, processing loop intervals were
> ~13 seconds.
> 
> With this change applied, loop intervals go down to ~7 seconds.
> 
> [0] https://github.com/openvswitch/ovs/commit/1cc618c32524
> 
> Signed-off-by: Dumitru Ceara <dceara@redhat.com>
> ---
>  northd/northd.c | 105 ++++++++++++++++++++++++++++++++++++++----------
>  1 file changed, 83 insertions(+), 22 deletions(-)
> 
> diff --git a/northd/northd.c b/northd/northd.c
> index a2cf8d6fc7..f16ca1da63 100644
> --- a/northd/northd.c
> +++ b/northd/northd.c
> @@ -3951,6 +3951,56 @@ build_lb_port_related_data(struct hmap *datapaths, struct hmap *ports,
>      build_lb_svcs(input_data, ovnsb_txn, ports, lbs);
>  }
>  
> +static bool
> +sb_lb_needs_update(const struct ovn_northd_lb *lb,
> +                   const struct sbrec_load_balancer *slb)
> +{
> +    if (strcmp(lb->nlb->name, slb->name)) {
> +        return true;
> +    }
> +
> +    if (!smap_equal(&lb->nlb->vips, &slb->vips)) {
> +        return true;
> +    }
> +
> +    if ((lb->nlb->protocol && !slb->protocol)
> +            || (!lb->nlb->protocol && slb->protocol)) {
> +        return true;
> +    }
> +
> +    if (strcmp(lb->nlb->protocol, slb->protocol)) {

lb->nlb->protocol or slb->protocol can be NULL.

I'll send a v2 to fix this, sorry for the noise!

Thanks,
Dumitru
diff mbox series

Patch

diff --git a/northd/northd.c b/northd/northd.c
index a2cf8d6fc7..f16ca1da63 100644
--- a/northd/northd.c
+++ b/northd/northd.c
@@ -3951,6 +3951,56 @@  build_lb_port_related_data(struct hmap *datapaths, struct hmap *ports,
     build_lb_svcs(input_data, ovnsb_txn, ports, lbs);
 }
 
+static bool
+sb_lb_needs_update(const struct ovn_northd_lb *lb,
+                   const struct sbrec_load_balancer *slb)
+{
+    if (strcmp(lb->nlb->name, slb->name)) {
+        return true;
+    }
+
+    if (!smap_equal(&lb->nlb->vips, &slb->vips)) {
+        return true;
+    }
+
+    if ((lb->nlb->protocol && !slb->protocol)
+            || (!lb->nlb->protocol && slb->protocol)) {
+        return true;
+    }
+
+    if (strcmp(lb->nlb->protocol, slb->protocol)) {
+        return true;
+    }
+
+    if (!smap_get_bool(&slb->options, "hairpin_orig_tuple", false)) {
+        return true;
+    }
+
+    if (strcmp(smap_get_def(&lb->nlb->options, "hairpin_snat_ip", ""),
+               smap_get_def(&slb->options, "hairpin_snat_ip", ""))) {
+        return true;
+    }
+
+    if (lb->n_nb_ls != slb->n_datapaths) {
+        return true;
+    }
+
+    struct hmapx nb_datapaths = HMAPX_INITIALIZER(&nb_datapaths);
+    for (size_t i = 0; i < lb->n_nb_ls; i++) {
+        hmapx_add(&nb_datapaths, CONST_CAST(void *, lb->nb_ls[i]->sb));
+    }
+
+    bool stale = false;
+    for (size_t i = 0; i < slb->n_datapaths; i++) {
+        if (!hmapx_contains(&nb_datapaths, slb->datapaths[i])) {
+            stale = true;
+            break;
+        }
+    }
+    hmapx_destroy(&nb_datapaths);
+    return stale;
+}
+
 /* Syncs relevant load balancers (applied to logical switches) to the
  * Southbound database.
  */
@@ -3997,21 +4047,8 @@  sync_lbs(struct northd_input *input_data, struct ovsdb_idl_txn *ovnsb_txn,
             continue;
         }
 
-        /* Store the fact that northd provides the original (destination IP +
-         * transport port) tuple.
-         */
-        struct smap options;
-        smap_clone(&options, &lb->nlb->options);
-        smap_replace(&options, "hairpin_orig_tuple", "true");
-
-        struct sbrec_datapath_binding **lb_dps =
-            xmalloc(lb->n_nb_ls * sizeof *lb_dps);
-        for (size_t i = 0; i < lb->n_nb_ls; i++) {
-            lb_dps[i] = CONST_CAST(struct sbrec_datapath_binding *,
-                                   lb->nb_ls[i]->sb);
-        }
-
-        if (!lb->slb) {
+        sbrec_lb = lb->slb;
+        if (!sbrec_lb) {
             sbrec_lb = sbrec_load_balancer_insert(ovnsb_txn);
             lb->slb = sbrec_lb;
             char *lb_id = xasprintf(
@@ -4021,13 +4058,37 @@  sync_lbs(struct northd_input *input_data, struct ovsdb_idl_txn *ovnsb_txn,
             sbrec_load_balancer_set_external_ids(sbrec_lb, &external_ids);
             free(lb_id);
         }
-        sbrec_load_balancer_set_name(lb->slb, lb->nlb->name);
-        sbrec_load_balancer_set_vips(lb->slb, &lb->nlb->vips);
-        sbrec_load_balancer_set_protocol(lb->slb, lb->nlb->protocol);
-        sbrec_load_balancer_set_datapaths(lb->slb, lb_dps, lb->n_nb_ls);
-        sbrec_load_balancer_set_options(lb->slb, &options);
-        smap_destroy(&options);
-        free(lb_dps);
+
+        /* Only update SB.Load_Balancer columns if needed. */
+        if (!lb->slb || sb_lb_needs_update(lb, sbrec_lb)) {
+            lb->slb = sbrec_lb;
+            sbrec_load_balancer_set_name(lb->slb, lb->nlb->name);
+            sbrec_load_balancer_set_vips(lb->slb, &lb->nlb->vips);
+            sbrec_load_balancer_set_protocol(lb->slb, lb->nlb->protocol);
+
+            struct sbrec_datapath_binding **lb_dps =
+                xmalloc(lb->n_nb_ls * sizeof *lb_dps);
+            for (size_t i = 0; i < lb->n_nb_ls; i++) {
+                lb_dps[i] = CONST_CAST(struct sbrec_datapath_binding *,
+                                       lb->nb_ls[i]->sb);
+            }
+            sbrec_load_balancer_set_datapaths(lb->slb, lb_dps, lb->n_nb_ls);
+            free(lb_dps);
+
+            /* Store the fact that northd provides the original (destination
+             * IP + transport port) tuple.
+             */
+            struct smap options;
+            smap_clone(&options, &lb->nlb->options);
+            smap_add(&options, "hairpin_orig_tuple", "true");
+            const char *hairpin_snat_ip =
+                smap_get(&lb->nlb->options, "hairpin_snat_ip");
+            if (hairpin_snat_ip) {
+                smap_add(&options, "hairpin_snat_ip", hairpin_snat_ip);
+            }
+            sbrec_load_balancer_set_options(lb->slb, &options);
+            smap_destroy(&options);
+        }
     }
 
     /* Datapath_Binding.load_balancers is not used anymore, it's still in the