On Wed, Nov 3, 2021 at 11:37 PM Han Zhou <hz...@ovn.org> wrote:

> On Wed, Oct 27, 2021 at 10:24 AM Mark Gray <mark.d.g...@redhat.com> wrote:
> >
> > '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:
>
> Thanks Mark for the revision. It does look more clear now with input data
> wrapped in northd_input. However, my suggestion was to move the inputs out
> of the northd_data completely (sorry if I wasn't clear on this).
>

You were clear on it. I just wasn't 100% sure how I should do it. However,
your description below has inspired me.


> The reason is, northd_data should contain only output data of en_northd,
> which is potentially going to be used as input data for other nodes that
> depend on en_northd. The other nodes depending on en_northd are supposed to
> access any data included in northd_data. If we put northd_input in the
> northd_data itself, those nodes may access them as well by mistake, and it
> is going to be hard to track the dependencies properly with the I-P engine.
> So my suggestion is just keep these two as independent structures, one for
> input and one for output. The northd_input will be used by en_northd's
> run() and handlers only. If another node depends on some common input in
> northd_input, they should declare the dependency in the I-P engine and get
> the data from their own input nodes.
>
> For northd_run(), it can have the signature like northd_run(northd_input,
> ..., north_output).
>

Yes makes sense.


>
> With convention of I-P in ovn-controller, we would name the struct
> northd_data ed_type_northd_data, to make it clear this is the output data
> maintained for the I-P engine node. I understand that you don't want
> anything related to I-P engine in the northd.h/c, so not following the
> naming convention is ok for me. I'd just remind that these data types are
>

Happy to change the names as you see fit. Let me know if you would like me
to.


> interfaces between different engine nodes, so be aware that it is less of
> an encapsulation but more like table definitions in a DB schema.
>
> The rest of the patch LGTM.
>
> Thanks,
> Han
>
> >
> > * 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.g...@redhat.com>
> > ---
> >
> > Notes:
> >     v4: Add northd_input (move DB references and indexes to this struct)
> >         Remove the use of client_ctx from engine_context
> >
> >  lib/inc-proc-eng.h       |   3 +
> >  northd/en-northd.c       | 106 ++++++-
> >  northd/inc-proc-northd.c |  42 ++-
> >  northd/inc-proc-northd.h |   6 +-
> >  northd/northd.c          | 603 +++++++++++++++++++++------------------
> >  northd/northd.h          |  75 ++++-
> >  northd/ovn-northd.c      |  85 ++----
> >  7 files changed, 568 insertions(+), 352 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..36ea890535fe 100644
> > --- a/northd/en-northd.c
> > +++ b/northd/en-northd.c
> > @@ -20,26 +20,122 @@
> >
> >  #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);
> >
> > +    northd_destroy(data);
> > +    northd_init(data);
> > +
> > +    struct northd_data *northd_data = data;
> > +    northd_data->input.sbrec_chassis_by_name =
> > +        engine_ovsdb_node_get_index(
> > +            engine_get_input("SB_chassis", node),
> > +            "sbrec_chassis_by_name");
> > +    northd_data->input.sbrec_chassis_by_hostname =
> > +        engine_ovsdb_node_get_index(
> > +            engine_get_input("SB_chassis", node),
> > +            "sbrec_chassis_by_hostname");
> > +    northd_data->input.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");
> > +    northd_data->input.sbrec_mcast_group_by_name_dp =
> > +        engine_ovsdb_node_get_index(
> > +            engine_get_input("SB_multicast_group", node),
> > +            "sbrec_mcast_group_by_name");
> > +    northd_data->input.sbrec_ip_mcast_by_dp =
> > +        engine_ovsdb_node_get_index(
> > +            engine_get_input("SB_ip_multicast", node),
> > +            "sbrec_ip_mcast_by_dp");
> > +
> > +    northd_data->input.nbrec_nb_global_table =
> > +        EN_OVSDB_GET(engine_get_input("NB_nb_global", node));
> > +    northd_data->input.nbrec_logical_switch =
> > +        EN_OVSDB_GET(engine_get_input("NB_logical_switch", node));
> > +    northd_data->input.nbrec_logical_router =
> > +        EN_OVSDB_GET(engine_get_input("NB_logical_router", node));
> > +    northd_data->input.nbrec_load_balancer_table =
> > +        EN_OVSDB_GET(engine_get_input("NB_load_balancer", node));
> > +    northd_data->input.nbrec_port_group_table =
> > +        EN_OVSDB_GET(engine_get_input("NB_port_group", node));
> > +    northd_data->input.nbrec_bfd_table =
> > +        EN_OVSDB_GET(engine_get_input("NB_bfd", node));
> > +    northd_data->input.nbrec_address_set_table =
> > +        EN_OVSDB_GET(engine_get_input("NB_address_set", node));
> > +    northd_data->input.nbrec_meter_table =
> > +        EN_OVSDB_GET(engine_get_input("NB_meter", node));
> > +    northd_data->input.nbrec_acl_table =
> > +        EN_OVSDB_GET(engine_get_input("NB_acl", node));
> > +
> > +    northd_data->input.sbrec_sb_global_table =
> > +        EN_OVSDB_GET(engine_get_input("SB_sb_global", node));
> > +    northd_data->input.sbrec_datapath_binding_table =
> > +        EN_OVSDB_GET(engine_get_input("SB_datapath_binding", node));
> > +    northd_data->input.sbrec_port_binding_table =
> > +        EN_OVSDB_GET(engine_get_input("SB_port_binding", node));
> > +    northd_data->input.sbrec_mac_binding_table =
> > +        EN_OVSDB_GET(engine_get_input("SB_mac_binding", node));
> > +    northd_data->input.sbrec_ha_chassis_group_table =
> > +        EN_OVSDB_GET(engine_get_input("SB_ha_chassis_group", node));
> > +    northd_data->input.sbrec_chassis =
> > +        EN_OVSDB_GET(engine_get_input("SB_chassis", node));
> > +    northd_data->input.sbrec_fdb_table =
> > +        EN_OVSDB_GET(engine_get_input("SB_fdb", node));
> > +    northd_data->input.sbrec_load_balancer_table =
> > +        EN_OVSDB_GET(engine_get_input("SB_load_balancer", node));
> > +    northd_data->input.sbrec_service_monitor_table =
> > +        EN_OVSDB_GET(engine_get_input("SB_service_monitor", node));
> > +    northd_data->input.sbrec_bfd_table =
> > +        EN_OVSDB_GET(engine_get_input("SB_bfd", node));
> > +    northd_data->input.sbrec_logical_flow_table =
> > +        EN_OVSDB_GET(engine_get_input("SB_logical_flow", node));
> > +    northd_data->input.sbrec_multicast_group_table =
> > +        EN_OVSDB_GET(engine_get_input("SB_multicast_group", node));
> > +    northd_data->input.sbrec_address_set_table =
> > +        EN_OVSDB_GET(engine_get_input("SB_address_set", node));
> > +    northd_data->input.sbrec_port_group_table =
> > +        EN_OVSDB_GET(engine_get_input("SB_port_group", node));
> > +    northd_data->input.sbrec_meter_table =
> > +        EN_OVSDB_GET(engine_get_input("SB_meter", node));
> > +    northd_data->input.sbrec_dns_table =
> > +        EN_OVSDB_GET(engine_get_input("SB_dns", node));
> > +    northd_data->input.sbrec_ip_multicast_table =
> > +        EN_OVSDB_GET(engine_get_input("SB_ip_multicast", node));
> > +    northd_data->input.sbrec_igmp_group_table =
> > +        EN_OVSDB_GET(engine_get_input("SB_igmp_group", node));
> > +    northd_data->input.sbrec_chassis_private_table =
> > +        EN_OVSDB_GET(engine_get_input("SB_chassis_private", node));
> > +
> > +    northd_run(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 455b9b0e867a..9649d94772ad 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_data *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,
> > +                            data->input.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,
> > +                              data->input.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,
> > +                               data->input.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_data *data)
> >  {
> >      const struct sbrec_chassis *chassis;
> > -    SBREC_CHASSIS_FOR_EACH (chassis, ovnsb_idl) {
> > +    SBREC_CHASSIS_TABLE_FOR_EACH (chassis, data->input.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_data *data)
> >  {
> > -    if (is_vxlan_mode(ctx->ovnsb_idl)) {
> > +    if (is_vxlan_mode(data)) {
> >          /* OVN_MAX_DP_GLOBAL_NUM doesn't apply for vxlan mode. */
> >          return OVN_MAX_DP_VXLAN_KEY;
> >      }
> > @@ -1382,14 +1387,14 @@ 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_data *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),
> > +
>  get_ovn_max_dp_key_local(data),
> >                                              hint);
> >          if (!od->tunnel_key) {
> >              if (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_data *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(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_data *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(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(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(data, &dp_tnlids, od);
> >      }
> >
> >      /* Keep nonconflicting tunnel IDs that are already assigned. */
> > @@ -1463,10 +1471,10 @@ 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(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(data, datapaths, &dp_tnlids, od,
> &hint);
> >      }
> >
> >      /* Sync tunnel ids from nb to sb. */
> > @@ -1477,7 +1485,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 +2415,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_data *data,
> >                     struct hmap *datapaths, struct hmap *ports,
> >                     struct hmap *chassis_qdisc_queues,
> >                     struct hmap *tag_alloc_table, struct ovs_list
> *sb_only,
> > @@ -2418,7 +2426,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,
> > +                                 data->input.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 +2878,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 +2963,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_data *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 +2972,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);
> > +            data->input.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 +2992,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 +3017,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_data *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 +3028,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);
> > +            data->input.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 +3048,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 +3096,8 @@ ovn_update_ipv6_prefix(struct hmap *ports)
> >  }
> >
> >  static void
> > -ovn_port_update_sbrec(struct northd_context *ctx,
> > +ovn_port_update_sbrec(struct northd_data *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 +3133,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(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 +3144,7 @@ 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(data, ovnsb_txn,
> >
>  sbrec_chassis_by_name,
> >                                                        op->nbrp, op->sb);
> >                  }
> > @@ -3255,7 +3268,7 @@ 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,
> > +                        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 +3467,12 @@ 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_data *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,
> > +                             data->input.sbrec_mac_binding_table) {
> >          const struct ovn_datapath *od =
> >              ovn_datapath_from_sbrec(datapaths, b->datapath);
> >
> > @@ -3470,11 +3484,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_data *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,
> > +
>  data->input.sbrec_ha_chassis_group_table) {
> >          if (!sset_contains(active_ha_chassis_groups, b->name)) {
> >              sbrec_ha_chassis_group_delete(b);
> >          }
> > @@ -3482,10 +3497,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_data *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,
> > +                         data->input.sbrec_fdb_table) {
> >          bool delete = true;
> >          struct ovn_datapath *od
> >              = ovn_datapath_find_by_key(datapaths, fdb_e->dp_key);
> > @@ -3509,7 +3526,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 +3546,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 +3558,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 +3599,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 +3733,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_data *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,
> > +                               data->input.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 +3776,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,
> > +                            data->input.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 +3816,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 +3849,14 @@ 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_data *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,
> > +                            data->input.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 +3868,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 +3946,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_data *data,
> >                                   struct ovn_port *op)
> >  {
> >      const struct smap *options = (op->nbsp
> > @@ -3933,7 +3954,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(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 +3973,11 @@ 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_data *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(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 +3998,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_data *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 +4012,7 @@ 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(data, datapaths, ports, &chassis_qdisc_queues,
> >                         &tag_alloc_table, &sb_only, &nb_only, &both);
> >
> >      /* Purge stale Mac_Bindings if ports are deleted. */
> > @@ -3999,10 +4021,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(data, op);
> >      }
> >      LIST_FOR_EACH (op, list, &nb_only) {
> > -        ovn_port_assign_requested_tnl_id(ctx, op);
> > +        ovn_port_assign_requested_tnl_id(data, op);
> >      }
> >
> >      /* Keep nonconflicting tunnel IDs that are already assigned. */
> > @@ -4014,10 +4036,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(data, ports, op);
> >      }
> >      LIST_FOR_EACH_SAFE (op, next, list, &nb_only) {
> > -        ovn_port_allocate_key(ctx, ports, op);
> > +        ovn_port_allocate_key(data, ports, op);
> >      }
> >
> >      /* For logical ports that are in both databases, update the
> southbound
> > @@ -4034,7 +4056,7 @@ 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(data, ovnsb_txn, sbrec_chassis_by_name,
> >                                sbrec_chassis_by_hostname,
> >                                op, &chassis_qdisc_queues,
> >                                &active_ha_chassis_grps);
> > @@ -4042,9 +4064,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,
> > -                              sbrec_chassis_by_hostname, op,
> > +        op->sb = sbrec_port_binding_insert(ovnsb_txn);
> > +        ovn_port_update_sbrec(data, ovnsb_txn, sbrec_chassis_by_name,
> > +                             sbrec_chassis_by_hostname, op,
> >                                &chassis_qdisc_queues,
> >                                &active_ha_chassis_grps);
> >          sbrec_port_binding_set_logical_port(op->sb, op->key);
> > @@ -4059,12 +4081,12 @@ build_ports(struct northd_context *ctx,
> >          }
> >      }
> >      if (remove_mac_bindings) {
> > -        cleanup_mac_bindings(ctx, datapaths, ports);
> > +        cleanup_mac_bindings(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(data, &active_ha_chassis_grps);
> >      sset_destroy(&active_ha_chassis_grps);
> >  }
> >
> > @@ -4247,7 +4269,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_data *data,
> > +                   struct hmap *igmp_groups,
> >                     struct ovn_datapath *datapath,
> >                     const struct in6_addr *address,
> >                     const char *address_s)
> > @@ -4259,7 +4282,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(data->input.sbrec_mcast_group_by_name_dp,
> > +                               address_s,
> >                                 datapath->sb);
> >
> >          igmp_group->datapath = datapath;
> > @@ -4466,7 +4490,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 +6243,14 @@ 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_data *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,
> > +                                  data->input.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);
> > @@ -8242,12 +8267,12 @@ 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_data *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, data->input.nbrec_bfd_table) {
> >          bfd_e = bfd_port_lookup(bfd_map, nb_bt->logical_port,
> nb_bt->dst_ip);
> >          if (!bfd_e) {
> >              continue;
> > @@ -8325,8 +8350,8 @@ 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_data *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;
> > @@ -8336,7 +8361,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, data->input.sbrec_bfd_table) {
> >          bfd_e = xmalloc(sizeof *bfd_e);
> >          bfd_e->sb_bt = sb_bt;
> >          hash = hash_string(sb_bt->dst_ip, 0);
> > @@ -8346,7 +8371,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, data->input.nbrec_bfd_table) {
> >          if (!nb_bt->status) {
> >              /* default state is admin_down */
> >              nbrec_bfd_set_status(nb_bt, "admin_down");
> > @@ -8359,7 +8384,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());
> > @@ -13308,8 +13333,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;
> > @@ -13320,7 +13345,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);
> > @@ -13330,7 +13355,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)
> > @@ -13349,7 +13374,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);
> >  }
> > @@ -13360,13 +13385,7 @@ 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_data *data, struct ovsdb_idl_txn
> *ovnsb_txn)
> >  {
> >      struct hmap lflows;
> >
> > @@ -13388,10 +13407,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 */
> > @@ -13461,7 +13481,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,
> > +
> data->input.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;
> > @@ -13469,7 +13490,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;
> > @@ -13477,7 +13499,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;
> > @@ -13501,7 +13523,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,
> > @@ -13553,7 +13575,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;
> > @@ -13574,7 +13596,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
> > @@ -13594,11 +13616,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);
> > @@ -13647,8 +13669,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,
> > +                                data->input.sbrec_multicast_group_table)
> {
> > +        struct ovn_datapath *od =
> ovn_datapath_from_sbrec(&data->datapaths,
> >
>  sbmc->datapath);
> >
> >          if (!od || ovn_datapath_is_stale(od)) {
> > @@ -13658,31 +13681,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)
> >  {
> > @@ -13690,7 +13714,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);
> >      }
> >
> > @@ -13709,23 +13733,26 @@ 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_data *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,
> > +                                   data->input.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,
> > +                                     data->input.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++) {
> > @@ -13742,11 +13769,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);
> > @@ -13767,7 +13794,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);
> > @@ -13777,7 +13804,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);
> > @@ -13787,8 +13814,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,
> > +                              data->input.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);
> > @@ -13807,12 +13835,14 @@ 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_data *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,
> > +                               data->input.sbrec_port_group_table) {
> >          shash_add(&sb_port_groups, sb_port_group->name, sb_port_group);
> >      }
> >
> > @@ -13828,7 +13858,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));
> >              }
> >
> > @@ -13935,7 +13965,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,
> > @@ -13946,7 +13976,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;
> > @@ -13959,7 +13989,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);
> > @@ -13974,7 +14004,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)
> >  {
> > @@ -13986,7 +14017,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);
> >  }
> > @@ -13997,19 +14028,20 @@ 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_data *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, data->input.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, data->input.nbrec_meter_table)
> {
> > +        sync_meters_iterate_nb_meter(ovnsb_txn, nb_meter->name,
> nb_meter,
> >                                       &sb_meters, &used_sb_meters);
> >      }
> >
> > @@ -14019,8 +14051,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, data->input.nbrec_acl_table) {
> > +        sync_acl_fair_meter(ovnsb_txn, meter_groups, acl,
> >                              &sb_meters, &used_sb_meters);
> >      }
> >
> > @@ -14069,7 +14101,8 @@ 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_data *data, struct ovsdb_idl_txn
> *ovnsb_txn,
> > +                 struct hmap *datapaths)
> >  {
> >      struct hmap dns_map = HMAP_INITIALIZER(&dns_map);
> >      struct ovn_datapath *od;
> > @@ -14097,7 +14130,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,
> > +                                   data->input.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)) {
> > @@ -14117,7 +14151,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));
> > @@ -14187,7 +14221,8 @@ 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_data *data, struct ovsdb_idl_txn
> *ovnsb_txn,
> > +               struct hmap *datapaths)
> >  {
> >      struct ovn_datapath *od;
> >
> > @@ -14197,10 +14232,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(data->input.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);
> >      }
> > @@ -14208,7 +14243,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,
> > +                                   data->input.sbrec_ip_multicast_table)
> {
> >          od = ovn_datapath_from_sbrec(datapaths, sb->datapath);
> >          if (!od || ovn_datapath_is_stale(od)) {
> >              sbrec_ip_multicast_delete(sb);
> > @@ -14217,7 +14253,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_data *data,
> >                     struct hmap *datapaths, struct hmap *ports,
> >                     struct hmap *mcast_groups,
> >                     struct hmap *igmp_groups)
> > @@ -14271,7 +14307,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,
> > +                                     data->input.sbrec_igmp_group_table)
> {
> >          /* If this is a stale group (e.g., controller had crashed,
> >           * purge it).
> >           */
> > @@ -14313,7 +14350,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(data, igmp_groups, od, &group_address,
> >                                 sb_igmp->address);
> >
> >          /* Add the extracted ports to the IGMP group. */
> > @@ -14357,7 +14394,7 @@ 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(data, igmp_groups,
> router_port->od,
> >                                         address, igmp_group->
> mcgroup.name
> );
> >                  struct ovn_port **router_igmp_ports =
> >                      xmalloc(sizeof *router_igmp_ports);
> > @@ -14392,47 +14429,101 @@ build_mcast_groups(struct northd_context *ctx,
> >  }
> >
> >  static void
> > -build_meter_groups(struct northd_context *ctx,
> > +build_meter_groups(struct northd_data *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, data->input.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_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(
> > +
> data->input.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(
> > +
> data->input.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);
> > @@ -14469,17 +14560,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(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);
> > @@ -14503,73 +14596,35 @@ 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(data, ovnsb_txn, &data->datapaths, &data->lr_list);
> > +    build_ovn_lbs(data, ovnsb_txn, &data->datapaths, &data->lbs);
> > +    build_lrouter_lbs(&data->datapaths, &data->lbs);
> > +    build_ports(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(data, ovnsb_txn, &data->ports, &data->lbs);
> > +    build_ipam(&data->datapaths, &data->ports);
> > +    build_port_group_lswitches(data, &data->port_groups, &data->ports);
> > +    build_lrouter_groups(&data->ports, &data->lr_list);
> > +    build_ip_mcast(data, ovnsb_txn, &data->datapaths);
> > +    build_mcast_groups(data, &data->datapaths, &data->ports,
> > +                       &data->mcast_groups, &data->igmp_groups);
> > +    build_meter_groups(data, &data->meter_groups);
> > +    build_bfd_table(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(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(data, ovnsb_txn, &data->datapaths);
> > +    sync_port_groups(data, ovnsb_txn, &data->port_groups);
> > +    sync_meters(data, ovnsb_txn, &data->meter_groups);
> > +    sync_dns_entries(data, ovnsb_txn, &data->datapaths);
> > +    cleanup_stale_fdp_entries(data, &data->datapaths);
> > +    bfd_cleanup_connections(data, &data->bfd_connections);
> >      stopwatch_stop(CLEAR_LFLOWS_CTX_STOPWATCH_NAME, time_msec());
> >
> >  }
> > @@ -14614,7 +14669,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_data *data,
> >                                 struct shash *ha_ref_chassis_map)
> >  {
> >      struct hmap ha_ch_grps = HMAP_INITIALIZER(&ha_ch_grps);
> > @@ -14622,7 +14677,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,
> > +
>  data->input.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,
> > @@ -14686,7 +14742,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_data *data,
> >                                     const struct sbrec_port_binding *sb,
> >                                     struct ovn_port *op,
> >                                     struct shash *ha_ref_chassis_map)
> > @@ -14712,7 +14768,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);
> > +            data->input.sbrec_ha_chassis_grp_by_name, ha_group_name);
> >
> >          if (sb_ha_chassis_grp) {
> >              struct ha_ref_chassis_info *ref_ch_info =
> > @@ -14727,14 +14783,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_data *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,
> > +
>  data->input.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);
> > @@ -14745,7 +14804,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,
> > +
> data->input.sbrec_port_binding_table) {
> >          struct ovn_port *op = ovn_port_find(ports, sb->logical_port);
> >
> >          if (!op || !op->nbsp) {
> > @@ -14770,10 +14830,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(data, sb, op,
> >                                                 ha_ref_chassis_map);
> >          }
> >      }
> > @@ -14781,12 +14841,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_data *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(
> > +                               data->input.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);
> > @@ -14799,7 +14860,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,
> > +
>  data->input.sbrec_chassis_private_table) {
> >              const struct sbrec_chassis *chassis = chassis_priv->chassis;
> >              if (chassis) {
> >                  if (smap_get_bool(&chassis->other_config,
> > @@ -14833,45 +14895,44 @@ 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_data *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(data, ovnsb_txn, ports,
> &ha_ref_chassis_map);
> > +    update_northbound_cfg(data, sb_loop, loop_start_time);
> > +    if (ovnsb_txn) {
> > +        update_sb_ha_group_ref_chassis(data, &ha_ref_chassis_map);
> >      }
> >      shash_destroy(&ha_ref_chassis_map);
> >  }
> >
> >  void
> > -ovn_db_run(struct northd_context *ctx)
> > +northd_run(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(data, ovnnb_txn, ovnsb_txn,
> > +                 data->input.sbrec_chassis_by_name,
> > +                 data->input.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(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..5ceb2c6c6216 100644
> > --- a/northd/northd.h
> > +++ b/northd/northd.h
> > @@ -16,25 +16,76 @@
> >
> >  #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;
> > +};
> > +
> > +struct northd_data {
> > +    /* Input data for 'en-northd'. This is data generated by
> 'en-northd's
> > +     * input nodes. */
> > +    struct northd_input input;
> >
> > -    const char *ovn_internal_version;
> > -    bool use_parallel_build;
> > +    /* 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_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_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..e291e26872ec 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,10 @@ 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);
> > +    VLOG_INFO("OVN internal version is : [%s]",
> ovn_get_internal_version());
> >
> >      stopwatch_create(NORTHD_LOOP_STOPWATCH_NAME, SW_MS);
> >      stopwatch_create(OVNNB_DB_RUN_STOPWATCH_NAME, SW_MS);
> > @@ -999,24 +982,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 +995,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 +1084,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);
> > --
> > 2.27.0
> >
> _______________________________________________
> dev mailing list
> d...@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to