diff --git a/northd/en-sb-sync.c b/northd/en-sb-sync.c
index c3ba315df..23884d394 100644
--- a/northd/en-sb-sync.c
+++ b/northd/en-sb-sync.c
@@ -22,6 +22,7 @@
 #include "openvswitch/util.h"
 
 #include "en-sb-sync.h"
+#include "include/ovn/expr.h"
 #include "lib/inc-proc-eng.h"
 #include "lib/lb.h"
 #include "lib/ovn-nb-idl.h"
@@ -41,6 +42,13 @@ static void sync_address_sets(const struct nbrec_address_set_table *,
                               const struct sbrec_address_set_table *,
                               struct ovsdb_idl_txn *ovnsb_txn,
                               struct hmap *datapaths);
+static const struct sbrec_address_set *sb_address_set_lookup_by_name(
+    struct ovsdb_idl_index *, const char *name);
+static void update_sb_addr_set(char **nb_addresses, size_t n_addresses,
+                               const struct sbrec_address_set *);
+static void build_port_group_address_set(const struct nbrec_port_group *,
+                                         struct svec *ipv4_addrs,
+                                         struct svec *ipv6_addrs);
 
 void *
 en_sb_sync_init(struct engine_node *node OVS_UNUSED,
@@ -94,6 +102,98 @@ en_address_set_sync_cleanup(void *data OVS_UNUSED)
 
 }
 
+bool
+address_set_sync_nb_address_set_handler(struct engine_node *node OVS_UNUSED,
+                                        void *data OVS_UNUSED)
+{
+    const struct nbrec_address_set_table *nb_address_set_table =
+        EN_OVSDB_GET(engine_get_input("NB_address_set", node));
+
+    /* Return false if an address set is created or deleted.
+     * Handle I-P for only updated address sets. */
+    const struct nbrec_address_set *nb_addr_set;
+    NBREC_ADDRESS_SET_TABLE_FOR_EACH_TRACKED (nb_addr_set,
+                                              nb_address_set_table) {
+        if (nbrec_address_set_is_new(nb_addr_set) ||
+                nbrec_address_set_is_deleted(nb_addr_set)) {
+            return false;
+        }
+    }
+
+    struct ovsdb_idl_index *sbrec_address_set_by_name =
+        engine_ovsdb_node_get_index(
+                engine_get_input("SB_address_set", node),
+                "sbrec_address_set_by_name");
+
+    NBREC_ADDRESS_SET_TABLE_FOR_EACH_TRACKED (nb_addr_set,
+                                              nb_address_set_table) {
+        const struct sbrec_address_set *sb_addr_set =
+            sb_address_set_lookup_by_name(sbrec_address_set_by_name,
+                                          nb_addr_set->name);
+        if (!sb_addr_set) {
+            return false;
+        }
+        update_sb_addr_set(nb_addr_set->addresses, nb_addr_set->n_addresses,
+                           sb_addr_set);
+    }
+
+    return true;
+}
+
+bool
+address_set_sync_nb_port_group_handler(struct engine_node *node OVS_UNUSED,
+                                       void *data OVS_UNUSED)
+{
+    const struct nbrec_port_group *nb_pg;
+    const struct nbrec_port_group_table *nb_port_group_table =
+        EN_OVSDB_GET(engine_get_input("NB_port_group", node));
+    NBREC_PORT_GROUP_TABLE_FOR_EACH_TRACKED (nb_pg, nb_port_group_table) {
+        if (nbrec_port_group_is_new(nb_pg) ||
+                nbrec_port_group_is_deleted(nb_pg)) {
+            return false;
+        }
+    }
+
+    struct ovsdb_idl_index *sbrec_address_set_by_name =
+        engine_ovsdb_node_get_index(
+                engine_get_input("SB_address_set", node),
+                "sbrec_address_set_by_name");
+    NBREC_PORT_GROUP_TABLE_FOR_EACH_TRACKED (nb_pg, nb_port_group_table) {
+        char *ipv4_addrs_name = xasprintf("%s_ip4", nb_pg->name);
+        const struct sbrec_address_set *sb_addr_set_v4 =
+            sb_address_set_lookup_by_name(sbrec_address_set_by_name,
+                                          ipv4_addrs_name);
+        if (!sb_addr_set_v4) {
+            free(ipv4_addrs_name);
+            return false;
+        }
+        char *ipv6_addrs_name = xasprintf("%s_ip6", nb_pg->name);
+        const struct sbrec_address_set *sb_addr_set_v6 =
+            sb_address_set_lookup_by_name(sbrec_address_set_by_name,
+                                          ipv6_addrs_name);
+        if (!sb_addr_set_v6) {
+            free(ipv4_addrs_name);
+            free(ipv6_addrs_name);
+            return false;
+        }
+
+        struct svec ipv4_addrs = SVEC_EMPTY_INITIALIZER;
+        struct svec ipv6_addrs = SVEC_EMPTY_INITIALIZER;
+        build_port_group_address_set(nb_pg, &ipv4_addrs, &ipv6_addrs);
+        update_sb_addr_set(ipv4_addrs.names, ipv4_addrs.n,
+                           sb_addr_set_v4);
+        update_sb_addr_set(ipv6_addrs.names, ipv6_addrs.n,
+                           sb_addr_set_v6);
+
+        free(ipv4_addrs_name);
+        free(ipv6_addrs_name);
+        svec_destroy(&ipv4_addrs);
+        svec_destroy(&ipv6_addrs);
+    }
+
+    return true;
+}
+
 /* static functions. */
 static void
 sync_address_set(struct ovsdb_idl_txn *ovnsb_txn, const char *name,
@@ -148,18 +248,7 @@ sync_address_sets(
                                      nb_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++) {
-            for (size_t j = 0; j < nb_port_group->ports[i]->n_addresses; j++) {
-                const char *addrs = nb_port_group->ports[i]->addresses[j];
-                if (!is_dynamic_lsp_address(addrs)) {
-                    split_addresses(addrs, &ipv4_addrs, &ipv6_addrs);
-                }
-            }
-            if (nb_port_group->ports[i]->dynamic_addresses) {
-                split_addresses(nb_port_group->ports[i]->dynamic_addresses,
-                                &ipv4_addrs, &ipv6_addrs);
-            }
-        }
+        build_port_group_address_set(nb_port_group, &ipv4_addrs, &ipv6_addrs);
         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(ovnsb_txn, ipv4_addrs_name,
@@ -228,3 +317,85 @@ sync_address_sets(
     }
     shash_destroy(&sb_address_sets);
 }
+
+static void
+update_sb_addr_set(char **nb_addresses, size_t n_addresses,
+                   const struct sbrec_address_set *sb_as)
+{
+    struct expr_constant_set *cs_nb_as =
+        expr_constant_set_create_integers(
+            (const char *const *) nb_addresses, n_addresses);
+    struct expr_constant_set *cs_sb_as =
+        expr_constant_set_create_integers(
+            (const char *const *) sb_as->addresses, sb_as->n_addresses);
+
+    struct expr_constant_set *addr_added = NULL;
+    struct expr_constant_set *addr_deleted = NULL;
+    expr_constant_set_integers_diff(cs_sb_as, cs_nb_as, &addr_added,
+                                    &addr_deleted);
+
+    if (addr_added && addr_added->n_values) {
+        for (size_t i = 0; i < addr_added->n_values; i++) {
+            struct ds ds = DS_EMPTY_INITIALIZER;
+            expr_constant_format(&addr_added->values[i], EXPR_C_INTEGER, &ds);
+            sbrec_address_set_update_addresses_addvalue(sb_as, ds_cstr(&ds));
+            ds_destroy(&ds);
+        }
+    }
+
+    if (addr_deleted && addr_deleted->n_values) {
+        for (size_t i = 0; i < addr_deleted->n_values; i++) {
+            struct ds ds = DS_EMPTY_INITIALIZER;
+            expr_constant_format(&addr_deleted->values[i],
+                                 EXPR_C_INTEGER, &ds);
+            sbrec_address_set_update_addresses_delvalue(sb_as, ds_cstr(&ds));
+            ds_destroy(&ds);
+        }
+    }
+
+    expr_constant_set_destroy(cs_nb_as);
+    free(cs_nb_as);
+    expr_constant_set_destroy(cs_sb_as);
+    free(cs_sb_as);
+    expr_constant_set_destroy(addr_added);
+    free(addr_added);
+    expr_constant_set_destroy(addr_deleted);
+    free(addr_deleted);
+}
+
+static void
+build_port_group_address_set(const struct nbrec_port_group *nb_port_group,
+                             struct svec *ipv4_addrs,
+                             struct svec *ipv6_addrs)
+{
+    for (size_t i = 0; i < nb_port_group->n_ports; i++) {
+        for (size_t j = 0; j < nb_port_group->ports[i]->n_addresses; j++) {
+            const char *addrs = nb_port_group->ports[i]->addresses[j];
+            if (!is_dynamic_lsp_address(addrs)) {
+                split_addresses(addrs, ipv4_addrs, ipv6_addrs);
+            }
+        }
+        if (nb_port_group->ports[i]->dynamic_addresses) {
+            split_addresses(nb_port_group->ports[i]->dynamic_addresses,
+                            ipv4_addrs, ipv6_addrs);
+        }
+    }
+}
+
+/* Finds and returns the address set with the given 'name', or NULL if no such
+ * address set exists. */
+static const struct sbrec_address_set *
+sb_address_set_lookup_by_name(struct ovsdb_idl_index *sbrec_addr_set_by_name,
+                              const char *name)
+{
+    struct sbrec_address_set *target = sbrec_address_set_index_init_row(
+        sbrec_addr_set_by_name);
+    sbrec_address_set_index_set_name(target, name);
+
+    struct sbrec_address_set *retval = sbrec_address_set_index_find(
+        sbrec_addr_set_by_name, target);
+
+    sbrec_address_set_index_destroy_row(target);
+
+    return retval;
+}
diff --git a/northd/en-sb-sync.h b/northd/en-sb-sync.h
index f99d6a9fc..a63453fe5 100644
--- a/northd/en-sb-sync.h
+++ b/northd/en-sb-sync.h
@@ -3,12 +3,18 @@
 
 #include "lib/inc-proc-eng.h"
 
+/* en_sb_sync engine node functions. */
 void *en_sb_sync_init(struct engine_node *, struct engine_arg *);
 void en_sb_sync_run(struct engine_node *, void *data);
 void en_sb_sync_cleanup(void *data);
 
+/* en_address_set_sync engine node functions. */
 void *en_address_set_sync_init(struct engine_node *, struct engine_arg *);
 void en_address_set_sync_run(struct engine_node *, void *data);
 void en_address_set_sync_cleanup(void *data);
+bool address_set_sync_nb_address_set_handler(struct engine_node *,
+                                             void *data);
+bool address_set_sync_nb_port_group_handler(struct engine_node *,
+                                            void *data);
 
 #endif
diff --git a/northd/inc-proc-northd.c b/northd/inc-proc-northd.c
index b48f53f17..e2c25046a 100644
--- a/northd/inc-proc-northd.c
+++ b/northd/inc-proc-northd.c
@@ -238,8 +238,10 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
      * the NB database tables.
      * Right now this engine only syncs the SB Address_Set table.
      */
-    engine_add_input(&en_address_set_sync, &en_nb_address_set, NULL);
-    engine_add_input(&en_address_set_sync, &en_nb_port_group, NULL);
+    engine_add_input(&en_address_set_sync, &en_nb_address_set,
+                     address_set_sync_nb_address_set_handler);
+    engine_add_input(&en_address_set_sync, &en_nb_port_group,
+                     address_set_sync_nb_port_group_handler);
     engine_add_input(&en_address_set_sync, &en_nb_load_balancer, NULL);
     engine_add_input(&en_address_set_sync, &en_nb_load_balancer_group, NULL);
     engine_add_input(&en_address_set_sync, &en_nb_logical_router, NULL);
@@ -248,8 +250,12 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
 
     /* We need the en_northd generated data as input to en_address_set_sync
      * node to access the data generated by it (eg. struct ovn_datapath).
+     * The handler is noop since en_northd always falls back to full recompute
+     * (since it has no input handlers) and it doesn't yet indicate what
+     * changed. It doesn't make sense to add NULL handler for this input,
+     * otherwise 'en_address_set_sync' will always fall back to full recompute.
      */
-    engine_add_input(&en_address_set_sync, &en_northd, NULL);
+    engine_add_input(&en_address_set_sync, &en_northd, engine_noop_handler);
 
     engine_add_input(&en_sb_sync, &en_address_set_sync, NULL);
     engine_add_input(&en_northd_output, &en_sb_sync,
@@ -300,6 +306,12 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
     engine_ovsdb_node_add_index(&en_sb_mac_binding,
                                 "sbrec_mac_binding_by_datapath",
                                 sbrec_mac_binding_by_datapath);
+
+    struct ovsdb_idl_index *sbrec_address_set_by_name
+        = ovsdb_idl_index_create1(sb->idl, &sbrec_address_set_col_name);
+    engine_ovsdb_node_add_index(&en_sb_address_set,
+                                "sbrec_address_set_by_name",
+                                sbrec_address_set_by_name);
 }
 
 void inc_proc_northd_run(struct ovsdb_idl_txn *ovnnb_txn,
