diff mbox series

[ovs-dev,v4,2/4] ovn-ic: Introduce incremental processing.

Message ID 20251216202825.177582-3-guilherme.paulo@luizalabs.com
State Accepted
Delegated to: Numan Siddique
Headers show
Series ovn-ic: Introduce I+P for ovn-ic. | expand

Checks

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

Commit Message

Paulo Guilherme Silva Dec. 16, 2025, 8:28 p.m. UTC
Initial implementation adds a single node (ic). This single
node executes the ovn-ic processing pipeline but does not do so
incrementally.

In order to develop incremental processing for ovn-ic, the code
will be organised with a .c/.h file for each I-P node following
the naming convention en-<node name>.c/.h. These files will
contain definition of the node data, the main node processing
functions and change handlers (if any). The purpose of these nodes
will be coordination of the nodes work and implemention of the
relevant interfaces to plug into the I-P framework. The actual
work that will be executed by the node will be organised into
a companion file or files. Ideally this file will follow the
naming convention of the node: e.g. en-<node name>.c is
associated with <node name>.c.

Initial node topology sees the ic node dependent on all DB
nodes. This structure and approach was heavily influenced
by the way I+P was developed and evolved in the northd daemon.

Signed-off-by: Paulo Guilherme Silva <guilherme.paulo@luizalabs.com>
---
 ic/automake.mk     |   7 +-
 ic/en-ic.c         |  50 +++++
 ic/en-ic.h         |  18 ++
 ic/inc-proc-ic.c   | 269 +++++++++++++++++++++++++
 ic/inc-proc-ic.h   |  48 +++++
 ic/ovn-ic.c        | 481 ++++++++++++++++++++++++++-------------------
 ic/ovn-ic.h        |  61 ++++++
 lib/inc-proc-eng.h |  21 ++
 8 files changed, 754 insertions(+), 201 deletions(-)
 create mode 100644 ic/en-ic.c
 create mode 100644 ic/en-ic.h
 create mode 100644 ic/inc-proc-ic.c
 create mode 100644 ic/inc-proc-ic.h
 create mode 100644 ic/ovn-ic.h

Comments

Dumitru Ceara Jan. 5, 2026, 11:49 a.m. UTC | #1
On 12/22/25 9:51 PM, Paulo Guilherme (MGC URPE SDN) wrote:
> Hi Dumitru,
> 

Hi Paulo,

> I apologize for bringing this up again.
> 

No problem!

> Could you help me with this commit?
> 
> I submitted the v4 of patch ovn-ic:incremental processing, but the Build
> and Test step is depriving. In my fork of ovn, Build and Test step
> approved. I tried to execute de process again with 'Recheck-request:
> github-robot-_Build_and_Test', but I noticed the pipeline doesn't start
> again. What could I do to resolve this?
> 

I don't see any email reply to that patch with 'Recheck-request':

https://patchwork.ozlabs.org/project/ovn/patch/20251216202825.177582-3-guilherme.paulo@luizalabs.com/

Maybe you didn't reply to the patch with ovs-dev in cc?

The failure you hit:

"628: ovn.at:43274       lb_force_snat_ip=router_ip select correct
network for snat -- parallelization=yes -- ovn_monitor_all=yes"

is due to a known bug:
https://issues.redhat.com/browse/FDP-2870

I'll trigger recheck on the series as the failure on patch 4/4 seems to
be an infrastructure issue:

Error: stat /tmp/image.tar: no such file or directory

Regards,
Dumitru

> Regards,
> Paulo
> 
> Em sex., 19 de dez. de 2025 às 11:27, Paulo Guilherme (MGC URPE SDN) <
> guilherme.paulo@luizalabs.com> escreveu:
> 
>> Recheck-request: github-robot-_Build_and_Test
>>
>> Em qua., 17 de dez. de 2025 às 06:45, 0-day Robot <robot@bytheb.org>
>> escreveu:
>>
>>> From: robot@bytheb.org
>>>
>>> Test-Label: github-robot: Build and Test
>>> Test-Status: fail
>>> http://patchwork.ozlabs.org/api/patches/2174959/
>>>
>>> _github build: failed_
>>> Build URL: https://github.com/ovsrobot/ovn/actions/runs/20282240556
>>>
>>
>
Dumitru Ceara Jan. 5, 2026, 11:50 a.m. UTC | #2
On 12/16/25 9:28 PM, Paulo Guilherme Silva via dev wrote:
> Initial implementation adds a single node (ic). This single
> node executes the ovn-ic processing pipeline but does not do so
> incrementally.
> 
> In order to develop incremental processing for ovn-ic, the code
> will be organised with a .c/.h file for each I-P node following
> the naming convention en-<node name>.c/.h. These files will
> contain definition of the node data, the main node processing
> functions and change handlers (if any). The purpose of these nodes
> will be coordination of the nodes work and implemention of the
> relevant interfaces to plug into the I-P framework. The actual
> work that will be executed by the node will be organised into
> a companion file or files. Ideally this file will follow the
> naming convention of the node: e.g. en-<node name>.c is
> associated with <node name>.c.
> 
> Initial node topology sees the ic node dependent on all DB
> nodes. This structure and approach was heavily influenced
> by the way I+P was developed and evolved in the northd daemon.
> 
> Signed-off-by: Paulo Guilherme Silva <guilherme.paulo@luizalabs.com>
> ---

Recheck-request: github-robot-_Build_and_Test
Dumitru Ceara Jan. 12, 2026, 2:38 p.m. UTC | #3
On 1/12/26 2:18 PM, Paulo Guilherme (MGC URPE SDN) wrote:
> Hi Dumitru,
> 

Hi Paulo,

> I have another question about our patch series inc-processing_ovn-ic. In
> this moment, i have the following local patches:
> 
> dbc8f637e ovn-ic: Introduces I+P engine for gateway.
> 393db49dd ovn-ic: Introduces I+P engine for route.
> 8e287043d ovn-ic: Introduces I+P engine for port-binding.
> 1a4d2825b ovn-ic: Introduces I+P engine for Enum-Datapaths.
> 9b9ce18c3 ovn-ic: Introduce structs ic_data and ic_input.
> 2735f3c6b docs: Update OVN I+P Engine documentation.
> 82bb5e944 ovn-ic: Support dumping a visual representation of the I-P graph.
> 0470c8bba ovn-ic: Introduce incremental processing.
> 053690db7 ovn: Move run_idl_loop function to lib/ovn-util.
> 
> We haven't finished the feature yet.
> 
> So the question is, for you and the OVN community, Is it better to

In general, for OVN community related discussions, including patch
reviews/discussion/debugging, please try to keep dev@openvswitch.org in
CC.  It's more beneficial to the community if more people can choose to
participate in the discussion.

> complete the entire feature and submit one unique complete patch series
> or submit each new patch in patch series format?
> 

I'm not sure I understand the question completely.  Do you mean to say
that your current implementation does some partial incremental
processing in ovn-ic, for some of types database resource changes, and
falls back to recompute if other changes changes are detected?

If so, then it's probably acceptable to incorporate this version (after
review and approval in the community).  It makes sense in my opinion to
add incremental processing support (which normally requires more complex
code and sometimes increases memory usage) only for the most common
types of changes, potentially in multiple releases, to maximize the
impact vs cost aspect.

Regards,
Dumitru

> Regards, 
> Paulo
> 
> Em seg., 5 de jan. de 2026 às 08:49, Dumitru Ceara <dceara@redhat.com
> <mailto:dceara@redhat.com>> escreveu:
> 
>     On 12/22/25 9:51 PM, Paulo Guilherme (MGC URPE SDN) wrote:
>     > Hi Dumitru,
>     >
> 
>     Hi Paulo,
> 
>     > I apologize for bringing this up again.
>     >
> 
>     No problem!
> 
>     > Could you help me with this commit?
>     >
>     > I submitted the v4 of patch ovn-ic:incremental processing, but the
>     Build
>     > and Test step is depriving. In my fork of ovn, Build and Test step
>     > approved. I tried to execute de process again with 'Recheck-request:
>     > github-robot-_Build_and_Test', but I noticed the pipeline doesn't
>     start
>     > again. What could I do to resolve this?
>     >
> 
>     I don't see any email reply to that patch with 'Recheck-request':
> 
>     https://patchwork.ozlabs.org/project/ovn/
>     patch/20251216202825.177582-3-guilherme.paulo@luizalabs.com/
>     <https://patchwork.ozlabs.org/project/ovn/
>     patch/20251216202825.177582-3-guilherme.paulo@luizalabs.com/>
> 
>     Maybe you didn't reply to the patch with ovs-dev in cc?
> 
>     The failure you hit:
> 
>     "628: ovn.at:43274 <http://ovn.at:43274>     
>      lb_force_snat_ip=router_ip select correct
>     network for snat -- parallelization=yes -- ovn_monitor_all=yes"
> 
>     is due to a known bug:
>     https://issues.redhat.com/browse/FDP-2870 <https://
>     issues.redhat.com/browse/FDP-2870>
> 
>     I'll trigger recheck on the series as the failure on patch 4/4 seems to
>     be an infrastructure issue:
> 
>     Error: stat /tmp/image.tar: no such file or directory
> 
>     Regards,
>     Dumitru
> 
>     > Regards,
>     > Paulo
>     >
>     > Em sex., 19 de dez. de 2025 às 11:27, Paulo Guilherme (MGC URPE SDN) <
>     > guilherme.paulo@luizalabs.com
>     <mailto:guilherme.paulo@luizalabs.com>> escreveu:
>     >
>     >> Recheck-request: github-robot-_Build_and_Test
>     >>
>     >> Em qua., 17 de dez. de 2025 às 06:45, 0-day Robot
>     <robot@bytheb.org <mailto:robot@bytheb.org>>
>     >> escreveu:
>     >>
>     >>> From: robot@bytheb.org <mailto:robot@bytheb.org>
>     >>>
>     >>> Test-Label: github-robot: Build and Test
>     >>> Test-Status: fail
>     >>> http://patchwork.ozlabs.org/api/patches/2174959/ <http://
>     patchwork.ozlabs.org/api/patches/2174959/>
>     >>>
>     >>> _github build: failed_
>     >>> Build URL: https://github.com/ovsrobot/ovn/actions/
>     runs/20282240556 <https://github.com/ovsrobot/ovn/actions/
>     runs/20282240556>
>     >>>
>     >>
>     >
> 
> 
> 
> /‘Esta mensagem é direcionada apenas para os endereços constantes no
> cabeçalho inicial. Se você não está listado nos endereços constantes no
> cabeçalho, pedimos-lhe que desconsidere completamente o conteúdo dessa
> mensagem e cuja cópia, encaminhamento e/ou execução das ações citadas
> estão imediatamente anuladas e proibidas’./
> 
> / //‘Apesar do Magazine Luiza tomar todas as precauções razoáveis para
> assegurar que nenhum vírus esteja presente nesse e-mail, a empresa não
> poderá aceitar a responsabilidade por quaisquer perdas ou danos causados
> por esse e-mail ou por seus anexos’./
>
Dumitru Ceara Jan. 12, 2026, 3:30 p.m. UTC | #4
On 1/12/26 4:22 PM, Paulo Guilherme (MGC URPE SDN) wrote:
> Thanks for answer,
> 

Hi Paulo,

Please Reply-All and keep dev@openvswitch.org in CC.

> Actually, my question was more related to sending the patches. Giving an
> overview of the work so far we converted those fundamental functions
> 
> void
> ovn_db_run(struct ic_input *input_data,
>            struct ic_data *ic_data,
>            struct engine_context *eng_ctx)
> {
>     gateway_run(eng_ctx, input_data);
>     enumerate_datapaths(input_data, &ic_data->dp_tnlids, &ic_data-
>>isb_ts_dps, &ic_data->isb_tr_dps);
>     ts_run(eng_ctx, input_data, &ic_data->dp_tnlids, &ic_data->isb_ts_dps);
>     tr_run(eng_ctx, input_data, &ic_data->dp_tnlids, &ic_data->isb_tr_dps);
>     port_binding_run(eng_ctx, input_data);
>     route_run(eng_ctx, input_data);
>     sync_service_monitor(eng_ctx, input_data);
> }
> 
> in engine nodes. Currently, these new engines are only operating in full
> recompute mode. The next step will be to develop the handles for
> incremental processing.
> 
> void inc_proc_ic_init(struct ovsdb_idl_loop *nb,
>                       struct ovsdb_idl_loop *sb,
>                       struct ovsdb_idl_loop *icnb,
>                       struct ovsdb_idl_loop *icsb)
> {
>     /* Define relationships between nodes where first argument is dependent
>      * on the second argument */
>     engine_add_input(&en_gateway, &en_icsb_gateway, NULL);
>     engine_add_input(&en_gateway, &en_sb_chassis, NULL);
> 
>     engine_add_input(&en_enum_datapaths, &en_nb_logical_switch, NULL);
>     engine_add_input(&en_enum_datapaths, &en_icnb_transit_switch, NULL);
>     engine_add_input(&en_enum_datapaths, &en_icsb_datapath_binding, NULL);
> 
>     engine_add_input(&en_tr, &en_nb_logical_router, NULL);
>     engine_add_input(&en_tr, &en_icnb_transit_router, NULL);
>     engine_add_input(&en_tr, &en_icnb_transit_router_port, NULL);
> 
>     engine_add_input(&en_port_binding, &en_icnb_transit_switch, NULL);
>     engine_add_input(&en_port_binding, &en_icnb_transit_router, NULL);
>     engine_add_input(&en_port_binding, &en_icsb_port_binding, NULL);
>     engine_add_input(&en_port_binding, &en_nb_logical_switch, NULL);
>     engine_add_input(&en_port_binding, &en_sb_port_binding, NULL);
>     engine_add_input(&en_port_binding, &en_nb_logical_router, NULL);
>     engine_add_input(&en_port_binding, &en_sb_chassis, NULL);
> 
>     engine_add_input(&en_route, &en_nb_nb_global, NULL);
>     engine_add_input(&en_route, &en_nb_logical_switch, NULL);
>     engine_add_input(&en_route, &en_nb_logical_router, NULL);
>     engine_add_input(&en_route, &en_icnb_transit_switch, NULL);
>     engine_add_input(&en_route, &en_icsb_port_binding, NULL);
>     engine_add_input(&en_route, &en_icsb_route, NULL);
>     engine_add_input(&en_route, &en_nb_logical_router_static_route, NULL);
> 
> The original question was whether I should wait for all the I+P engines
> and handles to be finalized before submitting a single patch series?
> Or would I send a patch every time an engine is finalized?
> 

Ah I see now.  It's fine to get the current version merged and when you
have new incremental processing engine nodes work finished submit new
patches.  That's how we did it for ovn-controller and ovn-northd too.

Otherwise rebasing would be horrible for whoever is working on
incremental processing.

Regards,
Dumitru

> Regards,
> Paulo
> 
> Em seg., 12 de jan. de 2026 às 11:38, Dumitru Ceara <dceara@redhat.com
> <mailto:dceara@redhat.com>> escreveu:
> 
>     On 1/12/26 2:18 PM, Paulo Guilherme (MGC URPE SDN) wrote:
>     > Hi Dumitru,
>     >
> 
>     Hi Paulo,
> 
>     > I have another question about our patch series inc-processing_ovn-
>     ic. In
>     > this moment, i have the following local patches:
>     >
>     > dbc8f637e ovn-ic: Introduces I+P engine for gateway.
>     > 393db49dd ovn-ic: Introduces I+P engine for route.
>     > 8e287043d ovn-ic: Introduces I+P engine for port-binding.
>     > 1a4d2825b ovn-ic: Introduces I+P engine for Enum-Datapaths.
>     > 9b9ce18c3 ovn-ic: Introduce structs ic_data and ic_input.
>     > 2735f3c6b docs: Update OVN I+P Engine documentation.
>     > 82bb5e944 ovn-ic: Support dumping a visual representation of the
>     I-P graph.
>     > 0470c8bba ovn-ic: Introduce incremental processing.
>     > 053690db7 ovn: Move run_idl_loop function to lib/ovn-util.
>     >
>     > We haven't finished the feature yet.
>     >
>     > So the question is, for you and the OVN community, Is it better to
> 
>     In general, for OVN community related discussions, including patch
>     reviews/discussion/debugging, please try to keep dev@openvswitch.org
>     <mailto:dev@openvswitch.org> in
>     CC.  It's more beneficial to the community if more people can choose to
>     participate in the discussion.
> 
>     > complete the entire feature and submit one unique complete patch
>     series
>     > or submit each new patch in patch series format?
>     >
> 
>     I'm not sure I understand the question completely.  Do you mean to say
>     that your current implementation does some partial incremental
>     processing in ovn-ic, for some of types database resource changes, and
>     falls back to recompute if other changes changes are detected?
> 
>     If so, then it's probably acceptable to incorporate this version (after
>     review and approval in the community).  It makes sense in my opinion to
>     add incremental processing support (which normally requires more complex
>     code and sometimes increases memory usage) only for the most common
>     types of changes, potentially in multiple releases, to maximize the
>     impact vs cost aspect.
> 
>     Regards,
>     Dumitru
> 
>     > Regards, 
>     > Paulo
>     >
>     > Em seg., 5 de jan. de 2026 às 08:49, Dumitru Ceara
>     <dceara@redhat.com <mailto:dceara@redhat.com>
>     > <mailto:dceara@redhat.com <mailto:dceara@redhat.com>>> escreveu:
>     >
>     >     On 12/22/25 9:51 PM, Paulo Guilherme (MGC URPE SDN) wrote:
>     >     > Hi Dumitru,
>     >     >
>     >
>     >     Hi Paulo,
>     >
>     >     > I apologize for bringing this up again.
>     >     >
>     >
>     >     No problem!
>     >
>     >     > Could you help me with this commit?
>     >     >
>     >     > I submitted the v4 of patch ovn-ic:incremental processing,
>     but the
>     >     Build
>     >     > and Test step is depriving. In my fork of ovn, Build and
>     Test step
>     >     > approved. I tried to execute de process again with 'Recheck-
>     request:
>     >     > github-robot-_Build_and_Test', but I noticed the pipeline
>     doesn't
>     >     start
>     >     > again. What could I do to resolve this?
>     >     >
>     >
>     >     I don't see any email reply to that patch with 'Recheck-request':
>     >
>     >     https://patchwork.ozlabs.org/project/ovn/ <https://
>     patchwork.ozlabs.org/project/ovn/>
>     >     patch/20251216202825.177582-3-guilherme.paulo@luizalabs.com/
>     <http://20251216202825.177582-3-guilherme.paulo@luizalabs.com/>
>     >     <https://patchwork.ozlabs.org/project/ovn/ <https://
>     patchwork.ozlabs.org/project/ovn/>
>     >     patch/20251216202825.177582-3-guilherme.paulo@luizalabs.com/
>     <http://20251216202825.177582-3-guilherme.paulo@luizalabs.com/>>
>     >
>     >     Maybe you didn't reply to the patch with ovs-dev in cc?
>     >
>     >     The failure you hit:
>     >
>     >     "628: ovn.at:43274 <http://ovn.at:43274> <http://ovn.at:43274
>     <http://ovn.at:43274>>     
>     >      lb_force_snat_ip=router_ip select correct
>     >     network for snat -- parallelization=yes -- ovn_monitor_all=yes"
>     >
>     >     is due to a known bug:
>     >     https://issues.redhat.com/browse/FDP-2870 <https://
>     issues.redhat.com/browse/FDP-2870> <https://
>     >     issues.redhat.com/browse/FDP-2870 <http://issues.redhat.com/
>     browse/FDP-2870>>
>     >
>     >     I'll trigger recheck on the series as the failure on patch 4/4
>     seems to
>     >     be an infrastructure issue:
>     >
>     >     Error: stat /tmp/image.tar: no such file or directory
>     >
>     >     Regards,
>     >     Dumitru
>     >
>     >     > Regards,
>     >     > Paulo
>     >     >
>     >     > Em sex., 19 de dez. de 2025 às 11:27, Paulo Guilherme (MGC
>     URPE SDN) <
>     >     > guilherme.paulo@luizalabs.com
>     <mailto:guilherme.paulo@luizalabs.com>
>     >     <mailto:guilherme.paulo@luizalabs.com
>     <mailto:guilherme.paulo@luizalabs.com>>> escreveu:
>     >     >
>     >     >> Recheck-request: github-robot-_Build_and_Test
>     >     >>
>     >     >> Em qua., 17 de dez. de 2025 às 06:45, 0-day Robot
>     >     <robot@bytheb.org <mailto:robot@bytheb.org>
>     <mailto:robot@bytheb.org <mailto:robot@bytheb.org>>>
>     >     >> escreveu:
>     >     >>
>     >     >>> From: robot@bytheb.org <mailto:robot@bytheb.org>
>     <mailto:robot@bytheb.org <mailto:robot@bytheb.org>>
>     >     >>>
>     >     >>> Test-Label: github-robot: Build and Test
>     >     >>> Test-Status: fail
>     >     >>> http://patchwork.ozlabs.org/api/patches/2174959/ <http://
>     patchwork.ozlabs.org/api/patches/2174959/> <http://
>     >     patchwork.ozlabs.org/api/patches/2174959/ <http://
>     patchwork.ozlabs.org/api/patches/2174959/>>
>     >     >>>
>     >     >>> _github build: failed_
>     >     >>> Build URL: https://github.com/ovsrobot/ovn/actions/
>     <https://github.com/ovsrobot/ovn/actions/>
>     >     runs/20282240556 <https://github.com/ovsrobot/ovn/actions/
>     <https://github.com/ovsrobot/ovn/actions/>
>     >     runs/20282240556>
>     >     >>>
>     >     >>
>     >     >
>     >
>     >
>     >
>     > /‘Esta mensagem é direcionada apenas para os endereços constantes no
>     > cabeçalho inicial. Se você não está listado nos endereços
>     constantes no
>     > cabeçalho, pedimos-lhe que desconsidere completamente o conteúdo dessa
>     > mensagem e cuja cópia, encaminhamento e/ou execução das ações citadas
>     > estão imediatamente anuladas e proibidas’./
>     >
>     > / //‘Apesar do Magazine Luiza tomar todas as precauções razoáveis para
>     > assegurar que nenhum vírus esteja presente nesse e-mail, a empresa não
>     > poderá aceitar a responsabilidade por quaisquer perdas ou danos
>     causados
>     > por esse e-mail ou por seus anexos’./
>     >
> 
> 
> 
> /‘Esta mensagem é direcionada apenas para os endereços constantes no
> cabeçalho inicial. Se você não está listado nos endereços constantes no
> cabeçalho, pedimos-lhe que desconsidere completamente o conteúdo dessa
> mensagem e cuja cópia, encaminhamento e/ou execução das ações citadas
> estão imediatamente anuladas e proibidas’./
> 
> / //‘Apesar do Magazine Luiza tomar todas as precauções razoáveis para
> assegurar que nenhum vírus esteja presente nesse e-mail, a empresa não
> poderá aceitar a responsabilidade por quaisquer perdas ou danos causados
> por esse e-mail ou por seus anexos’./
>
diff mbox series

Patch

diff --git a/ic/automake.mk b/ic/automake.mk
index 8e71bc334..a69b1030d 100644
--- a/ic/automake.mk
+++ b/ic/automake.mk
@@ -1,6 +1,11 @@ 
 # ovn-ic
 bin_PROGRAMS += ic/ovn-ic
-ic_ovn_ic_SOURCES = ic/ovn-ic.c
+ic_ovn_ic_SOURCES = ic/ovn-ic.c \
+	ic/ovn-ic.h \
+	ic/en-ic.c \
+	ic/en-ic.h \
+	ic/inc-proc-ic.c \
+	ic/inc-proc-ic.h
 ic_ovn_ic_LDADD = \
 	lib/libovn.la \
 	$(OVSDB_LIBDIR)/libovsdb.la \
diff --git a/ic/en-ic.c b/ic/en-ic.c
new file mode 100644
index 000000000..2db9d3b84
--- /dev/null
+++ b/ic/en-ic.c
@@ -0,0 +1,50 @@ 
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+#include <config.h>
+
+#include <getopt.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "en-ic.h"
+#include "lib/inc-proc-eng.h"
+#include "lib/stopwatch-names.h"
+#include "ovn-ic.h"
+#include "openvswitch/vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(en_ic);
+
+enum engine_node_state
+en_ic_run(struct engine_node *node OVS_UNUSED, void *data OVS_UNUSED)
+{
+    const struct engine_context *eng_ctx = engine_get_context();
+    struct ic_context *ctx = eng_ctx->client_ctx;
+
+    ovn_db_run(ctx);
+
+    return EN_UPDATED;
+}
+
+void *
+en_ic_init(struct engine_node *node OVS_UNUSED,
+            struct engine_arg *arg OVS_UNUSED)
+{
+    return NULL;
+}
+
+void
+en_ic_cleanup(void *data OVS_UNUSED)
+{
+}
diff --git a/ic/en-ic.h b/ic/en-ic.h
new file mode 100644
index 000000000..a4b75bb0e
--- /dev/null
+++ b/ic/en-ic.h
@@ -0,0 +1,18 @@ 
+#ifndef EN_IC_H
+#define EN_IC_H 1
+
+#include <config.h>
+
+#include <getopt.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "lib/inc-proc-eng.h"
+
+enum engine_node_state en_ic_run(struct engine_node *node OVS_UNUSED,
+                                 void *data OVS_UNUSED);
+void *en_ic_init(struct engine_node *node OVS_UNUSED,
+                 struct engine_arg *arg);
+void en_ic_cleanup(void *data);
+
+#endif /* EN_IC_H */
diff --git a/ic/inc-proc-ic.c b/ic/inc-proc-ic.c
new file mode 100644
index 000000000..ba9fdeb9e
--- /dev/null
+++ b/ic/inc-proc-ic.c
@@ -0,0 +1,269 @@ 
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+
+#include <getopt.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "lib/inc-proc-eng.h"
+#include "lib/ovn-nb-idl.h"
+#include "lib/ovn-sb-idl.h"
+#include "lib/ovn-ic-nb-idl.h"
+#include "lib/ovn-ic-sb-idl.h"
+#include "openvswitch/poll-loop.h"
+#include "openvswitch/vlog.h"
+#include "inc-proc-ic.h"
+#include "en-ic.h"
+#include "unixctl.h"
+#include "util.h"
+
+VLOG_DEFINE_THIS_MODULE(inc_proc_ic);
+
+#define NB_NODES \
+    NB_NODE(nb_global, "nb_global") \
+    NB_NODE(logical_router_static_route, "logical_router_static_route") \
+    NB_NODE(logical_router, "logical_router") \
+    NB_NODE(logical_router_port, "logical_router_port") \
+    NB_NODE(logical_switch, "logical_switch") \
+    NB_NODE(logical_switch_port, "logical_switch_port") \
+    NB_NODE(load_balancer, "load_balancer") \
+    NB_NODE(load_balancer_group, "load_balancer_group")
+
+    enum nb_engine_node {
+#define NB_NODE(NAME, NAME_STR) NB_##NAME,
+    NB_NODES
+#undef NB_NODE
+    };
+
+/* Define engine node functions for nodes that represent NB tables
+ *
+ * en_nb_<TABLE_NAME>_run()
+ * en_nb_<TABLE_NAME>_init()
+ * en_nb_<TABLE_NAME>_cleanup()
+ */
+#define NB_NODE(NAME, NAME_STR) ENGINE_FUNC_NB(NAME);
+    NB_NODES
+#undef NB_NODE
+
+#define SB_NODES \
+    SB_NODE(sb_global, "sb_global") \
+    SB_NODE(chassis, "chassis") \
+    SB_NODE(encap, "encap") \
+    SB_NODE(datapath_binding, "datapath_binding") \
+    SB_NODE(port_binding, "port_binding") \
+    SB_NODE(service_monitor, "service_monitor")
+
+    enum sb_engine_node {
+#define SB_NODE(NAME, NAME_STR) SB_##NAME,
+    SB_NODES
+#undef SB_NODE
+};
+
+/* Define engine node functions for nodes that represent SB tables
+ *
+ * en_sb_<TABLE_NAME>_run()
+ * en_sb_<TABLE_NAME>_init()
+ * en_sb_<TABLE_NAME>_cleanup()
+ */
+#define SB_NODE(NAME, NAME_STR) ENGINE_FUNC_SB(NAME);
+    SB_NODES
+#undef SB_NODE
+
+#define ICNB_NODES \
+    ICNB_NODE(ic_nb_global, "ic_nb_global") \
+    ICNB_NODE(transit_switch, "transit_switch") \
+    ICNB_NODE(transit_router, "transit_router") \
+    ICNB_NODE(transit_router_port, "transit_router_port")
+
+    enum icnb_engine_node {
+#define ICNB_NODE(NAME, NAME_STR) ICNB_##NAME,
+    ICNB_NODES
+#undef ICNB_NODE
+    };
+
+/* Define engine node functions for nodes that represent ICNB tables
+ *
+ * en_icnb_<TABLE_NAME>_run()
+ * en_icnb_<TABLE_NAME>_init()
+ * en_icnb_<TABLE_NAME>_cleanup()
+ */
+#define ICNB_NODE(NAME, NAME_STR) ENGINE_FUNC_ICNB(NAME);
+    ICNB_NODES
+#undef ICNB_NODE
+
+#define ICSB_NODES \
+    ICSB_NODE(ic_sb_global, "ic_sb_global") \
+    ICSB_NODE(availability_zone, "availability_zone") \
+    ICSB_NODE(service_monitor, "service_monitor") \
+    ICSB_NODE(route, "route") \
+    ICSB_NODE(datapath_binding, "datapath_binding") \
+    ICSB_NODE(encap, "encap") \
+    ICSB_NODE(gateway, "gateway") \
+    ICSB_NODE(port_binding, "port_binding")
+
+    enum icsb_engine_node {
+#define ICSB_NODE(NAME, NAME_STR) ICSB_##NAME,
+    ICSB_NODES
+#undef ICSB_NODE
+    };
+
+/* Define engine node functions for nodes that represent ICSB tables
+ *
+ * en_icsb_<TABLE_NAME>_run()
+ * en_icsb_<TABLE_NAME>_init()
+ * en_icsb_<TABLE_NAME>_cleanup()
+ */
+#define ICSB_NODE(NAME, NAME_STR) ENGINE_FUNC_ICSB(NAME);
+    ICSB_NODES
+#undef ICSB_NODE
+
+/* Define engine nodes for NB, SB, ICNB and ICSB tables
+ *
+ * struct engine_node en_nb_<TABLE_NAME>
+ * struct engine_node en_sb_<TABLE_NAME>
+ * struct engine_node en_icnb_<TABLE_NAME>
+ * struct engine_node en_icsb_<TABLE_NAME>
+ *
+ * Define nodes as static to avoid sparse errors.
+ */
+#define NB_NODE(NAME, NAME_STR) static ENGINE_NODE_NB(NAME);
+    NB_NODES
+#undef NB_NODE
+
+#define SB_NODE(NAME, NAME_STR) static ENGINE_NODE_SB(NAME);
+    SB_NODES
+#undef SB_NODE
+
+#define ICNB_NODE(NAME, NAME_STR) static ENGINE_NODE_ICNB(NAME);
+    ICNB_NODES
+#undef ICNB_NODE
+
+#define ICSB_NODE(NAME, NAME_STR) static ENGINE_NODE_ICSB(NAME);
+    ICSB_NODES
+#undef ICSB_NODE
+
+/* Define engine nodes for other nodes. They should be defined as static to
+ * avoid sparse errors. */
+static ENGINE_NODE(ic);
+
+void inc_proc_ic_init(struct ovsdb_idl_loop *nb,
+                      struct ovsdb_idl_loop *sb,
+                      struct ovsdb_idl_loop *icnb,
+                      struct ovsdb_idl_loop *icsb)
+{
+    /* Define relationships between nodes where first argument is dependent
+     * on the second argument */
+    engine_add_input(&en_ic, &en_nb_nb_global, NULL);
+    engine_add_input(&en_ic, &en_nb_logical_router_static_route, NULL);
+    engine_add_input(&en_ic, &en_nb_logical_router, NULL);
+    engine_add_input(&en_ic, &en_nb_logical_router_port, NULL);
+    engine_add_input(&en_ic, &en_nb_logical_switch, NULL);
+    engine_add_input(&en_ic, &en_nb_logical_switch_port, NULL);
+    engine_add_input(&en_ic, &en_nb_load_balancer, NULL);
+    engine_add_input(&en_ic, &en_nb_load_balancer_group, NULL);
+
+    engine_add_input(&en_ic, &en_sb_sb_global, NULL);
+    engine_add_input(&en_ic, &en_sb_chassis, NULL);
+    engine_add_input(&en_ic, &en_sb_encap, NULL);
+    engine_add_input(&en_ic, &en_sb_datapath_binding, NULL);
+    engine_add_input(&en_ic, &en_sb_port_binding, NULL);
+    engine_add_input(&en_ic, &en_sb_service_monitor, NULL);
+
+    engine_add_input(&en_ic, &en_icnb_ic_nb_global, NULL);
+    engine_add_input(&en_ic, &en_icnb_transit_switch, NULL);
+    engine_add_input(&en_ic, &en_icnb_transit_router, NULL);
+    engine_add_input(&en_ic, &en_icnb_transit_router_port, NULL);
+
+    engine_add_input(&en_ic, &en_icsb_encap, NULL);
+    engine_add_input(&en_ic, &en_icsb_service_monitor, NULL);
+    engine_add_input(&en_ic, &en_icsb_ic_sb_global, NULL);
+    engine_add_input(&en_ic, &en_icsb_port_binding, NULL);
+    engine_add_input(&en_ic, &en_icsb_availability_zone, NULL);
+    engine_add_input(&en_ic, &en_icsb_gateway, NULL);
+    engine_add_input(&en_ic, &en_icsb_route, NULL);
+    engine_add_input(&en_ic, &en_icsb_datapath_binding, NULL);
+
+    struct engine_arg engine_arg = {
+        .nb_idl = nb->idl,
+        .sb_idl = sb->idl,
+        .icnb_idl = icnb->idl,
+        .icsb_idl = icsb->idl,
+    };
+
+    engine_init(&en_ic, &engine_arg);
+}
+
+/* Returns true if the incremental processing ended up updating nodes. */
+bool
+inc_proc_ic_run(struct ic_context *ctx,
+                struct ic_engine_context *ic_eng_ctx)
+{
+    ovs_assert(ctx->ovnnb_txn && ctx->ovnsb_txn &&
+               ctx->ovninb_txn && ctx->ovnisb_txn);
+
+    int64_t start = time_msec();
+    engine_init_run();
+
+    struct engine_context eng_ctx = {
+        .client_ctx = ctx,
+    };
+
+    engine_set_context(&eng_ctx);
+    engine_run(true);
+
+    if (!engine_has_run()) {
+        if (engine_need_run()) {
+            VLOG_DBG("engine did not run, force recompute next time.");
+            engine_set_force_recompute_immediate();
+        } else {
+            VLOG_DBG("engine did not run, and it was not needed");
+        }
+    } else if (engine_canceled()) {
+        VLOG_DBG("engine was canceled, force recompute next time.");
+        engine_set_force_recompute_immediate();
+    } else {
+        engine_clear_force_recompute();
+    }
+
+    int64_t now = time_msec();
+    /* Postpone the next run by length of current run with maximum capped
+     * by "northd-backoff-interval-ms" interval. */
+    ic_eng_ctx->next_run_ms = now + MIN(now - start, ic_eng_ctx->backoff_ms);
+
+    return engine_has_updated();
+}
+
+void
+inc_proc_ic_cleanup(void)
+{
+    engine_cleanup();
+    engine_set_context(NULL);
+}
+
+bool
+inc_proc_ic_can_run(struct ic_engine_context *ctx)
+{
+    if (engine_get_force_recompute() || time_msec() >= ctx->next_run_ms ||
+        ctx->nb_idl_duration_ms >= IDL_LOOP_MAX_DURATION_MS ||
+        ctx->sb_idl_duration_ms >= IDL_LOOP_MAX_DURATION_MS ||
+        ctx->inb_idl_duration_ms >= IDL_LOOP_MAX_DURATION_MS ||
+        ctx->isb_idl_duration_ms >= IDL_LOOP_MAX_DURATION_MS) {
+        return true;
+    }
+
+    poll_timer_wait_until(ctx->next_run_ms);
+    return false;
+}
diff --git a/ic/inc-proc-ic.h b/ic/inc-proc-ic.h
new file mode 100644
index 000000000..9af147fb3
--- /dev/null
+++ b/ic/inc-proc-ic.h
@@ -0,0 +1,48 @@ 
+#ifndef INC_PROC_IC_H
+#define INC_PROC_IC_H 1
+
+#include <config.h>
+
+#include "ovn-ic.h"
+#include "ovsdb-idl.h"
+#include "lib/inc-proc-eng.h"
+
+struct ic_engine_context {
+    int64_t next_run_ms;
+    uint64_t nb_idl_duration_ms;
+    uint64_t sb_idl_duration_ms;
+    uint64_t inb_idl_duration_ms;
+    uint64_t isb_idl_duration_ms;
+    uint32_t backoff_ms;
+};
+
+void inc_proc_ic_init(struct ovsdb_idl_loop *nb,
+                      struct ovsdb_idl_loop *sb,
+                      struct ovsdb_idl_loop *icnb,
+                      struct ovsdb_idl_loop *icsb);
+
+bool inc_proc_ic_run(struct ic_context *ctx,
+                     struct ic_engine_context *ic_eng_ctx);
+
+void inc_proc_ic_cleanup(void);
+bool inc_proc_ic_can_run(struct ic_engine_context *ctx);
+
+static inline void
+inc_proc_ic_force_recompute(void)
+{
+    engine_set_force_recompute();
+}
+
+static inline void
+inc_proc_ic_force_recompute_immediate(void)
+{
+    engine_set_force_recompute_immediate();
+}
+
+static inline bool
+inc_proc_ic_get_force_recompute(void)
+{
+    return engine_get_force_recompute();
+}
+
+#endif /* INC_PROC_IC */
diff --git a/ic/ovn-ic.c b/ic/ovn-ic.c
index 0ef82f3a3..0e8c0953d 100644
--- a/ic/ovn-ic.c
+++ b/ic/ovn-ic.c
@@ -46,6 +46,8 @@ 
 #include "uuid.h"
 #include "openvswitch/vlog.h"
 #include "vec.h"
+#include "inc-proc-ic.h"
+#include "ovn-ic.h"
 
 VLOG_DEFINE_THIS_MODULE(ovn_ic);
 
@@ -55,45 +57,6 @@  static unixctl_cb_func ovn_ic_resume;
 static unixctl_cb_func ovn_ic_is_paused;
 static unixctl_cb_func ovn_ic_status;
 
-struct ic_context {
-    struct ovsdb_idl *ovnnb_idl;
-    struct ovsdb_idl *ovnsb_idl;
-    struct ovsdb_idl *ovninb_idl;
-    struct ovsdb_idl *ovnisb_idl;
-    struct ovsdb_idl_txn *ovnnb_txn;
-    struct ovsdb_idl_txn *ovnsb_txn;
-    struct ovsdb_idl_txn *ovninb_txn;
-    struct ovsdb_idl_txn *ovnisb_txn;
-    const struct icsbrec_availability_zone *runned_az;
-    struct ovsdb_idl_index *nbrec_ls_by_name;
-    struct ovsdb_idl_index *nbrec_lr_by_name;
-    struct ovsdb_idl_index *nbrec_lrp_by_name;
-    struct ovsdb_idl_index *nbrec_port_by_name;
-    struct ovsdb_idl_index *sbrec_chassis_by_name;
-    struct ovsdb_idl_index *sbrec_port_binding_by_name;
-    struct ovsdb_idl_index *sbrec_service_monitor_by_remote_type;
-    struct ovsdb_idl_index *sbrec_service_monitor_by_ic_learned;
-    struct ovsdb_idl_index *sbrec_service_monitor_by_remote_type_logical_port;
-    struct ovsdb_idl_index *icnbrec_transit_switch_by_name;
-    struct ovsdb_idl_index *icsbrec_port_binding_by_az;
-    struct ovsdb_idl_index *icsbrec_port_binding_by_ts;
-    struct ovsdb_idl_index *icsbrec_port_binding_by_ts_az;
-    struct ovsdb_idl_index *icsbrec_route_by_az;
-    struct ovsdb_idl_index *icsbrec_route_by_ts;
-    struct ovsdb_idl_index *icsbrec_route_by_ts_az;
-    struct ovsdb_idl_index *icsbrec_service_monitor_by_source_az;
-    struct ovsdb_idl_index *icsbrec_service_monitor_by_target_az;
-    struct ovsdb_idl_index *icsbrec_service_monitor_by_target_az_logical_port;
-};
-
-struct ic_state {
-    bool had_lock;
-    bool paused;
-};
-
-enum ic_datapath_type { IC_SWITCH, IC_ROUTER, IC_DATAPATH_MAX };
-enum ic_port_binding_type { IC_SWITCH_PORT, IC_ROUTER_PORT, IC_PORT_MAX };
-
 static const char *ovnnb_db;
 static const char *ovnsb_db;
 static const char *ovn_ic_nb_db;
@@ -3091,7 +3054,7 @@  update_sequence_numbers(struct ic_context *ctx,
     }
 }
 
-static void
+void
 ovn_db_run(struct ic_context *ctx)
 {
     struct hmap dp_tnlids = HMAP_INITIALIZER(&dp_tnlids);
@@ -3311,172 +3274,183 @@  main(int argc, char *argv[])
     /* ovn-ic-nb db. */
     struct ovsdb_idl_loop ovninb_idl_loop = OVSDB_IDL_LOOP_INITIALIZER(
         ovsdb_idl_create(ovn_ic_nb_db, &icnbrec_idl_class, true, true));
+    ovsdb_idl_track_add_all(ovninb_idl_loop.idl);
 
     /* ovn-ic-sb db. */
     struct ovsdb_idl_loop ovnisb_idl_loop = OVSDB_IDL_LOOP_INITIALIZER(
         ovsdb_idl_create(ovn_ic_sb_db, &icsbrec_idl_class, true, true));
+    ovsdb_idl_track_add_all(ovnisb_idl_loop.idl);
 
     /* ovn-nb db. */
     struct ovsdb_idl_loop ovnnb_idl_loop = OVSDB_IDL_LOOP_INITIALIZER(
         ovsdb_idl_create(ovnnb_db, &nbrec_idl_class, false, true));
 
     ovsdb_idl_add_table(ovnnb_idl_loop.idl, &nbrec_table_nb_global);
-    ovsdb_idl_add_column(ovnnb_idl_loop.idl, &nbrec_nb_global_col_name);
-    ovsdb_idl_add_column(ovnnb_idl_loop.idl, &nbrec_nb_global_col_options);
+    ovsdb_idl_track_add_column(ovnnb_idl_loop.idl,
+                               &nbrec_nb_global_col_name);
+    ovsdb_idl_track_add_column(ovnnb_idl_loop.idl,
+                               &nbrec_nb_global_col_options);
 
     ovsdb_idl_add_table(ovnnb_idl_loop.idl,
                         &nbrec_table_logical_router_static_route);
-    ovsdb_idl_add_column(ovnnb_idl_loop.idl,
-                         &nbrec_logical_router_static_route_col_route_table);
-    ovsdb_idl_add_column(ovnnb_idl_loop.idl,
-                         &nbrec_logical_router_static_route_col_ip_prefix);
-    ovsdb_idl_add_column(ovnnb_idl_loop.idl,
-                         &nbrec_logical_router_static_route_col_nexthop);
-    ovsdb_idl_add_column(ovnnb_idl_loop.idl,
-                         &nbrec_logical_router_static_route_col_external_ids);
-    ovsdb_idl_add_column(ovnnb_idl_loop.idl,
-                         &nbrec_logical_router_static_route_col_options);
-    ovsdb_idl_add_column(ovnnb_idl_loop.idl,
-                         &nbrec_logical_router_static_route_col_policy);
+    ovsdb_idl_track_add_column(ovnnb_idl_loop.idl,
+                    &nbrec_logical_router_static_route_col_route_table);
+    ovsdb_idl_track_add_column(ovnnb_idl_loop.idl,
+                    &nbrec_logical_router_static_route_col_ip_prefix);
+    ovsdb_idl_track_add_column(ovnnb_idl_loop.idl,
+                    &nbrec_logical_router_static_route_col_nexthop);
+    ovsdb_idl_track_add_column(ovnnb_idl_loop.idl,
+                    &nbrec_logical_router_static_route_col_external_ids);
+    ovsdb_idl_track_add_column(ovnnb_idl_loop.idl,
+                    &nbrec_logical_router_static_route_col_options);
+    ovsdb_idl_track_add_column(ovnnb_idl_loop.idl,
+                    &nbrec_logical_router_static_route_col_policy);
 
     ovsdb_idl_add_table(ovnnb_idl_loop.idl, &nbrec_table_logical_router);
-    ovsdb_idl_add_column(ovnnb_idl_loop.idl,
-                         &nbrec_logical_router_col_name);
-    ovsdb_idl_add_column(ovnnb_idl_loop.idl,
-                         &nbrec_logical_router_col_static_routes);
-    ovsdb_idl_add_column(ovnnb_idl_loop.idl,
-                         &nbrec_logical_router_col_ports);
-    ovsdb_idl_add_column(ovnnb_idl_loop.idl,
-                         &nbrec_logical_router_col_options);
-    ovsdb_idl_add_column(ovnnb_idl_loop.idl,
-                         &nbrec_logical_router_col_external_ids);
-    ovsdb_idl_add_column(ovnnb_idl_loop.idl,
-                         &nbrec_logical_router_col_enabled);
-    ovsdb_idl_add_column(ovnnb_idl_loop.idl,
-                         &nbrec_logical_router_col_load_balancer);
-    ovsdb_idl_add_column(ovnnb_idl_loop.idl,
-                         &nbrec_logical_router_col_load_balancer_group);
+    ovsdb_idl_track_add_column(ovnnb_idl_loop.idl,
+                               &nbrec_logical_router_col_name);
+    ovsdb_idl_track_add_column(ovnnb_idl_loop.idl,
+                               &nbrec_logical_router_col_static_routes);
+    ovsdb_idl_track_add_column(ovnnb_idl_loop.idl,
+                               &nbrec_logical_router_col_ports);
+    ovsdb_idl_track_add_column(ovnnb_idl_loop.idl,
+                               &nbrec_logical_router_col_options);
+    ovsdb_idl_track_add_column(ovnnb_idl_loop.idl,
+                               &nbrec_logical_router_col_external_ids);
+    ovsdb_idl_track_add_column(ovnnb_idl_loop.idl,
+                               &nbrec_logical_router_col_enabled);
+    ovsdb_idl_track_add_column(ovnnb_idl_loop.idl,
+                               &nbrec_logical_router_col_load_balancer);
+    ovsdb_idl_track_add_column(ovnnb_idl_loop.idl,
+                               &nbrec_logical_router_col_load_balancer_group);
 
     ovsdb_idl_add_table(ovnnb_idl_loop.idl, &nbrec_table_logical_router_port);
-    ovsdb_idl_add_column(ovnnb_idl_loop.idl,
-                         &nbrec_logical_router_port_col_mac);
-    ovsdb_idl_add_column(ovnnb_idl_loop.idl,
-                         &nbrec_logical_router_port_col_name);
-    ovsdb_idl_add_column(ovnnb_idl_loop.idl,
-                         &nbrec_logical_router_port_col_networks);
-    ovsdb_idl_add_column(ovnnb_idl_loop.idl,
-                         &nbrec_logical_router_port_col_external_ids);
-    ovsdb_idl_add_column(ovnnb_idl_loop.idl,
-                         &nbrec_logical_router_port_col_options);
+    ovsdb_idl_track_add_column(ovnnb_idl_loop.idl,
+                               &nbrec_logical_router_port_col_mac);
+    ovsdb_idl_track_add_column(ovnnb_idl_loop.idl,
+                               &nbrec_logical_router_port_col_name);
+    ovsdb_idl_track_add_column(ovnnb_idl_loop.idl,
+                               &nbrec_logical_router_port_col_networks);
+    ovsdb_idl_track_add_column(ovnnb_idl_loop.idl,
+                               &nbrec_logical_router_port_col_external_ids);
+    ovsdb_idl_track_add_column(ovnnb_idl_loop.idl,
+                               &nbrec_logical_router_port_col_options);
 
     ovsdb_idl_add_table(ovnnb_idl_loop.idl, &nbrec_table_logical_switch);
-    ovsdb_idl_add_column(ovnnb_idl_loop.idl,
-                         &nbrec_logical_switch_col_name);
-    ovsdb_idl_add_column(ovnnb_idl_loop.idl,
-                         &nbrec_logical_switch_col_ports);
-    ovsdb_idl_add_column(ovnnb_idl_loop.idl,
-                         &nbrec_logical_switch_col_other_config);
-    ovsdb_idl_add_column(ovnnb_idl_loop.idl,
-                         &nbrec_logical_switch_col_external_ids);
+    ovsdb_idl_track_add_column(ovnnb_idl_loop.idl,
+                               &nbrec_logical_switch_col_name);
+    ovsdb_idl_track_add_column(ovnnb_idl_loop.idl,
+                               &nbrec_logical_switch_col_ports);
+    ovsdb_idl_track_add_column(ovnnb_idl_loop.idl,
+                               &nbrec_logical_switch_col_other_config);
+    ovsdb_idl_track_add_column(ovnnb_idl_loop.idl,
+                               &nbrec_logical_switch_col_external_ids);
 
     ovsdb_idl_add_table(ovnnb_idl_loop.idl, &nbrec_table_logical_switch_port);
-    ovsdb_idl_add_column(ovnnb_idl_loop.idl,
-                         &nbrec_logical_switch_port_col_name);
-    ovsdb_idl_add_column(ovnnb_idl_loop.idl,
-                         &nbrec_logical_switch_port_col_addresses);
-    ovsdb_idl_add_column(ovnnb_idl_loop.idl,
-                         &nbrec_logical_switch_port_col_options);
-    ovsdb_idl_add_column(ovnnb_idl_loop.idl,
-                         &nbrec_logical_switch_port_col_type);
-    ovsdb_idl_add_column(ovnnb_idl_loop.idl,
-                         &nbrec_logical_switch_port_col_up);
-    ovsdb_idl_add_column(ovnnb_idl_loop.idl,
-                         &nbrec_logical_switch_port_col_addresses);
-    ovsdb_idl_add_column(ovnnb_idl_loop.idl,
-                         &nbrec_logical_switch_port_col_enabled);
-    ovsdb_idl_add_column(ovnnb_idl_loop.idl,
-                         &nbrec_logical_switch_port_col_external_ids);
-
-    ovsdb_idl_add_table(ovnnb_idl_loop.idl,
-                        &nbrec_table_load_balancer);
-    ovsdb_idl_add_column(ovnnb_idl_loop.idl,
-                         &nbrec_load_balancer_col_vips);
-
-    ovsdb_idl_add_table(ovnnb_idl_loop.idl,
-                        &nbrec_table_load_balancer_group);
-    ovsdb_idl_add_column(ovnnb_idl_loop.idl,
-                         &nbrec_load_balancer_group_col_load_balancer);
+    ovsdb_idl_track_add_column(ovnnb_idl_loop.idl,
+                               &nbrec_logical_switch_port_col_name);
+    ovsdb_idl_track_add_column(ovnnb_idl_loop.idl,
+                               &nbrec_logical_switch_port_col_addresses);
+    ovsdb_idl_track_add_column(ovnnb_idl_loop.idl,
+                               &nbrec_logical_switch_port_col_options);
+    ovsdb_idl_track_add_column(ovnnb_idl_loop.idl,
+                               &nbrec_logical_switch_port_col_type);
+    ovsdb_idl_track_add_column(ovnnb_idl_loop.idl,
+                               &nbrec_logical_switch_port_col_up);
+    ovsdb_idl_track_add_column(ovnnb_idl_loop.idl,
+                               &nbrec_logical_switch_port_col_addresses);
+    ovsdb_idl_track_add_column(ovnnb_idl_loop.idl,
+                               &nbrec_logical_switch_port_col_enabled);
+    ovsdb_idl_track_add_column(ovnnb_idl_loop.idl,
+                               &nbrec_logical_switch_port_col_external_ids);
+
+    ovsdb_idl_add_table(ovnnb_idl_loop.idl, &nbrec_table_load_balancer);
+    ovsdb_idl_track_add_column(ovnnb_idl_loop.idl,
+                               &nbrec_load_balancer_col_vips);
+
+    ovsdb_idl_add_table(ovnnb_idl_loop.idl, &nbrec_table_load_balancer_group);
+    ovsdb_idl_track_add_column(ovnnb_idl_loop.idl,
+                               &nbrec_load_balancer_group_col_load_balancer);
 
     /* ovn-sb db. */
     struct ovsdb_idl_loop ovnsb_idl_loop = OVSDB_IDL_LOOP_INITIALIZER(
         ovsdb_idl_create(ovnsb_db, &sbrec_idl_class, false, true));
 
     ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_sb_global);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_sb_global_col_options);
+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,
+                               &sbrec_sb_global_col_options);
 
     ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_chassis);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_chassis_col_encaps);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_chassis_col_name);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_chassis_col_hostname);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_chassis_col_other_config);
+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,
+                               &sbrec_chassis_col_encaps);
+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,
+                               &sbrec_chassis_col_name);
+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,
+                               &sbrec_chassis_col_hostname);
+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,
+                               &sbrec_chassis_col_other_config);
 
     ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_encap);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_encap_col_chassis_name);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_encap_col_type);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_encap_col_ip);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_encap_col_options);
+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,
+                               &sbrec_encap_col_chassis_name);
+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,
+                               &sbrec_encap_col_type);
+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,
+                               &sbrec_encap_col_ip);
+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,
+                               &sbrec_encap_col_options);
 
     ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_datapath_binding);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_datapath_binding_col_type);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
-                         &sbrec_datapath_binding_col_external_ids);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
-                         &sbrec_datapath_binding_col_nb_uuid);
+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,
+                               &sbrec_datapath_binding_col_type);
+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,
+                               &sbrec_datapath_binding_col_external_ids);
+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,
+                               &sbrec_datapath_binding_col_nb_uuid);
 
     ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_port_binding);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
-                         &sbrec_port_binding_col_datapath);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
-                         &sbrec_port_binding_col_mac);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
-                         &sbrec_port_binding_col_options);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
-                         &sbrec_port_binding_col_logical_port);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
-                         &sbrec_port_binding_col_external_ids);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
-                         &sbrec_port_binding_col_chassis);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
-                         &sbrec_port_binding_col_up);
-
-    ovsdb_idl_add_table(ovnsb_idl_loop.idl,
-                        &sbrec_table_service_monitor);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
-                         &sbrec_service_monitor_col_chassis_name);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
-                         &sbrec_service_monitor_col_external_ids);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
-                         &sbrec_service_monitor_col_type);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
-                         &sbrec_service_monitor_col_ip);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
-                         &sbrec_service_monitor_col_logical_port);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
-                         &sbrec_service_monitor_col_port);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
-                         &sbrec_service_monitor_col_protocol);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
-                         &sbrec_service_monitor_col_src_ip);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
-                         &sbrec_service_monitor_col_src_mac);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
-                         &sbrec_service_monitor_col_remote);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
-                         &sbrec_service_monitor_col_ic_learned);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
-                         &sbrec_service_monitor_col_status);
-    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
-                         &sbrec_service_monitor_col_options);
+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,
+                               &sbrec_port_binding_col_datapath);
+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,
+                               &sbrec_port_binding_col_mac);
+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,
+                               &sbrec_port_binding_col_options);
+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,
+                               &sbrec_port_binding_col_logical_port);
+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,
+                               &sbrec_port_binding_col_external_ids);
+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,
+                               &sbrec_port_binding_col_chassis);
+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,
+                               &sbrec_port_binding_col_up);
+
+    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_service_monitor);
+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,
+                               &sbrec_service_monitor_col_chassis_name);
+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,
+                               &sbrec_service_monitor_col_external_ids);
+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,
+                               &sbrec_service_monitor_col_type);
+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,
+                               &sbrec_service_monitor_col_ip);
+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,
+                               &sbrec_service_monitor_col_logical_port);
+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,
+                               &sbrec_service_monitor_col_port);
+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,
+                               &sbrec_service_monitor_col_protocol);
+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,
+                               &sbrec_service_monitor_col_src_ip);
+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,
+                               &sbrec_service_monitor_col_src_mac);
+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,
+                               &sbrec_service_monitor_col_remote);
+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,
+                               &sbrec_service_monitor_col_ic_learned);
+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,
+                               &sbrec_service_monitor_col_status);
+    ovsdb_idl_track_add_column(ovnsb_idl_loop.idl,
+                               &sbrec_service_monitor_col_options);
 
     /* Create IDL indexes */
     struct ovsdb_idl_index *nbrec_ls_by_name
@@ -3497,7 +3471,7 @@  main(int argc, char *argv[])
         = ovsdb_idl_index_create1(ovnsb_idl_loop.idl,
                                   &sbrec_chassis_col_name);
 
-   struct ovsdb_idl_index *sbrec_service_monitor_by_remote_type
+    struct ovsdb_idl_index *sbrec_service_monitor_by_remote_type
         = ovsdb_idl_index_create1(ovnsb_idl_loop.idl,
                                   &sbrec_service_monitor_col_remote);
 
@@ -3562,10 +3536,22 @@  main(int argc, char *argv[])
     unixctl_command_register("ic-sb-connection-status", "", 0, 0,
                              ovn_conn_show, ovnisb_idl_loop.idl);
 
+    /* Initialize incremental processing engine for ovn-northd */
+    inc_proc_ic_init(&ovnnb_idl_loop, &ovnsb_idl_loop,
+                     &ovninb_idl_loop, &ovnisb_idl_loop);
+
+    unsigned int ovnnb_cond_seqno = UINT_MAX;
+    unsigned int ovnsb_cond_seqno = UINT_MAX;
+    unsigned int ovninb_cond_seqno = UINT_MAX;
+    unsigned int ovnisb_cond_seqno = UINT_MAX;
+
     /* Main loop. */
+    struct ic_engine_context  eng_ctx = {0};
+
     exiting = false;
     state.had_lock = false;
     state.paused = false;
+
     while (!exiting) {
         update_ssl_config();
         update_idl_probe_interval(ovnsb_idl_loop.idl, ovnnb_idl_loop.idl,
@@ -3579,6 +3565,7 @@  main(int argc, char *argv[])
             simap_destroy(&usage);
         }
 
+        bool clear_idl_track = true;
         if (!state.paused) {
             if (!ovsdb_idl_has_lock(ovnsb_idl_loop.idl) &&
                 !ovsdb_idl_is_lock_contended(ovnsb_idl_loop.idl))
@@ -3590,15 +3577,67 @@  main(int argc, char *argv[])
                 ovsdb_idl_set_lock(ovnsb_idl_loop.idl, "ovn_ic");
             }
 
+            struct ovsdb_idl_txn *ovnnb_txn =
+                run_idl_loop(&ovnnb_idl_loop, "OVN_Northbound",
+                             &eng_ctx.nb_idl_duration_ms);
+            unsigned int new_ovnnb_cond_seqno =
+                        ovsdb_idl_get_condition_seqno(ovnnb_idl_loop.idl);
+            if (new_ovnnb_cond_seqno != ovnnb_cond_seqno) {
+                if (!new_ovnnb_cond_seqno) {
+                    VLOG_INFO("OVN NB IDL reconnected, force recompute.");
+                    inc_proc_ic_force_recompute();
+                }
+                ovnnb_cond_seqno = new_ovnnb_cond_seqno;
+            }
+
+            struct ovsdb_idl_txn *ovnsb_txn =
+                run_idl_loop(&ovnsb_idl_loop, "OVN_Southbound",
+                             &eng_ctx.sb_idl_duration_ms);
+            unsigned int new_ovnsb_cond_seqno =
+                        ovsdb_idl_get_condition_seqno(ovnsb_idl_loop.idl);
+            if (new_ovnsb_cond_seqno != ovnsb_cond_seqno) {
+                if (!new_ovnsb_cond_seqno) {
+                    VLOG_INFO("OVN SB IDL reconnected, force recompute.");
+                    inc_proc_ic_force_recompute();
+                }
+                ovnsb_cond_seqno = new_ovnsb_cond_seqno;
+            }
+
+            struct ovsdb_idl_txn *ovninb_txn =
+                run_idl_loop(&ovninb_idl_loop, "OVN_IC_Northbound",
+                             &eng_ctx.inb_idl_duration_ms);
+            unsigned int new_ovninb_cond_seqno =
+                        ovsdb_idl_get_condition_seqno(ovninb_idl_loop.idl);
+            if (new_ovninb_cond_seqno != ovninb_cond_seqno) {
+                if (!new_ovninb_cond_seqno) {
+                    VLOG_INFO("OVN INB IDL reconnected, force recompute.");
+                    inc_proc_ic_force_recompute();
+                }
+                ovninb_cond_seqno = new_ovninb_cond_seqno;
+            }
+
+            struct ovsdb_idl_txn *ovnisb_txn =
+                run_idl_loop(&ovnisb_idl_loop, "OVN_IC_Southbound",
+                             &eng_ctx.isb_idl_duration_ms);
+            unsigned int new_ovnisb_cond_seqno =
+                        ovsdb_idl_get_condition_seqno(ovnisb_idl_loop.idl);
+            if (new_ovnisb_cond_seqno != ovnisb_cond_seqno) {
+                if (!new_ovnisb_cond_seqno) {
+                    VLOG_INFO("OVN ISB IDL reconnected, force recompute.");
+                    inc_proc_ic_force_recompute();
+                }
+                ovnisb_cond_seqno = new_ovnisb_cond_seqno;
+            }
+
             struct ic_context ctx = {
                 .ovnnb_idl = ovnnb_idl_loop.idl,
-                .ovnnb_txn = ovsdb_idl_loop_run(&ovnnb_idl_loop),
+                .ovnnb_txn = ovnnb_txn,
                 .ovnsb_idl = ovnsb_idl_loop.idl,
-                .ovnsb_txn = ovsdb_idl_loop_run(&ovnsb_idl_loop),
+                .ovnsb_txn = ovnsb_txn,
                 .ovninb_idl = ovninb_idl_loop.idl,
-                .ovninb_txn = ovsdb_idl_loop_run(&ovninb_idl_loop),
+                .ovninb_txn = ovninb_txn,
                 .ovnisb_idl = ovnisb_idl_loop.idl,
-                .ovnisb_txn = ovsdb_idl_loop_run(&ovnisb_idl_loop),
+                .ovnisb_txn = ovnisb_txn,
                 .nbrec_ls_by_name = nbrec_ls_by_name,
                 .nbrec_lr_by_name = nbrec_lr_by_name,
                 .nbrec_lrp_by_name = nbrec_lrp_by_name,
@@ -3627,44 +3666,75 @@  main(int argc, char *argv[])
                     icsbrec_service_monitor_by_target_az_logical_port,
             };
 
-            if (!state.had_lock && ovsdb_idl_has_lock(ovnsb_idl_loop.idl)) {
+            if (!state.had_lock && ovsdb_idl_has_lock(ctx.ovnsb_idl)) {
                 VLOG_INFO("ovn-ic lock acquired. "
-                        "This ovn-ic instance is now active.");
+                            "This ovn-ic instance is now active.");
                 state.had_lock = true;
             } else if (state.had_lock &&
-                       !ovsdb_idl_has_lock(ovnsb_idl_loop.idl)) {
+                       !ovsdb_idl_has_lock(ctx.ovnsb_idl)) {
                 VLOG_INFO("ovn-ic lock lost. "
-                        "This ovn-ic instance is now on standby.");
+                            "This ovn-ic instance is now on standby.");
                 state.had_lock = false;
             }
 
-            if (ovsdb_idl_has_lock(ovnsb_idl_loop.idl) &&
+            if (ovsdb_idl_has_lock(ctx.ovnsb_idl) &&
                 ovsdb_idl_has_ever_connected(ctx.ovnnb_idl) &&
                 ovsdb_idl_has_ever_connected(ctx.ovnsb_idl) &&
                 ovsdb_idl_has_ever_connected(ctx.ovninb_idl) &&
                 ovsdb_idl_has_ever_connected(ctx.ovnisb_idl)) {
-                const struct icsbrec_availability_zone *az = az_run(&ctx);
-                VLOG_DBG("Availability zone: %s", az ? az->name :
-                                               "not created yet.");
-                if (az) {
-                    ovn_db_run(&ctx);
-                    update_sequence_numbers(&ctx, &ovnisb_idl_loop);
+                if (ctx.ovnnb_txn && ctx.ovnsb_txn && ctx.ovninb_txn &&
+                    ctx.ovnisb_txn && inc_proc_ic_can_run(&eng_ctx)) {
+                    ctx.runned_az = az_run(&ctx);
+                    VLOG_DBG("Availability zone: %s", ctx.runned_az ?
+                             ctx.runned_az->name : "not created yet.");
+                    if (ctx.runned_az) {
+                        (void) inc_proc_ic_run(&ctx, &eng_ctx);
+                        update_sequence_numbers(&ctx, &ovnisb_idl_loop);
+                    }
+                } else if (!inc_proc_ic_get_force_recompute()) {
+                    clear_idl_track = false;
+                }
+                /* If there are any errors, we force a full recompute in order
+                 * to ensure we handle all changes. */
+                if (!ovsdb_idl_loop_commit_and_wait(&ovnnb_idl_loop)) {
+                    VLOG_INFO("OVNNB commit failed, "
+                                "force recompute next time.");
+                    inc_proc_ic_force_recompute_immediate();
                 }
 
-            }
+                if (!ovsdb_idl_loop_commit_and_wait(&ovnsb_idl_loop)) {
+                    VLOG_INFO("OVNSB commit failed, "
+                                "force recompute next time.");
+                    inc_proc_ic_force_recompute_immediate();
+                }
+
+                if (!ovsdb_idl_loop_commit_and_wait(&ovninb_idl_loop)) {
+                    VLOG_INFO("OVNINB commit failed, "
+                                "force recompute next time.");
+                    inc_proc_ic_force_recompute_immediate();
+                }
 
-            int rc1 = ovsdb_idl_loop_commit_and_wait(&ovnnb_idl_loop);
-            int rc2 = ovsdb_idl_loop_commit_and_wait(&ovnsb_idl_loop);
-            int rc3 = ovsdb_idl_loop_commit_and_wait(&ovninb_idl_loop);
-            int rc4 = ovsdb_idl_loop_commit_and_wait(&ovnisb_idl_loop);
-            if (!rc1 || !rc2 || !rc3 || !rc4) {
-                VLOG_DBG(" a transaction failed in: %s %s %s %s",
-                         !rc1 ? "nb" : "", !rc2 ? "sb" : "",
-                         !rc3 ? "ic_nb" : "", !rc4 ? "ic_sb" : "");
-                /* A transaction failed. Wake up immediately to give
-                 * opportunity to send the proper transaction
-                 */
-                poll_immediate_wake();
+                if (!ovsdb_idl_loop_commit_and_wait(&ovnisb_idl_loop)) {
+                    VLOG_INFO("OVNISB commit failed, "
+                                "force recompute next time.");
+                    inc_proc_ic_force_recompute_immediate();
+                }
+            } else {
+                /* Make sure we send any pending requests, e.g., lock. */
+                int rc1 = ovsdb_idl_loop_commit_and_wait(&ovnnb_idl_loop);
+                int rc2 = ovsdb_idl_loop_commit_and_wait(&ovnsb_idl_loop);
+                int rc3 = ovsdb_idl_loop_commit_and_wait(&ovninb_idl_loop);
+                int rc4 = ovsdb_idl_loop_commit_and_wait(&ovnisb_idl_loop);
+                if (!rc1 || !rc2 || !rc3 || !rc4) {
+                    VLOG_DBG(" a transaction failed in: %s %s %s %s",
+                            !rc1 ? "nb" : "", !rc2 ? "sb" : "",
+                            !rc3 ? "ic_nb" : "", !rc4 ? "ic_sb" : "");
+                    /* A transaction failed. Wake up immediately to give
+                    * opportunity to send the proper transaction
+                    */
+                }
+                /* Force a full recompute next time we become active. */
+                inc_proc_ic_force_recompute();
             }
         } else {
             /* ovn-ic is paused
@@ -3690,6 +3760,16 @@  main(int argc, char *argv[])
             ovsdb_idl_wait(ovnsb_idl_loop.idl);
             ovsdb_idl_wait(ovninb_idl_loop.idl);
             ovsdb_idl_wait(ovnisb_idl_loop.idl);
+
+            /* Force a full recompute next time we become active. */
+            inc_proc_ic_force_recompute_immediate();
+        }
+
+        if (clear_idl_track) {
+            ovsdb_idl_track_clear(ovnnb_idl_loop.idl);
+            ovsdb_idl_track_clear(ovnsb_idl_loop.idl);
+            ovsdb_idl_track_clear(ovninb_idl_loop.idl);
+            ovsdb_idl_track_clear(ovnisb_idl_loop.idl);
         }
 
         unixctl_server_run(unixctl);
@@ -3704,6 +3784,7 @@  main(int argc, char *argv[])
             exiting = true;
         }
     }
+    inc_proc_ic_cleanup();
 
     unixctl_server_destroy(unixctl);
     ovsdb_idl_loop_destroy(&ovnnb_idl_loop);
diff --git a/ic/ovn-ic.h b/ic/ovn-ic.h
new file mode 100644
index 000000000..e8d7a970f
--- /dev/null
+++ b/ic/ovn-ic.h
@@ -0,0 +1,61 @@ 
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef OVN_IC_H
+#define OVN_IC_H 1
+
+#include "ovsdb-idl.h"
+#include "unixctl.h"
+
+struct ic_context {
+    struct ovsdb_idl *ovnnb_idl;
+    struct ovsdb_idl *ovnsb_idl;
+    struct ovsdb_idl *ovninb_idl;
+    struct ovsdb_idl *ovnisb_idl;
+    struct ovsdb_idl_txn *ovnnb_txn;
+    struct ovsdb_idl_txn *ovnsb_txn;
+    struct ovsdb_idl_txn *ovninb_txn;
+    struct ovsdb_idl_txn *ovnisb_txn;
+    const struct icsbrec_availability_zone *runned_az;
+    struct ovsdb_idl_index *nbrec_ls_by_name;
+    struct ovsdb_idl_index *nbrec_lr_by_name;
+    struct ovsdb_idl_index *nbrec_lrp_by_name;
+    struct ovsdb_idl_index *nbrec_port_by_name;
+    struct ovsdb_idl_index *sbrec_chassis_by_name;
+    struct ovsdb_idl_index *sbrec_port_binding_by_name;
+    struct ovsdb_idl_index *sbrec_service_monitor_by_remote_type;
+    struct ovsdb_idl_index *sbrec_service_monitor_by_ic_learned;
+    struct ovsdb_idl_index *sbrec_service_monitor_by_remote_type_logical_port;
+    struct ovsdb_idl_index *icnbrec_transit_switch_by_name;
+    struct ovsdb_idl_index *icsbrec_port_binding_by_az;
+    struct ovsdb_idl_index *icsbrec_port_binding_by_ts;
+    struct ovsdb_idl_index *icsbrec_port_binding_by_ts_az;
+    struct ovsdb_idl_index *icsbrec_route_by_az;
+    struct ovsdb_idl_index *icsbrec_route_by_ts;
+    struct ovsdb_idl_index *icsbrec_route_by_ts_az;
+    struct ovsdb_idl_index *icsbrec_service_monitor_by_source_az;
+    struct ovsdb_idl_index *icsbrec_service_monitor_by_target_az;
+    struct ovsdb_idl_index *icsbrec_service_monitor_by_target_az_logical_port;
+};
+
+struct ic_state {
+    bool had_lock;
+    bool paused;
+};
+
+enum ic_datapath_type { IC_SWITCH, IC_ROUTER, IC_DATAPATH_MAX };
+enum ic_port_binding_type { IC_SWITCH_PORT, IC_ROUTER_PORT, IC_PORT_MAX };
+
+void ovn_db_run(struct ic_context *ctx);
+
+#endif /* OVN_IC_H */
diff --git a/lib/inc-proc-eng.h b/lib/inc-proc-eng.h
index 02eeb46fe..1cb2466b2 100644
--- a/lib/inc-proc-eng.h
+++ b/lib/inc-proc-eng.h
@@ -158,6 +158,8 @@  struct engine_context {
     struct ovsdb_idl_txn *ovnsb_idl_txn;
     struct ovsdb_idl_txn *ovnnb_idl_txn;
     struct ovsdb_idl_txn *ovnbr_idl_txn;
+    struct ovsdb_idl_txn *ovnisb_idl_txn;
+    struct ovsdb_idl_txn *ovninb_idl_txn;
 
     void *client_ctx;
 };
@@ -167,6 +169,8 @@  struct engine_arg {
     struct ovsdb_idl *sb_idl;
     struct ovsdb_idl *nb_idl;
     struct ovsdb_idl *ovnbr_idl;
+    struct ovsdb_idl *icsb_idl;
+    struct ovsdb_idl *icnb_idl;
     struct ovsdb_idl *ovs_idl;
 };
 
@@ -547,6 +551,16 @@  en_##DB_NAME##_##TBL_NAME##_compute_failure_info(struct engine_node *node)  \
 #define ENGINE_FUNC_BR(TBL_NAME) \
     ENGINE_FUNC_OVSDB(ovnbr, TBL_NAME)
 
+/* Macro to define member functions of an engine node which represents
+ * a table of OVN ISB DB */
+#define ENGINE_FUNC_ICSB(TBL_NAME) \
+    ENGINE_FUNC_OVSDB(icsb, TBL_NAME)
+
+/* Macro to define member functions of an engine node which represents
+ * a table of OVN INB DB */
+#define ENGINE_FUNC_ICNB(TBL_NAME) \
+    ENGINE_FUNC_OVSDB(icnb, TBL_NAME)
+
 /* Macro to define member functions of an engine node which represents
  * a table of open_vswitch DB */
 #define ENGINE_FUNC_OVS(TBL_NAME) \
@@ -569,6 +583,13 @@  en_##DB_NAME##_##TBL_NAME##_compute_failure_info(struct engine_node *node)  \
 /* Macro to define an engine node which represents a table of OVN BR DB */
 #define ENGINE_NODE_BR(TBL_NAME) \
     ENGINE_NODE_OVSDB(ovnbr, "BR", TBL_NAME, #TBL_NAME);
+/* Macro to define an engine node which represents a table of OVN ISB DB */
+#define ENGINE_NODE_ICSB(TBL_NAME) \
+    ENGINE_NODE_OVSDB(icsb, "ICSB", TBL_NAME, #TBL_NAME);
+
+/* Macro to define an engine node which represents a table of OVN INB DB */
+#define ENGINE_NODE_ICNB(TBL_NAME) \
+    ENGINE_NODE_OVSDB(icnb, "ICNB", TBL_NAME, #TBL_NAME);
 
 /* Macro to define an engine node which represents a table of open_vswitch
  * DB */