get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/1524288/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 1524288,
    "url": "http://patchwork.ozlabs.org/api/patches/1524288/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/ovn/patch/20210903122148.826196-5-mark.d.gray@redhat.com/",
    "project": {
        "id": 68,
        "url": "http://patchwork.ozlabs.org/api/projects/68/?format=api",
        "name": "Open Virtual Network development",
        "link_name": "ovn",
        "list_id": "ovs-dev.openvswitch.org",
        "list_email": "ovs-dev@openvswitch.org",
        "web_url": "http://openvswitch.org/",
        "scm_url": "",
        "webscm_url": "",
        "list_archive_url": "",
        "list_archive_url_format": "",
        "commit_url_format": ""
    },
    "msgid": "<20210903122148.826196-5-mark.d.gray@redhat.com>",
    "list_archive_url": null,
    "date": "2021-09-03T12:21:45",
    "name": "[ovs-dev,v4,4/7] northd: Introduce incremental processing for northd",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": false,
    "hash": "11f789056a24de23763746c67a134e56708c744f",
    "submitter": {
        "id": 79963,
        "url": "http://patchwork.ozlabs.org/api/people/79963/?format=api",
        "name": "Mark Gray",
        "email": "mark.d.gray@redhat.com"
    },
    "delegate": null,
    "mbox": "http://patchwork.ozlabs.org/project/ovn/patch/20210903122148.826196-5-mark.d.gray@redhat.com/mbox/",
    "series": [
        {
            "id": 260884,
            "url": "http://patchwork.ozlabs.org/api/series/260884/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/ovn/list/?series=260884",
            "date": "2021-09-03T12:21:41",
            "name": "northd: Split northd and northd incremental processing framework",
            "version": 4,
            "mbox": "http://patchwork.ozlabs.org/series/260884/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/1524288/comments/",
    "check": "fail",
    "checks": "http://patchwork.ozlabs.org/api/patches/1524288/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<ovs-dev-bounces@openvswitch.org>",
        "X-Original-To": [
            "incoming@patchwork.ozlabs.org",
            "dev@openvswitch.org"
        ],
        "Delivered-To": [
            "patchwork-incoming@bilbo.ozlabs.org",
            "ovs-dev@lists.linuxfoundation.org"
        ],
        "Authentication-Results": [
            "ozlabs.org;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=IQhT2jWy;\n\tdkim-atps=neutral",
            "ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org\n (client-ip=140.211.166.133; helo=smtp2.osuosl.org;\n envelope-from=ovs-dev-bounces@openvswitch.org; receiver=<UNKNOWN>)",
            "smtp1.osuosl.org (amavisd-new);\n dkim=fail (1024-bit key) reason=\"fail (body has been altered)\"\n header.d=redhat.com",
            "relay.mimecast.com;\n auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=mark.d.gray@redhat.com"
        ],
        "Received": [
            "from smtp2.osuosl.org (smtp2.osuosl.org [140.211.166.133])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest\n SHA256)\n\t(No client certificate requested)\n\tby ozlabs.org (Postfix) with ESMTPS id 4H1H3s5qC2z9sPf\n\tfor <incoming@patchwork.ozlabs.org>; Fri,  3 Sep 2021 22:22:17 +1000 (AEST)",
            "from localhost (localhost [127.0.0.1])\n\tby smtp2.osuosl.org (Postfix) with ESMTP id 7E4FF40807;\n\tFri,  3 Sep 2021 12:22:15 +0000 (UTC)",
            "from smtp2.osuosl.org ([127.0.0.1])\n\tby localhost (smtp2.osuosl.org [127.0.0.1]) (amavisd-new, port 10024)\n\twith ESMTP id X3y7xPuebCvL; Fri,  3 Sep 2021 12:22:10 +0000 (UTC)",
            "from lists.linuxfoundation.org (lf-lists.osuosl.org\n [IPv6:2605:bc80:3010:104::8cd3:938])\n\tby smtp2.osuosl.org (Postfix) with ESMTPS id 700E8407F4;\n\tFri,  3 Sep 2021 12:22:07 +0000 (UTC)",
            "from lf-lists.osuosl.org (localhost [127.0.0.1])\n\tby lists.linuxfoundation.org (Postfix) with ESMTP id 0A9B9C0026;\n\tFri,  3 Sep 2021 12:22:06 +0000 (UTC)",
            "from smtp1.osuosl.org (smtp1.osuosl.org [IPv6:2605:bc80:3010::138])\n by lists.linuxfoundation.org (Postfix) with ESMTP id 0C60CC002D\n for <dev@openvswitch.org>; Fri,  3 Sep 2021 12:22:05 +0000 (UTC)",
            "from localhost (localhost [127.0.0.1])\n by smtp1.osuosl.org (Postfix) with ESMTP id CAEC983E5F\n for <dev@openvswitch.org>; Fri,  3 Sep 2021 12:22:03 +0000 (UTC)",
            "from smtp1.osuosl.org ([127.0.0.1])\n by localhost (smtp1.osuosl.org [127.0.0.1]) (amavisd-new, port 10024)\n with ESMTP id YJI9A_zcx4Ss for <dev@openvswitch.org>;\n Fri,  3 Sep 2021 12:22:01 +0000 (UTC)",
            "from us-smtp-delivery-124.mimecast.com\n (us-smtp-delivery-124.mimecast.com [170.10.133.124])\n by smtp1.osuosl.org (Postfix) with ESMTPS id 9474783E5D\n for <dev@openvswitch.org>; Fri,  3 Sep 2021 12:22:01 +0000 (UTC)",
            "from mail-wm1-f72.google.com (mail-wm1-f72.google.com\n [209.85.128.72]) (Using TLS) by relay.mimecast.com with ESMTP id\n us-mta-470-RM11Wc2sN3-a7HPfA9xevg-1; Fri, 03 Sep 2021 08:21:59 -0400",
            "by mail-wm1-f72.google.com with SMTP id\n n16-20020a1c7210000000b002ea2ed60dc6so1779193wmc.0\n for <dev@openvswitch.org>; Fri, 03 Sep 2021 05:21:59 -0700 (PDT)",
            "from wsfd-netdev91.ntdv.lab.eng.bos.redhat.com\n (nat-pool-bos-t.redhat.com. [66.187.233.206])\n by smtp.gmail.com with ESMTPSA id a6sm4836612wrh.97.2021.09.03.05.21.55\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Fri, 03 Sep 2021 05:21:56 -0700 (PDT)"
        ],
        "X-Virus-Scanned": [
            "amavisd-new at osuosl.org",
            "amavisd-new at osuosl.org"
        ],
        "X-Greylist": "domain auto-whitelisted by SQLgrey-1.8.0",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n s=mimecast20190719; t=1630671720;\n h=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n to:to:cc:cc:mime-version:mime-version:content-type:content-type:\n content-transfer-encoding:content-transfer-encoding:\n in-reply-to:in-reply-to:references:references;\n bh=9FyjInpUTTq2K5+Hnnqjyg/kH4hoQ5P0N1NVtHoGPjo=;\n b=IQhT2jWy4nBs3Guje81kLRdFBuiyVeOvynIBagJUtKUFlfKrCKIcnXnvTWeZVnD9h9+GNT\n cmyVWwefKwv+CLyZOMyYrTj23hOwDugukVcZwtMrVeck84Ixy0EI5J4wF/zJFQcslx1Rl9\n huBgWglHWBsFfGymwmBvYiOBghNcmWk=",
        "X-MC-Unique": "RM11Wc2sN3-a7HPfA9xevg-1",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20161025;\n h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to\n :references:mime-version:content-transfer-encoding;\n bh=bTPJ6+YuGRo04QTPugIy6Z//DnNau3INLFQ8N4z1a18=;\n b=Zl4PwxsXJxQdgEXSP4N8zRMCjn0pXVddDoONMHURfhpkZwRRrt22rfhgv0zAjMAAJT\n q9mbsoDN97Vv/Mqb1sVcloUX94c7D5RpldP71pvc5+y2e+36HyKPSjI0xmzFpR8MIDeE\n acOeRrSu+3TYtcgRXJUbII66nPzvcB28b9Dk1MXj1nwINcS7kyOfRepWX9IDg12pbnx8\n BGfeDNnEJhkHH8c/XvIcylGKPuQGNVnmthxt+lBiwYMMG9jfjymhhNUHXB9+kf++4+nr\n q1uEtOwcNcxaRx1W2szFiBnCogYfXIwbZ7RQnzbSjXCCDxYy/4AgjfHY6j6m8SECX7Bj\n rPFA==",
        "X-Gm-Message-State": "AOAM5323LDzEoBKCaZP4T3kHeXOjuKKkU6n5+vCurgA8dxGGGxTl7Yih\n lIVB6d86y4Jc2W6Yn+WwNcdwegFS54GFhecJ2t2C4LciTLh9okyFMjoVODB+aIRc2itOnYsIMEL\n GUBNYdI8A2YKlp2/lk++9nchGhIbZjG+4S16N4dIdB8BZlnQEBviDcaWMiM54T1TOQxhl",
        "X-Received": [
            "by 2002:adf:fec8:: with SMTP id q8mr3835788wrs.218.1630671717501;\n Fri, 03 Sep 2021 05:21:57 -0700 (PDT)",
            "by 2002:adf:fec8:: with SMTP id q8mr3835738wrs.218.1630671716898;\n Fri, 03 Sep 2021 05:21:56 -0700 (PDT)"
        ],
        "X-Google-Smtp-Source": "\n ABdhPJyDZPYf/9BxAw84Rcy6zb302sIKtG0ajqDqwuYoVY5empbwABgpzaVjUI4UJgH73Mk0+SzJig==",
        "From": "Mark Gray <mark.d.gray@redhat.com>",
        "To": "dev@openvswitch.org",
        "Date": "Fri,  3 Sep 2021 08:21:45 -0400",
        "Message-Id": "<20210903122148.826196-5-mark.d.gray@redhat.com>",
        "X-Mailer": "git-send-email 2.27.0",
        "In-Reply-To": "<20210903122148.826196-1-mark.d.gray@redhat.com>",
        "References": "<20210903122148.826196-1-mark.d.gray@redhat.com>",
        "MIME-Version": "1.0",
        "X-Mimecast-Spam-Score": "0",
        "X-Mimecast-Originator": "redhat.com",
        "Cc": "numans@redhat.com",
        "Subject": "[ovs-dev] [PATCH ovn v4 4/7] northd: Introduce incremental\n\tprocessing for northd",
        "X-BeenThere": "ovs-dev@openvswitch.org",
        "X-Mailman-Version": "2.1.15",
        "Precedence": "list",
        "List-Id": "<ovs-dev.openvswitch.org>",
        "List-Unsubscribe": "<https://mail.openvswitch.org/mailman/options/ovs-dev>,\n <mailto:ovs-dev-request@openvswitch.org?subject=unsubscribe>",
        "List-Archive": "<http://mail.openvswitch.org/pipermail/ovs-dev/>",
        "List-Post": "<mailto:ovs-dev@openvswitch.org>",
        "List-Help": "<mailto:ovs-dev-request@openvswitch.org?subject=help>",
        "List-Subscribe": "<https://mail.openvswitch.org/mailman/listinfo/ovs-dev>,\n <mailto:ovs-dev-request@openvswitch.org?subject=subscribe>",
        "Content-Type": "text/plain; charset=\"us-ascii\"",
        "Content-Transfer-Encoding": "7bit",
        "Errors-To": "ovs-dev-bounces@openvswitch.org",
        "Sender": "\"dev\" <ovs-dev-bounces@openvswitch.org>"
    },
    "content": "Initial implementation adds a single node (northd). This single\nnode executes the northd processing pipeline but does not do so\nincrementally.\n\nIn order to develop incremental processing for northd, the code\nwill be organised with a .c/.h file for each I-P node following\nthe naming convention en-<node name>.c/.h. These files will\ncontain definition of the node data, the main node processing\nfunctions and change handlers (if any). The purpose of these nodes\nwill be coordination of the nodes work and implemention of the\nrelevant interfaces to plugin to the I-P framework. The actual\nwork that will be executed by the node will be organised into\na companion file or files. Ideally this file will follow the\nnaming convention of the node: e.g. en-<node name>.c is\nassociated with <node name>.c.\n\nInitial node topology sees the northd node dependent on all DB\nnodes. This will evolve over time.\n\nCo-authored-by: Numan Siddique <numans@ovn.org>\nSigned-off-by: Numan Siddique <numans@ovn.org>\nSigned-off-by: Mark Gray <mark.d.gray@redhat.com>\n---\n lib/inc-proc-eng.h       |  16 +++\n northd/automake.mk       |   4 +\n northd/en-northd.c       |  45 +++++++\n northd/en-northd.h       |  17 +++\n northd/inc-proc-northd.c | 254 +++++++++++++++++++++++++++++++++++++++\n northd/inc-proc-northd.h |  15 +++\n northd/northd.c          |  12 +-\n northd/northd.h          |  10 +-\n northd/ovn-northd.c      | 202 ++++++++++++++++++++-----------\n 9 files changed, 491 insertions(+), 84 deletions(-)\n create mode 100644 northd/en-northd.c\n create mode 100644 northd/en-northd.h\n create mode 100644 northd/inc-proc-northd.c\n create mode 100644 northd/inc-proc-northd.h",
    "diff": "diff --git a/lib/inc-proc-eng.h b/lib/inc-proc-eng.h\nindex 1ccae559dff6..a3f5a7e64287 100644\n--- a/lib/inc-proc-eng.h\n+++ b/lib/inc-proc-eng.h\n@@ -63,15 +63,22 @@\n #define ENGINE_MAX_INPUT 256\n #define ENGINE_MAX_OVSDB_INDEX 256\n \n+#include <stdbool.h>\n+#include <stdint.h>\n+\n+#include \"compiler.h\"\n+\n struct engine_context {\n     struct ovsdb_idl_txn *ovs_idl_txn;\n     struct ovsdb_idl_txn *ovnsb_idl_txn;\n+    struct ovsdb_idl_txn *ovnnb_idl_txn;\n     void *client_ctx;\n };\n \n /* Arguments to be passed to the engine at engine_init(). */\n struct engine_arg {\n     struct ovsdb_idl *sb_idl;\n+    struct ovsdb_idl *nb_idl;\n     struct ovsdb_idl *ovs_idl;\n };\n \n@@ -347,6 +354,11 @@ static void en_##DB_NAME##_##TBL_NAME##_cleanup(void *data OVS_UNUSED) \\\n #define ENGINE_FUNC_SB(TBL_NAME) \\\n     ENGINE_FUNC_OVSDB(sb, TBL_NAME)\n \n+/* Macro to define member functions of an engine node which represents\n+ * a table of OVN NB DB */\n+#define ENGINE_FUNC_NB(TBL_NAME) \\\n+    ENGINE_FUNC_OVSDB(nb, TBL_NAME)\n+\n /* Macro to define member functions of an engine node which represents\n  * a table of open_vswitch DB */\n #define ENGINE_FUNC_OVS(TBL_NAME) \\\n@@ -360,6 +372,10 @@ static void en_##DB_NAME##_##TBL_NAME##_cleanup(void *data OVS_UNUSED) \\\n #define ENGINE_NODE_SB(TBL_NAME, TBL_NAME_STR) \\\n     ENGINE_NODE_OVSDB(sb, \"SB\", TBL_NAME, TBL_NAME_STR);\n \n+/* Macro to define an engine node which represents a table of OVN NB DB */\n+#define ENGINE_NODE_NB(TBL_NAME, TBL_NAME_STR) \\\n+    ENGINE_NODE_OVSDB(nb, \"NB\", TBL_NAME, TBL_NAME_STR);\n+\n /* Macro to define an engine node which represents a table of open_vswitch\n  * DB */\n #define ENGINE_NODE_OVS(TBL_NAME, TBL_NAME_STR) \\\ndiff --git a/northd/automake.mk b/northd/automake.mk\nindex 35ad8c09d9ba..f0c1fb11c83a 100644\n--- a/northd/automake.mk\n+++ b/northd/automake.mk\n@@ -4,6 +4,10 @@ northd_ovn_northd_SOURCES = \\\n \tnorthd/northd.c \\\n \tnorthd/northd.h \\\n \tnorthd/ovn-northd.c \\\n+\tnorthd/en-northd.c \\\n+\tnorthd/en-northd.h \\\n+\tnorthd/inc-proc-northd.c \\\n+\tnorthd/inc-proc-northd.h \\\n \tnorthd/ipam.c \\\n \tnorthd/ipam.h\n northd_ovn_northd_LDADD = \\\ndiff --git a/northd/en-northd.c b/northd/en-northd.c\nnew file mode 100644\nindex 000000000000..d310fa4dd31f\n--- /dev/null\n+++ b/northd/en-northd.c\n@@ -0,0 +1,45 @@\n+/*\n+ * Licensed under the Apache License, Version 2.0 (the \"License\");\n+ * you may not use this file except in compliance with the License.\n+ * You may obtain a copy of the License at:\n+ *\n+ *     http://www.apache.org/licenses/LICENSE-2.0\n+ *\n+ * Unless required by applicable law or agreed to in writing, software\n+ * distributed under the License is distributed on an \"AS IS\" BASIS,\n+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+ * See the License for the specific language governing permissions and\n+ * limitations under the License.\n+ */\n+\n+#include <config.h>\n+\n+#include <getopt.h>\n+#include <stdlib.h>\n+#include <stdio.h>\n+\n+#include \"en-northd.h\"\n+#include \"lib/inc-proc-eng.h\"\n+#include \"northd.h\"\n+#include \"openvswitch/vlog.h\"\n+\n+VLOG_DEFINE_THIS_MODULE(en_northd);\n+\n+void en_northd_run(struct engine_node *node, void *data OVS_UNUSED)\n+{\n+    const struct engine_context *eng_ctx = engine_get_context();\n+    struct northd_context *ctx = eng_ctx->client_ctx;\n+    ovn_db_run(ctx);\n+\n+    engine_set_node_state(node, EN_UPDATED);\n+\n+}\n+void *en_northd_init(struct engine_node *node OVS_UNUSED,\n+                     struct engine_arg *arg OVS_UNUSED)\n+{\n+    return NULL;\n+}\n+\n+void en_northd_cleanup(void *data OVS_UNUSED)\n+{\n+}\ndiff --git a/northd/en-northd.h b/northd/en-northd.h\nnew file mode 100644\nindex 000000000000..0e7f76245e69\n--- /dev/null\n+++ b/northd/en-northd.h\n@@ -0,0 +1,17 @@\n+#ifndef EN_NORTHD_H\n+#define EN_NORTHD_H 1\n+\n+#include <config.h>\n+\n+#include <getopt.h>\n+#include <stdlib.h>\n+#include <stdio.h>\n+\n+#include \"lib/inc-proc-eng.h\"\n+\n+void en_northd_run(struct engine_node *node OVS_UNUSED, void *data OVS_UNUSED);\n+void *en_northd_init(struct engine_node *node OVS_UNUSED,\n+                     struct engine_arg *arg);\n+void en_northd_cleanup(void *data);\n+\n+#endif /* EN_NORTHD_H */\ndiff --git a/northd/inc-proc-northd.c b/northd/inc-proc-northd.c\nnew file mode 100644\nindex 000000000000..243af43dda71\n--- /dev/null\n+++ b/northd/inc-proc-northd.c\n@@ -0,0 +1,254 @@\n+/*\n+ * Licensed under the Apache License, Version 2.0 (the \"License\");\n+ * you may not use this file except in compliance with the License.\n+ * You may obtain a copy of the License at:\n+ *\n+ *     http://www.apache.org/licenses/LICENSE-2.0\n+ *\n+ * Unless required by applicable law or agreed to in writing, software\n+ * distributed under the License is distributed on an \"AS IS\" BASIS,\n+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+ * See the License for the specific language governing permissions and\n+ * limitations under the License.\n+ */\n+\n+#include <config.h>\n+\n+#include <getopt.h>\n+#include <stdlib.h>\n+#include <stdio.h>\n+\n+#include \"lib/inc-proc-eng.h\"\n+#include \"lib/ovn-nb-idl.h\"\n+#include \"lib/ovn-sb-idl.h\"\n+#include \"openvswitch/poll-loop.h\"\n+#include \"openvswitch/vlog.h\"\n+#include \"inc-proc-northd.h\"\n+#include \"en-northd.h\"\n+#include \"util.h\"\n+\n+VLOG_DEFINE_THIS_MODULE(inc_proc_northd);\n+\n+#define NB_NODES \\\n+    NB_NODE(nb_global, \"nb_global\") \\\n+    NB_NODE(copp, \"copp\") \\\n+    NB_NODE(logical_switch, \"logical_switch\") \\\n+    NB_NODE(logical_switch_port, \"logical_switch_port\") \\\n+    NB_NODE(forwarding_group, \"forwarding_group\") \\\n+    NB_NODE(address_set, \"address_set\") \\\n+    NB_NODE(port_group, \"port_group\") \\\n+    NB_NODE(load_balancer, \"load_balancer\") \\\n+    NB_NODE(load_balancer_health_check, \"load_balancer_health_check\") \\\n+    NB_NODE(acl, \"acl\") \\\n+    NB_NODE(logical_router, \"logical_router\") \\\n+    NB_NODE(qos, \"qos\") \\\n+    NB_NODE(meter, \"meter\") \\\n+    NB_NODE(meter_band, \"meter_band\") \\\n+    NB_NODE(logical_router_port, \"logical_router_port\") \\\n+    NB_NODE(logical_router_static_route, \"logical_router_static_route\") \\\n+    NB_NODE(logical_router_policy, \"logical_router_policy\") \\\n+    NB_NODE(nat, \"nat\") \\\n+    NB_NODE(dhcp_options, \"dhcp_options\") \\\n+    NB_NODE(connection, \"connection\") \\\n+    NB_NODE(dns, \"dns\") \\\n+    NB_NODE(ssl, \"ssl\") \\\n+    NB_NODE(gateway_chassis, \"gateway_chassis\") \\\n+    NB_NODE(ha_chassis_group, \"ha_chassis_group\") \\\n+    NB_NODE(ha_chassis, \"ha_chassis\") \\\n+    NB_NODE(bfd, \"bfd\")\n+\n+    enum nb_engine_node {\n+#define NB_NODE(NAME, NAME_STR) NB_##NAME,\n+    NB_NODES\n+#undef NB_NODE\n+    };\n+\n+/* Define engine node functions for nodes that represent NB tables\n+ *\n+ * en_nb_<TABLE_NAME>_run()\n+ * en_nb_<TABLE_NAME>_init()\n+ * en_nb_<TABLE_NAME>_cleanup()\n+ */\n+#define NB_NODE(NAME, NAME_STR) ENGINE_FUNC_NB(NAME);\n+    NB_NODES\n+#undef NB_NODE\n+\n+#define SB_NODES \\\n+    SB_NODE(sb_global, \"sb_global\") \\\n+    SB_NODE(chassis, \"chassis\") \\\n+    SB_NODE(chassis_private, \"chassis_private\") \\\n+    SB_NODE(encap, \"encap\") \\\n+    SB_NODE(address_set, \"address_set\") \\\n+    SB_NODE(port_group, \"port_group\") \\\n+    SB_NODE(logical_flow, \"logical_flow\") \\\n+    SB_NODE(logical_dp_group, \"logical_DP_group\") \\\n+    SB_NODE(multicast_group, \"multicast_group\") \\\n+    SB_NODE(meter, \"meter\") \\\n+    SB_NODE(meter_band, \"meter_band\") \\\n+    SB_NODE(datapath_binding, \"datapath_binding\") \\\n+    SB_NODE(port_binding, \"port_binding\") \\\n+    SB_NODE(mac_binding, \"mac_binding\") \\\n+    SB_NODE(dhcp_options, \"dhcp_options\") \\\n+    SB_NODE(dhcpv6_options, \"dhcpv6_options\") \\\n+    SB_NODE(connection, \"connection\") \\\n+    SB_NODE(ssl, \"ssl\") \\\n+    SB_NODE(dns, \"dns\") \\\n+    SB_NODE(rbac_role, \"rbac_role\") \\\n+    SB_NODE(rbac_permission, \"rbac_permission\") \\\n+    SB_NODE(gateway_chassis, \"gateway_chassis\") \\\n+    SB_NODE(ha_chassis, \"ha_chassis\") \\\n+    SB_NODE(ha_chassis_group, \"ha_chassis_group\") \\\n+    SB_NODE(controller_event, \"controller_event\") \\\n+    SB_NODE(ip_multicast, \"ip_multicast\") \\\n+    SB_NODE(igmp_group, \"igmp_group\") \\\n+    SB_NODE(service_monitor, \"service_monitor\") \\\n+    SB_NODE(load_balancer, \"load_balancer\") \\\n+    SB_NODE(bfd, \"bfd\") \\\n+    SB_NODE(fdb, \"fdb\")\n+\n+enum sb_engine_node {\n+#define SB_NODE(NAME, NAME_STR) SB_##NAME,\n+    SB_NODES\n+#undef SB_NODE\n+};\n+\n+/* Define engine node functions for nodes that represent SB tables\n+ *\n+ * en_sb_<TABLE_NAME>_run()\n+ * en_sb_<TABLE_NAME>_init()\n+ * en_sb_<TABLE_NAME>_cleanup()\n+ */\n+#define SB_NODE(NAME, NAME_STR) ENGINE_FUNC_SB(NAME);\n+    SB_NODES\n+#undef SB_NODE\n+\n+/* Define engine nodes for NB and SB tables\n+ *\n+ * struct engine_node en_nb_<TABLE_NAME>\n+ * struct engine_node en_sb_<TABLE_NAME>\n+ *\n+ * Define nodes as static to avoid sparse errors.\n+ */\n+#define NB_NODE(NAME, NAME_STR) static ENGINE_NODE_NB(NAME, NAME_STR);\n+    NB_NODES\n+#undef NB_NODE\n+\n+#define SB_NODE(NAME, NAME_STR) static ENGINE_NODE_SB(NAME, NAME_STR);\n+    SB_NODES\n+#undef SB_NODE\n+\n+/* Define engine nodes for other nodes. They should be defined as static to\n+ * avoid sparse errors. */\n+static ENGINE_NODE(northd, \"northd\");\n+\n+void inc_proc_northd_init(struct ovsdb_idl_loop *nb,\n+                          struct ovsdb_idl_loop *sb)\n+{\n+    /* Define relationships between nodes where first argument is dependent\n+     * on the second argument */\n+    engine_add_input(&en_northd, &en_nb_nb_global, NULL);\n+    engine_add_input(&en_northd, &en_nb_copp, NULL);\n+    engine_add_input(&en_northd, &en_nb_logical_switch, NULL);\n+    engine_add_input(&en_northd, &en_nb_logical_switch_port, NULL);\n+    engine_add_input(&en_northd, &en_nb_forwarding_group, NULL);\n+    engine_add_input(&en_northd, &en_nb_address_set, NULL);\n+    engine_add_input(&en_northd, &en_nb_port_group, NULL);\n+    engine_add_input(&en_northd, &en_nb_load_balancer, NULL);\n+    engine_add_input(&en_northd, &en_nb_load_balancer_health_check, NULL);\n+    engine_add_input(&en_northd, &en_nb_acl, NULL);\n+    engine_add_input(&en_northd, &en_nb_logical_router, NULL);\n+    engine_add_input(&en_northd, &en_nb_qos, NULL);\n+    engine_add_input(&en_northd, &en_nb_meter, NULL);\n+    engine_add_input(&en_northd, &en_nb_meter_band, NULL);\n+    engine_add_input(&en_northd, &en_nb_logical_router_port, NULL);\n+    engine_add_input(&en_northd, &en_nb_logical_router_static_route, NULL);\n+    engine_add_input(&en_northd, &en_nb_logical_router_policy, NULL);\n+    engine_add_input(&en_northd, &en_nb_nat, NULL);\n+    engine_add_input(&en_northd, &en_nb_dhcp_options, NULL);\n+    engine_add_input(&en_northd, &en_nb_connection, NULL);\n+    engine_add_input(&en_northd, &en_nb_dns, NULL);\n+    engine_add_input(&en_northd, &en_nb_ssl, NULL);\n+    engine_add_input(&en_northd, &en_nb_gateway_chassis, NULL);\n+    engine_add_input(&en_northd, &en_nb_ha_chassis_group, NULL);\n+    engine_add_input(&en_northd, &en_nb_ha_chassis, NULL);\n+    engine_add_input(&en_northd, &en_nb_bfd, NULL);\n+\n+    engine_add_input(&en_northd, &en_sb_sb_global, NULL);\n+    engine_add_input(&en_northd, &en_sb_chassis, NULL);\n+    engine_add_input(&en_northd, &en_sb_chassis_private, NULL);\n+    engine_add_input(&en_northd, &en_sb_encap, NULL);\n+    engine_add_input(&en_northd, &en_sb_address_set, NULL);\n+    engine_add_input(&en_northd, &en_sb_port_group, NULL);\n+    engine_add_input(&en_northd, &en_sb_logical_flow, NULL);\n+    engine_add_input(&en_northd, &en_sb_logical_dp_group, NULL);\n+    engine_add_input(&en_northd, &en_sb_multicast_group, NULL);\n+    engine_add_input(&en_northd, &en_sb_meter, NULL);\n+    engine_add_input(&en_northd, &en_sb_meter_band, NULL);\n+    engine_add_input(&en_northd, &en_sb_datapath_binding, NULL);\n+    engine_add_input(&en_northd, &en_sb_port_binding, NULL);\n+    engine_add_input(&en_northd, &en_sb_mac_binding, NULL);\n+    engine_add_input(&en_northd, &en_sb_dhcp_options, NULL);\n+    engine_add_input(&en_northd, &en_sb_dhcpv6_options, NULL);\n+    engine_add_input(&en_northd, &en_sb_connection, NULL);\n+    engine_add_input(&en_northd, &en_sb_ssl, NULL);\n+    engine_add_input(&en_northd, &en_sb_dns, NULL);\n+    engine_add_input(&en_northd, &en_sb_rbac_role, NULL);\n+    engine_add_input(&en_northd, &en_sb_rbac_permission, NULL);\n+    engine_add_input(&en_northd, &en_sb_gateway_chassis, NULL);\n+    engine_add_input(&en_northd, &en_sb_ha_chassis, NULL);\n+    engine_add_input(&en_northd, &en_sb_ha_chassis_group, NULL);\n+    engine_add_input(&en_northd, &en_sb_controller_event, NULL);\n+    engine_add_input(&en_northd, &en_sb_ip_multicast, NULL);\n+    engine_add_input(&en_northd, &en_sb_igmp_group, NULL);\n+    engine_add_input(&en_northd, &en_sb_service_monitor, NULL);\n+    engine_add_input(&en_northd, &en_sb_load_balancer, NULL);\n+    engine_add_input(&en_northd, &en_sb_bfd, NULL);\n+    engine_add_input(&en_northd, &en_sb_fdb, NULL);\n+\n+    struct engine_arg engine_arg = {\n+        .nb_idl = nb->idl,\n+        .sb_idl = sb->idl,\n+    };\n+\n+    engine_init(&en_northd, &engine_arg);\n+}\n+\n+void inc_proc_northd_run(struct northd_context *ctx,\n+                         bool recompute) {\n+    engine_set_force_recompute(recompute);\n+    engine_init_run();\n+\n+    struct engine_context eng_ctx = {\n+        .ovnnb_idl_txn = ctx->ovnnb_txn,\n+        .ovnsb_idl_txn = ctx->ovnsb_txn,\n+        .client_ctx = ctx,\n+    };\n+\n+    engine_set_context(&eng_ctx);\n+\n+    if (ctx->ovnnb_txn && ctx->ovnsb_txn) {\n+        engine_run(true);\n+    }\n+\n+    if (!engine_has_run()) {\n+        if (engine_need_run()) {\n+            VLOG_DBG(\"engine did not run, force recompute next time.\");\n+            engine_set_force_recompute(true);\n+            poll_immediate_wake();\n+        } else {\n+            VLOG_DBG(\"engine did not run, and it was not needed\");\n+        }\n+    } else if (engine_aborted()) {\n+        VLOG_DBG(\"engine was aborted, force recompute next time.\");\n+        engine_set_force_recompute(true);\n+        poll_immediate_wake();\n+    } else {\n+        engine_set_force_recompute(false);\n+    }\n+}\n+\n+void inc_proc_northd_cleanup(void)\n+{\n+    engine_set_context(NULL);\n+    engine_cleanup();\n+}\ndiff --git a/northd/inc-proc-northd.h b/northd/inc-proc-northd.h\nnew file mode 100644\nindex 000000000000..09cb8f3b3a80\n--- /dev/null\n+++ b/northd/inc-proc-northd.h\n@@ -0,0 +1,15 @@\n+#ifndef INC_PROC_NORTHD_H\n+#define INC_PROC_NORTHD_H 1\n+\n+#include <config.h>\n+\n+#include \"northd.h\"\n+#include \"ovsdb-idl.h\"\n+\n+void inc_proc_northd_init(struct ovsdb_idl_loop *nb,\n+                          struct ovsdb_idl_loop *sb);\n+void inc_proc_northd_run(struct northd_context *ctx,\n+                         bool recompute);\n+void inc_proc_northd_cleanup(void);\n+\n+#endif /* INC_PROC_NORTHD */\ndiff --git a/northd/northd.c b/northd/northd.c\nindex 4a5260701fee..724baa3994d5 100644\n--- a/northd/northd.c\n+++ b/northd/northd.c\n@@ -14545,10 +14545,7 @@ ovnsb_db_run(struct northd_context *ctx,\n }\n \n void\n-ovn_db_run(struct northd_context *ctx,\n-           struct ovsdb_idl_index *sbrec_chassis_by_name,\n-           struct ovsdb_idl_loop *ovnsb_idl_loop,\n-           const char *ovn_internal_version)\n+ovn_db_run(struct northd_context *ctx)\n {\n     struct hmap datapaths, ports;\n     struct ovs_list lr_list;\n@@ -14559,13 +14556,14 @@ ovn_db_run(struct northd_context *ctx,\n     lflow_locks = ctx->lflow_locks;\n \n     int64_t start_time = time_wall_msec();\n+\n     stopwatch_start(OVNNB_DB_RUN_STOPWATCH_NAME, time_msec());\n-    ovnnb_db_run(ctx, sbrec_chassis_by_name, ovnsb_idl_loop,\n+    ovnnb_db_run(ctx, ctx->sbrec_chassis_by_name, ctx->ovnsb_idl_loop,\n                  &datapaths, &ports, &lr_list, start_time,\n-                 ovn_internal_version);\n+                 ctx->ovn_internal_version);\n     stopwatch_stop(OVNNB_DB_RUN_STOPWATCH_NAME, time_msec());\n     stopwatch_start(OVNSB_DB_RUN_STOPWATCH_NAME, time_msec());\n-    ovnsb_db_run(ctx, ovnsb_idl_loop, &ports, start_time);\n+    ovnsb_db_run(ctx, ctx->ovnsb_idl_loop, &ports, start_time);\n     stopwatch_stop(OVNSB_DB_RUN_STOPWATCH_NAME, time_msec());\n     destroy_datapaths_and_ports(&datapaths, &ports, &lr_list);\n }\ndiff --git a/northd/northd.h b/northd/northd.h\nindex 3209d4224803..fa941d8ec83b 100644\n--- a/northd/northd.h\n+++ b/northd/northd.h\n@@ -22,6 +22,8 @@ struct northd_context {\n     const char *ovnsb_db;\n     struct ovsdb_idl *ovnnb_idl;\n     struct ovsdb_idl *ovnsb_idl;\n+    struct ovsdb_idl_loop *ovnnb_idl_loop;\n+    struct ovsdb_idl_loop *ovnsb_idl_loop;\n     struct ovsdb_idl_txn *ovnnb_txn;\n     struct ovsdb_idl_txn *ovnsb_txn;\n     struct ovsdb_idl_index *sbrec_chassis_by_name;\n@@ -31,12 +33,10 @@ struct northd_context {\n \n     bool use_parallel_build;\n     struct hashrow_locks *lflow_locks;\n+\n+    const char *ovn_internal_version;\n };\n \n-void\n-ovn_db_run(struct northd_context *ctx,\n-           struct ovsdb_idl_index *sbrec_chassis_by_name,\n-           struct ovsdb_idl_loop *ovnsb_idl_loop,\n-           const char *ovn_internal_version);\n+void ovn_db_run(struct northd_context *ctx);\n \n #endif /* NORTHD_H */\ndiff --git a/northd/ovn-northd.c b/northd/ovn-northd.c\nindex ecee14e644af..873ea0254b4d 100644\n--- a/northd/ovn-northd.c\n+++ b/northd/ovn-northd.c\n@@ -22,6 +22,7 @@\n #include \"command-line.h\"\n #include \"daemon.h\"\n #include \"fatal-signal.h\"\n+#include \"inc-proc-northd.h\"\n #include \"lib/ip-mcast-index.h\"\n #include \"lib/mcast-group-index.h\"\n #include \"memory.h\"\n@@ -436,6 +437,14 @@ check_and_add_supported_dhcpv6_opts_to_sb_db(struct northd_context *ctx)\n }\n \f\n static void\n+add_column_noalert(struct ovsdb_idl *idl,\n+                   const struct ovsdb_idl_column *column)\n+{\n+    ovsdb_idl_add_column(idl, column);\n+    ovsdb_idl_omit_alert(idl, column);\n+}\n+\f\n+static void\n usage(void)\n {\n     printf(\"\\\n@@ -557,14 +566,6 @@ parse_options(int argc OVS_UNUSED, char *argv[] OVS_UNUSED,\n     free(short_options);\n }\n \n-static void\n-add_column_noalert(struct ovsdb_idl *idl,\n-                   const struct ovsdb_idl_column *column)\n-{\n-    ovsdb_idl_add_column(idl, column);\n-    ovsdb_idl_omit_alert(idl, column);\n-}\n-\n static void\n update_ssl_config(void)\n {\n@@ -629,6 +630,7 @@ main(int argc, char *argv[])\n     /* We want to detect (almost) all changes to the ovn-nb db. */\n     struct ovsdb_idl_loop ovnnb_idl_loop = OVSDB_IDL_LOOP_INITIALIZER(\n         ovsdb_idl_create(ovnnb_db, &nbrec_idl_class, true, true));\n+    ovsdb_idl_track_add_all(ovnnb_idl_loop.idl);\n     ovsdb_idl_omit_alert(ovnnb_idl_loop.idl,\n                          &nbrec_nb_global_col_nb_cfg_timestamp);\n     ovsdb_idl_omit_alert(ovnnb_idl_loop.idl, &nbrec_nb_global_col_sb_cfg);\n@@ -643,12 +645,13 @@ main(int argc, char *argv[])\n \n     /* We want to detect only selected changes to the ovn-sb db. */\n     struct ovsdb_idl_loop ovnsb_idl_loop = OVSDB_IDL_LOOP_INITIALIZER(\n-        ovsdb_idl_create(ovnsb_db, &sbrec_idl_class, false, true));\n-\n+        ovsdb_idl_create(ovnsb_db, &sbrec_idl_class, true, true));\n     ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_sb_global);\n     add_column_noalert(ovnsb_idl_loop.idl, &sbrec_sb_global_col_nb_cfg);\n     add_column_noalert(ovnsb_idl_loop.idl, &sbrec_sb_global_col_options);\n     add_column_noalert(ovnsb_idl_loop.idl, &sbrec_sb_global_col_ipsec);\n+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,\n+                               &sbrec_sb_global_col_connections);\n \n     ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_logical_flow);\n     add_column_noalert(ovnsb_idl_loop.idl,\n@@ -700,24 +703,26 @@ main(int argc, char *argv[])\n     add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_mac);\n     add_column_noalert(ovnsb_idl_loop.idl,\n                        &sbrec_port_binding_col_nat_addresses);\n-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_port_binding_col_chassis);\n-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,\n-                         &sbrec_port_binding_col_gateway_chassis);\n-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,\n-                         &sbrec_port_binding_col_ha_chassis_group);\n-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,\n-                         &sbrec_port_binding_col_virtual_parent);\n-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,\n-                         &sbrec_port_binding_col_up);\n-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,\n-                         &sbrec_gateway_chassis_col_chassis);\n-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_gateway_chassis_col_name);\n-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,\n-                         &sbrec_gateway_chassis_col_priority);\n-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,\n-                         &sbrec_gateway_chassis_col_external_ids);\n-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,\n-                         &sbrec_gateway_chassis_col_options);\n+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,\n+                               &sbrec_port_binding_col_chassis);\n+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,\n+                               &sbrec_port_binding_col_gateway_chassis);\n+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,\n+                               &sbrec_port_binding_col_ha_chassis_group);\n+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,\n+                               &sbrec_port_binding_col_virtual_parent);\n+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,\n+                               &sbrec_port_binding_col_up);\n+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,\n+                               &sbrec_gateway_chassis_col_chassis);\n+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,\n+                               &sbrec_gateway_chassis_col_name);\n+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,\n+                               &sbrec_gateway_chassis_col_priority);\n+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,\n+                               &sbrec_gateway_chassis_col_external_ids);\n+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,\n+                               &sbrec_gateway_chassis_col_options);\n     add_column_noalert(ovnsb_idl_loop.idl,\n                        &sbrec_port_binding_col_external_ids);\n     ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_mac_binding);\n@@ -760,32 +765,35 @@ main(int argc, char *argv[])\n     add_column_noalert(ovnsb_idl_loop.idl, &sbrec_rbac_permission_col_update);\n \n     ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_meter);\n-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_meter_col_name);\n-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_meter_col_unit);\n-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_meter_col_bands);\n+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, &sbrec_meter_col_name);\n+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, &sbrec_meter_col_unit);\n+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, &sbrec_meter_col_bands);\n \n     ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_meter_band);\n-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_meter_band_col_action);\n-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_meter_band_col_rate);\n-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_meter_band_col_burst_size);\n+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,\n+                               &sbrec_meter_band_col_action);\n+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, &sbrec_meter_band_col_rate);\n+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,\n+                               &sbrec_meter_band_col_burst_size);\n \n     ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_chassis);\n-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_chassis_col_name);\n-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_chassis_col_other_config);\n-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_chassis_col_encaps);\n+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, &sbrec_chassis_col_name);\n+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,\n+                               &sbrec_chassis_col_other_config);\n+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, &sbrec_chassis_col_encaps);\n \n     ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_encap);\n-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_encap_col_type);\n+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, &sbrec_encap_col_type);\n \n     ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_chassis_private);\n-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,\n-                         &sbrec_chassis_private_col_name);\n-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,\n-                         &sbrec_chassis_private_col_chassis);\n-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,\n-                         &sbrec_chassis_private_col_nb_cfg);\n-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,\n-                         &sbrec_chassis_private_col_nb_cfg_timestamp);\n+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,\n+                               &sbrec_chassis_private_col_name);\n+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,\n+                               &sbrec_chassis_private_col_chassis);\n+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,\n+                               &sbrec_chassis_private_col_nb_cfg);\n+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,\n+                               &sbrec_chassis_private_col_nb_cfg_timestamp);\n \n     ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_ha_chassis);\n     add_column_noalert(ovnsb_idl_loop.idl,\n@@ -806,10 +814,14 @@ main(int argc, char *argv[])\n                        &sbrec_ha_chassis_group_col_ref_chassis);\n \n     ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_igmp_group);\n-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_igmp_group_col_address);\n-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_igmp_group_col_datapath);\n-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_igmp_group_col_chassis);\n-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_igmp_group_col_ports);\n+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,\n+                               &sbrec_igmp_group_col_address);\n+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,\n+                               &sbrec_igmp_group_col_datapath);\n+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,\n+                               &sbrec_igmp_group_col_chassis);\n+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,\n+                               &sbrec_igmp_group_col_ports);\n \n     ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_ip_multicast);\n     add_column_noalert(ovnsb_idl_loop.idl,\n@@ -841,8 +853,8 @@ main(int argc, char *argv[])\n                        &sbrec_service_monitor_col_port);\n     add_column_noalert(ovnsb_idl_loop.idl,\n                        &sbrec_service_monitor_col_options);\n-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,\n-                         &sbrec_service_monitor_col_status);\n+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,\n+                               &sbrec_service_monitor_col_status);\n     add_column_noalert(ovnsb_idl_loop.idl,\n                        &sbrec_service_monitor_col_protocol);\n     add_column_noalert(ovnsb_idl_loop.idl,\n@@ -862,19 +874,20 @@ main(int argc, char *argv[])\n                        &sbrec_load_balancer_col_external_ids);\n \n     ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_bfd);\n-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_bfd_col_logical_port);\n-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_bfd_col_dst_ip);\n-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_bfd_col_status);\n-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_bfd_col_min_tx);\n-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_bfd_col_min_rx);\n-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_bfd_col_detect_mult);\n-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_bfd_col_disc);\n-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_bfd_col_src_port);\n+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,\n+                               &sbrec_bfd_col_logical_port);\n+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, &sbrec_bfd_col_dst_ip);\n+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, &sbrec_bfd_col_status);\n+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, &sbrec_bfd_col_min_tx);\n+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, &sbrec_bfd_col_min_rx);\n+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, &sbrec_bfd_col_detect_mult);\n+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, &sbrec_bfd_col_disc);\n+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, &sbrec_bfd_col_src_port);\n \n     ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_fdb);\n-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_fdb_col_mac);\n-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_fdb_col_dp_key);\n-    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_fdb_col_port_key);\n+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, &sbrec_fdb_col_mac);\n+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, &sbrec_fdb_col_dp_key);\n+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl, &sbrec_fdb_col_port_key);\n \n     struct ovsdb_idl_index *sbrec_chassis_by_name\n         = chassis_index_create(ovnsb_idl_loop.idl);\n@@ -898,9 +911,16 @@ main(int argc, char *argv[])\n     stopwatch_create(OVNNB_DB_RUN_STOPWATCH_NAME, SW_MS);\n     stopwatch_create(OVNSB_DB_RUN_STOPWATCH_NAME, SW_MS);\n \n+    /* Initialize incremental processing engine for ovn-northd */\n+    inc_proc_northd_init(&ovnnb_idl_loop, &ovnsb_idl_loop);\n+\n+    unsigned int ovnnb_cond_seqno = UINT_MAX;\n+    unsigned int ovnsb_cond_seqno = UINT_MAX;\n+\n     /* Main loop. */\n     exiting = false;\n \n+    bool recompute = false;\n     while (!exiting) {\n         update_ssl_config();\n         memory_run();\n@@ -924,19 +944,47 @@ main(int argc, char *argv[])\n                 ovsdb_idl_set_lock(ovnsb_idl_loop.idl, \"ovn_northd\");\n             }\n \n+\n+            struct ovsdb_idl_txn *ovnnb_txn =\n+                        ovsdb_idl_loop_run(&ovnnb_idl_loop);\n+            unsigned int new_ovnnb_cond_seqno =\n+                        ovsdb_idl_get_condition_seqno(ovnnb_idl_loop.idl);\n+            if (new_ovnnb_cond_seqno != ovnnb_cond_seqno) {\n+                if (!new_ovnnb_cond_seqno) {\n+                    VLOG_INFO(\"OVN NB IDL reconnected, force recompute.\");\n+                    recompute = true;\n+                }\n+                ovnnb_cond_seqno = new_ovnnb_cond_seqno;\n+            }\n+\n+            struct ovsdb_idl_txn *ovnsb_txn =\n+                        ovsdb_idl_loop_run(&ovnsb_idl_loop);\n+            unsigned int new_ovnsb_cond_seqno =\n+                        ovsdb_idl_get_condition_seqno(ovnsb_idl_loop.idl);\n+            if (new_ovnsb_cond_seqno != ovnsb_cond_seqno) {\n+                if (!new_ovnsb_cond_seqno) {\n+                    VLOG_INFO(\"OVN SB IDL reconnected, force recompute.\");\n+                    recompute = true;\n+                }\n+                ovnsb_cond_seqno = new_ovnsb_cond_seqno;\n+            }\n+\n             struct northd_context ctx = {\n                 .ovnnb_db = ovnnb_db,\n                 .ovnsb_db = ovnsb_db,\n                 .ovnnb_idl = ovnnb_idl_loop.idl,\n-                .ovnnb_txn = ovsdb_idl_loop_run(&ovnnb_idl_loop),\n+                .ovnnb_idl_loop = &ovnnb_idl_loop,\n+                .ovnnb_txn = ovnnb_txn,\n                 .ovnsb_idl = ovnsb_idl_loop.idl,\n-                .ovnsb_txn = ovsdb_idl_loop_run(&ovnsb_idl_loop),\n+                .ovnsb_idl_loop = &ovnsb_idl_loop,\n+                .ovnsb_txn = ovnsb_txn,\n                 .sbrec_chassis_by_name = sbrec_chassis_by_name,\n                 .sbrec_ha_chassis_grp_by_name = sbrec_ha_chassis_grp_by_name,\n                 .sbrec_mcast_group_by_name_dp = sbrec_mcast_group_by_name_dp,\n                 .sbrec_ip_mcast_by_dp = sbrec_ip_mcast_by_dp,\n                 .lflow_locks = &lflow_locks,\n                 .use_parallel_build = use_parallel_build,\n+                .ovn_internal_version = ovn_internal_version,\n             };\n \n             if (!state.had_lock && ovsdb_idl_has_lock(ovnsb_idl_loop.idl)) {\n@@ -952,18 +1000,15 @@ main(int argc, char *argv[])\n             }\n \n             if (ovsdb_idl_has_lock(ovnsb_idl_loop.idl)) {\n-\n-                ovn_db_run(&ctx, sbrec_chassis_by_name, &ovnsb_idl_loop,\n-                           ovn_internal_version);\n+                inc_proc_northd_run(&ctx, recompute);\n+                recompute = false;\n                 if (ctx.ovnsb_txn) {\n                     check_and_add_supported_dhcp_opts_to_sb_db(&ctx);\n                     check_and_add_supported_dhcpv6_opts_to_sb_db(&ctx);\n                     check_and_update_rbac(&ctx);\n                 }\n-            }\n \n-            ovsdb_idl_loop_commit_and_wait(&ovnnb_idl_loop);\n-            ovsdb_idl_loop_commit_and_wait(&ovnsb_idl_loop);\n+            }\n         } else {\n             /* ovn-northd is paused\n              *    - we still want to handle any db updates and update the\n@@ -986,6 +1031,19 @@ main(int argc, char *argv[])\n             ovsdb_idl_wait(ovnsb_idl_loop.idl);\n         }\n \n+        /* If there are any errors, we force a full recompute in order to\n+           ensure we handle any new tracked changes. */\n+        if (ovsdb_idl_loop_commit_and_wait(&ovnnb_idl_loop) != 1) {\n+            recompute = true;\n+        } else  {\n+            ovsdb_idl_track_clear(ovnnb_idl_loop.idl);\n+        }\n+        if (ovsdb_idl_loop_commit_and_wait(&ovnsb_idl_loop) != 1) {\n+            recompute = true;\n+        } else {\n+            ovsdb_idl_track_clear(ovnsb_idl_loop.idl);\n+        }\n+\n         stopwatch_stop(NORTHD_LOOP_STOPWATCH_NAME, time_msec());\n         stopwatch_start(NORTHD_LOOP_STOPWATCH_NAME, time_msec());\n         unixctl_server_run(unixctl);\n@@ -1012,7 +1070,7 @@ main(int argc, char *argv[])\n             exiting = true;\n         }\n     }\n-\n+    inc_proc_northd_cleanup();\n \n     free(ovn_internal_version);\n     unixctl_server_destroy(unixctl);\n",
    "prefixes": [
        "ovs-dev",
        "v4",
        "4/7"
    ]
}