diff mbox series

[ovs-dev,v3,8/8] northd: Handle load balancer/group changes for a logical router.

Message ID 20230725174031.2804807-1-numans@ovn.org
State Superseded
Headers show
Series northd: I-P for load balancer and lb groups | expand

Checks

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

Commit Message

Numan Siddique July 25, 2023, 5:40 p.m. UTC
From: Numan Siddique <numans@ovn.org>

When a logical router gets updated due to load balancer or load balancer
groups changes, it is now incrementally handled first in 'lb_data'
engine node similar to how logical switch changes are handled.  The
tracking data of 'lb_data' is updated similarly so that northd engine
handler - northd_handle_lb_data_changes_post_od() handles it.

A new handler northd_handle_lr_changes() is added in the 'northd' engine
node for logical router changes.  This handler returns true if only
load balancer or load balancer group columns are changed.  It returns
false for any other changes.

northd_handle_lb_data_changes_post_od() also sets the logical router
od's lb_ips accordingly.

Below are the scale testing results done with these patches applied
using ovn-heater.  The test ran the scenario  -
ocp-500-density-heavy.yml [1].

With these patches applied (with load balancer I-P handling in northd
engine node) the resuts are:

-------------------------------------------------------------------------------------------------------------------------------------------------------
			Min (s)	        Median (s)	90%ile (s)	99%ile (s)	Max (s)	        Mean (s)	Total (s)	Count	Failed
-------------------------------------------------------------------------------------------------------------------------------------------------------
Iteration Total		0.132929	2.157103	3.314847	3.331561	4.378626	1.581889	197.736147	125	0
Namespace.add_ports	0.005217	0.005760	0.006565	0.013348	0.021014	0.006106	0.763214	125	0
WorkerNode.bind_port	0.035205	0.045458	0.052278	0.059804	0.063941	0.045652	11.413122	250	0
WorkerNode.ping_port	0.005075	0.006814	3.088548	3.192577	4.242026	0.726453	181.613284	250	0
-------------------------------------------------------------------------------------------------------------------------------------------------------

The results with the present main are:

-------------------------------------------------------------------------------------------------------------------------------------------------------
                        Min (s)	        Median (s)	90%ile (s)	99%ile (s)	Max (s)	        Mean (s)	Total (s)	Count	Failed
-------------------------------------------------------------------------------------------------------------------------------------------------------
Iteration Total	        4.377260	6.486962	7.502040	8.322587	8.334701	6.559002	819.875306	125	0
Namespace.add_ports	0.005112	0.005484	0.005953	0.009153	0.011452	0.005662	0.707752	125	0
WorkerNode.bind_port	0.035360	0.042732	0.049152	0.053698	0.056635	0.043215	10.803700	250	0
WorkerNode.ping_port	0.005338	1.599904	7.229649	7.798039	8.206537	3.209860	802.464911	250	0
-------------------------------------------------------------------------------------------------------------------------------------------------------

Few observations:

 - The total time taken has come down significantly from 819 seconds to 197.
 - 99%ile with these patches is 3.3 seconds compared to 8.3 seconds for the
   main.
 - 90%file with these patches is 3.3 seconds compared to 7.5 seconds for
   the main.
 - CPU utilization of northd during the test with these patches
   is between 100% to 300% which is almost the same as main.
   Main difference being that, with these patches the test duration is
   less and hence overall less CPU utilization.

[1] - https://github.com/ovn-org/ovn-heater/blob/main/test-scenarios/ocp-500-density-heavy.yml

Signed-off-by: Numan Siddique <numans@ovn.org>
---
 lib/lb.c                 |   7 +-
 lib/lb.h                 |   1 +
 northd/en-lb-data.c      | 262 +++++++++++++++++++++++++++++++--------
 northd/en-lb-data.h      |   9 ++
 northd/en-northd.c       |  26 ++++
 northd/en-northd.h       |   1 +
 northd/inc-proc-northd.c |   5 +-
 northd/northd.c          | 142 ++++++++++++++++++++-
 northd/northd.h          |   3 +
 tests/ovn-northd.at      |   6 +-
 10 files changed, 405 insertions(+), 57 deletions(-)
diff mbox series

Patch

diff --git a/lib/lb.c b/lib/lb.c
index e1ac9fcf08..caddf37e54 100644
--- a/lib/lb.c
+++ b/lib/lb.c
@@ -794,6 +794,7 @@  ovn_lb_group_init(struct ovn_lb_group *lb_group,
         const struct uuid *lb_uuid =
             &nbrec_lb_group->load_balancer[i]->header_.uuid;
         lb_group->lbs[i] = ovn_northd_lb_find(lbs, lb_uuid);
+        lb_group->has_routable_lb |= lb_group->lbs[i]->routable;
     }
 }
 
@@ -814,6 +815,7 @@  static void
 ovn_lb_group_cleanup(struct ovn_lb_group *lb_group)
 {
     ovn_lb_ip_set_destroy(lb_group->lb_ips);
+    lb_group->has_routable_lb = false;
     free(lb_group->lbs);
 }
 
@@ -1078,7 +1080,10 @@  ovn_lb_datapaths_add_lr(struct ovn_lb_datapaths *lb_dps, size_t n,
                         struct ovn_datapath **ods)
 {
     for (size_t i = 0; i < n; i++) {
-        bitmap_set1(lb_dps->nb_lr_map, ods[i]->index);
+        if (!bitmap_is_set(lb_dps->nb_lr_map, ods[i]->index)) {
+            bitmap_set1(lb_dps->nb_lr_map, ods[i]->index);
+            lb_dps->n_nb_lr++;
+        }
     }
 }
 
diff --git a/lib/lb.h b/lib/lb.h
index 74905c73b7..4acc73e3cf 100644
--- a/lib/lb.h
+++ b/lib/lb.h
@@ -145,6 +145,7 @@  struct ovn_lb_group {
     size_t n_lbs;
     struct ovn_northd_lb **lbs;
     struct ovn_lb_ip_set *lb_ips;
+    bool has_routable_lb;
 };
 
 struct ovn_lb_group *ovn_lb_group_create(
diff --git a/northd/en-lb-data.c b/northd/en-lb-data.c
index ebc7a186cd..08519941d4 100644
--- a/northd/en-lb-data.c
+++ b/northd/en-lb-data.c
@@ -39,12 +39,23 @@  static void build_lbs(const struct nbrec_load_balancer_table *,
                       const struct nbrec_load_balancer_group_table *,
                       struct hmap *lbs, struct hmap *lb_groups);
 static void build_od_lb_map(const struct nbrec_logical_switch_table *,
-                             struct hmap *od_lb_map);
+                            const struct nbrec_logical_router_table *,
+                            struct hmap *ls_lb_map, struct hmap *lr_lb_map);
 static struct od_lb_data *find_od_lb_data(struct hmap *od_lb_map,
                                           const struct uuid *od_uuid);
 static void destroy_od_lb_data(struct od_lb_data *od_lb_data);
 static struct od_lb_data *create_od_lb_data(struct hmap *od_lb_map,
                                             const struct uuid *od_uuid);
+static void handle_od_lb_changes(struct nbrec_load_balancer **,
+                                 size_t n_nbrec_lbs,
+                                 struct od_lb_data *od_lb_data,
+                                 struct ed_type_lb_data *lb_data,
+                                 struct crupdated_od_lb_data *);
+static void handle_od_lbgrp_changes(struct nbrec_load_balancer_group **,
+                                    size_t n_nbrec_lbs,
+                                    struct od_lb_data *,
+                                    struct ed_type_lb_data *lb_data,
+                                    struct crupdated_od_lb_data *);
 
 static struct ovn_lb_group *create_lb_group(
     const struct nbrec_load_balancer_group *, struct hmap *lbs,
@@ -63,6 +74,8 @@  static inline void add_deleted_lb_group_to_tracked_data(
     struct ovn_lb_group *, struct tracked_lb_data *);
 static bool is_ls_lbs_changed(const struct nbrec_logical_switch *nbs);
 static bool is_ls_lbgrps_changed(const struct nbrec_logical_switch *nbs);
+static bool is_lr_lbs_changed(const struct nbrec_logical_router *);
+static bool is_lr_lbgrps_changed(const struct nbrec_logical_router *);
 
 /* 'lb_data' engine node manages the NB load balancers and load balancer
  * groups.  For each NB LB, it creates 'struct ovn_northd_lb' and
@@ -91,10 +104,13 @@  en_lb_data_run(struct engine_node *node, void *data)
         EN_OVSDB_GET(engine_get_input("NB_load_balancer_group", node));
     const struct nbrec_logical_switch_table *nb_ls_table =
         EN_OVSDB_GET(engine_get_input("NB_logical_switch", node));
+    const struct nbrec_logical_router_table *nb_lr_table =
+        EN_OVSDB_GET(engine_get_input("NB_logical_router", node));
 
     lb_data->tracked = false;
     build_lbs(nb_lb_table, nb_lbg_table, &lb_data->lbs, &lb_data->lb_groups);
-    build_od_lb_map(nb_ls_table, &lb_data->ls_lb_map);
+    build_od_lb_map(nb_ls_table, nb_lr_table, &lb_data->ls_lb_map,
+                    &lb_data->lr_lb_map);
 
     engine_set_node_state(node, EN_UPDATED);
 }
@@ -136,6 +152,7 @@  lb_data_load_balancer_handler(struct engine_node *node, void *data)
                         uuid_hash(&tracked_lb->header_.uuid));
             add_crupdated_lb_to_tracked_data(lb, trk_lb_data,
                                              lb->health_checks);
+            trk_lb_data->has_routable_lb |= lb->routable;
         } else if (nbrec_load_balancer_is_deleted(tracked_lb)) {
             lb = ovn_northd_lb_find(&lb_data->lbs,
                                     &tracked_lb->header_.uuid);
@@ -143,6 +160,7 @@  lb_data_load_balancer_handler(struct engine_node *node, void *data)
             hmap_remove(&lb_data->lbs, &lb->hmap_node);
             add_deleted_lb_to_tracked_data(lb, trk_lb_data,
                                            lb->health_checks);
+            trk_lb_data->has_routable_lb |= lb->routable;
         } else {
             /* Load balancer updated. */
             lb = ovn_northd_lb_find(&lb_data->lbs,
@@ -152,6 +170,7 @@  lb_data_load_balancer_handler(struct engine_node *node, void *data)
             ovn_northd_lb_reinit(lb, tracked_lb);
             health_checks |= lb->health_checks;
             add_crupdated_lb_to_tracked_data(lb, trk_lb_data, health_checks);
+            trk_lb_data->has_routable_lb |= lb->routable;
         }
     }
 
@@ -180,6 +199,8 @@  lb_data_load_balancer_group_handler(struct engine_node *node, void *data)
             for (size_t i = 0; i < lb_group->n_lbs; i++) {
                 hmapx_add(&clbg->assoc_lbs, lb_group->lbs[i]);
             }
+
+            trk_lb_data->has_routable_lb |= lb_group->has_routable_lb;
         } else if (nbrec_load_balancer_group_is_deleted(tracked_lb_group)) {
             struct ovn_lb_group *lb_group;
             lb_group = ovn_lb_group_find(&lb_data->lb_groups,
@@ -187,6 +208,7 @@  lb_data_load_balancer_group_handler(struct engine_node *node, void *data)
             ovs_assert(lb_group);
             hmap_remove(&lb_data->lb_groups, &lb_group->hmap_node);
             add_deleted_lb_group_to_tracked_data(lb_group, trk_lb_data);
+            trk_lb_data->has_routable_lb |= lb_group->has_routable_lb;
         } else if (nbrec_load_balancer_group_is_updated(tracked_lb_group,
                                 NBREC_LOAD_BALANCER_GROUP_COL_LOAD_BALANCER)) {
 
@@ -209,6 +231,8 @@  lb_data_load_balancer_group_handler(struct engine_node *node, void *data)
                 build_lrouter_lb_ips(lb_group->lb_ips, lb_group->lbs[i]);
             }
 
+            trk_lb_data->has_routable_lb |= lb_group->has_routable_lb;
+
             struct crupdated_lb_group *clbg =
                 add_crupdated_lb_group_to_tracked_data(lb_group, trk_lb_data);
 
@@ -275,6 +299,7 @@  lb_data_logical_switch_handler(struct engine_node *node, void *data)
             if (!is_ls_lbs_changed(nbs) && !is_ls_lbgrps_changed(nbs)) {
                 continue;
             }
+            changed = true;
             struct crupdated_od_lb_data *codlb = xzalloc(sizeof *codlb);
             codlb->od_uuid = nbs->header_.uuid;
             uuidset_init(&codlb->assoc_lbs);
@@ -288,59 +313,75 @@  lb_data_logical_switch_handler(struct engine_node *node, void *data)
             }
 
             if (is_ls_lbs_changed(nbs)) {
-                struct uuidset *pre_lb_uuids = od_lb_data->lbs;
-                od_lb_data->lbs = xzalloc(sizeof *od_lb_data->lbs);
-                uuidset_init(od_lb_data->lbs);
-
-                for (size_t i = 0; i < nbs->n_load_balancer; i++) {
-                    const struct uuid *lb_uuid =
-                        &nbs->load_balancer[i]->header_.uuid;
-                    uuidset_insert(od_lb_data->lbs, lb_uuid);
-
-                    if (!uuidset_find_and_delete(pre_lb_uuids,
-                                                    lb_uuid)) {
-                        /* Add this lb to the tracked data. */
-                        uuidset_insert(&codlb->assoc_lbs, lb_uuid);
-                        changed = true;
-                    }
-                }
+                handle_od_lb_changes(nbs->load_balancer, nbs->n_load_balancer,
+                                     od_lb_data, lb_data, codlb);
+            }
 
-                if (!uuidset_is_empty(pre_lb_uuids)) {
-                    trk_lb_data->has_dissassoc_lbs_from_od = true;
-                    changed = true;
-                }
+            if (is_ls_lbgrps_changed(nbs)) {
+                handle_od_lbgrp_changes(nbs->load_balancer_group,
+                                        nbs->n_load_balancer_group,
+                                        od_lb_data, lb_data, codlb);
+            }
+
+            ovs_list_insert(&trk_lb_data->crupdated_ls_lbs, &codlb->list_node);
+        }
+    }
+
+    if (changed) {
+        engine_set_node_state(node, EN_UPDATED);
+    }
+    return true;
+}
+
+bool
+lb_data_logical_router_handler(struct engine_node *node, void *data)
+{
+    struct ed_type_lb_data *lb_data = (struct ed_type_lb_data *) data;
+    const struct nbrec_logical_router_table *nbrec_lr_table =
+        EN_OVSDB_GET(engine_get_input("NB_logical_router", node));
+
+    struct tracked_lb_data *trk_lb_data = &lb_data->tracked_lb_data;
+    lb_data->tracked = true;
 
-                uuidset_destroy(pre_lb_uuids);
-                free(pre_lb_uuids);
+    bool changed = false;
+    const struct nbrec_logical_router *nbr;
+    NBREC_LOGICAL_ROUTER_TABLE_FOR_EACH_TRACKED (nbr, nbrec_lr_table) {
+        if (nbrec_logical_router_is_deleted(nbr)) {
+            struct od_lb_data *od_lb_data =
+                find_od_lb_data(&lb_data->lr_lb_map, &nbr->header_.uuid);
+            if (od_lb_data) {
+                hmap_remove(&lb_data->lr_lb_map, &od_lb_data->hmap_node);
+                destroy_od_lb_data(od_lb_data);
+            }
+        } else {
+            if (!is_lr_lbs_changed(nbr) && !is_lr_lbgrps_changed(nbr)) {
+                continue;
             }
+            changed = true;
+            struct crupdated_od_lb_data *codlb = xzalloc(sizeof *codlb);
+            codlb->od_uuid = nbr->header_.uuid;
+            uuidset_init(&codlb->assoc_lbs);
+            uuidset_init(&codlb->assoc_lbgrps);
 
-            if (is_ls_lbgrps_changed(nbs)) {
-                struct uuidset *pre_lbgrp_uuids = od_lb_data->lbgrps;
-                od_lb_data->lbgrps = xzalloc(sizeof *od_lb_data->lbgrps);
-                uuidset_init(od_lb_data->lbgrps);
-                for (size_t i = 0; i < nbs->n_load_balancer_group; i++) {
-                    const struct uuid *lbg_uuid =
-                        &nbs->load_balancer_group[i]->header_.uuid;
-                    uuidset_insert(od_lb_data->lbgrps, lbg_uuid);
-
-                    if (!uuidset_find_and_delete(pre_lbgrp_uuids,
-                                                 lbg_uuid)) {
-                        /* Add this lb group to the tracked data. */
-                        uuidset_insert(&codlb->assoc_lbgrps, lbg_uuid);
-                        changed = true;
-                    }
-                }
+            struct od_lb_data *od_lb_data =
+                find_od_lb_data(&lb_data->lr_lb_map, &nbr->header_.uuid);
+            if (!od_lb_data) {
+                od_lb_data = create_od_lb_data(&lb_data->lr_lb_map,
+                                                &nbr->header_.uuid);
+            }
 
-                if (!uuidset_is_empty(pre_lbgrp_uuids)) {
-                    trk_lb_data->has_dissassoc_lbgrps_from_od = true;
-                    changed = true;
-                }
+            if (is_lr_lbs_changed(nbr)) {
+                handle_od_lb_changes(nbr->load_balancer, nbr->n_load_balancer,
+                                     od_lb_data, lb_data, codlb);
+            }
 
-                uuidset_destroy(pre_lbgrp_uuids);
-                free(pre_lbgrp_uuids);
+            if (is_lr_lbgrps_changed(nbr)) {
+                handle_od_lbgrp_changes(nbr->load_balancer_group,
+                                        nbr->n_load_balancer_group,
+                                        od_lb_data, lb_data, codlb);
             }
 
-            ovs_list_insert(&trk_lb_data->crupdated_ls_lbs, &codlb->list_node);
+            ovs_list_insert(&trk_lb_data->crupdated_lr_lbs, &codlb->list_node);
         }
     }
 
@@ -357,6 +398,7 @@  lb_data_init(struct ed_type_lb_data *lb_data)
     hmap_init(&lb_data->lbs);
     hmap_init(&lb_data->lb_groups);
     hmap_init(&lb_data->ls_lb_map);
+    hmap_init(&lb_data->lr_lb_map);
 
     struct tracked_lb_data *trk_lb_data = &lb_data->tracked_lb_data;
     hmapx_init(&trk_lb_data->crupdated_lbs);
@@ -364,6 +406,7 @@  lb_data_init(struct ed_type_lb_data *lb_data)
     hmapx_init(&trk_lb_data->crupdated_lb_groups);
     hmapx_init(&trk_lb_data->deleted_lb_groups);
     ovs_list_init(&trk_lb_data->crupdated_ls_lbs);
+    ovs_list_init(&trk_lb_data->crupdated_lr_lbs);
 }
 
 static void
@@ -387,6 +430,11 @@  lb_data_destroy(struct ed_type_lb_data *lb_data)
     }
     hmap_destroy(&lb_data->ls_lb_map);
 
+    HMAP_FOR_EACH_POP (od_lb_data, hmap_node, &lb_data->lr_lb_map) {
+        destroy_od_lb_data(od_lb_data);
+    }
+    hmap_destroy(&lb_data->lr_lb_map);
+
     destroy_tracked_data(lb_data);
     hmapx_destroy(&lb_data->tracked_lb_data.crupdated_lbs);
     hmapx_destroy(&lb_data->tracked_lb_data.deleted_lbs);
@@ -430,7 +478,8 @@  create_lb_group(const struct nbrec_load_balancer_group *nbrec_lb_group,
 
 static void
 build_od_lb_map(const struct nbrec_logical_switch_table *nbrec_ls_table,
-                 struct hmap *ls_lb_map)
+                const struct nbrec_logical_router_table *nbrec_lr_table,
+                struct hmap *ls_lb_map, struct hmap *lr_lb_map)
 {
     const struct nbrec_logical_switch *nbrec_ls;
     NBREC_LOGICAL_SWITCH_TABLE_FOR_EACH (nbrec_ls, nbrec_ls_table) {
@@ -438,17 +487,35 @@  build_od_lb_map(const struct nbrec_logical_switch_table *nbrec_ls_table,
             continue;
         }
 
-        struct od_lb_data *od_lb_data =
+        struct od_lb_data *ls_lb_data =
             create_od_lb_data(ls_lb_map, &nbrec_ls->header_.uuid);
         for (size_t i = 0; i < nbrec_ls->n_load_balancer; i++) {
-            uuidset_insert(od_lb_data->lbs,
+            uuidset_insert(ls_lb_data->lbs,
                            &nbrec_ls->load_balancer[i]->header_.uuid);
         }
         for (size_t i = 0; i < nbrec_ls->n_load_balancer_group; i++) {
-            uuidset_insert(od_lb_data->lbgrps,
+            uuidset_insert(ls_lb_data->lbgrps,
                            &nbrec_ls->load_balancer_group[i]->header_.uuid);
         }
     }
+
+    const struct nbrec_logical_router *nbrec_lr;
+    NBREC_LOGICAL_ROUTER_TABLE_FOR_EACH (nbrec_lr, nbrec_lr_table) {
+        if (!nbrec_lr->n_load_balancer && !nbrec_lr->n_load_balancer_group) {
+            continue;
+        }
+
+        struct od_lb_data *lr_lb_data =
+            create_od_lb_data(lr_lb_map, &nbrec_lr->header_.uuid);
+        for (size_t i = 0; i < nbrec_lr->n_load_balancer; i++) {
+            uuidset_insert(lr_lb_data->lbs,
+                           &nbrec_lr->load_balancer[i]->header_.uuid);
+        }
+        for (size_t i = 0; i < nbrec_lr->n_load_balancer_group; i++) {
+            uuidset_insert(lr_lb_data->lbgrps,
+                           &nbrec_lr->load_balancer_group[i]->header_.uuid);
+        }
+    }
 }
 
 static struct od_lb_data *
@@ -490,6 +557,77 @@  destroy_od_lb_data(struct od_lb_data *od_lb_data)
     free(od_lb_data);
 }
 
+static void
+handle_od_lb_changes(struct nbrec_load_balancer **nbrec_lbs,
+                     size_t n_nbrec_lbs, struct od_lb_data *od_lb_data,
+                     struct ed_type_lb_data *lb_data,
+                     struct crupdated_od_lb_data *codlb)
+{
+    struct tracked_lb_data *trk_lb_data = &lb_data->tracked_lb_data;
+    struct uuidset *pre_lb_uuids = od_lb_data->lbs;
+    od_lb_data->lbs = xzalloc(sizeof *od_lb_data->lbs);
+    uuidset_init(od_lb_data->lbs);
+
+    for (size_t i = 0; i < n_nbrec_lbs; i++) {
+        const struct uuid *lb_uuid = &nbrec_lbs[i]->header_.uuid;
+        uuidset_insert(od_lb_data->lbs, lb_uuid);
+
+        if (!uuidset_find_and_delete(pre_lb_uuids, lb_uuid)) {
+            /* Add this lb to the tracked data. */
+            uuidset_insert(&codlb->assoc_lbs, lb_uuid);
+
+            if (!trk_lb_data->has_routable_lb) {
+                struct ovn_northd_lb *lb = ovn_northd_lb_find(&lb_data->lbs,
+                                                          lb_uuid);
+                ovs_assert(lb);
+                trk_lb_data->has_routable_lb |= lb->routable;
+            }
+        }
+    }
+
+    if (!uuidset_is_empty(pre_lb_uuids)) {
+        trk_lb_data->has_dissassoc_lbs_from_od = true;
+    }
+
+    uuidset_destroy(pre_lb_uuids);
+    free(pre_lb_uuids);
+}
+
+static void
+handle_od_lbgrp_changes(struct nbrec_load_balancer_group **nbrec_lbgrps,
+                        size_t n_nbrec_lbgrps, struct od_lb_data *od_lb_data,
+                        struct ed_type_lb_data *lb_data,
+                        struct crupdated_od_lb_data *codlb)
+{
+    struct tracked_lb_data *trk_lb_data = &lb_data->tracked_lb_data;
+    struct uuidset *pre_lbgrp_uuids = od_lb_data->lbgrps;
+    od_lb_data->lbgrps = xzalloc(sizeof *od_lb_data->lbgrps);
+    uuidset_init(od_lb_data->lbgrps);
+    for (size_t i = 0; i < n_nbrec_lbgrps; i++) {
+        const struct uuid *lbgrp_uuid = &nbrec_lbgrps[i]->header_.uuid;
+        uuidset_insert(od_lb_data->lbgrps, lbgrp_uuid);
+
+        if (!uuidset_find_and_delete(pre_lbgrp_uuids, lbgrp_uuid)) {
+            /* Add this lb group to the tracked data. */
+            uuidset_insert(&codlb->assoc_lbgrps, lbgrp_uuid);
+
+            if (!trk_lb_data->has_routable_lb) {
+                struct ovn_lb_group *lbgrp =
+                    ovn_lb_group_find(&lb_data->lb_groups, lbgrp_uuid);
+                ovs_assert(lbgrp);
+                trk_lb_data->has_routable_lb |= lbgrp->has_routable_lb;
+            }
+        }
+    }
+
+    if (!uuidset_is_empty(pre_lbgrp_uuids)) {
+        trk_lb_data->has_dissassoc_lbgrps_from_od = true;
+    }
+
+    uuidset_destroy(pre_lbgrp_uuids);
+    free(pre_lbgrp_uuids);
+}
+
 static void
 destroy_tracked_data(struct ed_type_lb_data *lb_data)
 {
@@ -498,6 +636,7 @@  destroy_tracked_data(struct ed_type_lb_data *lb_data)
     lb_data->tracked_lb_data.has_dissassoc_lbs_from_lb_grops = false;
     lb_data->tracked_lb_data.has_dissassoc_lbs_from_od = false;
     lb_data->tracked_lb_data.has_dissassoc_lbgrps_from_od = false;
+    lb_data->tracked_lb_data.has_routable_lb = false;
 
     struct hmapx_node *node;
     HMAPX_FOR_EACH_SAFE (node, &lb_data->tracked_lb_data.deleted_lbs) {
@@ -528,6 +667,13 @@  destroy_tracked_data(struct ed_type_lb_data *lb_data)
         uuidset_destroy(&codlb->assoc_lbgrps);
         free(codlb);
     }
+    LIST_FOR_EACH_SAFE (codlb, list_node,
+                        &lb_data->tracked_lb_data.crupdated_lr_lbs) {
+        ovs_list_remove(&codlb->list_node);
+        uuidset_destroy(&codlb->assoc_lbs);
+        uuidset_destroy(&codlb->assoc_lbgrps);
+        free(codlb);
+    }
 }
 
 static inline void
@@ -583,3 +729,17 @@  is_ls_lbgrps_changed(const struct nbrec_logical_switch *nbs) {
             ||  nbrec_logical_switch_is_updated(nbs,
                         NBREC_LOGICAL_SWITCH_COL_LOAD_BALANCER_GROUP));
 }
+
+static bool
+is_lr_lbs_changed(const struct nbrec_logical_router *nbr) {
+    return ((nbrec_logical_router_is_new(nbr) && nbr->n_load_balancer)
+            ||  nbrec_logical_router_is_updated(nbr,
+                        NBREC_LOGICAL_ROUTER_COL_LOAD_BALANCER));
+}
+
+static bool
+is_lr_lbgrps_changed(const struct nbrec_logical_router *nbr) {
+    return ((nbrec_logical_router_is_new(nbr) && nbr->n_load_balancer_group)
+            ||  nbrec_logical_router_is_updated(nbr,
+                        NBREC_LOGICAL_ROUTER_COL_LOAD_BALANCER_GROUP));
+}
diff --git a/northd/en-lb-data.h b/northd/en-lb-data.h
index 6269e9b548..a9feb13c66 100644
--- a/northd/en-lb-data.h
+++ b/northd/en-lb-data.h
@@ -45,6 +45,10 @@  struct tracked_lb_data {
      * 'struct crupdated_od_lb_data' */
     struct ovs_list crupdated_ls_lbs;
 
+    /* List of logical router <-> lb changes. List node is
+     * 'struct crupdated_od_lb_data' */
+    struct ovs_list crupdated_lr_lbs;
+
     /* Indicates if any of the tracked lb has health checks enabled. */
     bool has_health_checks;
 
@@ -57,6 +61,9 @@  struct tracked_lb_data {
 
     /* Indicates if a lb group was disassociated from a logical switch. */
     bool has_dissassoc_lbgrps_from_od;
+
+    /* Indicates if any lb (in the tracked data) has 'routable' flag set. */
+    bool has_routable_lb;
 };
 
 /* struct which maintains the data of the engine node lb_data. */
@@ -67,6 +74,7 @@  struct ed_type_lb_data {
     /* hmap of load balancer groups.  hmap node is 'struct ovn_lb_group *' */
     struct hmap lb_groups;
     struct hmap ls_lb_map;
+    struct hmap lr_lb_map;
 
     /* tracked data*/
     bool tracked;
@@ -81,5 +89,6 @@  void en_lb_data_clear_tracked_data(void *data);
 bool lb_data_load_balancer_handler(struct engine_node *, void *data);
 bool lb_data_load_balancer_group_handler(struct engine_node *, void *data);
 bool lb_data_logical_switch_handler(struct engine_node *, void *data);
+bool lb_data_logical_router_handler(struct engine_node *, void *data);
 
 #endif /* end of EN_NORTHD_LB_DATA_H */
diff --git a/northd/en-northd.c b/northd/en-northd.c
index 0c2810b5d7..8c3ed964a1 100644
--- a/northd/en-northd.c
+++ b/northd/en-northd.c
@@ -206,6 +206,26 @@  northd_sb_port_binding_handler(struct engine_node *node,
     return true;
 }
 
+bool
+northd_nb_logical_router_handler(struct engine_node *node,
+                                 void *data)
+{
+    struct northd_data *nd = data;
+    struct northd_input input_data;
+
+    northd_get_input_data(node, &input_data);
+
+    if (!northd_handle_lr_changes(&input_data, nd)) {
+        return false;
+    }
+
+    if (nd->change_tracked) {
+        engine_set_node_state(node, EN_UPDATED);
+    }
+
+    return true;
+}
+
 bool
 northd_lb_data_handler_pre_od(struct engine_node *node, void *data)
 {
@@ -244,6 +264,10 @@  northd_lb_data_handler_pre_od(struct engine_node *node, void *data)
         return false;
     }
 
+    if (lb_data->tracked_lb_data.has_routable_lb) {
+        return false;
+    }
+
     struct northd_data *nd = data;
 
     if (!northd_handle_lb_data_changes_pre_od(&lb_data->tracked_lb_data,
@@ -268,11 +292,13 @@  northd_lb_data_handler_post_od(struct engine_node *node, void *data)
     ovs_assert(!lb_data->tracked_lb_data.has_dissassoc_lbs_from_od);
     ovs_assert(!lb_data->tracked_lb_data.has_dissassoc_lbgrps_from_od);
     ovs_assert(!lb_data->tracked_lb_data.has_dissassoc_lbs_from_lb_grops);
+    ovs_assert(!lb_data->tracked_lb_data.has_routable_lb);
 
     struct northd_data *nd = data;
 
     if (!northd_handle_lb_data_changes_post_od(&lb_data->tracked_lb_data,
                                                &nd->ls_datapaths,
+                                               &nd->lr_datapaths,
                                                &nd->lb_datapaths_map,
                                                &nd->lb_group_datapaths_map)) {
         return false;
diff --git a/northd/en-northd.h b/northd/en-northd.h
index 5926e7a9d3..84d8673e1b 100644
--- a/northd/en-northd.h
+++ b/northd/en-northd.h
@@ -16,6 +16,7 @@  void en_northd_cleanup(void *data);
 void en_northd_clear_tracked_data(void *data);
 bool northd_nb_nb_global_handler(struct engine_node *, void *data OVS_UNUSED);
 bool northd_nb_logical_switch_handler(struct engine_node *, void *data);
+bool northd_nb_logical_router_handler(struct engine_node *, void *data);
 bool northd_sb_port_binding_handler(struct engine_node *, void *data);
 bool northd_lb_data_handler_pre_od(struct engine_node *, void *data);
 bool northd_lb_data_handler_post_od(struct engine_node *, void *data);
diff --git a/northd/inc-proc-northd.c b/northd/inc-proc-northd.c
index dc8b880fd8..9dbc2ec81a 100644
--- a/northd/inc-proc-northd.c
+++ b/northd/inc-proc-northd.c
@@ -155,10 +155,11 @@  void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
                      lb_data_load_balancer_group_handler);
     engine_add_input(&en_lb_data, &en_nb_logical_switch,
                      lb_data_logical_switch_handler);
+    engine_add_input(&en_lb_data, &en_nb_logical_router,
+                     lb_data_logical_router_handler);
 
     engine_add_input(&en_northd, &en_nb_port_group, NULL);
     engine_add_input(&en_northd, &en_nb_acl, NULL);
-    engine_add_input(&en_northd, &en_nb_logical_router, NULL);
     engine_add_input(&en_northd, &en_nb_mirror, NULL);
     engine_add_input(&en_northd, &en_nb_meter, NULL);
     engine_add_input(&en_northd, &en_nb_static_mac_binding, NULL);
@@ -186,6 +187,8 @@  void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
     engine_add_input(&en_northd, &en_lb_data, northd_lb_data_handler_pre_od);
     engine_add_input(&en_northd, &en_nb_logical_switch,
                      northd_nb_logical_switch_handler);
+    engine_add_input(&en_northd, &en_nb_logical_router,
+                     northd_nb_logical_router_handler);
     engine_add_input(&en_northd, &en_lb_data, northd_lb_data_handler_post_od);
 
     engine_add_input(&en_mac_binding_aging, &en_nb_nb_global, NULL);
diff --git a/northd/northd.c b/northd/northd.c
index 0c8eb21ac3..e6f07c50cf 100644
--- a/northd/northd.c
+++ b/northd/northd.c
@@ -5360,6 +5360,95 @@  fail:
     return false;
 }
 
+/* Returns true if the logical router has changes which are not
+ * incrementally handled.
+ * Presently supports i-p for the below changes:
+ *    - load balancers and load balancer groups.
+ */
+static bool
+check_unsupported_inc_proc_for_lr_changes(
+    const struct nbrec_logical_router *lr)
+{
+    /* Check if the columns are changed in this row. */
+    enum nbrec_logical_router_column_id col;
+    for (col = 0; col < NBREC_LOGICAL_ROUTER_N_COLUMNS; col++) {
+        if (nbrec_logical_router_is_updated(lr, col)) {
+            if (col == NBREC_LOGICAL_ROUTER_COL_LOAD_BALANCER ||
+                col == NBREC_LOGICAL_ROUTER_COL_LOAD_BALANCER_GROUP) {
+                continue;
+            }
+            return true;
+        }
+    }
+
+    /* Check if the referenced rows are changed.
+       XXX: Need a better OVSDB IDL interface for this check. */
+    for (size_t i = 0; i < lr->n_ports; i++) {
+        if (nbrec_logical_router_port_row_get_seqno(lr->ports[i],
+                                OVSDB_IDL_CHANGE_MODIFY) > 0) {
+            return true;
+        }
+    }
+    if (lr->copp && nbrec_copp_row_get_seqno(lr->copp,
+                                OVSDB_IDL_CHANGE_MODIFY) > 0) {
+        return true;
+    }
+    for (size_t i = 0; i < lr->n_nat; i++) {
+        if (nbrec_nat_row_get_seqno(lr->nat[i],
+                                OVSDB_IDL_CHANGE_MODIFY) > 0) {
+            return true;
+        }
+    }
+    for (size_t i = 0; i < lr->n_policies; i++) {
+        if (nbrec_logical_router_policy_row_get_seqno(lr->policies[i],
+                                OVSDB_IDL_CHANGE_MODIFY) > 0) {
+            return true;
+        }
+    }
+    for (size_t i = 0; i < lr->n_static_routes; i++) {
+        if (nbrec_logical_router_static_route_row_get_seqno(
+            lr->static_routes[i], OVSDB_IDL_CHANGE_MODIFY) > 0) {
+            return true;
+        }
+    }
+    return false;
+}
+
+/* Return true if changes are handled incrementally, false otherwise.
+ * When there are any changes, try to track what's exactly changed and set
+ * northd_data->change_tracked accordingly: change tracked - true, otherwise,
+ * false.
+ * Note: Changes to load balancer and load balancer groups associated with
+ * the logical routers are handled separately in the lb_data change
+ * handlers (northd_handle_lb_data_changes_pre_od and
+ * northd_handle_lb_data_changes_post_od).
+ * */
+bool
+northd_handle_lr_changes(const struct northd_input *ni,
+                         struct northd_data *nd)
+{
+    const struct nbrec_logical_router *changed_lr;
+
+    NBREC_LOGICAL_ROUTER_TABLE_FOR_EACH_TRACKED (changed_lr,
+                                             ni->nbrec_logical_router_table) {
+        if (nbrec_logical_router_is_new(changed_lr) ||
+            nbrec_logical_router_is_deleted(changed_lr)) {
+            goto fail;
+        }
+
+        /* Presently only able to handle load balancer and
+         * load balancer group changes. */
+        if (check_unsupported_inc_proc_for_lr_changes(changed_lr)) {
+            goto fail;
+        }
+    }
+
+    return true;
+fail:
+    destroy_northd_data_tracked_changes(nd);
+    return false;
+}
+
 bool
 northd_handle_sb_port_binding_changes(
     const struct sbrec_port_binding_table *sbrec_port_binding_table,
@@ -5500,6 +5589,7 @@  northd_handle_lb_data_changes_pre_od(struct tracked_lb_data *trk_lb_data,
 bool
 northd_handle_lb_data_changes_post_od(struct tracked_lb_data *trk_lb_data,
                                       struct ovn_datapaths *ls_datapaths,
+                                      struct ovn_datapaths *lr_datapaths,
                                       struct hmap *lb_datapaths_map,
                                       struct hmap *lb_group_datapaths_map)
 {
@@ -5544,6 +5634,45 @@  northd_handle_lb_data_changes_post_od(struct tracked_lb_data *trk_lb_data,
         init_lb_for_datapath(od);
     }
 
+    LIST_FOR_EACH (codlb, list_node, &trk_lb_data->crupdated_lr_lbs) {
+        od = ovn_datapath_find(&lr_datapaths->datapaths, &codlb->od_uuid);
+        ovs_assert(od);
+
+        struct uuidset_node *uuidnode;
+        UUIDSET_FOR_EACH (uuidnode, &codlb->assoc_lbs) {
+            lb_dps = ovn_lb_datapaths_find(lb_datapaths_map, &uuidnode->uuid);
+            ovs_assert(lb_dps);
+            ovn_lb_datapaths_add_lr(lb_dps, 1, &od);
+
+            /* Add the lb_ips of lb_dps to the od. */
+            build_lrouter_lb_ips(od->lb_ips, lb_dps->lb);
+            build_lrouter_lb_reachable_ips(od, lb_dps->lb);
+        }
+
+        UUIDSET_FOR_EACH (uuidnode, &codlb->assoc_lbgrps) {
+            lbgrp_dps = ovn_lb_group_datapaths_find(lb_group_datapaths_map,
+                                                    &uuidnode->uuid);
+            ovs_assert(lbgrp_dps);
+            ovn_lb_group_datapaths_add_lr(lbgrp_dps, od);
+
+            /* Associate all the lbs of the lbgrp to the datapath 'od' */
+            for (size_t j = 0; j < lbgrp_dps->lb_group->n_lbs; j++) {
+                const struct uuid *lb_uuid
+                    = &lbgrp_dps->lb_group->lbs[j]->nlb->header_.uuid;
+                lb_dps = ovn_lb_datapaths_find(lb_datapaths_map, lb_uuid);
+                ovs_assert(lb_dps);
+                ovn_lb_datapaths_add_lr(lb_dps, 1, &od);
+
+                /* Add the lb_ips of lb_dps to the od. */
+                build_lrouter_lb_ips(od->lb_ips, lb_dps->lb);
+                build_lrouter_lb_reachable_ips(od, lb_dps->lb);
+            }
+        }
+
+        /* Re-evaluate 'od->has_lb_vip' */
+        init_lb_for_datapath(od);
+    }
+
     HMAPX_FOR_EACH (hmapx_node, &trk_lb_data->crupdated_lbs) {
         lb = hmapx_node->data;
         const struct uuid *lb_uuid = &lb->nlb->header_.uuid;
@@ -5576,8 +5705,19 @@  northd_handle_lb_data_changes_post_od(struct tracked_lb_data *trk_lb_data,
             lb_uuid = &lb->nlb->header_.uuid;
             lb_dps = ovn_lb_datapaths_find(lb_datapaths_map, lb_uuid);
             ovs_assert(lb_dps);
+            for (size_t i = 0; i < lbgrp_dps->n_lr; i++) {
+                od = lbgrp_dps->lr[i];
+                ovn_lb_datapaths_add_lr(lb_dps, 1, &od);
+
+                /* Re-evaluate 'od->has_lb_vip' */
+                init_lb_for_datapath(od);
+
+                /* Add the lb_ips of lb_dps to the od. */
+                build_lrouter_lb_ips(od->lb_ips, lb_dps->lb);
+            }
+
             for (size_t i = 0; i < lbgrp_dps->n_ls; i++) {
-                od = lbgrp_dps->ls[i];
+               od = lbgrp_dps->ls[i];
                 ovn_lb_datapaths_add_ls(lb_dps, 1, &od);
 
                 /* Re-evaluate 'od->has_lb_vip' */
diff --git a/northd/northd.h b/northd/northd.h
index 8e39d10f58..2e6fe758dc 100644
--- a/northd/northd.h
+++ b/northd/northd.h
@@ -335,6 +335,8 @@  void ovnsb_db_run(struct ovsdb_idl_txn *ovnnb_txn,
 bool northd_handle_ls_changes(struct ovsdb_idl_txn *,
                               const struct northd_input *,
                               struct northd_data *);
+bool northd_handle_lr_changes(const struct northd_input *,
+                              struct northd_data *);
 void destroy_northd_data_tracked_changes(struct northd_data *);
 void northd_destroy(struct northd_data *data);
 void northd_init(struct northd_data *data);
@@ -357,6 +359,7 @@  bool northd_handle_lb_data_changes_pre_od(struct tracked_lb_data *,
                                           struct hmap *lb_group_datapaths_map);
 bool northd_handle_lb_data_changes_post_od(struct tracked_lb_data *,
                                            struct ovn_datapaths *ls_datapaths,
+                                           struct ovn_datapaths *lr_datapaths,
                                            struct hmap *lb_datapaths_map,
                                            struct hmap *lb_group_datapaths_map);
 
diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
index a7b47333c4..f836bdf95c 100644
--- a/tests/ovn-northd.at
+++ b/tests/ovn-northd.at
@@ -9869,7 +9869,7 @@  CHECK_NO_CHANGE_AFTER_RECOMPUTE
 
 check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
 check ovn-nbctl add logical_router lr0 load_balancer_group $lbg1_uuid
-check_engine_stats recompute recompute
+check_engine_stats norecompute recompute
 CHECK_NO_CHANGE_AFTER_RECOMPUTE
 
 check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
@@ -9918,7 +9918,7 @@  CHECK_NO_CHANGE_AFTER_RECOMPUTE
 
 check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
 check ovn-nbctl set logical_router lr1 load_balancer_group=$lbg1_uuid
-check_engine_stats recompute recompute
+check_engine_stats norecompute recompute
 CHECK_NO_CHANGE_AFTER_RECOMPUTE
 
 check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
@@ -9934,7 +9934,7 @@  CHECK_NO_CHANGE_AFTER_RECOMPUTE
 check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
 check ovn-nbctl --wait=sb lr-lb-add lr1 lb1
 check ovn-nbctl --wait=sb lr-lb-add lr1 lb2
-check_engine_stats recompute recompute
+check_engine_stats norecompute recompute
 CHECK_NO_CHANGE_AFTER_RECOMPUTE
 
 check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats