diff --git a/ovn/controller/binding.c b/ovn/controller/binding.c
index c26007d..f2552fa 100644
--- a/ovn/controller/binding.c
+++ b/ovn/controller/binding.c
@@ -33,15 +33,6 @@ VLOG_DEFINE_THIS_MODULE(binding);
 /* A set of the iface-id values of local interfaces on this chassis. */
 static struct sset local_ids = SSET_INITIALIZER(&local_ids);
 
-/* When this gets set to true, the next run will re-check all binding records. */
-static bool process_full_binding = false;
-
-void
-binding_reset_processing(void)
-{
-    process_full_binding = true;
-}
-
 void
 binding_register_ovs_idl(struct ovsdb_idl *ovs_idl)
 {
@@ -139,7 +130,6 @@ remove_local_datapath(struct hmap *local_datapaths, struct local_datapath *ld)
     }
     hmap_remove(local_datapaths, &ld->hmap_node);
     free(ld);
-    lflow_reset_processing();
 }
 
 static void
@@ -156,9 +146,6 @@ add_local_datapath(struct hmap *local_datapaths,
     memcpy(&ld->uuid, &binding_rec->header_.uuid, sizeof ld->uuid);
     hmap_insert(local_datapaths, &ld->hmap_node,
                 binding_rec->datapath->tunnel_key);
-    lport_index_reset();
-    mcgroup_index_reset();
-    lflow_reset_processing();
 }
 
 static void
@@ -268,80 +255,49 @@ binding_run(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int,
     }
 
     if (br_int) {
-        if (ctx->ovnsb_idl_txn && get_local_iface_ids(br_int, &lport_to_iface,
-                                                      all_lports)) {
-            process_full_binding = true;
-        }
-    } else {
-        /* We have no integration bridge, therefore no local logical ports.
-         * We'll remove our chassis from all port binding records below. */
-        process_full_binding = true;
+        get_local_iface_ids(br_int, &lport_to_iface, all_lports);
     }
 
-    /* Run through each binding record to see if it is resident on this
-     * chassis and update the binding accordingly.  This includes both
-     * directly connected logical ports and children of those ports. */
-    if (process_full_binding) {
-        /* Detect any entries in all_lports that have been deleted.
-         * In particular, this will catch localnet ports that we
-         * put in all_lports. */
-        struct sset removed_lports = SSET_INITIALIZER(&removed_lports);
-        sset_clone(&removed_lports, all_lports);
-
-        struct hmap keep_local_datapath_by_uuid =
-            HMAP_INITIALIZER(&keep_local_datapath_by_uuid);
-        SBREC_PORT_BINDING_FOR_EACH(binding_rec, ctx->ovnsb_idl) {
-            sset_find_and_delete(&removed_lports, binding_rec->logical_port);
-            consider_local_datapath(ctx, chassis_rec, binding_rec,
-                                    local_datapaths, &lport_to_iface,
-                                    all_lports);
-            struct local_datapath *ld = xzalloc(sizeof *ld);
-            memcpy(&ld->uuid, &binding_rec->header_.uuid, sizeof ld->uuid);
-            hmap_insert(&keep_local_datapath_by_uuid, &ld->uuid_hmap_node,
-                        uuid_hash(&ld->uuid));
-        }
-        struct local_datapath *old_ld, *next;
-        HMAP_FOR_EACH_SAFE (old_ld, next, hmap_node, local_datapaths) {
-            if (!local_datapath_lookup_by_uuid(&keep_local_datapath_by_uuid,
-                                               &old_ld->uuid)) {
-                remove_local_datapath(local_datapaths, old_ld);
-            }
-        }
-        struct local_datapath *ld;
-        HMAP_FOR_EACH_SAFE (ld, next, uuid_hmap_node,
-                            &keep_local_datapath_by_uuid) {
-            hmap_remove(&keep_local_datapath_by_uuid, &ld->uuid_hmap_node);
-            free(ld);
-        }
-        hmap_destroy(&keep_local_datapath_by_uuid);
+    /* Detect any entries in all_lports that have been deleted.
+     * In particular, this will catch localnet ports that we
+     * put in all_lports. */
+    struct sset removed_lports = SSET_INITIALIZER(&removed_lports);
+    sset_clone(&removed_lports, all_lports);
 
-        /* Any remaining entries in removed_lports are logical ports that
-         * have been deleted and should also be removed from all_ports. */
-        const char *cur_id;
-        SSET_FOR_EACH(cur_id, &removed_lports) {
-            sset_find_and_delete(all_lports, cur_id);
-        }
-        sset_destroy(&removed_lports);
-
-        process_full_binding = false;
-    } else {
-        SBREC_PORT_BINDING_FOR_EACH_TRACKED(binding_rec, ctx->ovnsb_idl) {
-            if (sbrec_port_binding_is_deleted(binding_rec)) {
-                /* If a port binding was bound to this chassis and removed before
-                 * the ovs interface was removed, we'll catch that here and trigger
-                 * a full bindings refresh.  This is to see if we need to clear
-                 * an entry out of local_datapaths. */
-                if (binding_rec->chassis == chassis_rec) {
-                    process_full_binding = true;
-                    poll_immediate_wake();
-                }
-            } else {
-                consider_local_datapath(ctx, chassis_rec, binding_rec,
-                                        local_datapaths, &lport_to_iface,
-                                        all_lports);
-            }
+    struct hmap keep_local_datapath_by_uuid =
+        HMAP_INITIALIZER(&keep_local_datapath_by_uuid);
+    SBREC_PORT_BINDING_FOR_EACH(binding_rec, ctx->ovnsb_idl) {
+        sset_find_and_delete(&removed_lports, binding_rec->logical_port);
+        consider_local_datapath(ctx, chassis_rec, binding_rec,
+                                local_datapaths, &lport_to_iface,
+                                all_lports);
+        struct local_datapath *ld = xzalloc(sizeof *ld);
+        memcpy(&ld->uuid, &binding_rec->header_.uuid, sizeof ld->uuid);
+        hmap_insert(&keep_local_datapath_by_uuid, &ld->uuid_hmap_node,
+                    uuid_hash(&ld->uuid));
+    }
+    struct local_datapath *old_ld, *next;
+    HMAP_FOR_EACH_SAFE (old_ld, next, hmap_node, local_datapaths) {
+        if (!local_datapath_lookup_by_uuid(&keep_local_datapath_by_uuid,
+                                           &old_ld->uuid)) {
+            remove_local_datapath(local_datapaths, old_ld);
         }
     }
+    struct local_datapath *ld;
+    HMAP_FOR_EACH_SAFE (ld, next, uuid_hmap_node,
+                        &keep_local_datapath_by_uuid) {
+        hmap_remove(&keep_local_datapath_by_uuid, &ld->uuid_hmap_node);
+        free(ld);
+    }
+    hmap_destroy(&keep_local_datapath_by_uuid);
+
+    /* Any remaining entries in removed_lports are logical ports that
+     * have been deleted and should also be removed from all_ports. */
+    const char *cur_id;
+    SSET_FOR_EACH(cur_id, &removed_lports) {
+        sset_find_and_delete(all_lports, cur_id);
+    }
+    sset_destroy(&removed_lports);
 
     shash_destroy(&lport_to_iface);
 }
diff --git a/ovn/controller/binding.h b/ovn/controller/binding.h
index fbd16c8..dd764f2 100644
--- a/ovn/controller/binding.h
+++ b/ovn/controller/binding.h
@@ -27,7 +27,6 @@ struct simap;
 struct sset;
 
 void binding_register_ovs_idl(struct ovsdb_idl *);
-void binding_reset_processing(void);
 void binding_run(struct controller_ctx *, const struct ovsrec_bridge *br_int,
                  const char *chassis_id, struct hmap *local_datapaths,
                  struct sset *all_lports);
diff --git a/ovn/controller/encaps.c b/ovn/controller/encaps.c
index d745e99..d99ba05 100644
--- a/ovn/controller/encaps.c
+++ b/ovn/controller/encaps.c
@@ -68,11 +68,6 @@ static struct tunnel_ctx tc = {
     .port_names = SSET_INITIALIZER(&tc.port_names),
 };
 
-/* Iterate over the full set of tunnels in both the OVS and SB databases on
- * the next wakeup. This is necessary when we add or remove a port in OVS to
- * handle the case where validation fails. */
-static bool process_full_bridge = false;
-
 static char *
 tunnel_create_name(const char *chassis_id)
 {
@@ -229,11 +224,6 @@ tunnel_add(const struct sbrec_chassis *chassis_rec,
     sset_add(&tc.port_names, port_name);
     free(port_name);
     free(ports);
-    binding_reset_processing();
-    lport_index_reset();
-    mcgroup_index_reset();
-    lflow_reset_processing();
-    process_full_bridge = true;
 }
 
 static void
@@ -259,10 +249,6 @@ bridge_delete_port(const struct ovsrec_bridge *br,
         ovsrec_bridge_verify_ports(br);
         ovsrec_bridge_set_ports(br, ports, n);
         free(ports);
-
-        binding_reset_processing();
-        lflow_reset_processing();
-        process_full_bridge = true;
     }
 }
 
@@ -365,65 +351,61 @@ encaps_run(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int,
      * common. It would also require more bookkeeping to match up ports and
      * interfaces. */
 
-    if (process_full_bridge || ovsrec_port_track_get_first(ctx->ovs_idl) ||
-        ovsrec_interface_track_get_first(ctx->ovs_idl)) {
-        const struct ovsrec_port *port_rec;
-        struct chassis_hash_node *chassis_node, *next;
+    const struct ovsrec_port *port_rec;
+    struct chassis_hash_node *chassis_node, *next;
 
-        process_full_bridge = false;
-        sset_clear(&tc.port_names);
+    sset_clear(&tc.port_names);
 
-        /* Find all of the tunnel ports to remote chassis.
-         * Delete the tunnel ports from unknown remote chassis. */
-        OVSREC_PORT_FOR_EACH (port_rec, ctx->ovs_idl) {
-            sset_add(&tc.port_names, port_rec->name);
-            for (int i = 0; i < port_rec->n_interfaces; i++) {
-                sset_add(&tc.port_names, port_rec->interfaces[i]->name);
-            }
+    /* Find all of the tunnel ports to remote chassis.
+     * Delete the tunnel ports from unknown remote chassis. */
+    OVSREC_PORT_FOR_EACH (port_rec, ctx->ovs_idl) {
+        sset_add(&tc.port_names, port_rec->name);
+        for (int i = 0; i < port_rec->n_interfaces; i++) {
+            sset_add(&tc.port_names, port_rec->interfaces[i]->name);
+        }
 
-            const char *chassis_id = smap_get(&port_rec->external_ids,
-                                              "ovn-chassis-id");
-            if (chassis_id) {
-                chassis_node = lookup_chassis_id(chassis_id);
-                if (chassis_node) {
-                    /* Populate the port's UUID the first time we see it after
-                     * the port was added. */
-                    if (uuid_is_zero(&chassis_node->port_uuid)) {
-                        chassis_node->port_uuid = port_rec->header_.uuid;
-                    }
-                } else {
-                    for (int i = 0; i < port_rec->n_interfaces; i++) {
-                        sset_find_and_delete(&tc.port_names,
-                                             port_rec->interfaces[i]->name);
-                    }
-                    sset_find_and_delete(&tc.port_names, port_rec->name);
-                    bridge_delete_port(tc.br_int, port_rec, NULL);
+        const char *chassis_id = smap_get(&port_rec->external_ids,
+                                          "ovn-chassis-id");
+        if (chassis_id) {
+            chassis_node = lookup_chassis_id(chassis_id);
+            if (chassis_node) {
+                /* Populate the port's UUID the first time we see it after
+                 * the port was added. */
+                if (uuid_is_zero(&chassis_node->port_uuid)) {
+                    chassis_node->port_uuid = port_rec->header_.uuid;
+                }
+            } else {
+                for (int i = 0; i < port_rec->n_interfaces; i++) {
+                    sset_find_and_delete(&tc.port_names,
+                                         port_rec->interfaces[i]->name);
                 }
+                sset_find_and_delete(&tc.port_names, port_rec->name);
+                bridge_delete_port(tc.br_int, port_rec, NULL);
             }
         }
+    }
 
-        /* For each chassis that we previously created, check that both the
-         * chassis and port still exist and are current. */
-        HMAP_FOR_EACH_SAFE (chassis_node, next, node, &tc.chassis_hmap) {
-            chassis_rec = sbrec_chassis_get_for_uuid(ctx->ovnsb_idl,
-                                                   &chassis_node->chassis_uuid);
-            port_rec = ovsrec_port_get_for_uuid(ctx->ovs_idl,
-                                                &chassis_node->port_uuid);
+    /* For each chassis that we previously created, check that both the
+     * chassis and port still exist and are current. */
+    HMAP_FOR_EACH_SAFE (chassis_node, next, node, &tc.chassis_hmap) {
+        chassis_rec = sbrec_chassis_get_for_uuid(ctx->ovnsb_idl,
+                                               &chassis_node->chassis_uuid);
+        port_rec = ovsrec_port_get_for_uuid(ctx->ovs_idl,
+                                            &chassis_node->port_uuid);
 
-            if (!chassis_rec) {
-                /* Delete tunnel port (if present) for missing chassis. */
-                bridge_delete_port(tc.br_int, port_rec, chassis_node);
-                continue;
-            }
+        if (!chassis_rec) {
+            /* Delete tunnel port (if present) for missing chassis. */
+            bridge_delete_port(tc.br_int, port_rec, chassis_node);
+            continue;
+        }
 
-            if (!port_rec) {
-                /* Delete our representation of the chassis, then add back. */
-                bridge_delete_port(tc.br_int, NULL, chassis_node);
-                check_and_add_tunnel(chassis_rec, local_chassis_id);
-            } else {
-                /* Update tunnel. */
-                check_and_update_tunnel(port_rec, chassis_rec);
-            }
+        if (!port_rec) {
+            /* Delete our representation of the chassis, then add back. */
+            bridge_delete_port(tc.br_int, NULL, chassis_node);
+            check_and_add_tunnel(chassis_rec, local_chassis_id);
+        } else {
+            /* Update tunnel. */
+            check_and_update_tunnel(port_rec, chassis_rec);
         }
     }
 
@@ -444,7 +426,6 @@ encaps_run(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int,
                      * new version and then iterate over everything to see if it
                      * is OK. */
                     delete_encap_uuid(encap_hash_node);
-                    process_full_bridge = true;
                     poll_immediate_wake();
                 }
 
diff --git a/ovn/controller/lflow.c b/ovn/controller/lflow.c
index 341ca08..6b13208 100644
--- a/ovn/controller/lflow.c
+++ b/ovn/controller/lflow.c
@@ -41,17 +41,6 @@ static struct shash symtab;
 /* Contains an internal expr datastructure that represents an address set. */
 static struct shash expr_address_sets;
 
-static bool full_flow_processing = false;
-static bool full_logical_flow_processing = false;
-static bool full_neighbor_flow_processing = false;
-
-void
-lflow_reset_processing(void)
-{
-    full_flow_processing = true;
-    physical_reset_processing();
-}
-
 void
 lflow_init(void)
 {
@@ -219,7 +208,8 @@ static void consider_logical_flow(const struct lport_index *lports,
                                   const struct simap *ct_zones,
                                   struct hmap *dhcp_opts_p,
                                   struct hmap *dhcpv6_opts_p,
-                                  uint32_t *conj_id_ofs_p);
+                                  uint32_t *conj_id_ofs_p,
+                                  struct hmap *flow_table);
 
 static bool
 lookup_port_cb(const void *aux_, const char *port_name, unsigned int *portp)
@@ -257,19 +247,13 @@ add_logical_flows(struct controller_ctx *ctx, const struct lport_index *lports,
                   const struct hmap *local_datapaths,
                   const struct hmap *patched_datapaths,
                   struct group_table *group_table,
-                  const struct simap *ct_zones)
+                  const struct simap *ct_zones,
+                  struct hmap *flow_table)
 {
     uint32_t conj_id_ofs = 1;
     const struct sbrec_logical_flow *lflow;
 
-    if (full_flow_processing) {
-        ovn_flow_table_clear();
-        ovn_group_table_clear(group_table, false);
-        full_logical_flow_processing = true;
-        full_neighbor_flow_processing = true;
-        full_flow_processing = false;
-        physical_reset_processing();
-    }
+    ovn_group_table_clear(group_table, false);
 
     struct hmap dhcp_opts = HMAP_INITIALIZER(&dhcp_opts);
     struct hmap dhcpv6_opts = HMAP_INITIALIZER(&dhcpv6_opts);
@@ -286,31 +270,11 @@ add_logical_flows(struct controller_ctx *ctx, const struct lport_index *lports,
                     dhcpv6_opt_row->type);
     }
 
-    if (full_logical_flow_processing) {
-        SBREC_LOGICAL_FLOW_FOR_EACH (lflow, ctx->ovnsb_idl) {
-            consider_logical_flow(lports, mcgroups, lflow, local_datapaths,
-                                  patched_datapaths, group_table, ct_zones,
-                                  &dhcp_opts, &dhcpv6_opts, &conj_id_ofs);
-        }
-        full_logical_flow_processing = false;
-    } else {
-        SBREC_LOGICAL_FLOW_FOR_EACH_TRACKED (lflow, ctx->ovnsb_idl) {
-            /* Remove any flows that should be removed. */
-            if (sbrec_logical_flow_is_deleted(lflow)) {
-                ofctrl_remove_flows(&lflow->header_.uuid);
-            } else {
-                /* Now, add/modify existing flows. If the logical
-                 * flow is a modification, just remove the flows
-                 * for this row, and then add new flows. */
-                if (!sbrec_logical_flow_is_new(lflow)) {
-                    ofctrl_remove_flows(&lflow->header_.uuid);
-                }
-                consider_logical_flow(lports, mcgroups, lflow,
-                                      local_datapaths, patched_datapaths,
-                                      group_table, ct_zones,
-                                      &dhcp_opts, &dhcpv6_opts, &conj_id_ofs);
-            }
-        }
+    SBREC_LOGICAL_FLOW_FOR_EACH (lflow, ctx->ovnsb_idl) {
+        consider_logical_flow(lports, mcgroups, lflow, local_datapaths,
+                              patched_datapaths, group_table, ct_zones,
+                              &dhcp_opts, &dhcpv6_opts, &conj_id_ofs,
+                              flow_table);
     }
 
     dhcp_opts_destroy(&dhcp_opts);
@@ -327,7 +291,8 @@ consider_logical_flow(const struct lport_index *lports,
                       const struct simap *ct_zones,
                       struct hmap *dhcp_opts_p,
                       struct hmap *dhcpv6_opts_p,
-                      uint32_t *conj_id_ofs_p)
+                      uint32_t *conj_id_ofs_p,
+                      struct hmap *flow_table)
 {
     /* Determine translation of logical table IDs to physical table IDs. */
     bool ingress = !strcmp(lflow->pipeline, "ingress");
@@ -468,8 +433,8 @@ consider_logical_flow(const struct lport_index *lports,
             m->match.flow.conj_id += *conj_id_ofs_p;
         }
         if (!m->n) {
-            ofctrl_add_flow(ptable, lflow->priority, &m->match, &ofpacts,
-                            &lflow->header_.uuid);
+            ofctrl_add_flow(flow_table, ptable, lflow->priority, &m->match,
+                            &ofpacts);
         } else {
             uint64_t conj_stubs[64 / 8];
             struct ofpbuf conj;
@@ -484,8 +449,8 @@ consider_logical_flow(const struct lport_index *lports,
                 dst->clause = src->clause;
                 dst->n_clauses = src->n_clauses;
             }
-            ofctrl_add_flow(ptable, lflow->priority, &m->match, &conj,
-                            &lflow->header_.uuid);
+            ofctrl_add_flow(flow_table, ptable, lflow->priority, &m->match,
+                            &conj);
             ofpbuf_uninit(&conj);
         }
     }
@@ -513,7 +478,8 @@ static void
 consider_neighbor_flow(const struct lport_index *lports,
                        const struct sbrec_mac_binding *b,
                        struct ofpbuf *ofpacts_p,
-                       struct match *match_p)
+                       struct match *match_p,
+                       struct hmap *flow_table)
 {
     const struct sbrec_port_binding *pb
         = lport_lookup_by_name(lports, b->logical_port);
@@ -555,8 +521,7 @@ consider_neighbor_flow(const struct lport_index *lports,
     ofpbuf_clear(ofpacts_p);
     put_load(mac.ea, sizeof mac.ea, MFF_ETH_DST, 0, 48, ofpacts_p);
 
-    ofctrl_add_flow(OFTABLE_MAC_BINDING, 100, match_p, ofpacts_p,
-                    &b->header_.uuid);
+    ofctrl_add_flow(flow_table, OFTABLE_MAC_BINDING, 100, match_p, ofpacts_p);
 }
 
 /* Adds an OpenFlow flow to flow tables for each MAC binding in the OVN
@@ -564,7 +529,8 @@ consider_neighbor_flow(const struct lport_index *lports,
  * numbers. */
 static void
 add_neighbor_flows(struct controller_ctx *ctx,
-                   const struct lport_index *lports)
+                   const struct lport_index *lports,
+                   struct hmap *flow_table)
 {
     struct ofpbuf ofpacts;
     struct match match;
@@ -572,22 +538,8 @@ add_neighbor_flows(struct controller_ctx *ctx,
     ofpbuf_init(&ofpacts, 0);
 
     const struct sbrec_mac_binding *b;
-    if (full_neighbor_flow_processing) {
-        SBREC_MAC_BINDING_FOR_EACH (b, ctx->ovnsb_idl) {
-            consider_neighbor_flow(lports, b, &ofpacts, &match);
-        }
-        full_neighbor_flow_processing = false;
-    } else {
-        SBREC_MAC_BINDING_FOR_EACH_TRACKED (b, ctx->ovnsb_idl) {
-            if (sbrec_mac_binding_is_deleted(b)) {
-                ofctrl_remove_flows(&b->header_.uuid);
-            } else {
-                if (!sbrec_mac_binding_is_new(b)) {
-                    ofctrl_remove_flows(&b->header_.uuid);
-                }
-                consider_neighbor_flow(lports, b, &ofpacts, &match);
-            }
-        }
+    SBREC_MAC_BINDING_FOR_EACH (b, ctx->ovnsb_idl) {
+        consider_neighbor_flow(lports, b, &ofpacts, &match, flow_table);
     }
 
     ofpbuf_uninit(&ofpacts);
@@ -601,12 +553,13 @@ lflow_run(struct controller_ctx *ctx, const struct lport_index *lports,
           const struct hmap *local_datapaths,
           const struct hmap *patched_datapaths,
           struct group_table *group_table,
-          const struct simap *ct_zones)
+          const struct simap *ct_zones,
+          struct hmap *flow_table)
 {
     update_address_sets(ctx);
     add_logical_flows(ctx, lports, mcgroups, local_datapaths,
-                      patched_datapaths, group_table, ct_zones);
-    add_neighbor_flows(ctx, lports);
+                      patched_datapaths, group_table, ct_zones, flow_table);
+    add_neighbor_flows(ctx, lports, flow_table);
 }
 
 void
diff --git a/ovn/controller/lflow.h b/ovn/controller/lflow.h
index ac058ff..d3ca5d1 100644
--- a/ovn/controller/lflow.h
+++ b/ovn/controller/lflow.h
@@ -66,8 +66,8 @@ void lflow_run(struct controller_ctx *, const struct lport_index *,
                const struct hmap *local_datapaths,
                const struct hmap *patched_datapaths,
                struct group_table *group_table,
-               const struct simap *ct_zones);
+               const struct simap *ct_zones,
+               struct hmap *flow_table);
 void lflow_destroy(void);
-void lflow_reset_processing(void);
 
 #endif /* ovn/lflow.h */
diff --git a/ovn/controller/lport.c b/ovn/controller/lport.c
index 5d8d0d0..e1ecf21 100644
--- a/ovn/controller/lport.c
+++ b/ovn/controller/lport.c
@@ -17,7 +17,6 @@
 
 #include "lport.h"
 #include "hash.h"
-#include "lflow.h"
 #include "openvswitch/vlog.h"
 #include "ovn/lib/ovn-sb-idl.h"
 
@@ -25,112 +24,49 @@ VLOG_DEFINE_THIS_MODULE(lport);
 
 /* A logical port. */
 struct lport {
-    struct hmap_node name_node;  /* Index by name. */
-    struct hmap_node key_node;   /* Index by (dp_key, port_key). */
-    struct hmap_node uuid_node;  /* Index by row uuid. */
-    struct uuid uuid;
+    struct hmap_node name_node; /* Index by name. */
+    struct hmap_node key_node;  /* Index by (dp_key, port_key). */
     const struct sbrec_port_binding *pb;
 };
 
-static bool full_lport_rebuild = false;
-
-void
-lport_index_reset(void)
-{
-    full_lport_rebuild = true;
-}
-
 void
-lport_index_init(struct lport_index *lports)
+lport_index_init(struct lport_index *lports, struct ovsdb_idl *ovnsb_idl)
 {
     hmap_init(&lports->by_name);
     hmap_init(&lports->by_key);
-    hmap_init(&lports->by_uuid);
-}
 
-bool
-lport_index_remove(struct lport_index *lports, const struct uuid *uuid)
-{
-    const struct lport *port_ = lport_lookup_by_uuid(lports, uuid);
-    struct lport *port = CONST_CAST(struct lport *, port_);
-    if (port) {
-        hmap_remove(&lports->by_name, &port->name_node);
-        hmap_remove(&lports->by_key, &port->key_node);
-        hmap_remove(&lports->by_uuid, &port->uuid_node);
-        free(port);
-        return true;
+    const struct sbrec_port_binding *pb;
+    SBREC_PORT_BINDING_FOR_EACH (pb, ovnsb_idl) {
+        if (lport_lookup_by_name(lports, pb->logical_port)) {
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+            VLOG_WARN_RL(&rl, "duplicate logical port name '%s'",
+                         pb->logical_port);
+            continue;
+        }
+
+        struct lport *p = xmalloc(sizeof *p);
+        hmap_insert(&lports->by_name, &p->name_node,
+                    hash_string(pb->logical_port, 0));
+        hmap_insert(&lports->by_key, &p->key_node,
+                    hash_int(pb->tunnel_key, pb->datapath->tunnel_key));
+        p->pb = pb;
     }
-    return false;
 }
 
 void
-lport_index_clear(struct lport_index *lports)
+lport_index_destroy(struct lport_index *lports)
 {
     /* Destroy all of the "struct lport"s.
      *
-     * We have to remove the node from all indexes. */
+     * We don't have to remove the node from both indexes. */
     struct lport *port, *next;
     HMAP_FOR_EACH_SAFE (port, next, name_node, &lports->by_name) {
         hmap_remove(&lports->by_name, &port->name_node);
-        hmap_remove(&lports->by_key, &port->key_node);
-        hmap_remove(&lports->by_uuid, &port->uuid_node);
         free(port);
     }
-    lflow_reset_processing();
-}
-
-static void
-consider_lport_index(struct lport_index *lports,
-                     const struct sbrec_port_binding *pb)
-{
-    if (lport_lookup_by_name(lports, pb->logical_port)) {
-        return;
-    }
-
-    struct lport *p = xmalloc(sizeof *p);
-    hmap_insert(&lports->by_name, &p->name_node,
-                hash_string(pb->logical_port, 0));
-    hmap_insert(&lports->by_key, &p->key_node,
-                hash_int(pb->tunnel_key, pb->datapath->tunnel_key));
-    hmap_insert(&lports->by_uuid, &p->uuid_node,
-                uuid_hash(&pb->header_.uuid));
-    memcpy(&p->uuid, &pb->header_.uuid, sizeof p->uuid);
-    p->pb = pb;
-    lflow_reset_processing();
-}
-
-void
-lport_index_fill(struct lport_index *lports, struct ovsdb_idl *ovnsb_idl)
-{
-    const struct sbrec_port_binding *pb;
-    if (full_lport_rebuild) {
-        lport_index_clear(lports);
-        SBREC_PORT_BINDING_FOR_EACH (pb, ovnsb_idl) {
-            consider_lport_index(lports, pb);
-        }
-        full_lport_rebuild = false;
-    } else {
-        SBREC_PORT_BINDING_FOR_EACH_TRACKED (pb, ovnsb_idl) {
-            if (sbrec_port_binding_is_deleted(pb)) {
-                while (lport_index_remove(lports, &pb->header_.uuid)) {
-                    ;
-                }
-                lflow_reset_processing();
-            } else {
-                consider_lport_index(lports, pb);
-            }
-        }
-    }
-}
-
-void
-lport_index_destroy(struct lport_index *lports)
-{
-    lport_index_clear(lports);
 
     hmap_destroy(&lports->by_name);
     hmap_destroy(&lports->by_key);
-    hmap_destroy(&lports->by_uuid);
 }
 
 /* Finds and returns the lport with the given 'name', or NULL if no such lport
@@ -148,20 +84,6 @@ lport_lookup_by_name(const struct lport_index *lports, const char *name)
     return NULL;
 }
 
-const struct lport *
-lport_lookup_by_uuid(const struct lport_index *lports,
-                     const struct uuid *uuid)
-{
-    const struct lport *lport;
-    HMAP_FOR_EACH_WITH_HASH (lport, uuid_node, uuid_hash(uuid),
-                             &lports->by_uuid) {
-        if (uuid_equals(uuid, &lport->uuid)) {
-            return lport;
-        }
-    }
-    return NULL;
-}
-
 const struct sbrec_port_binding *
 lport_lookup_by_key(const struct lport_index *lports,
                     uint32_t dp_key, uint16_t port_key)
@@ -179,113 +101,43 @@ lport_lookup_by_key(const struct lport_index *lports,
 
 struct mcgroup {
     struct hmap_node dp_name_node; /* Index by (logical datapath, name). */
-    struct hmap_node uuid_node;    /* Index by insert uuid. */
-    struct uuid uuid;
     const struct sbrec_multicast_group *mg;
 };
 
-static bool full_mc_rebuild = false;
-
 void
-mcgroup_index_reset(void)
-{
-    full_mc_rebuild = true;
-}
-
-void
-mcgroup_index_init(struct mcgroup_index *mcgroups)
+mcgroup_index_init(struct mcgroup_index *mcgroups, struct ovsdb_idl *ovnsb_idl)
 {
     hmap_init(&mcgroups->by_dp_name);
-    hmap_init(&mcgroups->by_uuid);
-}
 
-void
-mcgroup_index_remove(struct mcgroup_index *mcgroups, const struct uuid *uuid)
-{
-    const struct mcgroup *mcgroup_ = mcgroup_lookup_by_uuid(mcgroups, uuid);
-    struct mcgroup *mcgroup = CONST_CAST(struct mcgroup *, mcgroup_);
-    if (mcgroup) {
-        hmap_remove(&mcgroups->by_dp_name, &mcgroup->dp_name_node);
-        hmap_remove(&mcgroups->by_uuid, &mcgroup->uuid_node);
-        free(mcgroup);
+    const struct sbrec_multicast_group *mg;
+    SBREC_MULTICAST_GROUP_FOR_EACH (mg, ovnsb_idl) {
+        const struct uuid *dp_uuid = &mg->datapath->header_.uuid;
+        if (mcgroup_lookup_by_dp_name(mcgroups, mg->datapath, mg->name)) {
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+            VLOG_WARN_RL(&rl, "datapath "UUID_FMT" contains duplicate "
+                         "multicast group '%s'", UUID_ARGS(dp_uuid), mg->name);
+            continue;
+        }
+
+        struct mcgroup *m = xmalloc(sizeof *m);
+        hmap_insert(&mcgroups->by_dp_name, &m->dp_name_node,
+                    hash_string(mg->name, uuid_hash(dp_uuid)));
+        m->mg = mg;
     }
-    lflow_reset_processing();
 }
 
 void
-mcgroup_index_clear(struct mcgroup_index *mcgroups)
+mcgroup_index_destroy(struct mcgroup_index *mcgroups)
 {
     struct mcgroup *mcgroup, *next;
     HMAP_FOR_EACH_SAFE (mcgroup, next, dp_name_node, &mcgroups->by_dp_name) {
         hmap_remove(&mcgroups->by_dp_name, &mcgroup->dp_name_node);
-        hmap_remove(&mcgroups->by_uuid, &mcgroup->uuid_node);
         free(mcgroup);
     }
-}
-
-static void
-consider_mcgroup_index(struct mcgroup_index *mcgroups,
-                       const struct sbrec_multicast_group *mg)
-{
-    const struct uuid *dp_uuid = &mg->datapath->header_.uuid;
-    if (mcgroup_lookup_by_dp_name(mcgroups, mg->datapath, mg->name)) {
-        return;
-    }
-
-    struct mcgroup *m = xmalloc(sizeof *m);
-    hmap_insert(&mcgroups->by_dp_name, &m->dp_name_node,
-                hash_string(mg->name, uuid_hash(dp_uuid)));
-    hmap_insert(&mcgroups->by_uuid, &m->uuid_node,
-                uuid_hash(&mg->header_.uuid));
-    memcpy(&m->uuid, &mg->header_.uuid, sizeof m->uuid);
-    m->mg = mg;
-    lflow_reset_processing();
-}
-
-void
-mcgroup_index_fill(struct mcgroup_index *mcgroups, struct ovsdb_idl *ovnsb_idl)
-{
-    const struct sbrec_multicast_group *mg;
-    if (full_mc_rebuild) {
-        mcgroup_index_clear(mcgroups);
-        SBREC_MULTICAST_GROUP_FOR_EACH (mg, ovnsb_idl) {
-            consider_mcgroup_index(mcgroups, mg);
-        }
-        full_mc_rebuild = false;
-    } else {
-        SBREC_MULTICAST_GROUP_FOR_EACH_TRACKED (mg, ovnsb_idl) {
-            if (sbrec_multicast_group_is_deleted(mg)) {
-                mcgroup_index_remove(mcgroups, &mg->header_.uuid);
-                lflow_reset_processing();
-            } else {
-                consider_mcgroup_index(mcgroups, mg);
-            }
-        }
-    }
-}
-
-void
-mcgroup_index_destroy(struct mcgroup_index *mcgroups)
-{
-    mcgroup_index_clear(mcgroups);
 
     hmap_destroy(&mcgroups->by_dp_name);
 }
 
-const struct mcgroup *
-mcgroup_lookup_by_uuid(const struct mcgroup_index *mcgroups,
-                       const struct uuid *uuid)
-{
-    const struct mcgroup *mcgroup;
-    HMAP_FOR_EACH_WITH_HASH (mcgroup, uuid_node, uuid_hash(uuid),
-                             &mcgroups->by_uuid) {
-        if (uuid_equals(&mcgroup->uuid, uuid)) {
-            return mcgroup;
-        }
-    }
-    return NULL;
-}
-
 const struct sbrec_multicast_group *
 mcgroup_lookup_by_dp_name(const struct mcgroup_index *mcgroups,
                           const struct sbrec_datapath_binding *dp,
diff --git a/ovn/controller/lport.h b/ovn/controller/lport.h
index 9e9c6d3..0cad74a 100644
--- a/ovn/controller/lport.h
+++ b/ovn/controller/lport.h
@@ -18,7 +18,6 @@
 
 #include <stdint.h>
 #include "openvswitch/hmap.h"
-#include "uuid.h"
 
 struct ovsdb_idl;
 struct sbrec_datapath_binding;
@@ -33,25 +32,15 @@ struct sbrec_datapath_binding;
 struct lport_index {
     struct hmap by_name;
     struct hmap by_key;
-    struct hmap by_uuid;
 };
 
-void lport_index_reset(void);
-void lport_index_init(struct lport_index *);
-void lport_index_fill(struct lport_index *, struct ovsdb_idl *);
-bool lport_index_remove(struct lport_index *, const struct uuid *);
-void lport_index_clear(struct lport_index *);
+void lport_index_init(struct lport_index *, struct ovsdb_idl *);
 void lport_index_destroy(struct lport_index *);
-void lport_index_rebuild(void);
 
 const struct sbrec_port_binding *lport_lookup_by_name(
     const struct lport_index *, const char *name);
 const struct sbrec_port_binding *lport_lookup_by_key(
     const struct lport_index *, uint32_t dp_key, uint16_t port_key);
-
-const struct lport *lport_lookup_by_uuid(
-    const struct lport_index *, const struct uuid *uuid);
-
 
 /* Multicast group index
  * =====================
@@ -65,23 +54,14 @@ const struct lport *lport_lookup_by_uuid(
 
 struct mcgroup_index {
     struct hmap by_dp_name;
-    struct hmap by_uuid;
 };
 
-void mcgroup_index_reset(void);
-void mcgroup_index_init(struct mcgroup_index *);
-void mcgroup_index_fill(struct mcgroup_index *, struct ovsdb_idl *);
-void mcgroup_index_remove(struct mcgroup_index *, const struct uuid *);
-void mcgroup_index_clear(struct mcgroup_index *);
+void mcgroup_index_init(struct mcgroup_index *, struct ovsdb_idl *);
 void mcgroup_index_destroy(struct mcgroup_index *);
-void mcgroup_index_rebuild(void);
 
 const struct sbrec_multicast_group *mcgroup_lookup_by_dp_name(
     const struct mcgroup_index *,
     const struct sbrec_datapath_binding *,
     const char *name);
 
-const struct mcgroup *mcgroup_lookup_by_uuid(
-    const struct mcgroup_index *, const struct uuid *uuid);
-
 #endif /* ovn/lport.h */
diff --git a/ovn/controller/ofctrl.c b/ovn/controller/ofctrl.c
index d7b3d3e..c20f44c 100644
--- a/ovn/controller/ofctrl.c
+++ b/ovn/controller/ofctrl.c
@@ -19,7 +19,6 @@
 #include "dirs.h"
 #include "flow.h"
 #include "hash.h"
-#include "hindex.h"
 #include "lflow.h"
 #include "ofctrl.h"
 #include "openflow/openflow.h"
@@ -47,8 +46,7 @@ VLOG_DEFINE_THIS_MODULE(ofctrl);
 
 /* An OpenFlow flow. */
 struct ovn_flow {
-    struct hmap_node match_hmap_node; /* For match based hashing. */
-    struct hindex_node uuid_hindex_node; /* For uuid based hashing. */
+    struct hmap_node hmap_node; /* For match based hashing. */
     struct ovs_list list_node; /* For handling lists of flows. */
 
     /* Key. */
@@ -56,15 +54,14 @@ struct ovn_flow {
     uint16_t priority;
     struct match match;
 
-    /* Data. UUID is used for disambiguation. */
-    struct uuid uuid;
+    /* Data. */
     struct ofpact *ofpacts;
     size_t ofpacts_len;
 };
 
-static uint32_t ovn_flow_match_hash(const struct ovn_flow *);
-static void ovn_flow_lookup(struct hmap *, const struct ovn_flow *target,
-                            struct ovs_list *answers);
+static uint32_t ovn_flow_hash(const struct ovn_flow *);
+static struct ovn_flow *ovn_flow_lookup(struct hmap *flow_table,
+                                        const struct ovn_flow *target);
 static char *ovn_flow_to_string(const struct ovn_flow *);
 static void ovn_flow_log(const struct ovn_flow *, const char *action);
 static void ovn_flow_destroy(struct ovn_flow *);
@@ -138,7 +135,6 @@ static enum mf_field_id mff_ovn_geneve;
 
 static ovs_be32 queue_msg(struct ofpbuf *);
 
-static void ovn_flow_table_destroy(void);
 static struct ofpbuf *encode_flow_mod(struct ofputil_flow_mod *);
 
 static struct ofpbuf *encode_group_mod(const struct ofputil_group_mod *);
@@ -146,7 +142,6 @@ static struct ofpbuf *encode_group_mod(const struct ofputil_group_mod *);
 static void ofctrl_recv(const struct ofp_header *, enum ofptype);
 
 static struct hmap match_flow_table = HMAP_INITIALIZER(&match_flow_table);
-static struct hindex uuid_flow_table = HINDEX_INITIALIZER(&uuid_flow_table);
 
 void
 ofctrl_init(struct group_table *group_table)
@@ -367,10 +362,6 @@ run_S_CLEAR_FLOWS(void)
     queue_msg(encode_group_mod(&gm));
     ofputil_uninit_group_mod(&gm);
 
-    /* Clear installed_flows, to match the state of the switch. */
-    ovn_flow_table_clear();
-    lflow_reset_processing();
-
     /* Clear existing groups, to match the state of the switch. */
     if (groups) {
         ovn_group_table_clear(groups, true);
@@ -513,7 +504,6 @@ void
 ofctrl_destroy(void)
 {
     rconn_destroy(swconn);
-    ovn_flow_table_destroy();
     rconn_packet_counter_destroy(tx_counter);
 }
 
@@ -563,144 +553,54 @@ ofctrl_recv(const struct ofp_header *oh, enum ofptype type)
 
 /* Flow table interfaces to the rest of ovn-controller. */
 
-static void
-log_ovn_flow_rl(struct vlog_rate_limit *rl, enum vlog_level level,
-                const struct ovn_flow *flow, const char *title)
-{
-    if (!vlog_should_drop(&this_module, level, rl)) {
-        char *s = ovn_flow_to_string(flow);
-        vlog(&this_module, level, "%s for parent "UUID_FMT": %s",
-             title, UUID_ARGS(&flow->uuid), s);
-        free(s);
-    }
-}
-
-/* Adds a flow to the collection associated with 'uuid'.  The flow has the
- * specified 'match' and 'actions' to the OpenFlow table numbered 'table_id'
- * with the given 'priority'.  The caller retains ownership of 'match' and
- * 'actions'.
+/* Adds a flow to 'desired_flows' with the specified 'match' and 'actions' to
+ * the OpenFlow table numbered 'table_id' with the given 'priority'.  The
+ * caller retains ownership of 'match' and 'actions'.
  *
- * Any number of flows may be associated with a given UUID.  The flows with a
- * given UUID must have a unique (table_id, priority, match) tuple.  A
- * duplicate within a generally indicates a bug in the ovn-controller code that
- * generated it, so this functions logs a warning.
+ * This just assembles the desired flow table in memory.  Nothing is actually
+ * sent to the switch until a later call to ofctrl_run().
  *
- * (table_id, priority, match) tuples should also be unique for flows with
- * different UUIDs, but it doesn't necessarily indicate a bug in
- * ovn-controller, for two reasons.  First, these duplicates could be caused by
- * logical flows generated by ovn-northd, which aren't ovn-controller's fault;
- * perhaps something should warn about these but the root cause is different.
- * Second, these duplicates might be transient, that is, they might go away
- * before the next call to ofctrl_run() if a call to ofctrl_remove_flows()
- * removes one or the other.
- *
- * This just assembles the desired flow tables in memory.  Nothing is actually
- * sent to the switch until a later call to ofctrl_run(). */
+ * The caller should initialize its own hmap to hold the flows. */
 void
-ofctrl_add_flow(uint8_t table_id, uint16_t priority,
-                const struct match *match, const struct ofpbuf *actions,
-                const struct uuid *uuid)
+ofctrl_add_flow(struct hmap *desired_flows,
+                uint8_t table_id, uint16_t priority,
+                const struct match *match, const struct ofpbuf *actions)
 {
-    /* Structure that uses table_id+priority+various things as hashes. */
     struct ovn_flow *f = xmalloc(sizeof *f);
     f->table_id = table_id;
     f->priority = priority;
     f->match = *match;
     f->ofpacts = xmemdup(actions->data, actions->size);
     f->ofpacts_len = actions->size;
-    f->uuid = *uuid;
-    f->match_hmap_node.hash = ovn_flow_match_hash(f);
-    f->uuid_hindex_node.hash = uuid_hash(&f->uuid);
-
-    /* Check to see if other flows exist with the same key (table_id priority,
-     * match criteria) and uuid.  If so, discard this flow and log a
-     * warning. */
-    struct ovs_list existing;
-    ovn_flow_lookup(&match_flow_table, f, &existing);
-    struct ovn_flow *d;
-    LIST_FOR_EACH (d, list_node, &existing) {
-        if (uuid_equals(&f->uuid, &d->uuid)) {
-            /* Duplicate flows with the same UUID indicate some kind of bug
-             * (see the function-level comment), but we distinguish two
-             * cases:
-             *
-             *     - If the actions for the duplicate flow are the same, then
-             *       it's benign; it's hard to imagine how there could be a
-             *       real problem.  Log at INFO level.
-             *
-             *     - If the actions are different, then one or the other set of
-             *       actions must be wrong or (perhaps more likely) we've got a
-             *       new set of actions replacing an old set but the caller
-             *       neglected to use ofctrl_remove_flows() or
-             *       ofctrl_set_flow() to do it properly.  Log at WARN level to
-             *       get some attention.
-             */
-            if (ofpacts_equal(f->ofpacts, f->ofpacts_len,
-                              d->ofpacts, d->ofpacts_len)) {
-                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
-                log_ovn_flow_rl(&rl, VLL_INFO, f, "duplicate flow");
-            } else {
-                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
-                log_ovn_flow_rl(&rl, VLL_WARN, f,
-                                "duplicate flow with modified action");
-
-                /* It seems likely that the newer actions are the correct
-                 * ones. */
-                free(d->ofpacts);
-                d->ofpacts = f->ofpacts;
-                d->ofpacts_len = f->ofpacts_len;
-                f->ofpacts = NULL;
-            }
-            ovn_flow_destroy(f);
-            return;
+    f->hmap_node.hash = ovn_flow_hash(f);
+
+    if (ovn_flow_lookup(desired_flows, f)) {
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
+        if (!VLOG_DROP_INFO(&rl)) {
+            char *s = ovn_flow_to_string(f);
+            VLOG_INFO("dropping duplicate flow: %s", s);
+            free(s);
         }
-    }
-
-    /* Otherwise, add the flow. */
-    hmap_insert(&match_flow_table, &f->match_hmap_node,
-                f->match_hmap_node.hash);
-    hindex_insert(&uuid_flow_table, &f->uuid_hindex_node,
-                f->uuid_hindex_node.hash);
-}
 
-/* Removes a bundles of flows from the flow table. */
-void
-ofctrl_remove_flows(const struct uuid *uuid)
-{
-    struct ovn_flow *f, *next;
-    HINDEX_FOR_EACH_WITH_HASH_SAFE (f, next, uuid_hindex_node, uuid_hash(uuid),
-                                    &uuid_flow_table) {
-        if (uuid_equals(&f->uuid, uuid)) {
-            hmap_remove(&match_flow_table, &f->match_hmap_node);
-            hindex_remove(&uuid_flow_table, &f->uuid_hindex_node);
-            ovn_flow_destroy(f);
-        }
+        ovn_flow_destroy(f);
+        return;
     }
 
-    /* Remove any group_info information created by this logical flow. */
-    struct group_info *g, *next_g;
-    HMAP_FOR_EACH_SAFE (g, next_g, hmap_node, &groups->desired_groups) {
-        if (uuid_equals(&g->lflow_uuid, uuid)) {
-            hmap_remove(&groups->desired_groups, &g->hmap_node);
-            ds_destroy(&g->group);
-            free(g);
-        }
-    }
+    hmap_insert(desired_flows, &f->hmap_node, f->hmap_node.hash);
 }
 
-/* Shortcut to remove all flows matching the supplied UUID and add this
- * flow. */
-void
-ofctrl_set_flow(uint8_t table_id, uint16_t priority,
-                const struct match *match, const struct ofpbuf *actions,
-                const struct uuid *uuid)
-{
-    ofctrl_remove_flows(uuid);
-    ofctrl_add_flow(table_id, priority, match, actions, uuid);
-}
 
 /* ovn_flow. */
 
+/* Returns a hash of the key in 'f'. */
+static uint32_t
+ovn_flow_hash(const struct ovn_flow *f)
+{
+    return hash_2words((f->table_id << 16) | f->priority,
+                       match_hash(&f->match, 0));
+
+}
+
 /* Duplicate an ovn_flow structure. */
 struct ovn_flow *
 ofctrl_dup_flow(struct ovn_flow *src)
@@ -711,60 +611,26 @@ ofctrl_dup_flow(struct ovn_flow *src)
     dst->match = src->match;
     dst->ofpacts = xmemdup(src->ofpacts, src->ofpacts_len);
     dst->ofpacts_len = src->ofpacts_len;
-    dst->uuid = src->uuid;
-    dst->match_hmap_node.hash = ovn_flow_match_hash(dst);
-    dst->uuid_hindex_node.hash = uuid_hash(&src->uuid);
+    dst->hmap_node.hash = ovn_flow_hash(dst);
     return dst;
 }
 
-/* Returns a hash of the match key in 'f'. */
-static uint32_t
-ovn_flow_match_hash(const struct ovn_flow *f)
-{
-    return hash_2words((f->table_id << 16) | f->priority,
-                       match_hash(&f->match, 0));
-}
-
-/* Compare two flows and return -1, 0, 1 based on whether a if less than,
- * equal to or greater than b. */
-static int
-ovn_flow_compare_flows(struct ovn_flow *a, struct ovn_flow *b)
-{
-    return uuid_compare_3way(&a->uuid, &b->uuid);
-}
-
-/* Given a list of ovn_flows, goes through the list and returns
- * a single flow, in a deterministic way. */
+/* Finds and returns an ovn_flow in 'flow_table' whose key is identical to
+ * 'target''s key, or NULL if there is none. */
 static struct ovn_flow *
-ovn_flow_select_from_list(struct ovs_list *flows)
-{
-    struct ovn_flow *candidate;
-    struct ovn_flow *answer = NULL;
-    LIST_FOR_EACH (candidate, list_node, flows) {
-        if (!answer || ovn_flow_compare_flows(candidate, answer) < 0) {
-            answer = candidate;
-        }
-    }
-    return answer;
-}
-
-/* Initializes and files in the supplied list with ovn_flows from 'flow_table'
- * whose key is identical to 'target''s key. */
-static void
-ovn_flow_lookup(struct hmap *flow_table, const struct ovn_flow *target,
-                struct ovs_list *answer)
+ovn_flow_lookup(struct hmap *flow_table, const struct ovn_flow *target)
 {
     struct ovn_flow *f;
 
-    ovs_list_init(answer);
-    HMAP_FOR_EACH_WITH_HASH (f, match_hmap_node, target->match_hmap_node.hash,
+    HMAP_FOR_EACH_WITH_HASH (f, hmap_node, target->hmap_node.hash,
                              flow_table) {
         if (f->table_id == target->table_id
             && f->priority == target->priority
             && match_equal(&f->match, &target->match)) {
-            ovs_list_push_back(answer, &f->list_node);
+            return f;
         }
     }
+    return NULL;
 }
 
 static char *
@@ -801,28 +667,15 @@ ovn_flow_destroy(struct ovn_flow *f)
 /* Flow tables of struct ovn_flow. */
 
 void
-ovn_flow_table_clear(void)
+ovn_flow_table_clear(struct hmap *flow_table)
 {
     struct ovn_flow *f, *next;
-    HMAP_FOR_EACH_SAFE (f, next, match_hmap_node, &match_flow_table) {
-        hmap_remove(&match_flow_table, &f->match_hmap_node);
-        hindex_remove(&uuid_flow_table, &f->uuid_hindex_node);
-        ovn_flow_destroy(f);
-    }
-
-    HMAP_FOR_EACH_SAFE (f, next, match_hmap_node, &installed_flows) {
-        hmap_remove(&installed_flows, &f->match_hmap_node);
+    HMAP_FOR_EACH_SAFE (f, next, hmap_node, flow_table) {
+        hmap_remove(&match_flow_table, &f->hmap_node);
         ovn_flow_destroy(f);
     }
 }
 
-static void
-ovn_flow_table_destroy(void)
-{
-    ovn_flow_table_clear();
-    hmap_destroy(&match_flow_table);
-    hindex_destroy(&uuid_flow_table);
-}
 
 /* Flow table update. */
 
@@ -912,7 +765,7 @@ add_group_mod(const struct ofputil_group_mod *gm, struct ovs_list *msgs)
  *
  * This should be called after ofctrl_run() within the main loop. */
 void
-ofctrl_put(int64_t nb_cfg)
+ofctrl_put(struct hmap *flow_table, int64_t nb_cfg)
 {
     /* The flow table can be updated if the connection to the switch is up and
      * in the correct state and not backlogged with existing flow_mods.  (Our
@@ -960,10 +813,9 @@ ofctrl_put(int64_t nb_cfg)
      * longer desired, delete them; if any of them should have different
      * actions, update them. */
     struct ovn_flow *i, *next;
-    HMAP_FOR_EACH_SAFE (i, next, match_hmap_node, &installed_flows) {
-        struct ovs_list matches;
-        ovn_flow_lookup(&match_flow_table, i, &matches);
-        if (ovs_list_is_empty(&matches)) {
+    HMAP_FOR_EACH_SAFE (i, next, hmap_node, &installed_flows) {
+        struct ovn_flow *d = ovn_flow_lookup(flow_table, i);
+        if (!d) {
             /* Installed flow is no longer desirable.  Delete it from the
              * switch and from installed_flows. */
             struct ofputil_flow_mod fm = {
@@ -975,19 +827,9 @@ ofctrl_put(int64_t nb_cfg)
             add_flow_mod(&fm, &msgs);
             ovn_flow_log(i, "removing installed");
 
-            hmap_remove(&installed_flows, &i->match_hmap_node);
+            hmap_remove(&installed_flows, &i->hmap_node);
             ovn_flow_destroy(i);
         } else {
-            /* Since we still have desired flows that match this key,
-             * select one and compare both its actions and uuid.
-             * If the actions aren't the same, queue and update
-             * action for the install flow.  If the uuid has changed
-             * update that as well. */
-            struct ovn_flow *d = ovn_flow_select_from_list(&matches);
-            if (!uuid_equals(&i->uuid, &d->uuid)) {
-                /* Update installed flow's UUID. */
-                i->uuid = d->uuid;
-            }
             if (!ofpacts_equal(i->ofpacts, i->ofpacts_len,
                                d->ofpacts, d->ofpacts_len)) {
                 /* Update actions in installed flow. */
@@ -1010,36 +852,26 @@ ofctrl_put(int64_t nb_cfg)
         }
     }
 
-    /* Iterate through the desired flows and add those that aren't found
-     * in the installed flow table. */
-    struct ovn_flow *c;
-    HMAP_FOR_EACH (c, match_hmap_node, &match_flow_table) {
-        struct ovs_list matches;
-        ovn_flow_lookup(&installed_flows, c, &matches);
-        if (ovs_list_is_empty(&matches)) {
-            /* We have a key that isn't in the installed flows, so
-             * look back into the desired flow list for all flows
-             * that match this key, and select the one to be installed. */
-            struct ovs_list candidates;
-            ovn_flow_lookup(&match_flow_table, c, &candidates);
-            struct ovn_flow *d = ovn_flow_select_from_list(&candidates);
-            /* Send flow_mod to add flow. */
-            struct ofputil_flow_mod fm = {
-                .match = d->match,
-                .priority = d->priority,
-                .table_id = d->table_id,
-                .ofpacts = d->ofpacts,
-                .ofpacts_len = d->ofpacts_len,
-                .command = OFPFC_ADD,
-            };
-            add_flow_mod(&fm, &msgs);
-            ovn_flow_log(d, "adding installed");
-
-            /* Copy 'd' from 'flow_table' to installed_flows. */
-            struct ovn_flow *new_node = ofctrl_dup_flow(d);
-            hmap_insert(&installed_flows, &new_node->match_hmap_node,
-                        new_node->match_hmap_node.hash);
-        }
+    /* The previous loop removed from 'flow_table' all of the flows that are
+     * already installed.  Thus, any flows remaining in 'flow_table' need to
+     * be added to the flow table. */
+    struct ovn_flow *d;
+    HMAP_FOR_EACH_SAFE (d, next, hmap_node, flow_table) {
+        /* Send flow_mod to add flow. */
+        struct ofputil_flow_mod fm = {
+            .match = d->match,
+            .priority = d->priority,
+            .table_id = d->table_id,
+            .ofpacts = d->ofpacts,
+            .ofpacts_len = d->ofpacts_len,
+            .command = OFPFC_ADD,
+        };
+        add_flow_mod(&fm, &msgs);
+        ovn_flow_log(d, "adding installed");
+
+        /* Move 'd' from 'flow_table' to installed_flows. */
+        hmap_remove(flow_table, &d->hmap_node);
+        hmap_insert(&installed_flows, &d->hmap_node, d->hmap_node.hash);
     }
 
     /* Iterate through the installed groups from previous runs. If they
diff --git a/ovn/controller/ofctrl.h b/ovn/controller/ofctrl.h
index d21a7fe..1eab72f 100644
--- a/ovn/controller/ofctrl.h
+++ b/ovn/controller/ofctrl.h
@@ -32,7 +32,7 @@ struct group_table;
 /* Interface for OVN main loop. */
 void ofctrl_init(struct group_table *group_table);
 enum mf_field_id ofctrl_run(const struct ovsrec_bridge *br_int);
-void ofctrl_put(int64_t nb_cfg);
+void ofctrl_put(struct hmap *flow_table, int64_t nb_cfg);
 void ofctrl_wait(void);
 void ofctrl_destroy(void);
 int64_t ofctrl_get_cur_cfg(void);
@@ -40,19 +40,13 @@ int64_t ofctrl_get_cur_cfg(void);
 struct ovn_flow *ofctrl_dup_flow(struct ovn_flow *source);
 
 /* Flow table interfaces to the rest of ovn-controller. */
-void ofctrl_add_flow(uint8_t table_id, uint16_t priority,
-                     const struct match *, const struct ofpbuf *ofpacts,
-                     const struct uuid *uuid);
-
-void ofctrl_remove_flows(const struct uuid *uuid);
-
-void ofctrl_set_flow(uint8_t table_id, uint16_t priority,
-                     const struct match *, const struct ofpbuf *ofpacts,
-                     const struct uuid *uuid);
+void ofctrl_add_flow(struct hmap *desired_flows, uint8_t table_id,
+                     uint16_t priority, const struct match *,
+                     const struct ofpbuf *ofpacts);
 
 void ofctrl_flow_table_clear(void);
 
-void ovn_flow_table_clear(void);
+void ovn_flow_table_clear(struct hmap *);
 
 void ovn_group_table_clear(struct group_table *group_table,
                            bool existing);
diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c
index 66a364f..414e6b5 100644
--- a/ovn/controller/ovn-controller.c
+++ b/ovn/controller/ovn-controller.c
@@ -309,9 +309,6 @@ get_nb_cfg(struct ovsdb_idl *idl)
 static struct hmap local_datapaths = HMAP_INITIALIZER(&local_datapaths);
 static struct hmap patched_datapaths = HMAP_INITIALIZER(&patched_datapaths);
 
-static struct lport_index lports;
-static struct mcgroup_index mcgroups;
-
 /* Contains the names of all logical ports currently bound to the chassis
  * managed by this instance of ovn-controller. The contents are managed
  * in binding.c, but consumed elsewhere. */
@@ -354,9 +351,6 @@ main(int argc, char *argv[])
     pinctrl_init();
     lflow_init();
 
-    lport_index_init(&lports);
-    mcgroup_index_init(&mcgroups);
-
     /* Connect to OVS OVSDB instance.  We do not monitor all tables by
      * default, so modules must register their interest explicitly.  */
     struct ovsdb_idl_loop ovs_idl_loop = OVSDB_IDL_LOOP_INITIALIZER(
@@ -412,9 +406,6 @@ main(int argc, char *argv[])
             free(ovnsb_remote);
             ovnsb_remote = new_ovnsb_remote;
             ovsdb_idl_set_remote(ovnsb_idl_loop.idl, ovnsb_remote, true);
-            binding_reset_processing();
-            lport_index_clear(&lports);
-            mcgroup_index_clear(&mcgroups);
         } else {
             free(new_ovnsb_remote);
         }
@@ -443,8 +434,10 @@ main(int argc, char *argv[])
             patch_run(&ctx, br_int, chassis_id, &local_datapaths,
                       &patched_datapaths);
 
-            lport_index_fill(&lports, ctx.ovnsb_idl);
-            mcgroup_index_fill(&mcgroups, ctx.ovnsb_idl);
+            static struct lport_index lports;
+            static struct mcgroup_index mcgroups;
+            lport_index_init(&lports, ctx.ovnsb_idl);
+            mcgroup_index_init(&mcgroups, ctx.ovnsb_idl);
 
             enum mf_field_id mff_ovn_geneve = ofctrl_run(br_int);
 
@@ -452,20 +445,25 @@ main(int argc, char *argv[])
             update_ct_zones(&all_lports, &patched_datapaths, &ct_zones,
                             ct_zone_bitmap);
 
+            struct hmap flow_table = HMAP_INITIALIZER(&flow_table);
             lflow_run(&ctx, &lports, &mcgroups, &local_datapaths,
-                      &patched_datapaths, &group_table, &ct_zones);
+                      &patched_datapaths, &group_table, &ct_zones,
+                      &flow_table);
 
             physical_run(&ctx, mff_ovn_geneve,
-                         br_int, chassis_id, &ct_zones,
+                         br_int, chassis_id, &ct_zones, &flow_table,
                          &local_datapaths, &patched_datapaths);
 
-            ofctrl_put(get_nb_cfg(ctx.ovnsb_idl));
+            ofctrl_put(&flow_table, get_nb_cfg(ctx.ovnsb_idl));
+            hmap_destroy(&flow_table);
             if (ctx.ovnsb_idl_txn) {
                 int64_t cur_cfg = ofctrl_get_cur_cfg();
                 if (cur_cfg && cur_cfg != chassis->nb_cfg) {
                     sbrec_chassis_set_nb_cfg(chassis, cur_cfg);
                 }
             }
+            mcgroup_index_destroy(&mcgroups);
+            lport_index_destroy(&lports);
         }
 
         unixctl_server_run(unixctl);
diff --git a/ovn/controller/patch.c b/ovn/controller/patch.c
index 02bf450..c8e47b4 100644
--- a/ovn/controller/patch.c
+++ b/ovn/controller/patch.c
@@ -95,9 +95,6 @@ create_patch_port(struct controller_ctx *ctx,
     ovsrec_bridge_verify_ports(src);
     ovsrec_bridge_set_ports(src, ports, src->n_ports + 1);
 
-    lport_index_reset();
-    mcgroup_index_reset();
-    lflow_reset_processing();
     free(ports);
 }
 
@@ -130,9 +127,6 @@ remove_port(struct controller_ctx *ctx,
             return;
         }
     }
-    lport_index_reset();
-    mcgroup_index_reset();
-    lflow_reset_processing();
 }
 
 /* Obtains external-ids:ovn-bridge-mappings from OVSDB and adds patch ports for
diff --git a/ovn/controller/physical.c b/ovn/controller/physical.c
index 23e3e2c..1898a2a 100644
--- a/ovn/controller/physical.c
+++ b/ovn/controller/physical.c
@@ -68,16 +68,6 @@ static struct hmap port_binding_uuids = HMAP_INITIALIZER(&port_binding_uuids);
 static struct hmap multicast_group_uuids =
     HMAP_INITIALIZER(&multicast_group_uuids);
 
-/* UUID to identify OF flows not associated with ovsdb rows. */
-static struct uuid *hc_uuid = NULL;
-static bool full_binding_processing = false;
-
-void
-physical_reset_processing(void)
-{
-    full_binding_processing = true;
-}
-
 /* Maps from a chassis to the OpenFlow port number of the tunnel that can be
  * used to reach that chassis. */
 struct chassis_tunnel {
@@ -179,7 +169,8 @@ consider_port_binding(enum mf_field_id mff_ovn_geneve,
                       struct hmap *local_datapaths,
                       struct hmap *patched_datapaths,
                       const struct sbrec_port_binding *binding,
-                      struct ofpbuf *ofpacts_p)
+                      struct ofpbuf *ofpacts_p,
+                      struct hmap *flow_table)
 {
     /* Skip the port binding if the port is on a datapath that is neither
      * local nor with any logical patch port connected, because local ports
@@ -343,9 +334,8 @@ consider_port_binding(enum mf_field_id mff_ovn_geneve,
 
         /* Resubmit to first logical ingress pipeline table. */
         put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, ofpacts_p);
-        ofctrl_add_flow(OFTABLE_PHY_TO_LOG,
-                        tag ? 150 : 100, &match, ofpacts_p,
-                        &binding->header_.uuid);
+        ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG,
+                        tag ? 150 : 100, &match, ofpacts_p);
 
         if (!tag && (!strcmp(binding->type, "localnet")
                      || !strcmp(binding->type, "l2gateway"))) {
@@ -355,8 +345,7 @@ consider_port_binding(enum mf_field_id mff_ovn_geneve,
              * action. */
             ofpbuf_pull(ofpacts_p, ofpacts_orig_size);
             match_set_dl_tci_masked(&match, 0, htons(VLAN_CFI));
-            ofctrl_add_flow(0, 100, &match, ofpacts_p,
-                            &binding->header_.uuid);
+            ofctrl_add_flow(flow_table, 0, 100, &match, ofpacts_p);
         }
 
         /* Table 33, priority 100.
@@ -386,8 +375,8 @@ consider_port_binding(enum mf_field_id mff_ovn_geneve,
 
         /* Resubmit to table 34. */
         put_resubmit(OFTABLE_CHECK_LOOPBACK, ofpacts_p);
-        ofctrl_add_flow(OFTABLE_LOCAL_OUTPUT, 100,
-                        &match, ofpacts_p, &binding->header_.uuid);
+        ofctrl_add_flow(flow_table, OFTABLE_LOCAL_OUTPUT, 100,
+                        &match, ofpacts_p);
 
         /* Table 34, Priority 100.
          * =======================
@@ -401,8 +390,8 @@ consider_port_binding(enum mf_field_id mff_ovn_geneve,
                              0, MLF_ALLOW_LOOPBACK);
         match_set_reg(&match, MFF_LOG_INPORT - MFF_REG0, port_key);
         match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key);
-        ofctrl_add_flow(OFTABLE_CHECK_LOOPBACK, 100,
-                        &match, ofpacts_p, &binding->header_.uuid);
+        ofctrl_add_flow(flow_table, OFTABLE_CHECK_LOOPBACK, 100,
+                        &match, ofpacts_p);
 
         /* Table 64, Priority 100.
          * =======================
@@ -422,8 +411,8 @@ consider_port_binding(enum mf_field_id mff_ovn_geneve,
         put_load(0, MFF_IN_PORT, 0, 16, ofpacts_p);
         put_resubmit(OFTABLE_LOG_TO_PHY, ofpacts_p);
         put_stack(MFF_IN_PORT, ofpact_put_STACK_POP(ofpacts_p));
-        ofctrl_add_flow(OFTABLE_SAVE_INPORT, 100,
-                        &match, ofpacts_p, &binding->header_.uuid);
+        ofctrl_add_flow(flow_table, OFTABLE_SAVE_INPORT, 100,
+                        &match, ofpacts_p);
 
         /* Table 65, Priority 100.
          * =======================
@@ -457,8 +446,8 @@ consider_port_binding(enum mf_field_id mff_ovn_geneve,
             ofpact_put_STRIP_VLAN(ofpacts_p);
             put_stack(MFF_IN_PORT, ofpact_put_STACK_POP(ofpacts_p));
         }
-        ofctrl_add_flow(OFTABLE_LOG_TO_PHY, 100,
-                        &match, ofpacts_p, &binding->header_.uuid);
+        ofctrl_add_flow(flow_table, OFTABLE_LOG_TO_PHY, 100,
+                        &match, ofpacts_p);
     } else if (!tun) {
         /* Remote port connected by localnet port */
         /* Table 33, priority 100.
@@ -480,8 +469,8 @@ consider_port_binding(enum mf_field_id mff_ovn_geneve,
 
         /* Resubmit to table 33. */
         put_resubmit(OFTABLE_LOCAL_OUTPUT, ofpacts_p);
-        ofctrl_add_flow(OFTABLE_LOCAL_OUTPUT, 100,
-                        &match, ofpacts_p, &binding->header_.uuid);
+        ofctrl_add_flow(flow_table, OFTABLE_LOCAL_OUTPUT, 100,
+                        &match, ofpacts_p);
     } else {
         /* Remote port connected by tunnel */
 
@@ -506,8 +495,8 @@ consider_port_binding(enum mf_field_id mff_ovn_geneve,
 
         /* Resubmit to table 33. */
         put_resubmit(OFTABLE_LOCAL_OUTPUT, ofpacts_p);
-        ofctrl_add_flow(OFTABLE_REMOTE_OUTPUT, 150, &match, ofpacts_p,
-                        &binding->header_.uuid);
+        ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 150, &match,
+                        ofpacts_p);
 
 
         match_init_catchall(&match);
@@ -522,8 +511,8 @@ consider_port_binding(enum mf_field_id mff_ovn_geneve,
 
         /* Output to tunnel. */
         ofpact_put_OUTPUT(ofpacts_p)->port = ofport;
-        ofctrl_add_flow(OFTABLE_REMOTE_OUTPUT, 100,
-                        &match, ofpacts_p, &binding->header_.uuid);
+        ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 100,
+                        &match, ofpacts_p);
     }
 }
 
@@ -533,7 +522,8 @@ consider_mc_group(enum mf_field_id mff_ovn_geneve,
                   struct hmap *local_datapaths,
                   const struct sbrec_multicast_group *mc,
                   struct ofpbuf *ofpacts_p,
-                  struct ofpbuf *remote_ofpacts_p)
+                  struct ofpbuf *remote_ofpacts_p,
+                  struct hmap *flow_table)
 {
     struct sset remote_chassis = SSET_INITIALIZER(&remote_chassis);
     struct match match;
@@ -601,8 +591,8 @@ consider_mc_group(enum mf_field_id mff_ovn_geneve,
          * group as the logical output port. */
         put_load(mc->tunnel_key, MFF_LOG_OUTPORT, 0, 32, ofpacts_p);
 
-        ofctrl_add_flow(OFTABLE_LOCAL_OUTPUT, 100,
-                        &match, ofpacts_p, &mc->header_.uuid);
+        ofctrl_add_flow(flow_table, OFTABLE_LOCAL_OUTPUT, 100,
+                        &match, ofpacts_p);
     }
 
     /* Table 32, priority 100.
@@ -639,52 +629,19 @@ consider_mc_group(enum mf_field_id mff_ovn_geneve,
             if (local_ports) {
                 put_resubmit(OFTABLE_LOCAL_OUTPUT, remote_ofpacts_p);
             }
-            ofctrl_add_flow(OFTABLE_REMOTE_OUTPUT, 100,
-                            &match, remote_ofpacts_p, &mc->header_.uuid);
+            ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 100,
+                            &match, remote_ofpacts_p);
         }
     }
     sset_destroy(&remote_chassis);
 }
 
-static bool
-find_uuid_in_hmap(struct hmap *hmap_p, struct uuid *uuid)
-{
-    struct uuid_hash_node *candidate;
-    HMAP_FOR_EACH_WITH_HASH (candidate, node, uuid_hash(uuid), hmap_p) {
-        if (uuid_equals(uuid, &candidate->uuid)) {
-            return true;
-        }
-    }
-    return false;
-}
-
-/* Deletes the flows whose UUIDs are in 'old' but not 'new', and then replaces
- * 'old' by 'new'. */
-static void
-rationalize_hmap_and_delete_flows(struct hmap *old, struct hmap *new)
-{
-    struct uuid_hash_node *uuid_node, *old_node;
-    HMAP_FOR_EACH_SAFE (uuid_node, old_node, node, old) {
-        if (!find_uuid_in_hmap(new, &uuid_node->uuid)) {
-            ofctrl_remove_flows(&uuid_node->uuid);
-        }
-    }
-    hmap_swap(old, new);
-    HMAP_FOR_EACH_POP(uuid_node, node, new) {
-        free(uuid_node);
-    }
-}
-
 void
 physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve,
              const struct ovsrec_bridge *br_int, const char *this_chassis_id,
-             const struct simap *ct_zones,
+             const struct simap *ct_zones, struct hmap *flow_table,
              struct hmap *local_datapaths, struct hmap *patched_datapaths)
 {
-    if (!hc_uuid) {
-        hc_uuid = xmalloc(sizeof(struct uuid));
-        uuid_generate(hc_uuid);
-    }
 
     /* This bool tracks physical mapping changes. */
     bool physical_map_changed = false;
@@ -822,10 +779,7 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve,
         }
     }
     if (physical_map_changed) {
-        full_binding_processing = true;
-
         /* Reprocess logical flow table immediately. */
-        lflow_reset_processing();
         poll_immediate_wake();
     }
 
@@ -835,68 +789,34 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve,
     /* Set up flows in table 0 for physical-to-logical translation and in table
      * 64 for logical-to-physical translation. */
     const struct sbrec_port_binding *binding;
-    if (full_binding_processing) {
-        struct hmap new_port_binding_uuids =
-            HMAP_INITIALIZER(&new_port_binding_uuids);
-        SBREC_PORT_BINDING_FOR_EACH (binding, ctx->ovnsb_idl) {
-            /* Because it is possible in the above code to enter this
-             * for loop without having cleared the flow table first, we
-             * should clear the old flows to avoid collisions. */
-            ofctrl_remove_flows(&binding->header_.uuid);
-            consider_port_binding(mff_ovn_geneve, ct_zones, local_datapaths,
-                                  patched_datapaths, binding, &ofpacts);
-            struct uuid_hash_node *hash_node = xzalloc(sizeof *hash_node);
-            hash_node->uuid = binding->header_.uuid;
-            hmap_insert(&new_port_binding_uuids, &hash_node->node,
-                        uuid_hash(&hash_node->uuid));
-        }
-        rationalize_hmap_and_delete_flows(&port_binding_uuids,
-                                          &new_port_binding_uuids);
-        hmap_destroy(&new_port_binding_uuids);
-        full_binding_processing = false;
-    } else {
-        SBREC_PORT_BINDING_FOR_EACH_TRACKED (binding, ctx->ovnsb_idl) {
-            if (sbrec_port_binding_is_deleted(binding)) {
-                ofctrl_remove_flows(&binding->header_.uuid);
-            } else {
-                if (!sbrec_port_binding_is_new(binding)) {
-                    ofctrl_remove_flows(&binding->header_.uuid);
-                }
-                consider_port_binding(mff_ovn_geneve, ct_zones, local_datapaths,
-                                      patched_datapaths, binding, &ofpacts);
-            }
-        }
+    SBREC_PORT_BINDING_FOR_EACH (binding, ctx->ovnsb_idl) {
+        /* Because it is possible in the above code to enter this
+         * for loop without having cleared the flow table first, we
+         * should clear the old flows to avoid collisions. */
+        consider_port_binding(mff_ovn_geneve, ct_zones, local_datapaths,
+                              patched_datapaths, binding, &ofpacts,
+                              flow_table);
+        struct uuid_hash_node *hash_node = xzalloc(sizeof *hash_node);
+        hash_node->uuid = binding->header_.uuid;
     }
 
     /* Handle output to multicast groups, in tables 32 and 33. */
     const struct sbrec_multicast_group *mc;
     struct ofpbuf remote_ofpacts;
     ofpbuf_init(&remote_ofpacts, 0);
-    struct hmap new_multicast_group_uuids =
-        HMAP_INITIALIZER(&new_multicast_group_uuids);
     SBREC_MULTICAST_GROUP_FOR_EACH (mc, ctx->ovnsb_idl) {
         /* As multicast groups are always reprocessed each time,
          * the first step is to clean the old flows for the group
          * so that we avoid warning messages on collisions. */
-        ofctrl_remove_flows(&mc->header_.uuid);
         consider_mc_group(mff_ovn_geneve, ct_zones,
-                          local_datapaths, mc, &ofpacts, &remote_ofpacts);
+                          local_datapaths, mc, &ofpacts, &remote_ofpacts,
+                          flow_table);
         struct uuid_hash_node *hash_node = xzalloc(sizeof *hash_node);
         hash_node->uuid = mc->header_.uuid;
-        hmap_insert(&new_multicast_group_uuids, &hash_node->node,
-                    uuid_hash(&hash_node->uuid));
     }
-    rationalize_hmap_and_delete_flows(&multicast_group_uuids,
-                                      &new_multicast_group_uuids);
-    hmap_destroy(&new_multicast_group_uuids);
 
     ofpbuf_uninit(&remote_ofpacts);
 
-    /* Because flows using the hard-coded uuid are recalculated each
-     * cycle, let's first remove the old flows to avoid duplicate flow
-     * warnings. */
-    ofctrl_remove_flows(hc_uuid);
-
     /* Table 0, priority 100.
      * ======================
      *
@@ -932,8 +852,7 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve,
 
         put_resubmit(OFTABLE_LOCAL_OUTPUT, &ofpacts);
 
-        ofctrl_add_flow(OFTABLE_PHY_TO_LOG, 100, &match, &ofpacts,
-                        hc_uuid);
+        ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG, 100, &match, &ofpacts);
     }
 
     /* Add flows for VXLAN encapsulations.  Due to the limited amount of
@@ -965,7 +884,8 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve,
             put_load(1, MFF_LOG_FLAGS, MLF_RCV_FROM_VXLAN_BIT, 1, &ofpacts);
             put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, &ofpacts);
 
-            ofctrl_add_flow(OFTABLE_PHY_TO_LOG, 100, &match, &ofpacts, hc_uuid);
+            ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG, 100, &match,
+                            &ofpacts);
         }
     }
 
@@ -978,7 +898,7 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve,
     match_init_catchall(&match);
     ofpbuf_clear(&ofpacts);
     put_resubmit(OFTABLE_LOCAL_OUTPUT, &ofpacts);
-    ofctrl_add_flow(OFTABLE_REMOTE_OUTPUT, 0, &match, &ofpacts, hc_uuid);
+    ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 0, &match, &ofpacts);
 
     /* Table 34, Priority 0.
      * =======================
@@ -992,7 +912,7 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve,
         put_load(0, MFF_REG0 + i, 0, 32, &ofpacts);
     }
     put_resubmit(OFTABLE_LOG_EGRESS_PIPELINE, &ofpacts);
-    ofctrl_add_flow(OFTABLE_CHECK_LOOPBACK, 0, &match, &ofpacts, hc_uuid);
+    ofctrl_add_flow(flow_table, OFTABLE_CHECK_LOOPBACK, 0, &match, &ofpacts);
 
     /* Table 64, Priority 0.
      * =======================
@@ -1002,7 +922,7 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve,
     match_init_catchall(&match);
     ofpbuf_clear(&ofpacts);
     put_resubmit(OFTABLE_LOG_TO_PHY, &ofpacts);
-    ofctrl_add_flow(OFTABLE_SAVE_INPORT, 0, &match, &ofpacts, hc_uuid);
+    ofctrl_add_flow(flow_table, OFTABLE_SAVE_INPORT, 0, &match, &ofpacts);
 
     ofpbuf_uninit(&ofpacts);
 
diff --git a/ovn/controller/physical.h b/ovn/controller/physical.h
index 28845b2..86ce93c 100644
--- a/ovn/controller/physical.h
+++ b/ovn/controller/physical.h
@@ -43,8 +43,7 @@ struct simap;
 void physical_register_ovs_idl(struct ovsdb_idl *);
 void physical_run(struct controller_ctx *, enum mf_field_id mff_ovn_geneve,
                   const struct ovsrec_bridge *br_int, const char *chassis_id,
-                  const struct simap *ct_zones,
+                  const struct simap *ct_zones, struct hmap *flow_table,
                   struct hmap *local_datapaths, struct hmap *patched_datapaths);
-void physical_reset_processing(void);
 
 #endif /* ovn/physical.h */
