diff mbox series

[ovs-dev,v5,3/6] northd: Introduce struct northd_data

Message ID 20211109193607.31630-4-mrmarkgray@gmail.com
State Accepted
Delegated to: Han Zhou
Headers show
Series northd: Introduce incremental processing framework | expand

Checks

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

Commit Message

Mark Gray Nov. 9, 2021, 7:36 p.m. UTC
From: Mark Gray <mark.d.gray@redhat.com>

'struct northd_data' is used to hold the global state data for the
incremental processing node 'en_northd'. This structure will also hold
'struct northd_input' which will hold references to output data from
its input nodes. In particular, this will hold references to database tables
and indexes. In order to achieve this, we refactor in the following way:

* Introduce northd_init() which initializes this data.
* Introduce northd_destroy() which clears this data for a new iteration.
* Remove 'ovn_internal_version' from the context passed to northd
  as it can be determined within northd using ovn_get_internal_version.
* Remove 'use_parallel_build' from the context as it is read from DB as
  suggested by Han.
* Refactor northd.c to use 'struct northd_data' and 'struct northd_input'
  where applicable.

Signed-off-by: Mark Gray <mark.d.gray@redhat.com>
---
 lib/inc-proc-eng.h       |   3 +
 northd/en-northd.c       | 107 ++++++-
 northd/inc-proc-northd.c |  42 ++-
 northd/inc-proc-northd.h |   6 +-
 northd/northd.c          | 632 ++++++++++++++++++++++-----------------
 northd/northd.h          |  73 ++++-
 northd/ovn-northd.c      |  87 ++----
 7 files changed, 596 insertions(+), 354 deletions(-)

Comments

0-day Robot Nov. 9, 2021, 8 p.m. UTC | #1
Bleep bloop.  Greetings Mark Gray, I am a robot and I have tried out your patch.
Thanks for your contribution.

I encountered some error that I wasn't expecting.  See the details below.


checkpatch:
WARNING: Comment with 'xxx' marker
#1500 FILE: northd/northd.c:14516:
    /* XXX Having to explicitly clean up macam here

Lines checked: 2176, Warnings: 1, Errors: 0


Please check this out.  If you feel there has been an error, please email aconole@redhat.com

Thanks,
0-day Robot
Han Zhou Nov. 17, 2021, 6:41 a.m. UTC | #2
On Tue, Nov 9, 2021 at 11:36 AM Mark Gray <mrmarkgray@gmail.com> wrote:
>
> From: Mark Gray <mark.d.gray@redhat.com>
>
> 'struct northd_data' is used to hold the global state data for the
> incremental processing node 'en_northd'. This structure will also hold
> 'struct northd_input' which will hold references to output data from
> its input nodes. In particular, this will hold references to database
tables
> and indexes. In order to achieve this, we refactor in the following way:
>
> * Introduce northd_init() which initializes this data.
> * Introduce northd_destroy() which clears this data for a new iteration.
> * Remove 'ovn_internal_version' from the context passed to northd
>   as it can be determined within northd using ovn_get_internal_version.
> * Remove 'use_parallel_build' from the context as it is read from DB as
>   suggested by Han.
> * Refactor northd.c to use 'struct northd_data' and 'struct northd_input'
>   where applicable.
>
> Signed-off-by: Mark Gray <mark.d.gray@redhat.com>
> ---
>  lib/inc-proc-eng.h       |   3 +
>  northd/en-northd.c       | 107 ++++++-
>  northd/inc-proc-northd.c |  42 ++-
>  northd/inc-proc-northd.h |   6 +-
>  northd/northd.c          | 632 ++++++++++++++++++++++-----------------
>  northd/northd.h          |  73 ++++-
>  northd/ovn-northd.c      |  87 ++----
>  7 files changed, 596 insertions(+), 354 deletions(-)
>
> diff --git a/lib/inc-proc-eng.h b/lib/inc-proc-eng.h
> index f89a40bd54ca..1823750c814c 100644
> --- a/lib/inc-proc-eng.h
> +++ b/lib/inc-proc-eng.h
> @@ -72,6 +72,9 @@ struct engine_context {
>      struct ovsdb_idl_txn *ovs_idl_txn;
>      struct ovsdb_idl_txn *ovnsb_idl_txn;
>      struct ovsdb_idl_txn *ovnnb_idl_txn;
> +
> +    struct ovsdb_idl_loop *ovnsb_idl_loop;
> +
>      void *client_ctx;
>  };
>
> diff --git a/northd/en-northd.c b/northd/en-northd.c
> index d310fa4dd31f..f39dbd9da3e0 100644
> --- a/northd/en-northd.c
> +++ b/northd/en-northd.c
> @@ -20,26 +20,123 @@
>
>  #include "en-northd.h"
>  #include "lib/inc-proc-eng.h"
> +#include "openvswitch/list.h" /* TODO This is needed for
ovn-parallel-hmap.h.
> +                               * lib/ovn-parallel-hmap.h should be
updated
> +                               * to include this dependency itself */
> +#include "lib/ovn-parallel-hmap.h"
>  #include "northd.h"
> +#include "lib/util.h"
>  #include "openvswitch/vlog.h"
>
>  VLOG_DEFINE_THIS_MODULE(en_northd);
>
> -void en_northd_run(struct engine_node *node, void *data OVS_UNUSED)
> +void en_northd_run(struct engine_node *node, void *data)
>  {
>      const struct engine_context *eng_ctx = engine_get_context();
> -    struct northd_context *ctx = eng_ctx->client_ctx;
> -    ovn_db_run(ctx);
>
> +    struct northd_input input_data;
> +
> +    northd_destroy(data);
> +    northd_init(data);
> +
> +    input_data.sbrec_chassis_by_name =
> +        engine_ovsdb_node_get_index(
> +            engine_get_input("SB_chassis", node),
> +            "sbrec_chassis_by_name");
> +    input_data.sbrec_chassis_by_hostname =
> +        engine_ovsdb_node_get_index(
> +            engine_get_input("SB_chassis", node),
> +            "sbrec_chassis_by_hostname");
> +    input_data.sbrec_ha_chassis_grp_by_name =
> +        engine_ovsdb_node_get_index(
> +            engine_get_input("SB_ha_chassis_group", node),
> +            "sbrec_ha_chassis_grp_by_name");
> +    input_data.sbrec_mcast_group_by_name_dp =
> +        engine_ovsdb_node_get_index(
> +            engine_get_input("SB_multicast_group", node),
> +            "sbrec_mcast_group_by_name");
> +    input_data.sbrec_ip_mcast_by_dp =
> +        engine_ovsdb_node_get_index(
> +            engine_get_input("SB_ip_multicast", node),
> +            "sbrec_ip_mcast_by_dp");
> +
> +    input_data.nbrec_nb_global_table =
> +        EN_OVSDB_GET(engine_get_input("NB_nb_global", node));
> +    input_data.nbrec_logical_switch =
> +        EN_OVSDB_GET(engine_get_input("NB_logical_switch", node));
> +    input_data.nbrec_logical_router =
> +        EN_OVSDB_GET(engine_get_input("NB_logical_router", node));
> +    input_data.nbrec_load_balancer_table =
> +        EN_OVSDB_GET(engine_get_input("NB_load_balancer", node));
> +    input_data.nbrec_port_group_table =
> +        EN_OVSDB_GET(engine_get_input("NB_port_group", node));
> +    input_data.nbrec_bfd_table =
> +        EN_OVSDB_GET(engine_get_input("NB_bfd", node));
> +    input_data.nbrec_address_set_table =
> +        EN_OVSDB_GET(engine_get_input("NB_address_set", node));
> +    input_data.nbrec_meter_table =
> +        EN_OVSDB_GET(engine_get_input("NB_meter", node));
> +    input_data.nbrec_acl_table =
> +        EN_OVSDB_GET(engine_get_input("NB_acl", node));
> +
> +    input_data.sbrec_sb_global_table =
> +        EN_OVSDB_GET(engine_get_input("SB_sb_global", node));
> +    input_data.sbrec_datapath_binding_table =
> +        EN_OVSDB_GET(engine_get_input("SB_datapath_binding", node));
> +    input_data.sbrec_port_binding_table =
> +        EN_OVSDB_GET(engine_get_input("SB_port_binding", node));
> +    input_data.sbrec_mac_binding_table =
> +        EN_OVSDB_GET(engine_get_input("SB_mac_binding", node));
> +    input_data.sbrec_ha_chassis_group_table =
> +        EN_OVSDB_GET(engine_get_input("SB_ha_chassis_group", node));
> +    input_data.sbrec_chassis =
> +        EN_OVSDB_GET(engine_get_input("SB_chassis", node));
> +    input_data.sbrec_fdb_table =
> +        EN_OVSDB_GET(engine_get_input("SB_fdb", node));
> +    input_data.sbrec_load_balancer_table =
> +        EN_OVSDB_GET(engine_get_input("SB_load_balancer", node));
> +    input_data.sbrec_service_monitor_table =
> +        EN_OVSDB_GET(engine_get_input("SB_service_monitor", node));
> +    input_data.sbrec_bfd_table =
> +        EN_OVSDB_GET(engine_get_input("SB_bfd", node));
> +    input_data.sbrec_logical_flow_table =
> +        EN_OVSDB_GET(engine_get_input("SB_logical_flow", node));
> +    input_data.sbrec_multicast_group_table =
> +        EN_OVSDB_GET(engine_get_input("SB_multicast_group", node));
> +    input_data.sbrec_address_set_table =
> +        EN_OVSDB_GET(engine_get_input("SB_address_set", node));
> +    input_data.sbrec_port_group_table =
> +        EN_OVSDB_GET(engine_get_input("SB_port_group", node));
> +    input_data.sbrec_meter_table =
> +        EN_OVSDB_GET(engine_get_input("SB_meter", node));
> +    input_data.sbrec_dns_table =
> +        EN_OVSDB_GET(engine_get_input("SB_dns", node));
> +    input_data.sbrec_ip_multicast_table =
> +        EN_OVSDB_GET(engine_get_input("SB_ip_multicast", node));
> +    input_data.sbrec_igmp_group_table =
> +        EN_OVSDB_GET(engine_get_input("SB_igmp_group", node));
> +    input_data.sbrec_chassis_private_table =
> +        EN_OVSDB_GET(engine_get_input("SB_chassis_private", node));
> +
> +    northd_run(&input_data, data,
> +               eng_ctx->ovnnb_idl_txn,
> +               eng_ctx->ovnsb_idl_txn,
> +               eng_ctx->ovnsb_idl_loop);
>      engine_set_node_state(node, EN_UPDATED);
>
>  }
>  void *en_northd_init(struct engine_node *node OVS_UNUSED,
>                       struct engine_arg *arg OVS_UNUSED)
>  {
> -    return NULL;
> +    struct northd_data *data = xmalloc(sizeof *data);
> +
> +    northd_init(data);
> +
> +    return data;
>  }
>
> -void en_northd_cleanup(void *data OVS_UNUSED)
> +void en_northd_cleanup(void *data)
>  {
> +    northd_destroy(data);
> +    free(data);

This will cause double free. I saw that you removed it in the next patch,
but it shouldn't be here in the first place. I don't think it needs another
revision, and I can just remove this line before merging.

...

> +void
> +northd_destroy(struct northd_data *data)
> +{
> +    struct ovn_northd_lb *lb;
> +    HMAP_FOR_EACH_POP (lb, hmap_node, &data->lbs) {
> +        ovn_northd_lb_destroy(lb);
> +    }
> +    hmap_destroy(&data->lbs);
> +
> +    struct ovn_igmp_group *igmp_group, *next_igmp_group;
> +
> +    HMAP_FOR_EACH_SAFE (igmp_group, next_igmp_group, hmap_node,
> +                        &data->igmp_groups) {
> +        ovn_igmp_group_destroy(&data->igmp_groups, igmp_group);
> +    }
> +
> +    struct ovn_port_group *pg, *next_pg;
> +    HMAP_FOR_EACH_SAFE (pg, next_pg, key_node, &data->port_groups) {
> +        ovn_port_group_destroy(&data->port_groups, pg);
> +    }
> +
> +    hmap_destroy(&data->igmp_groups);
> +    hmap_destroy(&data->mcast_groups);
> +    hmap_destroy(&data->port_groups);
> +    hmap_destroy(&data->bfd_connections);
> +
> +    struct shash_node *node, *next;
> +    SHASH_FOR_EACH_SAFE (node, next, &data->meter_groups) {
> +        shash_delete(&data->meter_groups, node);
> +    }
> +    shash_destroy(&data->meter_groups);
> +
> +    /* XXX Having to explicitly clean up macam here
> +     * is a bit strange. We don't explicitly initialize
> +     * macam in this module, but this is the logical place
> +     * to clean it up. Ideally, more IPAM logic can be factored
> +     * out of ovn-northd and this can be taken care of there
> +     * as well.
> +     */
> +    cleanup_macam();

Yes, it does look weird here since macam is a static member of the ipam
module but it is updated in the I-P engine. We should either avoid updating
it in I-P engine, or move the data as part of the I-P engine. But I think
it is ok to leave it as a follow-up patch whichever method is appropriate.

Thanks,
Han
diff mbox series

Patch

diff --git a/lib/inc-proc-eng.h b/lib/inc-proc-eng.h
index f89a40bd54ca..1823750c814c 100644
--- a/lib/inc-proc-eng.h
+++ b/lib/inc-proc-eng.h
@@ -72,6 +72,9 @@  struct engine_context {
     struct ovsdb_idl_txn *ovs_idl_txn;
     struct ovsdb_idl_txn *ovnsb_idl_txn;
     struct ovsdb_idl_txn *ovnnb_idl_txn;
+
+    struct ovsdb_idl_loop *ovnsb_idl_loop;
+
     void *client_ctx;
 };
 
diff --git a/northd/en-northd.c b/northd/en-northd.c
index d310fa4dd31f..f39dbd9da3e0 100644
--- a/northd/en-northd.c
+++ b/northd/en-northd.c
@@ -20,26 +20,123 @@ 
 
 #include "en-northd.h"
 #include "lib/inc-proc-eng.h"
+#include "openvswitch/list.h" /* TODO This is needed for ovn-parallel-hmap.h.
+                               * lib/ovn-parallel-hmap.h should be updated
+                               * to include this dependency itself */
+#include "lib/ovn-parallel-hmap.h"
 #include "northd.h"
+#include "lib/util.h"
 #include "openvswitch/vlog.h"
 
 VLOG_DEFINE_THIS_MODULE(en_northd);
 
-void en_northd_run(struct engine_node *node, void *data OVS_UNUSED)
+void en_northd_run(struct engine_node *node, void *data)
 {
     const struct engine_context *eng_ctx = engine_get_context();
-    struct northd_context *ctx = eng_ctx->client_ctx;
-    ovn_db_run(ctx);
 
+    struct northd_input input_data;
+
+    northd_destroy(data);
+    northd_init(data);
+
+    input_data.sbrec_chassis_by_name =
+        engine_ovsdb_node_get_index(
+            engine_get_input("SB_chassis", node),
+            "sbrec_chassis_by_name");
+    input_data.sbrec_chassis_by_hostname =
+        engine_ovsdb_node_get_index(
+            engine_get_input("SB_chassis", node),
+            "sbrec_chassis_by_hostname");
+    input_data.sbrec_ha_chassis_grp_by_name =
+        engine_ovsdb_node_get_index(
+            engine_get_input("SB_ha_chassis_group", node),
+            "sbrec_ha_chassis_grp_by_name");
+    input_data.sbrec_mcast_group_by_name_dp =
+        engine_ovsdb_node_get_index(
+            engine_get_input("SB_multicast_group", node),
+            "sbrec_mcast_group_by_name");
+    input_data.sbrec_ip_mcast_by_dp =
+        engine_ovsdb_node_get_index(
+            engine_get_input("SB_ip_multicast", node),
+            "sbrec_ip_mcast_by_dp");
+
+    input_data.nbrec_nb_global_table =
+        EN_OVSDB_GET(engine_get_input("NB_nb_global", node));
+    input_data.nbrec_logical_switch =
+        EN_OVSDB_GET(engine_get_input("NB_logical_switch", node));
+    input_data.nbrec_logical_router =
+        EN_OVSDB_GET(engine_get_input("NB_logical_router", node));
+    input_data.nbrec_load_balancer_table =
+        EN_OVSDB_GET(engine_get_input("NB_load_balancer", node));
+    input_data.nbrec_port_group_table =
+        EN_OVSDB_GET(engine_get_input("NB_port_group", node));
+    input_data.nbrec_bfd_table =
+        EN_OVSDB_GET(engine_get_input("NB_bfd", node));
+    input_data.nbrec_address_set_table =
+        EN_OVSDB_GET(engine_get_input("NB_address_set", node));
+    input_data.nbrec_meter_table =
+        EN_OVSDB_GET(engine_get_input("NB_meter", node));
+    input_data.nbrec_acl_table =
+        EN_OVSDB_GET(engine_get_input("NB_acl", node));
+
+    input_data.sbrec_sb_global_table =
+        EN_OVSDB_GET(engine_get_input("SB_sb_global", node));
+    input_data.sbrec_datapath_binding_table =
+        EN_OVSDB_GET(engine_get_input("SB_datapath_binding", node));
+    input_data.sbrec_port_binding_table =
+        EN_OVSDB_GET(engine_get_input("SB_port_binding", node));
+    input_data.sbrec_mac_binding_table =
+        EN_OVSDB_GET(engine_get_input("SB_mac_binding", node));
+    input_data.sbrec_ha_chassis_group_table =
+        EN_OVSDB_GET(engine_get_input("SB_ha_chassis_group", node));
+    input_data.sbrec_chassis =
+        EN_OVSDB_GET(engine_get_input("SB_chassis", node));
+    input_data.sbrec_fdb_table =
+        EN_OVSDB_GET(engine_get_input("SB_fdb", node));
+    input_data.sbrec_load_balancer_table =
+        EN_OVSDB_GET(engine_get_input("SB_load_balancer", node));
+    input_data.sbrec_service_monitor_table =
+        EN_OVSDB_GET(engine_get_input("SB_service_monitor", node));
+    input_data.sbrec_bfd_table =
+        EN_OVSDB_GET(engine_get_input("SB_bfd", node));
+    input_data.sbrec_logical_flow_table =
+        EN_OVSDB_GET(engine_get_input("SB_logical_flow", node));
+    input_data.sbrec_multicast_group_table =
+        EN_OVSDB_GET(engine_get_input("SB_multicast_group", node));
+    input_data.sbrec_address_set_table =
+        EN_OVSDB_GET(engine_get_input("SB_address_set", node));
+    input_data.sbrec_port_group_table =
+        EN_OVSDB_GET(engine_get_input("SB_port_group", node));
+    input_data.sbrec_meter_table =
+        EN_OVSDB_GET(engine_get_input("SB_meter", node));
+    input_data.sbrec_dns_table =
+        EN_OVSDB_GET(engine_get_input("SB_dns", node));
+    input_data.sbrec_ip_multicast_table =
+        EN_OVSDB_GET(engine_get_input("SB_ip_multicast", node));
+    input_data.sbrec_igmp_group_table =
+        EN_OVSDB_GET(engine_get_input("SB_igmp_group", node));
+    input_data.sbrec_chassis_private_table =
+        EN_OVSDB_GET(engine_get_input("SB_chassis_private", node));
+
+    northd_run(&input_data, data,
+               eng_ctx->ovnnb_idl_txn,
+               eng_ctx->ovnsb_idl_txn,
+               eng_ctx->ovnsb_idl_loop);
     engine_set_node_state(node, EN_UPDATED);
 
 }
 void *en_northd_init(struct engine_node *node OVS_UNUSED,
                      struct engine_arg *arg OVS_UNUSED)
 {
-    return NULL;
+    struct northd_data *data = xmalloc(sizeof *data);
+
+    northd_init(data);
+
+    return data;
 }
 
-void en_northd_cleanup(void *data OVS_UNUSED)
+void en_northd_cleanup(void *data)
 {
+    northd_destroy(data);
+    free(data);
 }
diff --git a/northd/inc-proc-northd.c b/northd/inc-proc-northd.c
index 85baeb07d3d9..d2da0489cc1c 100644
--- a/northd/inc-proc-northd.c
+++ b/northd/inc-proc-northd.c
@@ -18,9 +18,12 @@ 
 #include <stdlib.h>
 #include <stdio.h>
 
+#include "chassis-index.h"
+#include "ip-mcast-index.h"
 #include "lib/inc-proc-eng.h"
 #include "lib/ovn-nb-idl.h"
 #include "lib/ovn-sb-idl.h"
+#include "mcast-group-index.h"
 #include "openvswitch/poll-loop.h"
 #include "openvswitch/vlog.h"
 #include "inc-proc-northd.h"
@@ -210,23 +213,52 @@  void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
         .sb_idl = sb->idl,
     };
 
+    struct ovsdb_idl_index *sbrec_chassis_by_name =
+                         chassis_index_create(sb->idl);
+    struct ovsdb_idl_index *sbrec_ha_chassis_grp_by_name =
+                         ha_chassis_group_index_create(sb->idl);
+    struct ovsdb_idl_index *sbrec_mcast_group_by_name_dp =
+                         mcast_group_index_create(sb->idl);
+    struct ovsdb_idl_index *sbrec_ip_mcast_by_dp =
+                         ip_mcast_index_create(sb->idl);
+    struct ovsdb_idl_index *sbrec_chassis_by_hostname =
+        chassis_hostname_index_create(sb->idl);
+
     engine_init(&en_northd, &engine_arg);
+
+    engine_ovsdb_node_add_index(&en_sb_chassis,
+                                "sbrec_chassis_by_name",
+                                sbrec_chassis_by_name);
+    engine_ovsdb_node_add_index(&en_sb_chassis,
+                                "sbrec_chassis_by_hostname",
+                                sbrec_chassis_by_hostname);
+    engine_ovsdb_node_add_index(&en_sb_ha_chassis_group,
+                                "sbrec_ha_chassis_grp_by_name",
+                                sbrec_ha_chassis_grp_by_name);
+    engine_ovsdb_node_add_index(&en_sb_multicast_group,
+                                "sbrec_mcast_group_by_name",
+                                sbrec_mcast_group_by_name_dp);
+    engine_ovsdb_node_add_index(&en_sb_ip_multicast,
+                                "sbrec_ip_mcast_by_dp",
+                                sbrec_ip_mcast_by_dp);
 }
 
-void inc_proc_northd_run(struct northd_context *ctx,
+void inc_proc_northd_run(struct ovsdb_idl_txn *ovnnb_txn,
+                         struct ovsdb_idl_txn *ovnsb_txn,
+                         struct ovsdb_idl_loop *ovnsb_idl_loop,
                          bool recompute) {
     engine_set_force_recompute(recompute);
     engine_init_run();
 
     struct engine_context eng_ctx = {
-        .ovnnb_idl_txn = ctx->ovnnb_txn,
-        .ovnsb_idl_txn = ctx->ovnsb_txn,
-        .client_ctx = ctx,
+        .ovnnb_idl_txn = ovnnb_txn,
+        .ovnsb_idl_txn = ovnsb_txn,
+        .ovnsb_idl_loop = ovnsb_idl_loop,
     };
 
     engine_set_context(&eng_ctx);
 
-    if (ctx->ovnnb_txn && ctx->ovnsb_txn) {
+    if (ovnnb_txn && ovnsb_txn) {
         engine_run(true);
     }
 
diff --git a/northd/inc-proc-northd.h b/northd/inc-proc-northd.h
index 09cb8f3b3a80..b6c38e68749d 100644
--- a/northd/inc-proc-northd.h
+++ b/northd/inc-proc-northd.h
@@ -6,9 +6,13 @@ 
 #include "northd.h"
 #include "ovsdb-idl.h"
 
+
+
 void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
                           struct ovsdb_idl_loop *sb);
-void inc_proc_northd_run(struct northd_context *ctx,
+void inc_proc_northd_run(struct ovsdb_idl_txn *ovnnb_txn,
+                         struct ovsdb_idl_txn *ovnsb_txn,
+                         struct ovsdb_idl_loop *ovnsb_idl_loop,
                          bool recompute);
 void inc_proc_northd_cleanup(void);
 
diff --git a/northd/northd.c b/northd/northd.c
index 938dadf5563c..17b9d09a7887 100644
--- a/northd/northd.c
+++ b/northd/northd.c
@@ -1261,21 +1261,24 @@  ovn_datapath_update_external_ids(struct ovn_datapath *od)
 }
 
 static void
-join_datapaths(struct northd_context *ctx, struct hmap *datapaths,
-               struct ovs_list *sb_only, struct ovs_list *nb_only,
-               struct ovs_list *both, struct ovs_list *lr_list)
+join_datapaths(struct northd_input *input_data,
+               struct ovsdb_idl_txn *ovnsb_txn,
+               struct hmap *datapaths, struct ovs_list *sb_only,
+               struct ovs_list *nb_only, struct ovs_list *both,
+               struct ovs_list *lr_list)
 {
     ovs_list_init(sb_only);
     ovs_list_init(nb_only);
     ovs_list_init(both);
 
     const struct sbrec_datapath_binding *sb, *sb_next;
-    SBREC_DATAPATH_BINDING_FOR_EACH_SAFE (sb, sb_next, ctx->ovnsb_idl) {
+    SBREC_DATAPATH_BINDING_TABLE_FOR_EACH_SAFE (sb, sb_next,
+                            input_data->sbrec_datapath_binding_table) {
         struct uuid key;
         if (!smap_get_uuid(&sb->external_ids, "logical-switch", &key) &&
             !smap_get_uuid(&sb->external_ids, "logical-router", &key)) {
             ovsdb_idl_txn_add_comment(
-                ctx->ovnsb_txn,
+                ovnsb_txn,
                 "deleting Datapath_Binding "UUID_FMT" that lacks "
                 "external-ids:logical-switch and "
                 "external-ids:logical-router",
@@ -1300,7 +1303,8 @@  join_datapaths(struct northd_context *ctx, struct hmap *datapaths,
     }
 
     const struct nbrec_logical_switch *nbs;
-    NBREC_LOGICAL_SWITCH_FOR_EACH (nbs, ctx->ovnnb_idl) {
+    NBREC_LOGICAL_SWITCH_TABLE_FOR_EACH (nbs,
+                              input_data->nbrec_logical_switch) {
         struct ovn_datapath *od = ovn_datapath_find(datapaths,
                                                     &nbs->header_.uuid);
         if (od) {
@@ -1320,7 +1324,8 @@  join_datapaths(struct northd_context *ctx, struct hmap *datapaths,
     }
 
     const struct nbrec_logical_router *nbr;
-    NBREC_LOGICAL_ROUTER_FOR_EACH (nbr, ctx->ovnnb_idl) {
+    NBREC_LOGICAL_ROUTER_TABLE_FOR_EACH (nbr,
+                               input_data->nbrec_logical_router) {
         if (!lrouter_is_enabled(nbr)) {
             continue;
         }
@@ -1358,10 +1363,10 @@  join_datapaths(struct northd_context *ctx, struct hmap *datapaths,
 }
 
 static bool
-is_vxlan_mode(struct ovsdb_idl *ovnsb_idl)
+is_vxlan_mode(struct northd_input *input_data)
 {
     const struct sbrec_chassis *chassis;
-    SBREC_CHASSIS_FOR_EACH (chassis, ovnsb_idl) {
+    SBREC_CHASSIS_TABLE_FOR_EACH (chassis, input_data->sbrec_chassis) {
         for (int i = 0; i < chassis->n_encaps; i++) {
             if (!strcmp(chassis->encaps[i]->type, "vxlan")) {
                 return true;
@@ -1372,9 +1377,9 @@  is_vxlan_mode(struct ovsdb_idl *ovnsb_idl)
 }
 
 static uint32_t
-get_ovn_max_dp_key_local(struct northd_context *ctx)
+get_ovn_max_dp_key_local(struct northd_input *input_data)
 {
-    if (is_vxlan_mode(ctx->ovnsb_idl)) {
+    if (is_vxlan_mode(input_data)) {
         /* OVN_MAX_DP_GLOBAL_NUM doesn't apply for vxlan mode. */
         return OVN_MAX_DP_VXLAN_KEY;
     }
@@ -1382,15 +1387,15 @@  get_ovn_max_dp_key_local(struct northd_context *ctx)
 }
 
 static void
-ovn_datapath_allocate_key(struct northd_context *ctx,
+ovn_datapath_allocate_key(struct northd_input *input_data,
                           struct hmap *datapaths, struct hmap *dp_tnlids,
                           struct ovn_datapath *od, uint32_t *hint)
 {
     if (!od->tunnel_key) {
         od->tunnel_key = ovn_allocate_tnlid(dp_tnlids, "datapath",
-                                            OVN_MIN_DP_KEY_LOCAL,
-                                            get_ovn_max_dp_key_local(ctx),
-                                            hint);
+                                    OVN_MIN_DP_KEY_LOCAL,
+                                    get_ovn_max_dp_key_local(input_data),
+                                    hint);
         if (!od->tunnel_key) {
             if (od->sb) {
                 sbrec_datapath_binding_delete(od->sb);
@@ -1402,7 +1407,7 @@  ovn_datapath_allocate_key(struct northd_context *ctx,
 }
 
 static void
-ovn_datapath_assign_requested_tnl_id(struct northd_context *ctx,
+ovn_datapath_assign_requested_tnl_id(struct northd_input *input_data,
                                      struct hmap *dp_tnlids,
                                      struct ovn_datapath *od)
 {
@@ -1411,7 +1416,7 @@  ovn_datapath_assign_requested_tnl_id(struct northd_context *ctx,
                                        : &od->nbr->options);
     uint32_t tunnel_key = smap_get_int(other_config, "requested-tnl-key", 0);
     if (tunnel_key) {
-        if (is_vxlan_mode(ctx->ovnsb_idl) && tunnel_key >= 1 << 12) {
+        if (is_vxlan_mode(input_data) && tunnel_key >= 1 << 12) {
             static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
             VLOG_WARN_RL(&rl, "Tunnel key %"PRIu32" for datapath %s is "
                          "incompatible with VXLAN", tunnel_key,
@@ -1436,21 +1441,24 @@  ovn_datapath_assign_requested_tnl_id(struct northd_context *ctx,
  * Initializes 'datapaths' to contain a "struct ovn_datapath" for every logical
  * switch and router. */
 static void
-build_datapaths(struct northd_context *ctx, struct hmap *datapaths,
+build_datapaths(struct northd_input *input_data,
+                struct ovsdb_idl_txn *ovnsb_txn,
+                struct hmap *datapaths,
                 struct ovs_list *lr_list)
 {
     struct ovs_list sb_only, nb_only, both;
 
-    join_datapaths(ctx, datapaths, &sb_only, &nb_only, &both, lr_list);
+    join_datapaths(input_data, ovnsb_txn,
+                   datapaths, &sb_only, &nb_only, &both, lr_list);
 
     /* Assign explicitly requested tunnel ids first. */
     struct hmap dp_tnlids = HMAP_INITIALIZER(&dp_tnlids);
     struct ovn_datapath *od, *next;
     LIST_FOR_EACH (od, list, &both) {
-        ovn_datapath_assign_requested_tnl_id(ctx, &dp_tnlids, od);
+        ovn_datapath_assign_requested_tnl_id(input_data, &dp_tnlids, od);
     }
     LIST_FOR_EACH (od, list, &nb_only) {
-        ovn_datapath_assign_requested_tnl_id(ctx, &dp_tnlids, od);
+        ovn_datapath_assign_requested_tnl_id(input_data, &dp_tnlids, od);
     }
 
     /* Keep nonconflicting tunnel IDs that are already assigned. */
@@ -1463,10 +1471,12 @@  build_datapaths(struct northd_context *ctx, struct hmap *datapaths,
     /* Assign new tunnel ids where needed. */
     uint32_t hint = 0;
     LIST_FOR_EACH_SAFE (od, next, list, &both) {
-        ovn_datapath_allocate_key(ctx, datapaths, &dp_tnlids, od, &hint);
+        ovn_datapath_allocate_key(input_data,
+                                  datapaths, &dp_tnlids, od, &hint);
     }
     LIST_FOR_EACH_SAFE (od, next, list, &nb_only) {
-        ovn_datapath_allocate_key(ctx, datapaths, &dp_tnlids, od, &hint);
+        ovn_datapath_allocate_key(input_data,
+                                  datapaths, &dp_tnlids, od, &hint);
     }
 
     /* Sync tunnel ids from nb to sb. */
@@ -1477,7 +1487,7 @@  build_datapaths(struct northd_context *ctx, struct hmap *datapaths,
         ovn_datapath_update_external_ids(od);
     }
     LIST_FOR_EACH (od, list, &nb_only) {
-        od->sb = sbrec_datapath_binding_insert(ctx->ovnsb_txn);
+        od->sb = sbrec_datapath_binding_insert(ovnsb_txn);
         ovn_datapath_update_external_ids(od);
         sbrec_datapath_binding_set_tunnel_key(od->sb, od->tunnel_key);
     }
@@ -2407,7 +2417,7 @@  tag_alloc_create_new_tag(struct hmap *tag_alloc_table,
 
 
 static void
-join_logical_ports(struct northd_context *ctx,
+join_logical_ports(struct northd_input *input_data,
                    struct hmap *datapaths, struct hmap *ports,
                    struct hmap *chassis_qdisc_queues,
                    struct hmap *tag_alloc_table, struct ovs_list *sb_only,
@@ -2418,7 +2428,8 @@  join_logical_ports(struct northd_context *ctx,
     ovs_list_init(both);
 
     const struct sbrec_port_binding *sb;
-    SBREC_PORT_BINDING_FOR_EACH (sb, ctx->ovnsb_idl) {
+    SBREC_PORT_BINDING_TABLE_FOR_EACH (sb,
+                                 input_data->sbrec_port_binding_table) {
         struct ovn_port *op = ovn_port_create(ports, sb->logical_port,
                                               NULL, NULL, sb);
         ovs_list_push_back(sb_only, &op->list);
@@ -2869,12 +2880,12 @@  sbpb_gw_chassis_needs_update(
 }
 
 static struct sbrec_ha_chassis *
-create_sb_ha_chassis(struct northd_context *ctx,
+create_sb_ha_chassis(struct ovsdb_idl_txn *ovnsb_txn,
                      const struct sbrec_chassis *chassis,
                      const char *chassis_name, int priority)
 {
     struct sbrec_ha_chassis *sb_ha_chassis =
-        sbrec_ha_chassis_insert(ctx->ovnsb_txn);
+        sbrec_ha_chassis_insert(ovnsb_txn);
     sbrec_ha_chassis_set_chassis(sb_ha_chassis, chassis);
     sbrec_ha_chassis_set_priority(sb_ha_chassis, priority);
     /* Store the chassis_name in external_ids. If the chassis
@@ -2954,7 +2965,8 @@  chassis_group_list_changed(
 }
 
 static void
-sync_ha_chassis_group_for_sbpb(struct northd_context *ctx,
+sync_ha_chassis_group_for_sbpb(struct northd_input *input_data,
+                               struct ovsdb_idl_txn *ovnsb_txn,
                                const struct nbrec_ha_chassis_group *nb_ha_grp,
                                struct ovsdb_idl_index *sbrec_chassis_by_name,
                                const struct sbrec_port_binding *pb)
@@ -2962,10 +2974,10 @@  sync_ha_chassis_group_for_sbpb(struct northd_context *ctx,
     bool new_sb_chassis_group = false;
     const struct sbrec_ha_chassis_group *sb_ha_grp =
         ha_chassis_group_lookup_by_name(
-            ctx->sbrec_ha_chassis_grp_by_name, nb_ha_grp->name);
+            input_data->sbrec_ha_chassis_grp_by_name, nb_ha_grp->name);
 
     if (!sb_ha_grp) {
-        sb_ha_grp = sbrec_ha_chassis_group_insert(ctx->ovnsb_txn);
+        sb_ha_grp = sbrec_ha_chassis_group_insert(ovnsb_txn);
         sbrec_ha_chassis_group_set_name(sb_ha_grp, nb_ha_grp->name);
         new_sb_chassis_group = true;
     }
@@ -2982,7 +2994,7 @@  sync_ha_chassis_group_for_sbpb(struct northd_context *ctx,
             const struct sbrec_chassis *chassis =
                 chassis_lookup_by_name(sbrec_chassis_by_name,
                                        nb_ha_chassis->chassis_name);
-            sb_ha_chassis[i] = sbrec_ha_chassis_insert(ctx->ovnsb_txn);
+            sb_ha_chassis[i] = sbrec_ha_chassis_insert(ovnsb_txn);
             /* It's perfectly ok if the chassis is NULL. This could
              * happen when ovn-controller exits and removes its row
              * from the chassis table in OVN SB DB. */
@@ -3007,7 +3019,8 @@  sync_ha_chassis_group_for_sbpb(struct northd_context *ctx,
  */
 static void
 copy_gw_chassis_from_nbrp_to_sbpb(
-        struct northd_context *ctx,
+        struct northd_input *input_data,
+        struct ovsdb_idl_txn *ovnsb_txn,
         struct ovsdb_idl_index *sbrec_chassis_by_name,
         const struct nbrec_logical_router_port *lrp,
         const struct sbrec_port_binding *port_binding)
@@ -3017,9 +3030,9 @@  copy_gw_chassis_from_nbrp_to_sbpb(
      * for the distributed gateway router port. */
     const struct sbrec_ha_chassis_group *sb_ha_chassis_group =
         ha_chassis_group_lookup_by_name(
-            ctx->sbrec_ha_chassis_grp_by_name, lrp->name);
+            input_data->sbrec_ha_chassis_grp_by_name, lrp->name);
     if (!sb_ha_chassis_group) {
-        sb_ha_chassis_group = sbrec_ha_chassis_group_insert(ctx->ovnsb_txn);
+        sb_ha_chassis_group = sbrec_ha_chassis_group_insert(ovnsb_txn);
         sbrec_ha_chassis_group_set_name(sb_ha_chassis_group, lrp->name);
     }
 
@@ -3037,7 +3050,7 @@  copy_gw_chassis_from_nbrp_to_sbpb(
                                    lrp_gwc->chassis_name);
 
         sb_ha_chassis[n_sb_ha_ch] =
-            create_sb_ha_chassis(ctx, chassis, lrp_gwc->chassis_name,
+            create_sb_ha_chassis(ovnsb_txn, chassis, lrp_gwc->chassis_name,
                                  lrp_gwc->priority);
         n_sb_ha_ch++;
     }
@@ -3085,7 +3098,8 @@  ovn_update_ipv6_prefix(struct hmap *ports)
 }
 
 static void
-ovn_port_update_sbrec(struct northd_context *ctx,
+ovn_port_update_sbrec(struct northd_input *input_data,
+                      struct ovsdb_idl_txn *ovnsb_txn,
                       struct ovsdb_idl_index *sbrec_chassis_by_name,
                       struct ovsdb_idl_index *sbrec_chassis_by_hostname,
                       const struct ovn_port *op,
@@ -3121,7 +3135,8 @@  ovn_port_update_sbrec(struct northd_context *ctx,
                 }
 
                 /* HA Chassis group is set. Ignore 'gateway_chassis'. */
-                sync_ha_chassis_group_for_sbpb(ctx, op->nbrp->ha_chassis_group,
+                sync_ha_chassis_group_for_sbpb(input_data, ovnsb_txn,
+                                               op->nbrp->ha_chassis_group,
                                                sbrec_chassis_by_name, op->sb);
                 sset_add(active_ha_chassis_grps,
                          op->nbrp->ha_chassis_group->name);
@@ -3131,7 +3146,8 @@  ovn_port_update_sbrec(struct northd_context *ctx,
                  * associated with the lrp. */
                 if (sbpb_gw_chassis_needs_update(op->sb, op->nbrp,
                                                  sbrec_chassis_by_name)) {
-                    copy_gw_chassis_from_nbrp_to_sbpb(ctx,
+                    copy_gw_chassis_from_nbrp_to_sbpb(input_data,
+                                                      ovnsb_txn,
                                                       sbrec_chassis_by_name,
                                                       op->nbrp, op->sb);
                 }
@@ -3255,7 +3271,8 @@  ovn_port_update_sbrec(struct northd_context *ctx,
             if (!strcmp(op->nbsp->type, "external")) {
                 if (op->nbsp->ha_chassis_group) {
                     sync_ha_chassis_group_for_sbpb(
-                        ctx, op->nbsp->ha_chassis_group,
+                        input_data,
+                        ovnsb_txn, op->nbsp->ha_chassis_group,
                         sbrec_chassis_by_name, op->sb);
                     sset_add(active_ha_chassis_grps,
                              op->nbsp->ha_chassis_group->name);
@@ -3454,11 +3471,13 @@  ovn_port_update_sbrec(struct northd_context *ctx,
 /* Remove mac_binding entries that refer to logical_ports which are
  * deleted. */
 static void
-cleanup_mac_bindings(struct northd_context *ctx, struct hmap *datapaths,
+cleanup_mac_bindings(struct northd_input *input_data,
+                     struct hmap *datapaths,
                      struct hmap *ports)
 {
     const struct sbrec_mac_binding *b, *n;
-    SBREC_MAC_BINDING_FOR_EACH_SAFE (b, n, ctx->ovnsb_idl) {
+    SBREC_MAC_BINDING_TABLE_FOR_EACH_SAFE (b, n,
+                             input_data->sbrec_mac_binding_table) {
         const struct ovn_datapath *od =
             ovn_datapath_from_sbrec(datapaths, b->datapath);
 
@@ -3470,11 +3489,12 @@  cleanup_mac_bindings(struct northd_context *ctx, struct hmap *datapaths,
 }
 
 static void
-cleanup_sb_ha_chassis_groups(struct northd_context *ctx,
+cleanup_sb_ha_chassis_groups(struct northd_input *input_data,
                              struct sset *active_ha_chassis_groups)
 {
     const struct sbrec_ha_chassis_group *b, *n;
-    SBREC_HA_CHASSIS_GROUP_FOR_EACH_SAFE (b, n, ctx->ovnsb_idl) {
+    SBREC_HA_CHASSIS_GROUP_TABLE_FOR_EACH_SAFE (b, n,
+                                input_data->sbrec_ha_chassis_group_table) {
         if (!sset_contains(active_ha_chassis_groups, b->name)) {
             sbrec_ha_chassis_group_delete(b);
         }
@@ -3482,10 +3502,12 @@  cleanup_sb_ha_chassis_groups(struct northd_context *ctx,
 }
 
 static void
-cleanup_stale_fdp_entries(struct northd_context *ctx, struct hmap *datapaths)
+cleanup_stale_fdp_entries(struct northd_input *input_data,
+                          struct hmap *datapaths)
 {
     const struct sbrec_fdb *fdb_e, *next;
-    SBREC_FDB_FOR_EACH_SAFE (fdb_e, next, ctx->ovnsb_idl) {
+    SBREC_FDB_TABLE_FOR_EACH_SAFE (fdb_e, next,
+                         input_data->sbrec_fdb_table) {
         bool delete = true;
         struct ovn_datapath *od
             = ovn_datapath_find_by_key(datapaths, fdb_e->dp_key);
@@ -3509,7 +3531,7 @@  struct service_monitor_info {
 
 
 static struct service_monitor_info *
-create_or_get_service_mon(struct northd_context *ctx,
+create_or_get_service_mon(struct ovsdb_idl_txn *ovnsb_txn,
                           struct hmap *monitor_map,
                           const char *ip, const char *logical_port,
                           uint16_t service_port, const char *protocol)
@@ -3529,7 +3551,7 @@  create_or_get_service_mon(struct northd_context *ctx,
     }
 
     struct sbrec_service_monitor *sbrec_mon =
-        sbrec_service_monitor_insert(ctx->ovnsb_txn);
+        sbrec_service_monitor_insert(ovnsb_txn);
     sbrec_service_monitor_set_ip(sbrec_mon, ip);
     sbrec_service_monitor_set_port(sbrec_mon, service_port);
     sbrec_service_monitor_set_logical_port(sbrec_mon, logical_port);
@@ -3541,7 +3563,7 @@  create_or_get_service_mon(struct northd_context *ctx,
 }
 
 static void
-ovn_lb_svc_create(struct northd_context *ctx, struct ovn_northd_lb *lb,
+ovn_lb_svc_create(struct ovsdb_idl_txn *ovnsb_txn, struct ovn_northd_lb *lb,
                   struct hmap *monitor_map, struct hmap *ports)
 {
     for (size_t i = 0; i < lb->n_vips; i++) {
@@ -3582,7 +3604,7 @@  ovn_lb_svc_create(struct northd_context *ctx, struct ovn_northd_lb *lb,
             }
             backend_nb->health_check = true;
             struct service_monitor_info *mon_info =
-                create_or_get_service_mon(ctx, monitor_map,
+                create_or_get_service_mon(ovnsb_txn, monitor_map,
                                           backend->ip_str,
                                           backend_nb->op->nbsp->name,
                                           backend->port,
@@ -3716,15 +3738,17 @@  build_ovn_lr_lbs(struct hmap *datapaths, struct hmap *lbs)
 }
 
 static void
-build_ovn_lbs(struct northd_context *ctx, struct hmap *datapaths,
-              struct hmap *lbs)
+build_ovn_lbs(struct northd_input *input_data,
+              struct ovsdb_idl_txn *ovnsb_txn,
+              struct hmap *datapaths, struct hmap *lbs)
 {
     struct ovn_northd_lb *lb;
 
     hmap_init(lbs);
 
     const struct nbrec_load_balancer *nbrec_lb;
-    NBREC_LOAD_BALANCER_FOR_EACH (nbrec_lb, ctx->ovnnb_idl) {
+    NBREC_LOAD_BALANCER_TABLE_FOR_EACH (nbrec_lb,
+                               input_data->nbrec_load_balancer_table) {
         struct ovn_northd_lb *lb_nb = ovn_northd_lb_create(nbrec_lb);
         hmap_insert(lbs, &lb_nb->hmap_node,
                     uuid_hash(&nbrec_lb->header_.uuid));
@@ -3757,7 +3781,8 @@  build_ovn_lbs(struct northd_context *ctx, struct hmap *datapaths,
 
     /* Delete any stale SB load balancer rows. */
     const struct sbrec_load_balancer *sbrec_lb, *next;
-    SBREC_LOAD_BALANCER_FOR_EACH_SAFE (sbrec_lb, next, ctx->ovnsb_idl) {
+    SBREC_LOAD_BALANCER_TABLE_FOR_EACH_SAFE (sbrec_lb, next,
+                            input_data->sbrec_load_balancer_table) {
         const char *nb_lb_uuid = smap_get(&sbrec_lb->external_ids, "lb_id");
         struct uuid lb_uuid;
         if (!nb_lb_uuid || !uuid_from_string(&lb_uuid, nb_lb_uuid)) {
@@ -3796,7 +3821,7 @@  build_ovn_lbs(struct northd_context *ctx, struct hmap *datapaths,
         }
 
         if (!lb->slb) {
-            sbrec_lb = sbrec_load_balancer_insert(ctx->ovnsb_txn);
+            sbrec_lb = sbrec_load_balancer_insert(ovnsb_txn);
             lb->slb = sbrec_lb;
             char *lb_id = xasprintf(
                 UUID_FMT, UUID_ARGS(&lb->nlb->header_.uuid));
@@ -3829,13 +3854,15 @@  build_ovn_lbs(struct northd_context *ctx, struct hmap *datapaths,
 }
 
 static void
-build_ovn_lb_svcs(struct northd_context *ctx, struct hmap *ports,
-                  struct hmap *lbs)
+build_ovn_lb_svcs(struct northd_input *input_data,
+                  struct ovsdb_idl_txn *ovnsb_txn,
+                  struct hmap *ports, struct hmap *lbs)
 {
     struct hmap monitor_map = HMAP_INITIALIZER(&monitor_map);
 
     const struct sbrec_service_monitor *sbrec_mon;
-    SBREC_SERVICE_MONITOR_FOR_EACH (sbrec_mon, ctx->ovnsb_idl) {
+    SBREC_SERVICE_MONITOR_TABLE_FOR_EACH (sbrec_mon,
+                            input_data->sbrec_service_monitor_table) {
         uint32_t hash = sbrec_mon->port;
         hash = hash_string(sbrec_mon->ip, hash);
         hash = hash_string(sbrec_mon->logical_port, hash);
@@ -3847,7 +3874,7 @@  build_ovn_lb_svcs(struct northd_context *ctx, struct hmap *ports,
 
     struct ovn_northd_lb *lb;
     HMAP_FOR_EACH (lb, hmap_node, lbs) {
-        ovn_lb_svc_create(ctx, lb, &monitor_map, ports);
+        ovn_lb_svc_create(ovnsb_txn, lb, &monitor_map, ports);
     }
 
     struct service_monitor_info *mon_info;
@@ -3925,7 +3952,7 @@  ovn_port_add_tnlid(struct ovn_port *op, uint32_t tunnel_key)
 }
 
 static void
-ovn_port_assign_requested_tnl_id(struct northd_context *ctx,
+ovn_port_assign_requested_tnl_id(struct northd_input *input_data,
                                  struct ovn_port *op)
 {
     const struct smap *options = (op->nbsp
@@ -3933,7 +3960,7 @@  ovn_port_assign_requested_tnl_id(struct northd_context *ctx,
                                   : &op->nbrp->options);
     uint32_t tunnel_key = smap_get_int(options, "requested-tnl-key", 0);
     if (tunnel_key) {
-        if (is_vxlan_mode(ctx->ovnsb_idl) &&
+        if (is_vxlan_mode(input_data) &&
                 tunnel_key >= OVN_VXLAN_MIN_MULTICAST) {
             static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
             VLOG_WARN_RL(&rl, "Tunnel key %"PRIu32" for port %s "
@@ -3952,11 +3979,12 @@  ovn_port_assign_requested_tnl_id(struct northd_context *ctx,
 }
 
 static void
-ovn_port_allocate_key(struct northd_context *ctx, struct hmap *ports,
+ovn_port_allocate_key(struct northd_input *input_data,
+                      struct hmap *ports,
                       struct ovn_port *op)
 {
     if (!op->tunnel_key) {
-        uint8_t key_bits = is_vxlan_mode(ctx->ovnsb_idl)? 12 : 16;
+        uint8_t key_bits = is_vxlan_mode(input_data)? 12 : 16;
         op->tunnel_key = ovn_allocate_tnlid(&op->od->port_tnlids, "port",
                                             1, (1u << (key_bits - 1)) - 1,
                                             &op->od->port_key_hint);
@@ -3977,7 +4005,8 @@  ovn_port_allocate_key(struct northd_context *ctx, struct hmap *ports,
  * using the "struct ovn_datapath"s in 'datapaths' to look up logical
  * datapaths. */
 static void
-build_ports(struct northd_context *ctx,
+build_ports(struct northd_input *input_data,
+            struct ovsdb_idl_txn *ovnsb_txn,
             struct ovsdb_idl_index *sbrec_chassis_by_name,
             struct ovsdb_idl_index *sbrec_chassis_by_hostname,
             struct hmap *datapaths, struct hmap *ports)
@@ -3990,7 +4019,8 @@  build_ports(struct northd_context *ctx,
     struct sset active_ha_chassis_grps =
         SSET_INITIALIZER(&active_ha_chassis_grps);
 
-    join_logical_ports(ctx, datapaths, ports, &chassis_qdisc_queues,
+    join_logical_ports(input_data,
+                       datapaths, ports, &chassis_qdisc_queues,
                        &tag_alloc_table, &sb_only, &nb_only, &both);
 
     /* Purge stale Mac_Bindings if ports are deleted. */
@@ -3999,10 +4029,10 @@  build_ports(struct northd_context *ctx,
     /* Assign explicitly requested tunnel ids first. */
     struct ovn_port *op, *next;
     LIST_FOR_EACH (op, list, &both) {
-        ovn_port_assign_requested_tnl_id(ctx, op);
+        ovn_port_assign_requested_tnl_id(input_data, op);
     }
     LIST_FOR_EACH (op, list, &nb_only) {
-        ovn_port_assign_requested_tnl_id(ctx, op);
+        ovn_port_assign_requested_tnl_id(input_data, op);
     }
 
     /* Keep nonconflicting tunnel IDs that are already assigned. */
@@ -4014,10 +4044,10 @@  build_ports(struct northd_context *ctx,
 
     /* Assign new tunnel ids where needed. */
     LIST_FOR_EACH_SAFE (op, next, list, &both) {
-        ovn_port_allocate_key(ctx, ports, op);
+        ovn_port_allocate_key(input_data, ports, op);
     }
     LIST_FOR_EACH_SAFE (op, next, list, &nb_only) {
-        ovn_port_allocate_key(ctx, ports, op);
+        ovn_port_allocate_key(input_data, ports, op);
     }
 
     /* For logical ports that are in both databases, update the southbound
@@ -4034,7 +4064,8 @@  build_ports(struct northd_context *ctx,
         if (op->nbsp) {
             tag_alloc_create_new_tag(&tag_alloc_table, op->nbsp);
         }
-        ovn_port_update_sbrec(ctx, sbrec_chassis_by_name,
+        ovn_port_update_sbrec(input_data,
+                              ovnsb_txn, sbrec_chassis_by_name,
                               sbrec_chassis_by_hostname,
                               op, &chassis_qdisc_queues,
                               &active_ha_chassis_grps);
@@ -4042,8 +4073,9 @@  build_ports(struct northd_context *ctx,
 
     /* Add southbound record for each unmatched northbound record. */
     LIST_FOR_EACH_SAFE (op, next, list, &nb_only) {
-        op->sb = sbrec_port_binding_insert(ctx->ovnsb_txn);
-        ovn_port_update_sbrec(ctx, sbrec_chassis_by_name,
+        op->sb = sbrec_port_binding_insert(ovnsb_txn);
+        ovn_port_update_sbrec(input_data,
+                              ovnsb_txn, sbrec_chassis_by_name,
                               sbrec_chassis_by_hostname, op,
                               &chassis_qdisc_queues,
                               &active_ha_chassis_grps);
@@ -4059,12 +4091,12 @@  build_ports(struct northd_context *ctx,
         }
     }
     if (remove_mac_bindings) {
-        cleanup_mac_bindings(ctx, datapaths, ports);
+        cleanup_mac_bindings(input_data, datapaths, ports);
     }
 
     tag_alloc_destroy(&tag_alloc_table);
     destroy_chassis_queues(&chassis_qdisc_queues);
-    cleanup_sb_ha_chassis_groups(ctx, &active_ha_chassis_grps);
+    cleanup_sb_ha_chassis_groups(input_data, &active_ha_chassis_grps);
     sset_destroy(&active_ha_chassis_grps);
 }
 
@@ -4247,7 +4279,8 @@  ovn_igmp_group_find(struct hmap *igmp_groups,
 }
 
 static struct ovn_igmp_group *
-ovn_igmp_group_add(struct northd_context *ctx, struct hmap *igmp_groups,
+ovn_igmp_group_add(struct northd_input *input_data,
+                   struct hmap *igmp_groups,
                    struct ovn_datapath *datapath,
                    const struct in6_addr *address,
                    const char *address_s)
@@ -4259,7 +4292,8 @@  ovn_igmp_group_add(struct northd_context *ctx, struct hmap *igmp_groups,
         igmp_group = xmalloc(sizeof *igmp_group);
 
         const struct sbrec_multicast_group *mcgroup =
-            mcast_group_lookup(ctx->sbrec_mcast_group_by_name_dp, address_s,
+            mcast_group_lookup(input_data->sbrec_mcast_group_by_name_dp,
+                               address_s,
                                datapath->sb);
 
         igmp_group->datapath = datapath;
@@ -4466,7 +4500,7 @@  ovn_lflow_equal(const struct ovn_lflow *a, const struct ovn_datapath *od,
 /* If this option is 'true' northd will combine logical flows that differ by
  * logical datapath only by creating a datapath group. */
 static bool use_logical_dp_groups = false;
-static bool use_parallel_build = true;
+static bool use_parallel_build = false;
 
 static void
 ovn_lflow_init(struct ovn_lflow *lflow, struct ovn_datapath *od,
@@ -6219,13 +6253,15 @@  ovn_port_group_destroy(struct hmap *pgs, struct ovn_port_group *pg)
 }
 
 static void
-build_port_group_lswitches(struct northd_context *ctx, struct hmap *pgs,
+build_port_group_lswitches(struct northd_input *input_data,
+                           struct hmap *pgs,
                            struct hmap *ports)
 {
     hmap_init(pgs);
 
     const struct nbrec_port_group *nb_pg;
-    NBREC_PORT_GROUP_FOR_EACH (nb_pg, ctx->ovnnb_idl) {
+    NBREC_PORT_GROUP_TABLE_FOR_EACH (nb_pg,
+                                  input_data->nbrec_port_group_table) {
         struct ovn_port_group *pg = ovn_port_group_create(pgs, nb_pg);
         for (size_t i = 0; i < nb_pg->n_ports; i++) {
             struct ovn_port *op = ovn_port_find(ports, nb_pg->ports[i]->name);
@@ -8226,12 +8262,13 @@  bfd_port_lookup(struct hmap *bfd_map, const char *logical_port,
 }
 
 static void
-bfd_cleanup_connections(struct northd_context *ctx, struct hmap *bfd_map)
+bfd_cleanup_connections(struct northd_input *input_data,
+                        struct hmap *bfd_map)
 {
     const struct nbrec_bfd *nb_bt;
     struct bfd_entry *bfd_e;
 
-    NBREC_BFD_FOR_EACH (nb_bt, ctx->ovnnb_idl) {
+    NBREC_BFD_TABLE_FOR_EACH (nb_bt, input_data->nbrec_bfd_table) {
         bfd_e = bfd_port_lookup(bfd_map, nb_bt->logical_port, nb_bt->dst_ip);
         if (!bfd_e) {
             continue;
@@ -8309,8 +8346,9 @@  static int bfd_get_unused_port(unsigned long *bfd_src_ports)
 }
 
 static void
-build_bfd_table(struct northd_context *ctx, struct hmap *bfd_connections,
-                struct hmap *ports)
+build_bfd_table(struct northd_input *input_data,
+                struct ovsdb_idl_txn *ovnsb_txn,
+                struct hmap *bfd_connections, struct hmap *ports)
 {
     struct hmap sb_only = HMAP_INITIALIZER(&sb_only);
     const struct sbrec_bfd *sb_bt;
@@ -8320,7 +8358,7 @@  build_bfd_table(struct northd_context *ctx, struct hmap *bfd_connections,
 
     bfd_src_ports = bitmap_allocate(BFD_UDP_SRC_PORT_LEN);
 
-    SBREC_BFD_FOR_EACH (sb_bt, ctx->ovnsb_idl) {
+    SBREC_BFD_TABLE_FOR_EACH (sb_bt, input_data->sbrec_bfd_table) {
         bfd_e = xmalloc(sizeof *bfd_e);
         bfd_e->sb_bt = sb_bt;
         hash = hash_string(sb_bt->dst_ip, 0);
@@ -8330,7 +8368,7 @@  build_bfd_table(struct northd_context *ctx, struct hmap *bfd_connections,
     }
 
     const struct nbrec_bfd *nb_bt;
-    NBREC_BFD_FOR_EACH (nb_bt, ctx->ovnnb_idl) {
+    NBREC_BFD_TABLE_FOR_EACH (nb_bt, input_data->nbrec_bfd_table) {
         if (!nb_bt->status) {
             /* default state is admin_down */
             nbrec_bfd_set_status(nb_bt, "admin_down");
@@ -8343,7 +8381,7 @@  build_bfd_table(struct northd_context *ctx, struct hmap *bfd_connections,
                 continue;
             }
 
-            sb_bt = sbrec_bfd_insert(ctx->ovnsb_txn);
+            sb_bt = sbrec_bfd_insert(ovnsb_txn);
             sbrec_bfd_set_logical_port(sb_bt, nb_bt->logical_port);
             sbrec_bfd_set_dst_ip(sb_bt, nb_bt->dst_ip);
             sbrec_bfd_set_disc(sb_bt, 1 + random_uint32());
@@ -13316,8 +13354,8 @@  ovn_dp_group_find(const struct hmap *dp_groups,
 }
 
 static struct sbrec_logical_dp_group *
-ovn_sb_insert_logical_dp_group(struct northd_context *ctx,
-                                     const struct hmapx *od)
+ovn_sb_insert_logical_dp_group(struct ovsdb_idl_txn *ovnsb_txn,
+                               const struct hmapx *od)
 {
     struct sbrec_logical_dp_group *dp_group;
     const struct sbrec_datapath_binding **sb;
@@ -13328,7 +13366,7 @@  ovn_sb_insert_logical_dp_group(struct northd_context *ctx,
     HMAPX_FOR_EACH (node, od) {
         sb[n++] = ((struct ovn_datapath *) node->data)->sb;
     }
-    dp_group = sbrec_logical_dp_group_insert(ctx->ovnsb_txn);
+    dp_group = sbrec_logical_dp_group_insert(ovnsb_txn);
     sbrec_logical_dp_group_set_datapaths(
         dp_group, (struct sbrec_datapath_binding **) sb, n);
     free(sb);
@@ -13338,7 +13376,7 @@  ovn_sb_insert_logical_dp_group(struct northd_context *ctx,
 
 static void
 ovn_sb_set_lflow_logical_dp_group(
-    struct northd_context *ctx,
+    struct ovsdb_idl_txn *ovnsb_txn,
     struct hmap *dp_groups,
     const struct sbrec_logical_flow *sbflow,
     const struct hmapx *od_group)
@@ -13357,7 +13395,7 @@  ovn_sb_set_lflow_logical_dp_group(
     ovs_assert(dpg != NULL);
 
     if (!dpg->dp_group) {
-        dpg->dp_group = ovn_sb_insert_logical_dp_group(ctx, &dpg->map);
+        dpg->dp_group = ovn_sb_insert_logical_dp_group(ovnsb_txn, &dpg->map);
     }
     sbrec_logical_flow_set_logical_dp_group(sbflow, dpg->dp_group);
 }
@@ -13368,13 +13406,8 @@  static bool reset_parallel = false;
 
 /* Updates the Logical_Flow and Multicast_Group tables in the OVN_SB database,
  * constructing their contents based on the OVN_NB database. */
-static void
-build_lflows(struct northd_context *ctx, struct hmap *datapaths,
-             struct hmap *ports, struct hmap *port_groups,
-             struct hmap *mcgroups, struct hmap *igmp_groups,
-             struct shash *meter_groups,
-             struct hmap *lbs, struct hmap *bfd_connections,
-             bool ovn_internal_version_changed)
+void build_lflows(struct northd_input *input_data,
+                  struct northd_data *data, struct ovsdb_idl_txn *ovnsb_txn)
 {
     struct hmap lflows;
 
@@ -13396,10 +13429,11 @@  build_lflows(struct northd_context *ctx, struct hmap *datapaths,
         use_parallel_build = false;
         reset_parallel = true;
     }
-    build_lswitch_and_lrouter_flows(datapaths, ports,
-                                    port_groups, &lflows, mcgroups,
-                                    igmp_groups, meter_groups, lbs,
-                                    bfd_connections);
+    build_lswitch_and_lrouter_flows(&data->datapaths, &data->ports,
+                                    &data->port_groups, &lflows,
+                                    &data->mcast_groups, &data->igmp_groups,
+                                    &data->meter_groups, &data->lbs,
+                                    &data->bfd_connections);
 
     /* Parallel build may result in a suboptimal hash. Resize the
      * hash to a correct size before doing lookups */
@@ -13469,7 +13503,8 @@  build_lflows(struct northd_context *ctx, struct hmap *datapaths,
 
     /* Push changes to the Logical_Flow table to database. */
     const struct sbrec_logical_flow *sbflow, *next_sbflow;
-    SBREC_LOGICAL_FLOW_FOR_EACH_SAFE (sbflow, next_sbflow, ctx->ovnsb_idl) {
+    SBREC_LOGICAL_FLOW_TABLE_FOR_EACH_SAFE (sbflow, next_sbflow,
+                                     input_data->sbrec_logical_flow_table) {
         struct sbrec_logical_dp_group *dp_group = sbflow->logical_dp_group;
         struct ovn_datapath *logical_datapath_od = NULL;
         size_t i;
@@ -13477,7 +13512,8 @@  build_lflows(struct northd_context *ctx, struct hmap *datapaths,
         /* Find one valid datapath to get the datapath type. */
         struct sbrec_datapath_binding *dp = sbflow->logical_datapath;
         if (dp) {
-            logical_datapath_od = ovn_datapath_from_sbrec(datapaths, dp);
+            logical_datapath_od = ovn_datapath_from_sbrec(
+                                            &data->datapaths, dp);
             if (logical_datapath_od
                 && ovn_datapath_is_stale(logical_datapath_od)) {
                 logical_datapath_od = NULL;
@@ -13485,7 +13521,7 @@  build_lflows(struct northd_context *ctx, struct hmap *datapaths,
         }
         for (i = 0; dp_group && i < dp_group->n_datapaths; i++) {
             logical_datapath_od = ovn_datapath_from_sbrec(
-                                      datapaths, dp_group->datapaths[i]);
+                                    &data->datapaths, dp_group->datapaths[i]);
             if (logical_datapath_od
                 && !ovn_datapath_is_stale(logical_datapath_od)) {
                 break;
@@ -13509,7 +13545,7 @@  build_lflows(struct northd_context *ctx, struct hmap *datapaths,
             sbflow->priority, sbflow->match, sbflow->actions,
             sbflow->controller_meter, sbflow->hash);
         if (lflow) {
-            if (ovn_internal_version_changed) {
+            if (data->ovn_internal_version_changed) {
                 const char *stage_name = smap_get_def(&sbflow->external_ids,
                                                   "stage-name", "");
                 const char *stage_hint = smap_get_def(&sbflow->external_ids,
@@ -13561,7 +13597,7 @@  build_lflows(struct northd_context *ctx, struct hmap *datapaths,
                 /* Check all logical datapaths from the group. */
                 for (i = 0; i < dp_group->n_datapaths; i++) {
                     od[n_datapaths] = ovn_datapath_from_sbrec(
-                                        datapaths, dp_group->datapaths[i]);
+                                    &data->datapaths, dp_group->datapaths[i]);
                     if (!od[n_datapaths]
                         || ovn_datapath_is_stale(od[n_datapaths])) {
                         continue;
@@ -13582,7 +13618,7 @@  build_lflows(struct northd_context *ctx, struct hmap *datapaths,
             }
 
             if (update_dp_group) {
-                ovn_sb_set_lflow_logical_dp_group(ctx, &dp_groups,
+                ovn_sb_set_lflow_logical_dp_group(ovnsb_txn, &dp_groups,
                                                   sbflow, &lflow->od_group);
             } else if (lflow->dpg && !lflow->dpg->dp_group) {
                 /* Setting relation between unique datapath group and
@@ -13602,11 +13638,11 @@  build_lflows(struct northd_context *ctx, struct hmap *datapaths,
         const char *pipeline = ovn_stage_get_pipeline_name(lflow->stage);
         uint8_t table = ovn_stage_get_table(lflow->stage);
 
-        sbflow = sbrec_logical_flow_insert(ctx->ovnsb_txn);
+        sbflow = sbrec_logical_flow_insert(ovnsb_txn);
         if (lflow->od) {
             sbrec_logical_flow_set_logical_datapath(sbflow, lflow->od->sb);
         }
-        ovn_sb_set_lflow_logical_dp_group(ctx, &dp_groups,
+        ovn_sb_set_lflow_logical_dp_group(ovnsb_txn, &dp_groups,
                                           sbflow, &lflow->od_group);
         sbrec_logical_flow_set_pipeline(sbflow, pipeline);
         sbrec_logical_flow_set_table_id(sbflow, table);
@@ -13655,8 +13691,9 @@  build_lflows(struct northd_context *ctx, struct hmap *datapaths,
 
     /* Push changes to the Multicast_Group table to database. */
     const struct sbrec_multicast_group *sbmc, *next_sbmc;
-    SBREC_MULTICAST_GROUP_FOR_EACH_SAFE (sbmc, next_sbmc, ctx->ovnsb_idl) {
-        struct ovn_datapath *od = ovn_datapath_from_sbrec(datapaths,
+    SBREC_MULTICAST_GROUP_TABLE_FOR_EACH_SAFE (sbmc, next_sbmc,
+                                input_data->sbrec_multicast_group_table) {
+        struct ovn_datapath *od = ovn_datapath_from_sbrec(&data->datapaths,
                                                           sbmc->datapath);
 
         if (!od || ovn_datapath_is_stale(od)) {
@@ -13666,31 +13703,32 @@  build_lflows(struct northd_context *ctx, struct hmap *datapaths,
 
         struct multicast_group group = { .name = sbmc->name,
                                          .key = sbmc->tunnel_key };
-        struct ovn_multicast *mc = ovn_multicast_find(mcgroups, od, &group);
+        struct ovn_multicast *mc = ovn_multicast_find(&data->mcast_groups,
+                                                      od, &group);
         if (mc) {
             ovn_multicast_update_sbrec(mc, sbmc);
-            ovn_multicast_destroy(mcgroups, mc);
+            ovn_multicast_destroy(&data->mcast_groups, mc);
         } else {
             sbrec_multicast_group_delete(sbmc);
         }
     }
     struct ovn_multicast *mc, *next_mc;
-    HMAP_FOR_EACH_SAFE (mc, next_mc, hmap_node, mcgroups) {
+    HMAP_FOR_EACH_SAFE (mc, next_mc, hmap_node, &data->mcast_groups) {
         if (!mc->datapath) {
-            ovn_multicast_destroy(mcgroups, mc);
+            ovn_multicast_destroy(&data->mcast_groups, mc);
             continue;
         }
-        sbmc = sbrec_multicast_group_insert(ctx->ovnsb_txn);
+        sbmc = sbrec_multicast_group_insert(ovnsb_txn);
         sbrec_multicast_group_set_datapath(sbmc, mc->datapath->sb);
         sbrec_multicast_group_set_name(sbmc, mc->group->name);
         sbrec_multicast_group_set_tunnel_key(sbmc, mc->group->key);
         ovn_multicast_update_sbrec(mc, sbmc);
-        ovn_multicast_destroy(mcgroups, mc);
+        ovn_multicast_destroy(&data->mcast_groups, mc);
     }
 }
 
 static void
-sync_address_set(struct northd_context *ctx, const char *name,
+sync_address_set(struct ovsdb_idl_txn *ovnsb_txn, const char *name,
                  const char **addrs, size_t n_addrs,
                  struct shash *sb_address_sets)
 {
@@ -13698,7 +13736,7 @@  sync_address_set(struct northd_context *ctx, const char *name,
     sb_address_set = shash_find_and_delete(sb_address_sets,
                                            name);
     if (!sb_address_set) {
-        sb_address_set = sbrec_address_set_insert(ctx->ovnsb_txn);
+        sb_address_set = sbrec_address_set_insert(ovnsb_txn);
         sbrec_address_set_set_name(sb_address_set, name);
     }
 
@@ -13717,23 +13755,27 @@  sync_address_set(struct northd_context *ctx, const char *name,
  * in OVN_Northbound, so that the address sets used in Logical_Flows in
  * OVN_Southbound is checked against the proper set.*/
 static void
-sync_address_sets(struct northd_context *ctx, struct hmap *datapaths)
+sync_address_sets(struct northd_input *input_data,
+                  struct ovsdb_idl_txn *ovnsb_txn,
+                  struct hmap *datapaths)
 {
     struct shash sb_address_sets = SHASH_INITIALIZER(&sb_address_sets);
 
     const struct sbrec_address_set *sb_address_set;
-    SBREC_ADDRESS_SET_FOR_EACH (sb_address_set, ctx->ovnsb_idl) {
+    SBREC_ADDRESS_SET_TABLE_FOR_EACH (sb_address_set,
+                                   input_data->sbrec_address_set_table) {
         shash_add(&sb_address_sets, sb_address_set->name, sb_address_set);
     }
 
     /* Service monitor MAC. */
     const char *svc_monitor_macp = svc_monitor_mac;
-    sync_address_set(ctx, "svc_monitor_mac", &svc_monitor_macp, 1,
+    sync_address_set(ovnsb_txn, "svc_monitor_mac", &svc_monitor_macp, 1,
                      &sb_address_sets);
 
     /* sync port group generated address sets first */
     const struct nbrec_port_group *nb_port_group;
-    NBREC_PORT_GROUP_FOR_EACH (nb_port_group, ctx->ovnnb_idl) {
+    NBREC_PORT_GROUP_TABLE_FOR_EACH (nb_port_group,
+                                     input_data->nbrec_port_group_table) {
         struct svec ipv4_addrs = SVEC_EMPTY_INITIALIZER;
         struct svec ipv6_addrs = SVEC_EMPTY_INITIALIZER;
         for (size_t i = 0; i < nb_port_group->n_ports; i++) {
@@ -13750,11 +13792,11 @@  sync_address_sets(struct northd_context *ctx, struct hmap *datapaths)
         }
         char *ipv4_addrs_name = xasprintf("%s_ip4", nb_port_group->name);
         char *ipv6_addrs_name = xasprintf("%s_ip6", nb_port_group->name);
-        sync_address_set(ctx, ipv4_addrs_name,
+        sync_address_set(ovnsb_txn, ipv4_addrs_name,
                          /* "char **" is not compatible with "const char **" */
                          (const char **)ipv4_addrs.names,
                          ipv4_addrs.n, &sb_address_sets);
-        sync_address_set(ctx, ipv6_addrs_name,
+        sync_address_set(ovnsb_txn, ipv6_addrs_name,
                          /* "char **" is not compatible with "const char **" */
                          (const char **)ipv6_addrs.names,
                          ipv6_addrs.n, &sb_address_sets);
@@ -13775,7 +13817,7 @@  sync_address_sets(struct northd_context *ctx, struct hmap *datapaths)
             char *ipv4_addrs_name = lr_lb_address_set_name(od, AF_INET);
             const char **ipv4_addrs = sset_array(&od->lb_ips_v4);
 
-            sync_address_set(ctx, ipv4_addrs_name, ipv4_addrs,
+            sync_address_set(ovnsb_txn, ipv4_addrs_name, ipv4_addrs,
                              sset_count(&od->lb_ips_v4), &sb_address_sets);
             free(ipv4_addrs_name);
             free(ipv4_addrs);
@@ -13785,7 +13827,7 @@  sync_address_sets(struct northd_context *ctx, struct hmap *datapaths)
             char *ipv6_addrs_name = lr_lb_address_set_name(od, AF_INET6);
             const char **ipv6_addrs = sset_array(&od->lb_ips_v6);
 
-            sync_address_set(ctx, ipv6_addrs_name, ipv6_addrs,
+            sync_address_set(ovnsb_txn, ipv6_addrs_name, ipv6_addrs,
                              sset_count(&od->lb_ips_v6), &sb_address_sets);
             free(ipv6_addrs_name);
             free(ipv6_addrs);
@@ -13795,8 +13837,9 @@  sync_address_sets(struct northd_context *ctx, struct hmap *datapaths)
     /* sync user defined address sets, which may overwrite port group
      * generated address sets if same name is used */
     const struct nbrec_address_set *nb_address_set;
-    NBREC_ADDRESS_SET_FOR_EACH (nb_address_set, ctx->ovnnb_idl) {
-        sync_address_set(ctx, nb_address_set->name,
+    NBREC_ADDRESS_SET_TABLE_FOR_EACH (nb_address_set,
+                              input_data->nbrec_address_set_table) {
+        sync_address_set(ovnsb_txn, nb_address_set->name,
             /* "char **" is not compatible with "const char **" */
             (const char **)nb_address_set->addresses,
             nb_address_set->n_addresses, &sb_address_sets);
@@ -13815,12 +13858,15 @@  sync_address_sets(struct northd_context *ctx, struct hmap *datapaths)
  * contains lport uuids, while in OVN_Southbound we store the lport names.
  */
 static void
-sync_port_groups(struct northd_context *ctx, struct hmap *pgs)
+sync_port_groups(struct northd_input *input_data,
+                struct ovsdb_idl_txn *ovnsb_txn,
+                 struct hmap *pgs)
 {
     struct shash sb_port_groups = SHASH_INITIALIZER(&sb_port_groups);
 
     const struct sbrec_port_group *sb_port_group;
-    SBREC_PORT_GROUP_FOR_EACH (sb_port_group, ctx->ovnsb_idl) {
+    SBREC_PORT_GROUP_TABLE_FOR_EACH (sb_port_group,
+                               input_data->sbrec_port_group_table) {
         shash_add(&sb_port_groups, sb_port_group->name, sb_port_group);
     }
 
@@ -13836,7 +13882,7 @@  sync_port_groups(struct northd_context *ctx, struct hmap *pgs)
             sb_port_group = shash_find_and_delete(&sb_port_groups,
                                                   ds_cstr(&sb_name));
             if (!sb_port_group) {
-                sb_port_group = sbrec_port_group_insert(ctx->ovnsb_txn);
+                sb_port_group = sbrec_port_group_insert(ovnsb_txn);
                 sbrec_port_group_set_name(sb_port_group, ds_cstr(&sb_name));
             }
 
@@ -13943,7 +13989,7 @@  done:
 }
 
 static void
-sync_meters_iterate_nb_meter(struct northd_context *ctx,
+sync_meters_iterate_nb_meter(struct ovsdb_idl_txn *ovnsb_txn,
                              const char *meter_name,
                              const struct nbrec_meter *nb_meter,
                              struct shash *sb_meters,
@@ -13954,7 +14000,7 @@  sync_meters_iterate_nb_meter(struct northd_context *ctx,
 
     sb_meter = shash_find_data(sb_meters, meter_name);
     if (!sb_meter) {
-        sb_meter = sbrec_meter_insert(ctx->ovnsb_txn);
+        sb_meter = sbrec_meter_insert(ovnsb_txn);
         sbrec_meter_set_name(sb_meter, meter_name);
         shash_add(sb_meters, sb_meter->name, sb_meter);
         new_sb_meter = true;
@@ -13967,7 +14013,7 @@  sync_meters_iterate_nb_meter(struct northd_context *ctx,
         for (size_t i = 0; i < nb_meter->n_bands; i++) {
             const struct nbrec_meter_band *nb_band = nb_meter->bands[i];
 
-            sb_bands[i] = sbrec_meter_band_insert(ctx->ovnsb_txn);
+            sb_bands[i] = sbrec_meter_band_insert(ovnsb_txn);
 
             sbrec_meter_band_set_action(sb_bands[i], nb_band->action);
             sbrec_meter_band_set_rate(sb_bands[i], nb_band->rate);
@@ -13982,7 +14028,8 @@  sync_meters_iterate_nb_meter(struct northd_context *ctx,
 }
 
 static void
-sync_acl_fair_meter(struct northd_context *ctx, struct shash *meter_groups,
+sync_acl_fair_meter(struct ovsdb_idl_txn *ovnsb_txn,
+                    struct shash *meter_groups,
                     const struct nbrec_acl *acl, struct shash *sb_meters,
                     struct sset *used_sb_meters)
 {
@@ -13994,7 +14041,7 @@  sync_acl_fair_meter(struct northd_context *ctx, struct shash *meter_groups,
     }
 
     char *meter_name = alloc_acl_log_unique_meter_name(acl);
-    sync_meters_iterate_nb_meter(ctx, meter_name, nb_meter, sb_meters,
+    sync_meters_iterate_nb_meter(ovnsb_txn, meter_name, nb_meter, sb_meters,
                                  used_sb_meters);
     free(meter_name);
 }
@@ -14005,19 +14052,21 @@  sync_acl_fair_meter(struct northd_context *ctx, struct shash *meter_groups,
  * a private copy of its meter in the SB table.
  */
 static void
-sync_meters(struct northd_context *ctx, struct shash *meter_groups)
+sync_meters(struct northd_input *input_data,
+            struct ovsdb_idl_txn *ovnsb_txn,
+            struct shash *meter_groups)
 {
     struct shash sb_meters = SHASH_INITIALIZER(&sb_meters);
     struct sset used_sb_meters = SSET_INITIALIZER(&used_sb_meters);
 
     const struct sbrec_meter *sb_meter;
-    SBREC_METER_FOR_EACH (sb_meter, ctx->ovnsb_idl) {
+    SBREC_METER_TABLE_FOR_EACH (sb_meter, input_data->sbrec_meter_table) {
         shash_add(&sb_meters, sb_meter->name, sb_meter);
     }
 
     const struct nbrec_meter *nb_meter;
-    NBREC_METER_FOR_EACH (nb_meter, ctx->ovnnb_idl) {
-        sync_meters_iterate_nb_meter(ctx, nb_meter->name, nb_meter,
+    NBREC_METER_TABLE_FOR_EACH (nb_meter, input_data->nbrec_meter_table) {
+        sync_meters_iterate_nb_meter(ovnsb_txn, nb_meter->name, nb_meter,
                                      &sb_meters, &used_sb_meters);
     }
 
@@ -14027,8 +14076,8 @@  sync_meters(struct northd_context *ctx, struct shash *meter_groups)
      * rate-limited.
      */
     const struct nbrec_acl *acl;
-    NBREC_ACL_FOR_EACH (acl, ctx->ovnnb_idl) {
-        sync_acl_fair_meter(ctx, meter_groups, acl,
+    NBREC_ACL_TABLE_FOR_EACH (acl, input_data->nbrec_acl_table) {
+        sync_acl_fair_meter(ovnsb_txn, meter_groups, acl,
                             &sb_meters, &used_sb_meters);
     }
 
@@ -14077,7 +14126,9 @@  get_dns_info_from_hmap(struct hmap *dns_map, struct uuid *uuid)
 }
 
 static void
-sync_dns_entries(struct northd_context *ctx, struct hmap *datapaths)
+sync_dns_entries(struct northd_input *input_data,
+                 struct ovsdb_idl_txn *ovnsb_txn,
+                 struct hmap *datapaths)
 {
     struct hmap dns_map = HMAP_INITIALIZER(&dns_map);
     struct ovn_datapath *od;
@@ -14105,7 +14156,8 @@  sync_dns_entries(struct northd_context *ctx, struct hmap *datapaths)
     }
 
     const struct sbrec_dns *sbrec_dns, *next;
-    SBREC_DNS_FOR_EACH_SAFE (sbrec_dns, next, ctx->ovnsb_idl) {
+    SBREC_DNS_TABLE_FOR_EACH_SAFE (sbrec_dns, next,
+                                   input_data->sbrec_dns_table) {
         const char *nb_dns_uuid = smap_get(&sbrec_dns->external_ids, "dns_id");
         struct uuid dns_uuid;
         if (!nb_dns_uuid || !uuid_from_string(&dns_uuid, nb_dns_uuid)) {
@@ -14125,7 +14177,7 @@  sync_dns_entries(struct northd_context *ctx, struct hmap *datapaths)
     struct dns_info *dns_info;
     HMAP_FOR_EACH_POP (dns_info, hmap_node, &dns_map) {
         if (!dns_info->sb_dns) {
-            sbrec_dns = sbrec_dns_insert(ctx->ovnsb_txn);
+            sbrec_dns = sbrec_dns_insert(ovnsb_txn);
             dns_info->sb_dns = sbrec_dns;
             char *dns_id = xasprintf(
                 UUID_FMT, UUID_ARGS(&dns_info->nb_dns->header_.uuid));
@@ -14195,7 +14247,9 @@  destroy_datapaths_and_ports(struct hmap *datapaths, struct hmap *ports,
 }
 
 static void
-build_ip_mcast(struct northd_context *ctx, struct hmap *datapaths)
+build_ip_mcast(struct northd_input *input_data,
+               struct ovsdb_idl_txn *ovnsb_txn,
+               struct hmap *datapaths)
 {
     struct ovn_datapath *od;
 
@@ -14205,10 +14259,10 @@  build_ip_mcast(struct northd_context *ctx, struct hmap *datapaths)
         }
 
         const struct sbrec_ip_multicast *ip_mcast =
-            ip_mcast_lookup(ctx->sbrec_ip_mcast_by_dp, od->sb);
+            ip_mcast_lookup(input_data->sbrec_ip_mcast_by_dp, od->sb);
 
         if (!ip_mcast) {
-            ip_mcast = sbrec_ip_multicast_insert(ctx->ovnsb_txn);
+            ip_mcast = sbrec_ip_multicast_insert(ovnsb_txn);
         }
         store_mcast_info_for_switch_datapath(ip_mcast, od);
     }
@@ -14216,7 +14270,8 @@  build_ip_mcast(struct northd_context *ctx, struct hmap *datapaths)
     /* Delete southbound records without northbound matches. */
     const struct sbrec_ip_multicast *sb, *sb_next;
 
-    SBREC_IP_MULTICAST_FOR_EACH_SAFE (sb, sb_next, ctx->ovnsb_idl) {
+    SBREC_IP_MULTICAST_TABLE_FOR_EACH_SAFE (sb, sb_next,
+                                   input_data->sbrec_ip_multicast_table) {
         od = ovn_datapath_from_sbrec(datapaths, sb->datapath);
         if (!od || ovn_datapath_is_stale(od)) {
             sbrec_ip_multicast_delete(sb);
@@ -14225,7 +14280,7 @@  build_ip_mcast(struct northd_context *ctx, struct hmap *datapaths)
 }
 
 static void
-build_mcast_groups(struct northd_context *ctx,
+build_mcast_groups(struct northd_input *input_data,
                    struct hmap *datapaths, struct hmap *ports,
                    struct hmap *mcast_groups,
                    struct hmap *igmp_groups)
@@ -14279,7 +14334,8 @@  build_mcast_groups(struct northd_context *ctx,
 
     const struct sbrec_igmp_group *sb_igmp, *sb_igmp_next;
 
-    SBREC_IGMP_GROUP_FOR_EACH_SAFE (sb_igmp, sb_igmp_next, ctx->ovnsb_idl) {
+    SBREC_IGMP_GROUP_TABLE_FOR_EACH_SAFE (sb_igmp, sb_igmp_next,
+                                     input_data->sbrec_igmp_group_table) {
         /* If this is a stale group (e.g., controller had crashed,
          * purge it).
          */
@@ -14321,7 +14377,7 @@  build_mcast_groups(struct northd_context *ctx,
          * if the multicast group already exists.
          */
         struct ovn_igmp_group *igmp_group =
-            ovn_igmp_group_add(ctx, igmp_groups, od, &group_address,
+            ovn_igmp_group_add(input_data, igmp_groups, od, &group_address,
                                sb_igmp->address);
 
         /* Add the extracted ports to the IGMP group. */
@@ -14365,7 +14421,8 @@  build_mcast_groups(struct northd_context *ctx,
                 }
 
                 struct ovn_igmp_group *igmp_group_rtr =
-                    ovn_igmp_group_add(ctx, igmp_groups, router_port->od,
+                    ovn_igmp_group_add(input_data,
+                                       igmp_groups, router_port->od,
                                        address, igmp_group->mcgroup.name);
                 struct ovn_port **router_igmp_ports =
                     xmalloc(sizeof *router_igmp_ports);
@@ -14400,47 +14457,102 @@  build_mcast_groups(struct northd_context *ctx,
 }
 
 static void
-build_meter_groups(struct northd_context *ctx,
+build_meter_groups(struct northd_input *input_data,
                    struct shash *meter_groups)
 {
     const struct nbrec_meter *nb_meter;
-    NBREC_METER_FOR_EACH (nb_meter, ctx->ovnnb_idl) {
+    NBREC_METER_TABLE_FOR_EACH (nb_meter, input_data->nbrec_meter_table) {
         shash_add(meter_groups, nb_meter->name, nb_meter);
     }
 }
 
+void
+northd_init(struct northd_data *data)
+{
+    hmap_init(&data->datapaths);
+    hmap_init(&data->ports);
+    hmap_init(&data->port_groups);
+    hmap_init(&data->mcast_groups);
+    hmap_init(&data->igmp_groups);
+    shash_init(&data->meter_groups);
+    hmap_init(&data->lbs);
+    hmap_init(&data->bfd_connections);
+    ovs_list_init(&data->lr_list);
+    data->ovn_internal_version_changed = false;
+}
+
+void
+northd_destroy(struct northd_data *data)
+{
+    struct ovn_northd_lb *lb;
+    HMAP_FOR_EACH_POP (lb, hmap_node, &data->lbs) {
+        ovn_northd_lb_destroy(lb);
+    }
+    hmap_destroy(&data->lbs);
+
+    struct ovn_igmp_group *igmp_group, *next_igmp_group;
+
+    HMAP_FOR_EACH_SAFE (igmp_group, next_igmp_group, hmap_node,
+                        &data->igmp_groups) {
+        ovn_igmp_group_destroy(&data->igmp_groups, igmp_group);
+    }
+
+    struct ovn_port_group *pg, *next_pg;
+    HMAP_FOR_EACH_SAFE (pg, next_pg, key_node, &data->port_groups) {
+        ovn_port_group_destroy(&data->port_groups, pg);
+    }
+
+    hmap_destroy(&data->igmp_groups);
+    hmap_destroy(&data->mcast_groups);
+    hmap_destroy(&data->port_groups);
+    hmap_destroy(&data->bfd_connections);
+
+    struct shash_node *node, *next;
+    SHASH_FOR_EACH_SAFE (node, next, &data->meter_groups) {
+        shash_delete(&data->meter_groups, node);
+    }
+    shash_destroy(&data->meter_groups);
+
+    /* XXX Having to explicitly clean up macam here
+     * is a bit strange. We don't explicitly initialize
+     * macam in this module, but this is the logical place
+     * to clean it up. Ideally, more IPAM logic can be factored
+     * out of ovn-northd and this can be taken care of there
+     * as well.
+     */
+    cleanup_macam();
+
+    destroy_datapaths_and_ports(&data->datapaths, &data->ports,
+                                &data->lr_list);
+}
+
 static void
-ovnnb_db_run(struct northd_context *ctx,
+ovnnb_db_run(struct northd_input *input_data,
+             struct northd_data *data,
+             struct ovsdb_idl_txn *ovnnb_txn,
+             struct ovsdb_idl_txn *ovnsb_txn,
              struct ovsdb_idl_index *sbrec_chassis_by_name,
              struct ovsdb_idl_index *sbrec_chassis_by_hostname,
              struct ovsdb_idl_loop *sb_loop,
-             struct hmap *datapaths, struct hmap *ports,
-             struct ovs_list *lr_list,
-             int64_t loop_start_time,
-             const char *ovn_internal_version)
+             int64_t loop_start_time)
 {
-    if (!ctx->ovnsb_txn || !ctx->ovnnb_txn) {
+    if (!ovnsb_txn || !ovnnb_txn) {
         return;
     }
     stopwatch_start(BUILD_LFLOWS_CTX_STOPWATCH_NAME, time_msec());
-    struct hmap port_groups;
-    struct hmap mcast_groups;
-    struct hmap igmp_groups;
-    struct shash meter_groups = SHASH_INITIALIZER(&meter_groups);
-    struct hmap lbs;
-    struct hmap bfd_connections = HMAP_INITIALIZER(&bfd_connections);
-    bool ovn_internal_version_changed = true;
 
     /* Sync ipsec configuration.
      * Copy nb_cfg from northbound to southbound database.
      * Also set up to update sb_cfg once our southbound transaction commits. */
-    const struct nbrec_nb_global *nb = nbrec_nb_global_first(ctx->ovnnb_idl);
+    const struct nbrec_nb_global *nb = nbrec_nb_global_table_first(
+                                       input_data->nbrec_nb_global_table);
     if (!nb) {
-        nb = nbrec_nb_global_insert(ctx->ovnnb_txn);
+        nb = nbrec_nb_global_insert(ovnnb_txn);
     }
-    const struct sbrec_sb_global *sb = sbrec_sb_global_first(ctx->ovnsb_idl);
+    const struct sbrec_sb_global *sb = sbrec_sb_global_table_first(
+                                       input_data->sbrec_sb_global_table);
     if (!sb) {
-        sb = sbrec_sb_global_insert(ctx->ovnsb_txn);
+        sb = sbrec_sb_global_insert(ovnsb_txn);
     }
     if (nb->ipsec != sb->ipsec) {
         sbrec_sb_global_set_ipsec(sb, nb->ipsec);
@@ -14477,17 +14589,19 @@  ovnnb_db_run(struct northd_context *ctx,
         smap_replace(&options, "svc_monitor_mac", svc_monitor_mac);
     }
 
-    char *max_tunid = xasprintf("%d", get_ovn_max_dp_key_local(ctx));
+    char *max_tunid = xasprintf("%d", get_ovn_max_dp_key_local(input_data));
     smap_replace(&options, "max_tunid", max_tunid);
     free(max_tunid);
 
+    char *ovn_internal_version = ovn_get_internal_version();
     if (!strcmp(ovn_internal_version,
                 smap_get_def(&options, "northd_internal_version", ""))) {
-        ovn_internal_version_changed = false;
+        data->ovn_internal_version_changed = false;
     } else {
         smap_replace(&options, "northd_internal_version",
                      ovn_internal_version);
     }
+    free(ovn_internal_version);
 
     if (!smap_equal(&nb->options, &options)) {
         nbrec_nb_global_verify_options(nb);
@@ -14511,73 +14625,36 @@  ovnnb_db_run(struct northd_context *ctx,
     check_lsp_is_up = !smap_get_bool(&nb->options,
                                      "ignore_lsp_down", true);
 
-    build_datapaths(ctx, datapaths, lr_list);
-    build_ovn_lbs(ctx, datapaths, &lbs);
-    build_lrouter_lbs(datapaths, &lbs);
-    build_ports(ctx, sbrec_chassis_by_name, sbrec_chassis_by_hostname,
-                datapaths, ports);
-    build_ovn_lr_lbs(datapaths, &lbs);
-    build_ovn_lb_svcs(ctx, ports, &lbs);
-    build_ipam(datapaths, ports);
-    build_port_group_lswitches(ctx, &port_groups, ports);
-    build_lrouter_groups(ports, lr_list);
-    build_ip_mcast(ctx, datapaths);
-    build_mcast_groups(ctx, datapaths, ports, &mcast_groups, &igmp_groups);
-    build_meter_groups(ctx, &meter_groups);
-    build_bfd_table(ctx, &bfd_connections, ports);
+    build_datapaths(input_data, ovnsb_txn, &data->datapaths, &data->lr_list);
+    build_ovn_lbs(input_data, ovnsb_txn, &data->datapaths, &data->lbs);
+    build_lrouter_lbs(&data->datapaths, &data->lbs);
+    build_ports(input_data, ovnsb_txn, sbrec_chassis_by_name,
+                sbrec_chassis_by_hostname,
+                &data->datapaths, &data->ports);
+    build_ovn_lr_lbs(&data->datapaths, &data->lbs);
+    build_ovn_lb_svcs(input_data, ovnsb_txn, &data->ports, &data->lbs);
+    build_ipam(&data->datapaths, &data->ports);
+    build_port_group_lswitches(input_data, &data->port_groups, &data->ports);
+    build_lrouter_groups(&data->ports, &data->lr_list);
+    build_ip_mcast(input_data, ovnsb_txn, &data->datapaths);
+    build_mcast_groups(input_data, &data->datapaths, &data->ports,
+                       &data->mcast_groups, &data->igmp_groups);
+    build_meter_groups(input_data, &data->meter_groups);
+    build_bfd_table(input_data,
+                    ovnsb_txn, &data->bfd_connections, &data->ports);
     stopwatch_stop(BUILD_LFLOWS_CTX_STOPWATCH_NAME, time_msec());
     stopwatch_start(BUILD_LFLOWS_STOPWATCH_NAME, time_msec());
-    build_lflows(ctx, datapaths, ports, &port_groups, &mcast_groups,
-                 &igmp_groups, &meter_groups, &lbs, &bfd_connections,
-                 ovn_internal_version_changed);
+    build_lflows(input_data, data, ovnsb_txn);
     stopwatch_stop(BUILD_LFLOWS_STOPWATCH_NAME, time_msec());
     stopwatch_start(CLEAR_LFLOWS_CTX_STOPWATCH_NAME, time_msec());
-    ovn_update_ipv6_prefix(ports);
-
-    sync_address_sets(ctx, datapaths);
-    sync_port_groups(ctx, &port_groups);
-    sync_meters(ctx, &meter_groups);
-    sync_dns_entries(ctx, datapaths);
-    cleanup_stale_fdp_entries(ctx, datapaths);
-
-    struct ovn_northd_lb *lb;
-    HMAP_FOR_EACH_POP (lb, hmap_node, &lbs) {
-        ovn_northd_lb_destroy(lb);
-    }
-    hmap_destroy(&lbs);
-
-    struct ovn_igmp_group *igmp_group, *next_igmp_group;
-
-    HMAP_FOR_EACH_SAFE (igmp_group, next_igmp_group, hmap_node, &igmp_groups) {
-        ovn_igmp_group_destroy(&igmp_groups, igmp_group);
-    }
-
-    struct ovn_port_group *pg, *next_pg;
-    HMAP_FOR_EACH_SAFE (pg, next_pg, key_node, &port_groups) {
-        ovn_port_group_destroy(&port_groups, pg);
-    }
-
-    bfd_cleanup_connections(ctx, &bfd_connections);
-
-    hmap_destroy(&igmp_groups);
-    hmap_destroy(&mcast_groups);
-    hmap_destroy(&port_groups);
-    hmap_destroy(&bfd_connections);
-
-    struct shash_node *node, *next;
-    SHASH_FOR_EACH_SAFE (node, next, &meter_groups) {
-        shash_delete(&meter_groups, node);
-    }
-    shash_destroy(&meter_groups);
-
-    /* XXX Having to explicitly clean up macam here
-     * is a bit strange. We don't explicitly initialize
-     * macam in this module, but this is the logical place
-     * to clean it up. Ideally, more IPAM logic can be factored
-     * out of ovn-northd and this can be taken care of there
-     * as well.
-     */
-    cleanup_macam();
+    ovn_update_ipv6_prefix(&data->ports);
+
+    sync_address_sets(input_data,  ovnsb_txn, &data->datapaths);
+    sync_port_groups(input_data, ovnsb_txn, &data->port_groups);
+    sync_meters(input_data, ovnsb_txn, &data->meter_groups);
+    sync_dns_entries(input_data, ovnsb_txn, &data->datapaths);
+    cleanup_stale_fdp_entries(input_data, &data->datapaths);
+    bfd_cleanup_connections(input_data, &data->bfd_connections);
     stopwatch_stop(CLEAR_LFLOWS_CTX_STOPWATCH_NAME, time_msec());
 
 }
@@ -14622,7 +14699,7 @@  struct ha_chassis_group_node {
 };
 
 static void
-update_sb_ha_group_ref_chassis(struct northd_context *ctx,
+update_sb_ha_group_ref_chassis(struct northd_input *input_data,
                                struct shash *ha_ref_chassis_map)
 {
     struct hmap ha_ch_grps = HMAP_INITIALIZER(&ha_ch_grps);
@@ -14630,7 +14707,8 @@  update_sb_ha_group_ref_chassis(struct northd_context *ctx,
 
     /* Initialize a set of all ha_chassis_groups in SB. */
     const struct sbrec_ha_chassis_group *ha_ch_grp;
-    SBREC_HA_CHASSIS_GROUP_FOR_EACH (ha_ch_grp, ctx->ovnsb_idl) {
+    SBREC_HA_CHASSIS_GROUP_TABLE_FOR_EACH (ha_ch_grp,
+                                    input_data->sbrec_ha_chassis_group_table) {
         ha_ch_grp_node = xzalloc(sizeof *ha_ch_grp_node);
         ha_ch_grp_node->ha_ch_grp = ha_ch_grp;
         hmap_insert(&ha_ch_grps, &ha_ch_grp_node->hmap_node,
@@ -14694,7 +14772,7 @@  update_sb_ha_group_ref_chassis(struct northd_context *ctx,
  *  - 'ref_chassis' of hagrp1.
  */
 static void
-build_ha_chassis_group_ref_chassis(struct northd_context *ctx,
+build_ha_chassis_group_ref_chassis(struct northd_input *input_data,
                                    const struct sbrec_port_binding *sb,
                                    struct ovn_port *op,
                                    struct shash *ha_ref_chassis_map)
@@ -14720,7 +14798,7 @@  build_ha_chassis_group_ref_chassis(struct northd_context *ctx,
     SSET_FOR_EACH (ha_group_name, &lr_group->ha_chassis_groups) {
         const struct sbrec_ha_chassis_group *sb_ha_chassis_grp;
         sb_ha_chassis_grp = ha_chassis_group_lookup_by_name(
-            ctx->sbrec_ha_chassis_grp_by_name, ha_group_name);
+            input_data->sbrec_ha_chassis_grp_by_name, ha_group_name);
 
         if (sb_ha_chassis_grp) {
             struct ha_ref_chassis_info *ref_ch_info =
@@ -14735,14 +14813,17 @@  build_ha_chassis_group_ref_chassis(struct northd_context *ctx,
  * this column is not empty, it means we need to set the corresponding logical
  * port as 'up' in the northbound DB. */
 static void
-handle_port_binding_changes(struct northd_context *ctx, struct hmap *ports,
+handle_port_binding_changes(struct northd_input *input_data,
+                            struct ovsdb_idl_txn *ovnsb_txn,
+                            struct hmap *ports,
                             struct shash *ha_ref_chassis_map)
 {
     const struct sbrec_port_binding *sb;
     bool build_ha_chassis_ref = false;
-    if (ctx->ovnsb_txn) {
+    if (ovnsb_txn) {
         const struct sbrec_ha_chassis_group *ha_ch_grp;
-        SBREC_HA_CHASSIS_GROUP_FOR_EACH (ha_ch_grp, ctx->ovnsb_idl) {
+        SBREC_HA_CHASSIS_GROUP_TABLE_FOR_EACH (ha_ch_grp,
+                                    input_data->sbrec_ha_chassis_group_table) {
             if (ha_ch_grp->n_ha_chassis > 1) {
                 struct ha_ref_chassis_info *ref_ch_info =
                     xzalloc(sizeof *ref_ch_info);
@@ -14753,7 +14834,8 @@  handle_port_binding_changes(struct northd_context *ctx, struct hmap *ports,
         }
     }
 
-    SBREC_PORT_BINDING_FOR_EACH(sb, ctx->ovnsb_idl) {
+    SBREC_PORT_BINDING_TABLE_FOR_EACH (sb,
+                                       input_data->sbrec_port_binding_table) {
         struct ovn_port *op = ovn_port_find(ports, sb->logical_port);
 
         if (!op || !op->nbsp) {
@@ -14778,10 +14860,10 @@  handle_port_binding_changes(struct northd_context *ctx, struct hmap *ports,
             nbrec_logical_switch_port_set_up(op->nbsp, &up, 1);
         }
 
-        if (build_ha_chassis_ref && ctx->ovnsb_txn && sb->chassis) {
+        if (build_ha_chassis_ref && ovnsb_txn && sb->chassis) {
             /* Check and add the chassis which has claimed this 'sb'
              * to the ha chassis group's ref_chassis if required. */
-            build_ha_chassis_group_ref_chassis(ctx, sb, op,
+            build_ha_chassis_group_ref_chassis(input_data, sb, op,
                                                ha_ref_chassis_map);
         }
     }
@@ -14789,12 +14871,13 @@  handle_port_binding_changes(struct northd_context *ctx, struct hmap *ports,
 
 /* Updates the sb_cfg and hv_cfg columns in the northbound NB_Global table. */
 static void
-update_northbound_cfg(struct northd_context *ctx,
+update_northbound_cfg(struct northd_input *input_data,
                       struct ovsdb_idl_loop *sb_loop,
                       int64_t loop_start_time)
 {
     /* Update northbound sb_cfg if appropriate. */
-    const struct nbrec_nb_global *nbg = nbrec_nb_global_first(ctx->ovnnb_idl);
+    const struct nbrec_nb_global *nbg = nbrec_nb_global_table_first(
+                               input_data->nbrec_nb_global_table);
     int64_t sb_cfg = sb_loop->cur_cfg;
     if (nbg && sb_cfg && nbg->sb_cfg != sb_cfg) {
         nbrec_nb_global_set_sb_cfg(nbg, sb_cfg);
@@ -14807,7 +14890,8 @@  update_northbound_cfg(struct northd_context *ctx,
         const struct sbrec_chassis_private *chassis_priv;
         int64_t hv_cfg = nbg->nb_cfg;
         int64_t hv_cfg_ts = 0;
-        SBREC_CHASSIS_PRIVATE_FOR_EACH (chassis_priv, ctx->ovnsb_idl) {
+        SBREC_CHASSIS_PRIVATE_TABLE_FOR_EACH (chassis_priv,
+                                    input_data->sbrec_chassis_private_table) {
             const struct sbrec_chassis *chassis = chassis_priv->chassis;
             if (chassis) {
                 if (smap_get_bool(&chassis->other_config,
@@ -14841,45 +14925,47 @@  update_northbound_cfg(struct northd_context *ctx,
 
 /* Handle a fairly small set of changes in the southbound database. */
 static void
-ovnsb_db_run(struct northd_context *ctx,
+ovnsb_db_run(struct northd_input *input_data,
+             struct ovsdb_idl_txn *ovnnb_txn,
+             struct ovsdb_idl_txn *ovnsb_txn,
              struct ovsdb_idl_loop *sb_loop,
              struct hmap *ports,
              int64_t loop_start_time)
 {
-    if (!ctx->ovnnb_txn || !ovsdb_idl_has_ever_connected(ctx->ovnsb_idl)) {
+    if (!ovnnb_txn ||
+        !ovsdb_idl_has_ever_connected(ovsdb_idl_txn_get_idl(ovnsb_txn))) {
         return;
     }
 
     struct shash ha_ref_chassis_map = SHASH_INITIALIZER(&ha_ref_chassis_map);
-    handle_port_binding_changes(ctx, ports, &ha_ref_chassis_map);
-    update_northbound_cfg(ctx, sb_loop, loop_start_time);
-    if (ctx->ovnsb_txn) {
-        update_sb_ha_group_ref_chassis(ctx, &ha_ref_chassis_map);
+    handle_port_binding_changes(input_data,
+                                ovnsb_txn, ports, &ha_ref_chassis_map);
+    update_northbound_cfg(input_data, sb_loop, loop_start_time);
+
+    if (ovnsb_txn) {
+        update_sb_ha_group_ref_chassis(input_data,
+                                       &ha_ref_chassis_map);
     }
     shash_destroy(&ha_ref_chassis_map);
 }
 
-void
-ovn_db_run(struct northd_context *ctx)
+void northd_run(struct northd_input *input_data,
+                struct northd_data *data,
+                struct ovsdb_idl_txn *ovnnb_txn,
+                struct ovsdb_idl_txn *ovnsb_txn,
+                struct ovsdb_idl_loop *sb_loop)
 {
-    struct hmap datapaths, ports;
-    struct ovs_list lr_list;
-    ovs_list_init(&lr_list);
-    hmap_init(&datapaths);
-    hmap_init(&ports);
-    use_parallel_build = ctx->use_parallel_build;
-
     int64_t start_time = time_wall_msec();
 
     stopwatch_start(OVNNB_DB_RUN_STOPWATCH_NAME, time_msec());
-    ovnnb_db_run(ctx, ctx->sbrec_chassis_by_name,
-                 ctx->sbrec_chassis_by_hostname, ctx->ovnsb_idl_loop,
-                 &datapaths, &ports, &lr_list, start_time,
-                 ctx->ovn_internal_version);
+    ovnnb_db_run(input_data, data, ovnnb_txn, ovnsb_txn,
+                 input_data->sbrec_chassis_by_name,
+                 input_data->sbrec_chassis_by_hostname,
+                 sb_loop, start_time);
     stopwatch_stop(OVNNB_DB_RUN_STOPWATCH_NAME, time_msec());
     stopwatch_start(OVNSB_DB_RUN_STOPWATCH_NAME, time_msec());
-    ovnsb_db_run(ctx, ctx->ovnsb_idl_loop, &ports, start_time);
+    ovnsb_db_run(input_data, ovnnb_txn, ovnsb_txn, sb_loop,
+                 &data->ports, start_time);
     stopwatch_stop(OVNSB_DB_RUN_STOPWATCH_NAME, time_msec());
-    destroy_datapaths_and_ports(&datapaths, &ports, &lr_list);
 }
 
diff --git a/northd/northd.h b/northd/northd.h
index 4ebbe60af39b..12ad010f62e9 100644
--- a/northd/northd.h
+++ b/northd/northd.h
@@ -16,25 +16,74 @@ 
 
 #include "ovsdb-idl.h"
 
-struct northd_context {
-    const char *ovnnb_db;
-    const char *ovnsb_db;
-    struct ovsdb_idl *ovnnb_idl;
-    struct ovsdb_idl *ovnsb_idl;
-    struct ovsdb_idl_loop *ovnnb_idl_loop;
-    struct ovsdb_idl_loop *ovnsb_idl_loop;
-    struct ovsdb_idl_txn *ovnnb_txn;
-    struct ovsdb_idl_txn *ovnsb_txn;
+#include "openvswitch/hmap.h"
+
+struct northd_input {
+    /* Northbound table references */
+    const struct nbrec_nb_global_table *nbrec_nb_global_table;
+    const struct nbrec_logical_switch_table *nbrec_logical_switch;
+    const struct nbrec_logical_router_table *nbrec_logical_router;
+    const struct nbrec_load_balancer_table *nbrec_load_balancer_table;
+    const struct nbrec_port_group_table *nbrec_port_group_table;
+    const struct nbrec_bfd_table *nbrec_bfd_table;
+    const struct nbrec_address_set_table *nbrec_address_set_table;
+    const struct nbrec_meter_table *nbrec_meter_table;
+    const struct nbrec_acl_table *nbrec_acl_table;
+
+    /* Southbound table references */
+    const struct sbrec_sb_global_table *sbrec_sb_global_table;
+    const struct sbrec_datapath_binding_table *sbrec_datapath_binding_table;
+    const struct sbrec_port_binding_table *sbrec_port_binding_table;
+    const struct sbrec_mac_binding_table *sbrec_mac_binding_table;
+    const struct sbrec_ha_chassis_group_table *sbrec_ha_chassis_group_table;
+    const struct sbrec_chassis_table *sbrec_chassis;
+    const struct sbrec_fdb_table *sbrec_fdb_table;
+    const struct sbrec_load_balancer_table *sbrec_load_balancer_table;
+    const struct sbrec_service_monitor_table *sbrec_service_monitor_table;
+    const struct sbrec_bfd_table *sbrec_bfd_table;
+    const struct sbrec_logical_flow_table *sbrec_logical_flow_table;
+    const struct sbrec_multicast_group_table *sbrec_multicast_group_table;
+    const struct sbrec_address_set_table *sbrec_address_set_table;
+    const struct sbrec_port_group_table *sbrec_port_group_table;
+    const struct sbrec_meter_table *sbrec_meter_table;
+    const struct sbrec_dns_table *sbrec_dns_table;
+    const struct sbrec_ip_multicast_table *sbrec_ip_multicast_table;
+    const struct sbrec_igmp_group_table *sbrec_igmp_group_table;
+    const struct sbrec_chassis_private_table *sbrec_chassis_private_table;
+
+    /* Indexes */
     struct ovsdb_idl_index *sbrec_chassis_by_name;
     struct ovsdb_idl_index *sbrec_chassis_by_hostname;
     struct ovsdb_idl_index *sbrec_ha_chassis_grp_by_name;
     struct ovsdb_idl_index *sbrec_mcast_group_by_name_dp;
     struct ovsdb_idl_index *sbrec_ip_mcast_by_dp;
+};
 
-    const char *ovn_internal_version;
-    bool use_parallel_build;
+struct northd_data {
+    /* Global state for 'en-northd'. */
+    struct hmap datapaths;
+    struct hmap ports;
+    struct hmap port_groups;
+    struct hmap mcast_groups;
+    struct hmap igmp_groups;
+    struct shash meter_groups;
+    struct hmap lbs;
+    struct hmap bfd_connections;
+    struct ovs_list lr_list;
+    bool ovn_internal_version_changed;
 };
 
-void ovn_db_run(struct northd_context *ctx);
+void northd_run(struct northd_input *input_data,
+                struct northd_data *data,
+                struct ovsdb_idl_txn *ovnnb_txn,
+                struct ovsdb_idl_txn *ovnsb_txn,
+                struct ovsdb_idl_loop *sb_loop);
+void northd_destroy(struct northd_data *data);
+void northd_init(struct northd_data *data);
+void northd_indices_create(struct northd_data *data,
+                           struct ovsdb_idl *ovnsb_idl);
+void build_lflows(struct northd_input *input_data,
+                  struct northd_data *data,
+                  struct ovsdb_idl_txn *ovnsb_txn);
 
 #endif /* NORTHD_H */
diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
index e9f6a4bc4c12..cdbd8eaaffa5 100644
--- a/northd/ovn-northd.c
+++ b/northd/ovn-northd.c
@@ -31,7 +31,6 @@ 
 #include "ovsdb-idl.h"
 #include "lib/ovn-l7.h"
 #include "lib/ovn-nb-idl.h"
-#include "lib/ovn-parallel-hmap.h"
 #include "lib/ovn-sb-idl.h"
 #include "openvswitch/poll-loop.h"
 #include "simap.h"
@@ -70,7 +69,6 @@  static const char *ssl_ca_cert_file;
 #define DEFAULT_PROBE_INTERVAL_MSEC 5000
 static int northd_probe_interval_nb = 0;
 static int northd_probe_interval_sb = 0;
-static bool use_parallel_build = true;
 
 static const char *rbac_chassis_auth[] =
     {"name"};
@@ -313,12 +311,12 @@  ovn_rbac_validate_perm(const struct sbrec_rbac_permission *perm)
 
 static void
 ovn_rbac_create_perm(struct rbac_perm_cfg *pcfg,
-                     struct northd_context *ctx,
+                     struct ovsdb_idl_txn *ovnsb_txn,
                      const struct sbrec_rbac_role *rbac_role)
 {
     struct sbrec_rbac_permission *rbac_perm;
 
-    rbac_perm = sbrec_rbac_permission_insert(ctx->ovnsb_txn);
+    rbac_perm = sbrec_rbac_permission_insert(ovnsb_txn);
     sbrec_rbac_permission_set_table(rbac_perm, pcfg->table);
     sbrec_rbac_permission_set_authorization(rbac_perm,
                                             pcfg->auth,
@@ -332,7 +330,8 @@  ovn_rbac_create_perm(struct rbac_perm_cfg *pcfg,
 }
 
 static void
-check_and_update_rbac(struct northd_context *ctx)
+check_and_update_rbac(struct ovsdb_idl_txn *ovnsb_txn,
+                      struct ovsdb_idl *ovnsb_idl)
 {
     const struct sbrec_rbac_role *rbac_role = NULL;
     const struct sbrec_rbac_permission *perm_row, *perm_next;
@@ -343,12 +342,12 @@  check_and_update_rbac(struct northd_context *ctx)
         pcfg->row = NULL;
     }
 
-    SBREC_RBAC_PERMISSION_FOR_EACH_SAFE (perm_row, perm_next, ctx->ovnsb_idl) {
+    SBREC_RBAC_PERMISSION_FOR_EACH_SAFE (perm_row, perm_next, ovnsb_idl) {
         if (!ovn_rbac_validate_perm(perm_row)) {
             sbrec_rbac_permission_delete(perm_row);
         }
     }
-    SBREC_RBAC_ROLE_FOR_EACH_SAFE (role_row, role_row_next, ctx->ovnsb_idl) {
+    SBREC_RBAC_ROLE_FOR_EACH_SAFE (role_row, role_row_next, ovnsb_idl) {
         if (strcmp(role_row->name, "ovn-controller")) {
             sbrec_rbac_role_delete(role_row);
         } else {
@@ -357,19 +356,20 @@  check_and_update_rbac(struct northd_context *ctx)
     }
 
     if (!rbac_role) {
-        rbac_role = sbrec_rbac_role_insert(ctx->ovnsb_txn);
+        rbac_role = sbrec_rbac_role_insert(ovnsb_txn);
         sbrec_rbac_role_set_name(rbac_role, "ovn-controller");
     }
 
     for (pcfg = rbac_perm_cfg; pcfg->table; pcfg++) {
         if (!pcfg->row) {
-            ovn_rbac_create_perm(pcfg, ctx, rbac_role);
+            ovn_rbac_create_perm(pcfg, ovnsb_txn, rbac_role);
         }
     }
 }
 
 static void
-check_and_add_supported_dhcp_opts_to_sb_db(struct northd_context *ctx)
+check_and_add_supported_dhcp_opts_to_sb_db(struct ovsdb_idl_txn *ovnsb_txn,
+                                           struct ovsdb_idl *ovnsb_idl)
 {
     struct hmap dhcp_opts_to_add = HMAP_INITIALIZER(&dhcp_opts_to_add);
     for (size_t i = 0; (i < sizeof(supported_dhcp_opts) /
@@ -379,7 +379,7 @@  check_and_add_supported_dhcp_opts_to_sb_db(struct northd_context *ctx)
     }
 
     const struct sbrec_dhcp_options *opt_row, *opt_row_next;
-    SBREC_DHCP_OPTIONS_FOR_EACH_SAFE (opt_row, opt_row_next, ctx->ovnsb_idl) {
+    SBREC_DHCP_OPTIONS_FOR_EACH_SAFE (opt_row, opt_row_next, ovnsb_idl) {
         struct gen_opts_map *dhcp_opt =
             dhcp_opts_find(&dhcp_opts_to_add, opt_row->name);
         if (dhcp_opt) {
@@ -397,7 +397,7 @@  check_and_add_supported_dhcp_opts_to_sb_db(struct northd_context *ctx)
     struct gen_opts_map *opt;
     HMAP_FOR_EACH (opt, hmap_node, &dhcp_opts_to_add) {
         struct sbrec_dhcp_options *sbrec_dhcp_option =
-            sbrec_dhcp_options_insert(ctx->ovnsb_txn);
+            sbrec_dhcp_options_insert(ovnsb_txn);
         sbrec_dhcp_options_set_name(sbrec_dhcp_option, opt->name);
         sbrec_dhcp_options_set_code(sbrec_dhcp_option, opt->code);
         sbrec_dhcp_options_set_type(sbrec_dhcp_option, opt->type);
@@ -407,7 +407,8 @@  check_and_add_supported_dhcp_opts_to_sb_db(struct northd_context *ctx)
 }
 
 static void
-check_and_add_supported_dhcpv6_opts_to_sb_db(struct northd_context *ctx)
+check_and_add_supported_dhcpv6_opts_to_sb_db(struct ovsdb_idl_txn *ovnsb_txn,
+                                             struct ovsdb_idl *ovnsb_idl)
 {
     struct hmap dhcpv6_opts_to_add = HMAP_INITIALIZER(&dhcpv6_opts_to_add);
     for (size_t i = 0; (i < sizeof(supported_dhcpv6_opts) /
@@ -417,7 +418,7 @@  check_and_add_supported_dhcpv6_opts_to_sb_db(struct northd_context *ctx)
     }
 
     const struct sbrec_dhcpv6_options *opt_row, *opt_row_next;
-    SBREC_DHCPV6_OPTIONS_FOR_EACH_SAFE(opt_row, opt_row_next, ctx->ovnsb_idl) {
+    SBREC_DHCPV6_OPTIONS_FOR_EACH_SAFE(opt_row, opt_row_next, ovnsb_idl) {
         struct gen_opts_map *dhcp_opt =
             dhcp_opts_find(&dhcpv6_opts_to_add, opt_row->name);
         if (dhcp_opt) {
@@ -430,7 +431,7 @@  check_and_add_supported_dhcpv6_opts_to_sb_db(struct northd_context *ctx)
     struct gen_opts_map *opt;
     HMAP_FOR_EACH (opt, hmap_node, &dhcpv6_opts_to_add) {
         struct sbrec_dhcpv6_options *sbrec_dhcpv6_option =
-            sbrec_dhcpv6_options_insert(ctx->ovnsb_txn);
+            sbrec_dhcpv6_options_insert(ovnsb_txn);
         sbrec_dhcpv6_options_set_name(sbrec_dhcpv6_option, opt->name);
         sbrec_dhcpv6_options_set_code(sbrec_dhcpv6_option, opt->code);
         sbrec_dhcpv6_options_set_type(sbrec_dhcpv6_option, opt->type);
@@ -641,8 +642,6 @@  main(int argc, char *argv[])
 
     daemonize_complete();
 
-    use_parallel_build = can_parallelize_hashes(false);
-
     /* We want to detect (almost) all changes to the ovn-nb db. */
     struct ovsdb_idl_loop ovnnb_idl_loop = OVSDB_IDL_LOOP_INITIALIZER(
         ovsdb_idl_create(ovnnb_db, &nbrec_idl_class, true, true));
@@ -909,26 +908,12 @@  main(int argc, char *argv[])
     ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, &sbrec_fdb_col_dp_key);
     ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, &sbrec_fdb_col_port_key);
 
-    struct ovsdb_idl_index *sbrec_chassis_by_name
-        = chassis_index_create(ovnsb_idl_loop.idl);
-
-    struct ovsdb_idl_index *sbrec_chassis_by_hostname
-        = chassis_hostname_index_create(ovnsb_idl_loop.idl);
-
-    struct ovsdb_idl_index *sbrec_ha_chassis_grp_by_name
-        = ha_chassis_group_index_create(ovnsb_idl_loop.idl);
-
-    struct ovsdb_idl_index *sbrec_mcast_group_by_name_dp
-        = mcast_group_index_create(ovnsb_idl_loop.idl);
-
-    struct ovsdb_idl_index *sbrec_ip_mcast_by_dp
-        = ip_mcast_index_create(ovnsb_idl_loop.idl);
-
     unixctl_command_register("sb-connection-status", "", 0, 0,
                              ovn_conn_show, ovnsb_idl_loop.idl);
 
-    char *ovn_internal_version = ovn_get_internal_version();
-    VLOG_INFO("OVN internal version is : [%s]", ovn_internal_version);
+    char *ovn_version = ovn_get_internal_version();
+    VLOG_INFO("OVN internal version is : [%s]", ovn_version);
+    free(ovn_version);
 
     stopwatch_create(NORTHD_LOOP_STOPWATCH_NAME, SW_MS);
     stopwatch_create(OVNNB_DB_RUN_STOPWATCH_NAME, SW_MS);
@@ -999,24 +984,6 @@  main(int argc, char *argv[])
                 ovnsb_cond_seqno = new_ovnsb_cond_seqno;
             }
 
-            struct northd_context ctx = {
-                .ovnnb_db = ovnnb_db,
-                .ovnsb_db = ovnsb_db,
-                .ovnnb_idl = ovnnb_idl_loop.idl,
-                .ovnnb_idl_loop = &ovnnb_idl_loop,
-                .ovnnb_txn = ovnnb_txn,
-                .ovnsb_idl = ovnsb_idl_loop.idl,
-                .ovnsb_idl_loop = &ovnsb_idl_loop,
-                .ovnsb_txn = ovnsb_txn,
-                .sbrec_chassis_by_name = sbrec_chassis_by_name,
-                .sbrec_chassis_by_hostname = sbrec_chassis_by_hostname,
-                .sbrec_ha_chassis_grp_by_name = sbrec_ha_chassis_grp_by_name,
-                .sbrec_mcast_group_by_name_dp = sbrec_mcast_group_by_name_dp,
-                .sbrec_ip_mcast_by_dp = sbrec_ip_mcast_by_dp,
-                .use_parallel_build = use_parallel_build,
-                .ovn_internal_version = ovn_internal_version,
-            };
-
             if (!state.had_lock && ovsdb_idl_has_lock(ovnsb_idl_loop.idl)) {
                 VLOG_INFO("ovn-northd lock acquired. "
                         "This ovn-northd instance is now active.");
@@ -1030,12 +997,17 @@  main(int argc, char *argv[])
             }
 
             if (ovsdb_idl_has_lock(ovnsb_idl_loop.idl)) {
-                inc_proc_northd_run(&ctx, recompute);
+                inc_proc_northd_run(ovnnb_txn, ovnsb_txn,
+                                    &ovnsb_idl_loop,
+                                    recompute);
                 recompute = false;
-                if (ctx.ovnsb_txn) {
-                    check_and_add_supported_dhcp_opts_to_sb_db(&ctx);
-                    check_and_add_supported_dhcpv6_opts_to_sb_db(&ctx);
-                    check_and_update_rbac(&ctx);
+                if (ovnsb_txn) {
+                    check_and_add_supported_dhcp_opts_to_sb_db(
+                                 ovnsb_txn, ovnsb_idl_loop.idl);
+                    check_and_add_supported_dhcpv6_opts_to_sb_db(
+                                 ovnsb_txn, ovnsb_idl_loop.idl);
+                    check_and_update_rbac(
+                                 ovnsb_txn, ovnsb_idl_loop.idl);
                 }
 
             }
@@ -1114,7 +1086,6 @@  main(int argc, char *argv[])
     }
     inc_proc_northd_cleanup();
 
-    free(ovn_internal_version);
     unixctl_server_destroy(unixctl);
     ovsdb_idl_loop_destroy(&ovnnb_idl_loop);
     ovsdb_idl_loop_destroy(&ovnsb_idl_loop);