diff mbox series

[ovs-dev,v9,1/3] controller: Defer until sb data is available.

Message ID 67010dca0a8dcc390e873349d662c9163e01050f.1746623091.git.felix.huettner@stackit.cloud
State Changes Requested
Headers show
Series controller: Add garp_rarp engine. | expand

Checks

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

Commit Message

Felix Huettner May 7, 2025, 2:21 p.m. UTC
When starting the ovn-controller without monitor_all we need a few
iterations of the engine node and updating monitoring conditions until
we have loaded all data.
Previously this was handled by the number of main loop iterations of
ovn-controller and an arbitrary timeout.
However this is was always best effort and there was no guarantee that
we actually had loaded all data.

To solve this we now track the monitoring requests we sent out to
southbound and when that data becomes available.

For monitor_all we just need to have the initial monitoring conditions
fullfilled and are afterwards sure that we have all required data.

For non monitor_all we need at least one more iteration of the engine
node and updating sb monitoring conditions. We limit this to 20 rounds
for now, which should be sufficient and is at least the value of what
the previous implementation provided.
However if we notice that we do not update monitoring condition anymore
after a engine run we can also assume that we have finished loading all
data.

Signed-off-by: Felix Huettner <felix.huettner@stackit.cloud>
---
v8->v9: addressed reviews
v7->v8: introduced

 controller/ovn-controller.c | 27 ++++++++++++++++++++++++++-
 lib/ovn-util.c              | 19 ++++++++++++++-----
 tests/ovn-controller.at     |  1 -
 tests/ovn-performance.at    |  3 ---
 tests/ovn.at                | 10 ----------
 5 files changed, 40 insertions(+), 20 deletions(-)

Comments

Felix Huettner May 19, 2025, 12:58 p.m. UTC | #1
On Wed, May 07, 2025 at 04:21:02PM +0200, Felix Huettner via dev wrote:
> When starting the ovn-controller without monitor_all we need a few
> iterations of the engine node and updating monitoring conditions until
> we have loaded all data.
> Previously this was handled by the number of main loop iterations of
> ovn-controller and an arbitrary timeout.
> However this is was always best effort and there was no guarantee that
> we actually had loaded all data.
> 
> To solve this we now track the monitoring requests we sent out to
> southbound and when that data becomes available.
> 
> For monitor_all we just need to have the initial monitoring conditions
> fullfilled and are afterwards sure that we have all required data.
> 
> For non monitor_all we need at least one more iteration of the engine
> node and updating sb monitoring conditions. We limit this to 20 rounds
> for now, which should be sufficient and is at least the value of what
> the previous implementation provided.
> However if we notice that we do not update monitoring condition anymore
> after a engine run we can also assume that we have finished loading all
> data.
> 

Recheck-request: github-robot

> Signed-off-by: Felix Huettner <felix.huettner@stackit.cloud>
> ---
> v8->v9: addressed reviews
> v7->v8: introduced
> 
>  controller/ovn-controller.c | 27 ++++++++++++++++++++++++++-
>  lib/ovn-util.c              | 19 ++++++++++++++-----
>  tests/ovn-controller.at     |  1 -
>  tests/ovn-performance.at    |  3 ---
>  tests/ovn.at                | 10 ----------
>  5 files changed, 40 insertions(+), 20 deletions(-)
> 
> diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
> index 13a566d26..946e6146d 100644
> --- a/controller/ovn-controller.c
> +++ b/controller/ovn-controller.c
> @@ -6029,6 +6029,7 @@ main(int argc, char *argv[])
>      unsigned int ovs_cond_seqno = UINT_MAX;
>      unsigned int ovnsb_cond_seqno = UINT_MAX;
>      unsigned int ovnsb_expected_cond_seqno = UINT_MAX;
> +    bool has_computed_once = false;
>  
>      struct controller_engine_ctx ctrl_engine_ctx = {
>          .lflow_cache = lflow_cache_create(),
> @@ -6103,6 +6104,20 @@ main(int argc, char *argv[])
>              ovnsb_cond_seqno = new_ovnsb_cond_seqno;
>          }
>  
> +        /* Check if we have received all initial dumps of the southbound
> +         * based on the monitor condtions we set.
> +         * If we have sb_monitor_all that means we have all data that we would
> +         * ever need.
> +         * If we monitor cases individually we need at least one engine run. */
> +        if (ovnsb_cond_seqno == ovnsb_expected_cond_seqno
> +            && ovnsb_expected_cond_seqno != UINT_MAX) {
> +            if (sb_monitor_all) {
> +                daemon_started_recently_ignore();
> +            } else if (has_computed_once) {
> +                daemon_started_recently_countdown();
> +            }
> +        }
> +
>          struct engine_context eng_ctx = {
>              .ovs_idl_txn = ovs_idl_txn,
>              .ovnsb_idl_txn = ovnsb_idl_txn,
> @@ -6260,8 +6275,9 @@ main(int argc, char *argv[])
>  
>                      stopwatch_stop(CONTROLLER_LOOP_STOPWATCH_NAME,
>                                     time_msec());
> +
>                      if (engine_has_updated()) {
> -                        daemon_started_recently_countdown();
> +                        has_computed_once = true;
>                      }
>  
>                      ct_zones_data = engine_get_data(&en_ct_zones);
> @@ -6380,6 +6396,15 @@ main(int argc, char *argv[])
>                                      &runtime_data->local_datapaths,
>                                      sb_monitor_all);
>                          }
> +                        /* If there is no new expected seqno we have
> +                         * finished loading all needed data from
> +                         * southbound. We then need to run one more time since
> +                         * we might behave differently. */
> +                        if (daemon_started_recently()
> +                            && ovnsb_cond_seqno == ovnsb_expected_cond_seqno) {
> +                            daemon_started_recently_ignore();
> +                            poll_immediate_wake();
> +                        }
>                          if (ovs_idl_txn) {
>                              update_qos(sbrec_port_binding_by_name, ovs_idl_txn,
>                                         ovsrec_port_by_qos,
> diff --git a/lib/ovn-util.c b/lib/ovn-util.c
> index af17082a9..153a69ff6 100644
> --- a/lib/ovn-util.c
> +++ b/lib/ovn-util.c
> @@ -933,13 +933,24 @@ get_bridge(const struct ovsrec_bridge_table *bridge_table, const char *br_name)
>      return NULL;
>  }
>  
> +/* This counts the amount of iterations of monitoring condition updates and
> + * their respective update towards us.
> + * This only needs to handle the case of non monitor_all since that will use
> + * the ignore feature below.
> + * In this case we need at least 2 iterations:
> + * 1. the initial iteration where we pull the sb content based on the few
> + *    conditions we now have.
> + * 2. after the first engine run we have enough information to update the
> + *    monitoring conditions to their final values.
> + * However based on the structure of the datapaths we might need more. The
> + * value below is just a hard limit of iterations. We detect if we are done
> + * earlier and then skip further iterations. */
>  #define DAEMON_STARTUP_DELAY_SEED 20
> -#define DAEMON_STARTUP_DELAY_MS   10000
>  
>  static int64_t startup_ts;
>  static int startup_delay = DAEMON_STARTUP_DELAY_SEED;
>  
> -/* Used by debug command only, for tests. */
> +/* Used if we do not need the startup delay (e.g. when using monitor_all). */
>  static bool ignore_startup_delay = false;
>  
>  OVS_CONSTRUCTOR(startup_ts_initializer) {
> @@ -980,9 +991,7 @@ daemon_started_recently(void)
>      if (startup_delay) {
>          return true;
>      }
> -
> -    /* Ensure that at least an amount of time has passed. */
> -    return time_wall_msec() - startup_ts <= DAEMON_STARTUP_DELAY_MS;
> +    return false;
>  }
>  
>  /* Builds a unique address set compatible name ([a-zA-Z_.][a-zA-Z_.0-9]*)
> diff --git a/tests/ovn-controller.at b/tests/ovn-controller.at
> index a878a440b..781ee242a 100644
> --- a/tests/ovn-controller.at
> +++ b/tests/ovn-controller.at
> @@ -161,7 +161,6 @@ check_patches \
>      'br-eth0 patch-localnet2-to-br-int patch-br-int-to-localnet2'
>  
>  # Delete the mapping and the ovn-bridge-mapping patch ports should go away.
> -check ovn-appctl -t ovn-controller debug/ignore-startup-delay
>  AT_CHECK([ovs-vsctl remove Open_vSwitch . external-ids ovn-bridge-mappings])
>  check_bridge_mappings
>  check_patches
> diff --git a/tests/ovn-performance.at b/tests/ovn-performance.at
> index 44bd4f7e4..4ddb1bd81 100644
> --- a/tests/ovn-performance.at
> +++ b/tests/ovn-performance.at
> @@ -238,9 +238,6 @@ for i in `seq 1 5`; do
>      as hv$i
>      ovs-vsctl add-br br-phys
>      ovn_attach n1 br-phys 192.168.0.$i
> -    # Do not postpone patch ports related changes, as they may occur more or less anywere within the test,
> -    # hence causing unexpected recomputes.
> -    check ovn-appctl -t ovn-controller debug/ignore-startup-delay
>      if [[ $i -ge 3 ]] ; then
>          ovs-vsctl add-br br-ex
>          ovs-vsctl set open . external_ids:ovn-bridge-mappings="public:br-ex"
> diff --git a/tests/ovn.at b/tests/ovn.at
> index 596a7be2b..9af74774f 100644
> --- a/tests/ovn.at
> +++ b/tests/ovn.at
> @@ -35301,7 +35301,6 @@ OVS_WAIT_UNTIL([
>      test x430 = x$(as hv1 ovs-vsctl get Interface ${iface1_uuid} mtu_request)
>  ])
>  
> -as hv1 check ovn-appctl -t ovn-controller debug/ignore-startup-delay
>  # Check that pointing requested-chassis somewhere else will unplug the port
>  check ovn-nbctl --wait=hv set Logical_Switch_Port lsp1 \
>      options:requested-chassis=non-existent-chassis
> @@ -35309,7 +35308,6 @@ OVS_WAIT_UNTIL([
>      ! as hv1 ovs-vsctl get Interface ${iface1_uuid} _uuid
>  ])
>  
> -as hv2 check ovn-appctl -t ovn-controller debug/ignore-startup-delay
>  # Check that removing an lport will unplug it
>  AT_CHECK([test x${iface2_uuid} = x$(as hv2 ovs-vsctl get Interface ${iface2_uuid} _uuid)], [0], [])
>  check ovn-nbctl --wait=hv lsp-del ${lsp2_uuid}
> @@ -38073,9 +38071,6 @@ ovs-vsctl -- add-port br-phys-2 fake-phys-2-to-int-2 -- \
>  
>  ovn_attach n1 br-phys 192.168.1.1 24
>  
> -# allow controller to immediately clean patch ports up
> -check ovn-appctl -t ovn-controller debug/ignore-startup-delay
> -
>  # check that patch port on br-int is cleaned up (and also its peer)
>  OVS_WAIT_UNTIL([
>      test 0 = $(ovs-vsctl --columns _uuid --bare find Port name=fake-int-to-phys | wc -l)
> @@ -38141,9 +38136,6 @@ check ovn-nbctl lsp-set-options ln_port network_name=phys-1
>  wait_for_ports_up
>  check ovn-nbctl --wait=hv sync
>  
> -# Allow controller to immediately clean patch ports up if any.
> -check ovn-appctl -t ovn-controller debug/ignore-startup-delay
> -
>  # check that patch port created on br-int and br-phys-1.
>  OVS_WAIT_UNTIL([
>      test 1 = $(ovs-vsctl --columns _uuid --bare find Port name=patch-br-int-to-ln_port | wc -l)
> @@ -41407,8 +41399,6 @@ check ovn-nbctl lsp-add sw0 sw0p1
>  
>  check ovn-nbctl --wait=hv sync
>  
> -check ovn-appctl -t ovn-controller debug/ignore-startup-delay
> -
>  # Waits until the OVS database contains exactly the specified patch ports.
>  # Each argument should be of the form BRIDGE PORT PEER.
>  check_patch_ports () {
> -- 
> 2.43.0
> 
> 
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
Ales Musil May 27, 2025, 5:18 a.m. UTC | #2
On Wed, May 7, 2025 at 4:21 PM Felix Huettner via dev <
ovs-dev@openvswitch.org> wrote:

> When starting the ovn-controller without monitor_all we need a few
> iterations of the engine node and updating monitoring conditions until
> we have loaded all data.
> Previously this was handled by the number of main loop iterations of
> ovn-controller and an arbitrary timeout.
> However this is was always best effort and there was no guarantee that
> we actually had loaded all data.
>
> To solve this we now track the monitoring requests we sent out to
> southbound and when that data becomes available.
>
> For monitor_all we just need to have the initial monitoring conditions
> fullfilled and are afterwards sure that we have all required data.
>
> For non monitor_all we need at least one more iteration of the engine
> node and updating sb monitoring conditions. We limit this to 20 rounds
> for now, which should be sufficient and is at least the value of what
> the previous implementation provided.
> However if we notice that we do not update monitoring condition anymore
> after a engine run we can also assume that we have finished loading all
> data.
>
> Signed-off-by: Felix Huettner <felix.huettner@stackit.cloud>
> ---
>

Hi Felix,

sorry for the recent silence on this one. There is related discussion
happening on
https://patchwork.ozlabs.org/project/ovn/patch/20250318100817.117922-2-alekssmirnov@k2.cloud/
.
It would be nice to get your input there too so we can move on
with this.


> v8->v9: addressed reviews
> v7->v8: introduced
>
>  controller/ovn-controller.c | 27 ++++++++++++++++++++++++++-
>  lib/ovn-util.c              | 19 ++++++++++++++-----
>  tests/ovn-controller.at     |  1 -
>  tests/ovn-performance.at    |  3 ---
>  tests/ovn.at                | 10 ----------
>  5 files changed, 40 insertions(+), 20 deletions(-)
>
> diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
> index 13a566d26..946e6146d 100644
> --- a/controller/ovn-controller.c
> +++ b/controller/ovn-controller.c
> @@ -6029,6 +6029,7 @@ main(int argc, char *argv[])
>      unsigned int ovs_cond_seqno = UINT_MAX;
>      unsigned int ovnsb_cond_seqno = UINT_MAX;
>      unsigned int ovnsb_expected_cond_seqno = UINT_MAX;
> +    bool has_computed_once = false;
>
>      struct controller_engine_ctx ctrl_engine_ctx = {
>          .lflow_cache = lflow_cache_create(),
> @@ -6103,6 +6104,20 @@ main(int argc, char *argv[])
>              ovnsb_cond_seqno = new_ovnsb_cond_seqno;
>          }
>
> +        /* Check if we have received all initial dumps of the southbound
> +         * based on the monitor condtions we set.
> +         * If we have sb_monitor_all that means we have all data that we
> would
> +         * ever need.
> +         * If we monitor cases individually we need at least one engine
> run. */
> +        if (ovnsb_cond_seqno == ovnsb_expected_cond_seqno
> +            && ovnsb_expected_cond_seqno != UINT_MAX) {
> +            if (sb_monitor_all) {
> +                daemon_started_recently_ignore();
> +            } else if (has_computed_once) {
> +                daemon_started_recently_countdown();
> +            }
> +        }
> +
>          struct engine_context eng_ctx = {
>              .ovs_idl_txn = ovs_idl_txn,
>              .ovnsb_idl_txn = ovnsb_idl_txn,
> @@ -6260,8 +6275,9 @@ main(int argc, char *argv[])
>
>                      stopwatch_stop(CONTROLLER_LOOP_STOPWATCH_NAME,
>                                     time_msec());
> +
>                      if (engine_has_updated()) {
> -                        daemon_started_recently_countdown();
> +                        has_computed_once = true;
>                      }
>
>                      ct_zones_data = engine_get_data(&en_ct_zones);
> @@ -6380,6 +6396,15 @@ main(int argc, char *argv[])
>                                      &runtime_data->local_datapaths,
>                                      sb_monitor_all);
>                          }
> +                        /* If there is no new expected seqno we have
> +                         * finished loading all needed data from
> +                         * southbound. We then need to run one more time
> since
> +                         * we might behave differently. */
> +                        if (daemon_started_recently()
> +                            && ovnsb_cond_seqno ==
> ovnsb_expected_cond_seqno) {
> +                            daemon_started_recently_ignore();
> +                            poll_immediate_wake();
> +                        }
>                          if (ovs_idl_txn) {
>                              update_qos(sbrec_port_binding_by_name,
> ovs_idl_txn,
>                                         ovsrec_port_by_qos,
> diff --git a/lib/ovn-util.c b/lib/ovn-util.c
> index af17082a9..153a69ff6 100644
> --- a/lib/ovn-util.c
> +++ b/lib/ovn-util.c
> @@ -933,13 +933,24 @@ get_bridge(const struct ovsrec_bridge_table
> *bridge_table, const char *br_name)
>      return NULL;
>  }
>
> +/* This counts the amount of iterations of monitoring condition updates
> and
> + * their respective update towards us.
> + * This only needs to handle the case of non monitor_all since that will
> use
> + * the ignore feature below.
> + * In this case we need at least 2 iterations:
> + * 1. the initial iteration where we pull the sb content based on the few
> + *    conditions we now have.
> + * 2. after the first engine run we have enough information to update the
> + *    monitoring conditions to their final values.
> + * However based on the structure of the datapaths we might need more. The
> + * value below is just a hard limit of iterations. We detect if we are
> done
> + * earlier and then skip further iterations. */
>  #define DAEMON_STARTUP_DELAY_SEED 20
> -#define DAEMON_STARTUP_DELAY_MS   10000
>
>  static int64_t startup_ts;
>  static int startup_delay = DAEMON_STARTUP_DELAY_SEED;
>
> -/* Used by debug command only, for tests. */
> +/* Used if we do not need the startup delay (e.g. when using
> monitor_all). */
>  static bool ignore_startup_delay = false;
>
>  OVS_CONSTRUCTOR(startup_ts_initializer) {
> @@ -980,9 +991,7 @@ daemon_started_recently(void)
>      if (startup_delay) {
>          return true;
>      }
> -
> -    /* Ensure that at least an amount of time has passed. */
> -    return time_wall_msec() - startup_ts <= DAEMON_STARTUP_DELAY_MS;
> +    return false;
>  }
>
>  /* Builds a unique address set compatible name ([a-zA-Z_.][a-zA-Z_.0-9]*)
> diff --git a/tests/ovn-controller.at b/tests/ovn-controller.at
> index a878a440b..781ee242a 100644
> --- a/tests/ovn-controller.at
> +++ b/tests/ovn-controller.at
> @@ -161,7 +161,6 @@ check_patches \
>      'br-eth0 patch-localnet2-to-br-int patch-br-int-to-localnet2'
>
>  # Delete the mapping and the ovn-bridge-mapping patch ports should go
> away.
> -check ovn-appctl -t ovn-controller debug/ignore-startup-delay
>  AT_CHECK([ovs-vsctl remove Open_vSwitch . external-ids
> ovn-bridge-mappings])
>  check_bridge_mappings
>  check_patches
> diff --git a/tests/ovn-performance.at b/tests/ovn-performance.at
> index 44bd4f7e4..4ddb1bd81 100644
> --- a/tests/ovn-performance.at
> +++ b/tests/ovn-performance.at
> @@ -238,9 +238,6 @@ for i in `seq 1 5`; do
>      as hv$i
>      ovs-vsctl add-br br-phys
>      ovn_attach n1 br-phys 192.168.0.$i
> -    # Do not postpone patch ports related changes, as they may occur more
> or less anywere within the test,
> -    # hence causing unexpected recomputes.
> -    check ovn-appctl -t ovn-controller debug/ignore-startup-delay
>      if [[ $i -ge 3 ]] ; then
>          ovs-vsctl add-br br-ex
>          ovs-vsctl set open .
> external_ids:ovn-bridge-mappings="public:br-ex"
> diff --git a/tests/ovn.at b/tests/ovn.at
> index 596a7be2b..9af74774f 100644
> --- a/tests/ovn.at
> +++ b/tests/ovn.at
> @@ -35301,7 +35301,6 @@ OVS_WAIT_UNTIL([
>      test x430 = x$(as hv1 ovs-vsctl get Interface ${iface1_uuid}
> mtu_request)
>  ])
>
> -as hv1 check ovn-appctl -t ovn-controller debug/ignore-startup-delay
>  # Check that pointing requested-chassis somewhere else will unplug the
> port
>  check ovn-nbctl --wait=hv set Logical_Switch_Port lsp1 \
>      options:requested-chassis=non-existent-chassis
> @@ -35309,7 +35308,6 @@ OVS_WAIT_UNTIL([
>      ! as hv1 ovs-vsctl get Interface ${iface1_uuid} _uuid
>  ])
>
> -as hv2 check ovn-appctl -t ovn-controller debug/ignore-startup-delay
>  # Check that removing an lport will unplug it
>  AT_CHECK([test x${iface2_uuid} = x$(as hv2 ovs-vsctl get Interface
> ${iface2_uuid} _uuid)], [0], [])
>  check ovn-nbctl --wait=hv lsp-del ${lsp2_uuid}
> @@ -38073,9 +38071,6 @@ ovs-vsctl -- add-port br-phys-2
> fake-phys-2-to-int-2 -- \
>
>  ovn_attach n1 br-phys 192.168.1.1 24
>
> -# allow controller to immediately clean patch ports up
> -check ovn-appctl -t ovn-controller debug/ignore-startup-delay
> -
>  # check that patch port on br-int is cleaned up (and also its peer)
>  OVS_WAIT_UNTIL([
>      test 0 = $(ovs-vsctl --columns _uuid --bare find Port
> name=fake-int-to-phys | wc -l)
> @@ -38141,9 +38136,6 @@ check ovn-nbctl lsp-set-options ln_port
> network_name=phys-1
>  wait_for_ports_up
>  check ovn-nbctl --wait=hv sync
>
> -# Allow controller to immediately clean patch ports up if any.
> -check ovn-appctl -t ovn-controller debug/ignore-startup-delay
> -
>  # check that patch port created on br-int and br-phys-1.
>  OVS_WAIT_UNTIL([
>      test 1 = $(ovs-vsctl --columns _uuid --bare find Port
> name=patch-br-int-to-ln_port | wc -l)
> @@ -41407,8 +41399,6 @@ check ovn-nbctl lsp-add sw0 sw0p1
>
>  check ovn-nbctl --wait=hv sync
>
> -check ovn-appctl -t ovn-controller debug/ignore-startup-delay
> -
>  # Waits until the OVS database contains exactly the specified patch ports.
>  # Each argument should be of the form BRIDGE PORT PEER.
>  check_patch_ports () {
> --
> 2.43.0
>
>
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
>
Thanks,
Ales
Felix Huettner May 27, 2025, 2:56 p.m. UTC | #3
On Tue, May 27, 2025 at 07:18:46AM +0200, Ales Musil wrote:
> On Wed, May 7, 2025 at 4:21 PM Felix Huettner via dev <
> ovs-dev@openvswitch.org> wrote:
> 
> > When starting the ovn-controller without monitor_all we need a few
> > iterations of the engine node and updating monitoring conditions until
> > we have loaded all data.
> > Previously this was handled by the number of main loop iterations of
> > ovn-controller and an arbitrary timeout.
> > However this is was always best effort and there was no guarantee that
> > we actually had loaded all data.
> >
> > To solve this we now track the monitoring requests we sent out to
> > southbound and when that data becomes available.
> >
> > For monitor_all we just need to have the initial monitoring conditions
> > fullfilled and are afterwards sure that we have all required data.
> >
> > For non monitor_all we need at least one more iteration of the engine
> > node and updating sb monitoring conditions. We limit this to 20 rounds
> > for now, which should be sufficient and is at least the value of what
> > the previous implementation provided.
> > However if we notice that we do not update monitoring condition anymore
> > after a engine run we can also assume that we have finished loading all
> > data.
> >
> > Signed-off-by: Felix Huettner <felix.huettner@stackit.cloud>
> > ---
> >
> 
> Hi Felix,
> 
> sorry for the recent silence on this one. There is related discussion
> happening on
> https://patchwork.ozlabs.org/project/ovn/patch/20250318100817.117922-2-alekssmirnov@k2.cloud/
> .
> It would be nice to get your input there too so we can move on
> with this.

Hi Ales,

no worries i was also busy on other topics.

I think Alexander has a valid point there.
I would wait for his further input and then send a new version that is
also rebased against main.

Thanks a lot,
Felix

> 
> 
> > v8->v9: addressed reviews
> > v7->v8: introduced
> >
> >  controller/ovn-controller.c | 27 ++++++++++++++++++++++++++-
> >  lib/ovn-util.c              | 19 ++++++++++++++-----
> >  tests/ovn-controller.at     |  1 -
> >  tests/ovn-performance.at    |  3 ---
> >  tests/ovn.at                | 10 ----------
> >  5 files changed, 40 insertions(+), 20 deletions(-)
> >
> > diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
> > index 13a566d26..946e6146d 100644
> > --- a/controller/ovn-controller.c
> > +++ b/controller/ovn-controller.c
> > @@ -6029,6 +6029,7 @@ main(int argc, char *argv[])
> >      unsigned int ovs_cond_seqno = UINT_MAX;
> >      unsigned int ovnsb_cond_seqno = UINT_MAX;
> >      unsigned int ovnsb_expected_cond_seqno = UINT_MAX;
> > +    bool has_computed_once = false;
> >
> >      struct controller_engine_ctx ctrl_engine_ctx = {
> >          .lflow_cache = lflow_cache_create(),
> > @@ -6103,6 +6104,20 @@ main(int argc, char *argv[])
> >              ovnsb_cond_seqno = new_ovnsb_cond_seqno;
> >          }
> >
> > +        /* Check if we have received all initial dumps of the southbound
> > +         * based on the monitor condtions we set.
> > +         * If we have sb_monitor_all that means we have all data that we
> > would
> > +         * ever need.
> > +         * If we monitor cases individually we need at least one engine
> > run. */
> > +        if (ovnsb_cond_seqno == ovnsb_expected_cond_seqno
> > +            && ovnsb_expected_cond_seqno != UINT_MAX) {
> > +            if (sb_monitor_all) {
> > +                daemon_started_recently_ignore();
> > +            } else if (has_computed_once) {
> > +                daemon_started_recently_countdown();
> > +            }
> > +        }
> > +
> >          struct engine_context eng_ctx = {
> >              .ovs_idl_txn = ovs_idl_txn,
> >              .ovnsb_idl_txn = ovnsb_idl_txn,
> > @@ -6260,8 +6275,9 @@ main(int argc, char *argv[])
> >
> >                      stopwatch_stop(CONTROLLER_LOOP_STOPWATCH_NAME,
> >                                     time_msec());
> > +
> >                      if (engine_has_updated()) {
> > -                        daemon_started_recently_countdown();
> > +                        has_computed_once = true;
> >                      }
> >
> >                      ct_zones_data = engine_get_data(&en_ct_zones);
> > @@ -6380,6 +6396,15 @@ main(int argc, char *argv[])
> >                                      &runtime_data->local_datapaths,
> >                                      sb_monitor_all);
> >                          }
> > +                        /* If there is no new expected seqno we have
> > +                         * finished loading all needed data from
> > +                         * southbound. We then need to run one more time
> > since
> > +                         * we might behave differently. */
> > +                        if (daemon_started_recently()
> > +                            && ovnsb_cond_seqno ==
> > ovnsb_expected_cond_seqno) {
> > +                            daemon_started_recently_ignore();
> > +                            poll_immediate_wake();
> > +                        }
> >                          if (ovs_idl_txn) {
> >                              update_qos(sbrec_port_binding_by_name,
> > ovs_idl_txn,
> >                                         ovsrec_port_by_qos,
> > diff --git a/lib/ovn-util.c b/lib/ovn-util.c
> > index af17082a9..153a69ff6 100644
> > --- a/lib/ovn-util.c
> > +++ b/lib/ovn-util.c
> > @@ -933,13 +933,24 @@ get_bridge(const struct ovsrec_bridge_table
> > *bridge_table, const char *br_name)
> >      return NULL;
> >  }
> >
> > +/* This counts the amount of iterations of monitoring condition updates
> > and
> > + * their respective update towards us.
> > + * This only needs to handle the case of non monitor_all since that will
> > use
> > + * the ignore feature below.
> > + * In this case we need at least 2 iterations:
> > + * 1. the initial iteration where we pull the sb content based on the few
> > + *    conditions we now have.
> > + * 2. after the first engine run we have enough information to update the
> > + *    monitoring conditions to their final values.
> > + * However based on the structure of the datapaths we might need more. The
> > + * value below is just a hard limit of iterations. We detect if we are
> > done
> > + * earlier and then skip further iterations. */
> >  #define DAEMON_STARTUP_DELAY_SEED 20
> > -#define DAEMON_STARTUP_DELAY_MS   10000
> >
> >  static int64_t startup_ts;
> >  static int startup_delay = DAEMON_STARTUP_DELAY_SEED;
> >
> > -/* Used by debug command only, for tests. */
> > +/* Used if we do not need the startup delay (e.g. when using
> > monitor_all). */
> >  static bool ignore_startup_delay = false;
> >
> >  OVS_CONSTRUCTOR(startup_ts_initializer) {
> > @@ -980,9 +991,7 @@ daemon_started_recently(void)
> >      if (startup_delay) {
> >          return true;
> >      }
> > -
> > -    /* Ensure that at least an amount of time has passed. */
> > -    return time_wall_msec() - startup_ts <= DAEMON_STARTUP_DELAY_MS;
> > +    return false;
> >  }
> >
> >  /* Builds a unique address set compatible name ([a-zA-Z_.][a-zA-Z_.0-9]*)
> > diff --git a/tests/ovn-controller.at b/tests/ovn-controller.at
> > index a878a440b..781ee242a 100644
> > --- a/tests/ovn-controller.at
> > +++ b/tests/ovn-controller.at
> > @@ -161,7 +161,6 @@ check_patches \
> >      'br-eth0 patch-localnet2-to-br-int patch-br-int-to-localnet2'
> >
> >  # Delete the mapping and the ovn-bridge-mapping patch ports should go
> > away.
> > -check ovn-appctl -t ovn-controller debug/ignore-startup-delay
> >  AT_CHECK([ovs-vsctl remove Open_vSwitch . external-ids
> > ovn-bridge-mappings])
> >  check_bridge_mappings
> >  check_patches
> > diff --git a/tests/ovn-performance.at b/tests/ovn-performance.at
> > index 44bd4f7e4..4ddb1bd81 100644
> > --- a/tests/ovn-performance.at
> > +++ b/tests/ovn-performance.at
> > @@ -238,9 +238,6 @@ for i in `seq 1 5`; do
> >      as hv$i
> >      ovs-vsctl add-br br-phys
> >      ovn_attach n1 br-phys 192.168.0.$i
> > -    # Do not postpone patch ports related changes, as they may occur more
> > or less anywere within the test,
> > -    # hence causing unexpected recomputes.
> > -    check ovn-appctl -t ovn-controller debug/ignore-startup-delay
> >      if [[ $i -ge 3 ]] ; then
> >          ovs-vsctl add-br br-ex
> >          ovs-vsctl set open .
> > external_ids:ovn-bridge-mappings="public:br-ex"
> > diff --git a/tests/ovn.at b/tests/ovn.at
> > index 596a7be2b..9af74774f 100644
> > --- a/tests/ovn.at
> > +++ b/tests/ovn.at
> > @@ -35301,7 +35301,6 @@ OVS_WAIT_UNTIL([
> >      test x430 = x$(as hv1 ovs-vsctl get Interface ${iface1_uuid}
> > mtu_request)
> >  ])
> >
> > -as hv1 check ovn-appctl -t ovn-controller debug/ignore-startup-delay
> >  # Check that pointing requested-chassis somewhere else will unplug the
> > port
> >  check ovn-nbctl --wait=hv set Logical_Switch_Port lsp1 \
> >      options:requested-chassis=non-existent-chassis
> > @@ -35309,7 +35308,6 @@ OVS_WAIT_UNTIL([
> >      ! as hv1 ovs-vsctl get Interface ${iface1_uuid} _uuid
> >  ])
> >
> > -as hv2 check ovn-appctl -t ovn-controller debug/ignore-startup-delay
> >  # Check that removing an lport will unplug it
> >  AT_CHECK([test x${iface2_uuid} = x$(as hv2 ovs-vsctl get Interface
> > ${iface2_uuid} _uuid)], [0], [])
> >  check ovn-nbctl --wait=hv lsp-del ${lsp2_uuid}
> > @@ -38073,9 +38071,6 @@ ovs-vsctl -- add-port br-phys-2
> > fake-phys-2-to-int-2 -- \
> >
> >  ovn_attach n1 br-phys 192.168.1.1 24
> >
> > -# allow controller to immediately clean patch ports up
> > -check ovn-appctl -t ovn-controller debug/ignore-startup-delay
> > -
> >  # check that patch port on br-int is cleaned up (and also its peer)
> >  OVS_WAIT_UNTIL([
> >      test 0 = $(ovs-vsctl --columns _uuid --bare find Port
> > name=fake-int-to-phys | wc -l)
> > @@ -38141,9 +38136,6 @@ check ovn-nbctl lsp-set-options ln_port
> > network_name=phys-1
> >  wait_for_ports_up
> >  check ovn-nbctl --wait=hv sync
> >
> > -# Allow controller to immediately clean patch ports up if any.
> > -check ovn-appctl -t ovn-controller debug/ignore-startup-delay
> > -
> >  # check that patch port created on br-int and br-phys-1.
> >  OVS_WAIT_UNTIL([
> >      test 1 = $(ovs-vsctl --columns _uuid --bare find Port
> > name=patch-br-int-to-ln_port | wc -l)
> > @@ -41407,8 +41399,6 @@ check ovn-nbctl lsp-add sw0 sw0p1
> >
> >  check ovn-nbctl --wait=hv sync
> >
> > -check ovn-appctl -t ovn-controller debug/ignore-startup-delay
> > -
> >  # Waits until the OVS database contains exactly the specified patch ports.
> >  # Each argument should be of the form BRIDGE PORT PEER.
> >  check_patch_ports () {
> > --
> > 2.43.0
> >
> >
> > _______________________________________________
> > dev mailing list
> > dev@openvswitch.org
> > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> >
> >
> Thanks,
> Ales
Ales Musil May 27, 2025, 3:07 p.m. UTC | #4
On Tue, May 27, 2025 at 4:56 PM Felix Huettner <felix.huettner@stackit.cloud>
wrote:

> On Tue, May 27, 2025 at 07:18:46AM +0200, Ales Musil wrote:
> > On Wed, May 7, 2025 at 4:21 PM Felix Huettner via dev <
> > ovs-dev@openvswitch.org> wrote:
> >
> > > When starting the ovn-controller without monitor_all we need a few
> > > iterations of the engine node and updating monitoring conditions until
> > > we have loaded all data.
> > > Previously this was handled by the number of main loop iterations of
> > > ovn-controller and an arbitrary timeout.
> > > However this is was always best effort and there was no guarantee that
> > > we actually had loaded all data.
> > >
> > > To solve this we now track the monitoring requests we sent out to
> > > southbound and when that data becomes available.
> > >
> > > For monitor_all we just need to have the initial monitoring conditions
> > > fullfilled and are afterwards sure that we have all required data.
> > >
> > > For non monitor_all we need at least one more iteration of the engine
> > > node and updating sb monitoring conditions. We limit this to 20 rounds
> > > for now, which should be sufficient and is at least the value of what
> > > the previous implementation provided.
> > > However if we notice that we do not update monitoring condition anymore
> > > after a engine run we can also assume that we have finished loading all
> > > data.
> > >
> > > Signed-off-by: Felix Huettner <felix.huettner@stackit.cloud>
> > > ---
> > >
> >
> > Hi Felix,
> >
> > sorry for the recent silence on this one. There is related discussion
> > happening on
> >
> https://patchwork.ozlabs.org/project/ovn/patch/20250318100817.117922-2-alekssmirnov@k2.cloud/
> > .
> > It would be nice to get your input there too so we can move on
> > with this.
>
> Hi Ales,
>

Hi Felix,


> no worries i was also busy on other topics.
>
> I think Alexander has a valid point there.
> I would wait for his further input and then send a new version that is
> also rebased against main.
>

Based on this I'll mark the series as changes requested for now
and we will continue on the next revision when the conversation is
settled.


>
> Thanks a lot,
> Felix
>
> >
> >
> > > v8->v9: addressed reviews
> > > v7->v8: introduced
> > >
> > >  controller/ovn-controller.c | 27 ++++++++++++++++++++++++++-
> > >  lib/ovn-util.c              | 19 ++++++++++++++-----
> > >  tests/ovn-controller.at     |  1 -
> > >  tests/ovn-performance.at    |  3 ---
> > >  tests/ovn.at                | 10 ----------
> > >  5 files changed, 40 insertions(+), 20 deletions(-)
> > >
> > > diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
> > > index 13a566d26..946e6146d 100644
> > > --- a/controller/ovn-controller.c
> > > +++ b/controller/ovn-controller.c
> > > @@ -6029,6 +6029,7 @@ main(int argc, char *argv[])
> > >      unsigned int ovs_cond_seqno = UINT_MAX;
> > >      unsigned int ovnsb_cond_seqno = UINT_MAX;
> > >      unsigned int ovnsb_expected_cond_seqno = UINT_MAX;
> > > +    bool has_computed_once = false;
> > >
> > >      struct controller_engine_ctx ctrl_engine_ctx = {
> > >          .lflow_cache = lflow_cache_create(),
> > > @@ -6103,6 +6104,20 @@ main(int argc, char *argv[])
> > >              ovnsb_cond_seqno = new_ovnsb_cond_seqno;
> > >          }
> > >
> > > +        /* Check if we have received all initial dumps of the
> southbound
> > > +         * based on the monitor condtions we set.
> > > +         * If we have sb_monitor_all that means we have all data that
> we
> > > would
> > > +         * ever need.
> > > +         * If we monitor cases individually we need at least one
> engine
> > > run. */
> > > +        if (ovnsb_cond_seqno == ovnsb_expected_cond_seqno
> > > +            && ovnsb_expected_cond_seqno != UINT_MAX) {
> > > +            if (sb_monitor_all) {
> > > +                daemon_started_recently_ignore();
> > > +            } else if (has_computed_once) {
> > > +                daemon_started_recently_countdown();
> > > +            }
> > > +        }
> > > +
> > >          struct engine_context eng_ctx = {
> > >              .ovs_idl_txn = ovs_idl_txn,
> > >              .ovnsb_idl_txn = ovnsb_idl_txn,
> > > @@ -6260,8 +6275,9 @@ main(int argc, char *argv[])
> > >
> > >                      stopwatch_stop(CONTROLLER_LOOP_STOPWATCH_NAME,
> > >                                     time_msec());
> > > +
> > >                      if (engine_has_updated()) {
> > > -                        daemon_started_recently_countdown();
> > > +                        has_computed_once = true;
> > >                      }
> > >
> > >                      ct_zones_data = engine_get_data(&en_ct_zones);
> > > @@ -6380,6 +6396,15 @@ main(int argc, char *argv[])
> > >                                      &runtime_data->local_datapaths,
> > >                                      sb_monitor_all);
> > >                          }
> > > +                        /* If there is no new expected seqno we have
> > > +                         * finished loading all needed data from
> > > +                         * southbound. We then need to run one more
> time
> > > since
> > > +                         * we might behave differently. */
> > > +                        if (daemon_started_recently()
> > > +                            && ovnsb_cond_seqno ==
> > > ovnsb_expected_cond_seqno) {
> > > +                            daemon_started_recently_ignore();
> > > +                            poll_immediate_wake();
> > > +                        }
> > >                          if (ovs_idl_txn) {
> > >                              update_qos(sbrec_port_binding_by_name,
> > > ovs_idl_txn,
> > >                                         ovsrec_port_by_qos,
> > > diff --git a/lib/ovn-util.c b/lib/ovn-util.c
> > > index af17082a9..153a69ff6 100644
> > > --- a/lib/ovn-util.c
> > > +++ b/lib/ovn-util.c
> > > @@ -933,13 +933,24 @@ get_bridge(const struct ovsrec_bridge_table
> > > *bridge_table, const char *br_name)
> > >      return NULL;
> > >  }
> > >
> > > +/* This counts the amount of iterations of monitoring condition
> updates
> > > and
> > > + * their respective update towards us.
> > > + * This only needs to handle the case of non monitor_all since that
> will
> > > use
> > > + * the ignore feature below.
> > > + * In this case we need at least 2 iterations:
> > > + * 1. the initial iteration where we pull the sb content based on the
> few
> > > + *    conditions we now have.
> > > + * 2. after the first engine run we have enough information to update
> the
> > > + *    monitoring conditions to their final values.
> > > + * However based on the structure of the datapaths we might need
> more. The
> > > + * value below is just a hard limit of iterations. We detect if we are
> > > done
> > > + * earlier and then skip further iterations. */
> > >  #define DAEMON_STARTUP_DELAY_SEED 20
> > > -#define DAEMON_STARTUP_DELAY_MS   10000
> > >
> > >  static int64_t startup_ts;
> > >  static int startup_delay = DAEMON_STARTUP_DELAY_SEED;
> > >
> > > -/* Used by debug command only, for tests. */
> > > +/* Used if we do not need the startup delay (e.g. when using
> > > monitor_all). */
> > >  static bool ignore_startup_delay = false;
> > >
> > >  OVS_CONSTRUCTOR(startup_ts_initializer) {
> > > @@ -980,9 +991,7 @@ daemon_started_recently(void)
> > >      if (startup_delay) {
> > >          return true;
> > >      }
> > > -
> > > -    /* Ensure that at least an amount of time has passed. */
> > > -    return time_wall_msec() - startup_ts <= DAEMON_STARTUP_DELAY_MS;
> > > +    return false;
> > >  }
> > >
> > >  /* Builds a unique address set compatible name
> ([a-zA-Z_.][a-zA-Z_.0-9]*)
> > > diff --git a/tests/ovn-controller.at b/tests/ovn-controller.at
> > > index a878a440b..781ee242a 100644
> > > --- a/tests/ovn-controller.at
> > > +++ b/tests/ovn-controller.at
> > > @@ -161,7 +161,6 @@ check_patches \
> > >      'br-eth0 patch-localnet2-to-br-int patch-br-int-to-localnet2'
> > >
> > >  # Delete the mapping and the ovn-bridge-mapping patch ports should go
> > > away.
> > > -check ovn-appctl -t ovn-controller debug/ignore-startup-delay
> > >  AT_CHECK([ovs-vsctl remove Open_vSwitch . external-ids
> > > ovn-bridge-mappings])
> > >  check_bridge_mappings
> > >  check_patches
> > > diff --git a/tests/ovn-performance.at b/tests/ovn-performance.at
> > > index 44bd4f7e4..4ddb1bd81 100644
> > > --- a/tests/ovn-performance.at
> > > +++ b/tests/ovn-performance.at
> > > @@ -238,9 +238,6 @@ for i in `seq 1 5`; do
> > >      as hv$i
> > >      ovs-vsctl add-br br-phys
> > >      ovn_attach n1 br-phys 192.168.0.$i
> > > -    # Do not postpone patch ports related changes, as they may occur
> more
> > > or less anywere within the test,
> > > -    # hence causing unexpected recomputes.
> > > -    check ovn-appctl -t ovn-controller debug/ignore-startup-delay
> > >      if [[ $i -ge 3 ]] ; then
> > >          ovs-vsctl add-br br-ex
> > >          ovs-vsctl set open .
> > > external_ids:ovn-bridge-mappings="public:br-ex"
> > > diff --git a/tests/ovn.at b/tests/ovn.at
> > > index 596a7be2b..9af74774f 100644
> > > --- a/tests/ovn.at
> > > +++ b/tests/ovn.at
> > > @@ -35301,7 +35301,6 @@ OVS_WAIT_UNTIL([
> > >      test x430 = x$(as hv1 ovs-vsctl get Interface ${iface1_uuid}
> > > mtu_request)
> > >  ])
> > >
> > > -as hv1 check ovn-appctl -t ovn-controller debug/ignore-startup-delay
> > >  # Check that pointing requested-chassis somewhere else will unplug the
> > > port
> > >  check ovn-nbctl --wait=hv set Logical_Switch_Port lsp1 \
> > >      options:requested-chassis=non-existent-chassis
> > > @@ -35309,7 +35308,6 @@ OVS_WAIT_UNTIL([
> > >      ! as hv1 ovs-vsctl get Interface ${iface1_uuid} _uuid
> > >  ])
> > >
> > > -as hv2 check ovn-appctl -t ovn-controller debug/ignore-startup-delay
> > >  # Check that removing an lport will unplug it
> > >  AT_CHECK([test x${iface2_uuid} = x$(as hv2 ovs-vsctl get Interface
> > > ${iface2_uuid} _uuid)], [0], [])
> > >  check ovn-nbctl --wait=hv lsp-del ${lsp2_uuid}
> > > @@ -38073,9 +38071,6 @@ ovs-vsctl -- add-port br-phys-2
> > > fake-phys-2-to-int-2 -- \
> > >
> > >  ovn_attach n1 br-phys 192.168.1.1 24
> > >
> > > -# allow controller to immediately clean patch ports up
> > > -check ovn-appctl -t ovn-controller debug/ignore-startup-delay
> > > -
> > >  # check that patch port on br-int is cleaned up (and also its peer)
> > >  OVS_WAIT_UNTIL([
> > >      test 0 = $(ovs-vsctl --columns _uuid --bare find Port
> > > name=fake-int-to-phys | wc -l)
> > > @@ -38141,9 +38136,6 @@ check ovn-nbctl lsp-set-options ln_port
> > > network_name=phys-1
> > >  wait_for_ports_up
> > >  check ovn-nbctl --wait=hv sync
> > >
> > > -# Allow controller to immediately clean patch ports up if any.
> > > -check ovn-appctl -t ovn-controller debug/ignore-startup-delay
> > > -
> > >  # check that patch port created on br-int and br-phys-1.
> > >  OVS_WAIT_UNTIL([
> > >      test 1 = $(ovs-vsctl --columns _uuid --bare find Port
> > > name=patch-br-int-to-ln_port | wc -l)
> > > @@ -41407,8 +41399,6 @@ check ovn-nbctl lsp-add sw0 sw0p1
> > >
> > >  check ovn-nbctl --wait=hv sync
> > >
> > > -check ovn-appctl -t ovn-controller debug/ignore-startup-delay
> > > -
> > >  # Waits until the OVS database contains exactly the specified patch
> ports.
> > >  # Each argument should be of the form BRIDGE PORT PEER.
> > >  check_patch_ports () {
> > > --
> > > 2.43.0
> > >
> > >
> > > _______________________________________________
> > > dev mailing list
> > > dev@openvswitch.org
> > > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> > >
> > >
> > Thanks,
> > Ales
>
>
Thanks,
Ales
diff mbox series

Patch

diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
index 13a566d26..946e6146d 100644
--- a/controller/ovn-controller.c
+++ b/controller/ovn-controller.c
@@ -6029,6 +6029,7 @@  main(int argc, char *argv[])
     unsigned int ovs_cond_seqno = UINT_MAX;
     unsigned int ovnsb_cond_seqno = UINT_MAX;
     unsigned int ovnsb_expected_cond_seqno = UINT_MAX;
+    bool has_computed_once = false;
 
     struct controller_engine_ctx ctrl_engine_ctx = {
         .lflow_cache = lflow_cache_create(),
@@ -6103,6 +6104,20 @@  main(int argc, char *argv[])
             ovnsb_cond_seqno = new_ovnsb_cond_seqno;
         }
 
+        /* Check if we have received all initial dumps of the southbound
+         * based on the monitor condtions we set.
+         * If we have sb_monitor_all that means we have all data that we would
+         * ever need.
+         * If we monitor cases individually we need at least one engine run. */
+        if (ovnsb_cond_seqno == ovnsb_expected_cond_seqno
+            && ovnsb_expected_cond_seqno != UINT_MAX) {
+            if (sb_monitor_all) {
+                daemon_started_recently_ignore();
+            } else if (has_computed_once) {
+                daemon_started_recently_countdown();
+            }
+        }
+
         struct engine_context eng_ctx = {
             .ovs_idl_txn = ovs_idl_txn,
             .ovnsb_idl_txn = ovnsb_idl_txn,
@@ -6260,8 +6275,9 @@  main(int argc, char *argv[])
 
                     stopwatch_stop(CONTROLLER_LOOP_STOPWATCH_NAME,
                                    time_msec());
+
                     if (engine_has_updated()) {
-                        daemon_started_recently_countdown();
+                        has_computed_once = true;
                     }
 
                     ct_zones_data = engine_get_data(&en_ct_zones);
@@ -6380,6 +6396,15 @@  main(int argc, char *argv[])
                                     &runtime_data->local_datapaths,
                                     sb_monitor_all);
                         }
+                        /* If there is no new expected seqno we have
+                         * finished loading all needed data from
+                         * southbound. We then need to run one more time since
+                         * we might behave differently. */
+                        if (daemon_started_recently()
+                            && ovnsb_cond_seqno == ovnsb_expected_cond_seqno) {
+                            daemon_started_recently_ignore();
+                            poll_immediate_wake();
+                        }
                         if (ovs_idl_txn) {
                             update_qos(sbrec_port_binding_by_name, ovs_idl_txn,
                                        ovsrec_port_by_qos,
diff --git a/lib/ovn-util.c b/lib/ovn-util.c
index af17082a9..153a69ff6 100644
--- a/lib/ovn-util.c
+++ b/lib/ovn-util.c
@@ -933,13 +933,24 @@  get_bridge(const struct ovsrec_bridge_table *bridge_table, const char *br_name)
     return NULL;
 }
 
+/* This counts the amount of iterations of monitoring condition updates and
+ * their respective update towards us.
+ * This only needs to handle the case of non monitor_all since that will use
+ * the ignore feature below.
+ * In this case we need at least 2 iterations:
+ * 1. the initial iteration where we pull the sb content based on the few
+ *    conditions we now have.
+ * 2. after the first engine run we have enough information to update the
+ *    monitoring conditions to their final values.
+ * However based on the structure of the datapaths we might need more. The
+ * value below is just a hard limit of iterations. We detect if we are done
+ * earlier and then skip further iterations. */
 #define DAEMON_STARTUP_DELAY_SEED 20
-#define DAEMON_STARTUP_DELAY_MS   10000
 
 static int64_t startup_ts;
 static int startup_delay = DAEMON_STARTUP_DELAY_SEED;
 
-/* Used by debug command only, for tests. */
+/* Used if we do not need the startup delay (e.g. when using monitor_all). */
 static bool ignore_startup_delay = false;
 
 OVS_CONSTRUCTOR(startup_ts_initializer) {
@@ -980,9 +991,7 @@  daemon_started_recently(void)
     if (startup_delay) {
         return true;
     }
-
-    /* Ensure that at least an amount of time has passed. */
-    return time_wall_msec() - startup_ts <= DAEMON_STARTUP_DELAY_MS;
+    return false;
 }
 
 /* Builds a unique address set compatible name ([a-zA-Z_.][a-zA-Z_.0-9]*)
diff --git a/tests/ovn-controller.at b/tests/ovn-controller.at
index a878a440b..781ee242a 100644
--- a/tests/ovn-controller.at
+++ b/tests/ovn-controller.at
@@ -161,7 +161,6 @@  check_patches \
     'br-eth0 patch-localnet2-to-br-int patch-br-int-to-localnet2'
 
 # Delete the mapping and the ovn-bridge-mapping patch ports should go away.
-check ovn-appctl -t ovn-controller debug/ignore-startup-delay
 AT_CHECK([ovs-vsctl remove Open_vSwitch . external-ids ovn-bridge-mappings])
 check_bridge_mappings
 check_patches
diff --git a/tests/ovn-performance.at b/tests/ovn-performance.at
index 44bd4f7e4..4ddb1bd81 100644
--- a/tests/ovn-performance.at
+++ b/tests/ovn-performance.at
@@ -238,9 +238,6 @@  for i in `seq 1 5`; do
     as hv$i
     ovs-vsctl add-br br-phys
     ovn_attach n1 br-phys 192.168.0.$i
-    # Do not postpone patch ports related changes, as they may occur more or less anywere within the test,
-    # hence causing unexpected recomputes.
-    check ovn-appctl -t ovn-controller debug/ignore-startup-delay
     if [[ $i -ge 3 ]] ; then
         ovs-vsctl add-br br-ex
         ovs-vsctl set open . external_ids:ovn-bridge-mappings="public:br-ex"
diff --git a/tests/ovn.at b/tests/ovn.at
index 596a7be2b..9af74774f 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -35301,7 +35301,6 @@  OVS_WAIT_UNTIL([
     test x430 = x$(as hv1 ovs-vsctl get Interface ${iface1_uuid} mtu_request)
 ])
 
-as hv1 check ovn-appctl -t ovn-controller debug/ignore-startup-delay
 # Check that pointing requested-chassis somewhere else will unplug the port
 check ovn-nbctl --wait=hv set Logical_Switch_Port lsp1 \
     options:requested-chassis=non-existent-chassis
@@ -35309,7 +35308,6 @@  OVS_WAIT_UNTIL([
     ! as hv1 ovs-vsctl get Interface ${iface1_uuid} _uuid
 ])
 
-as hv2 check ovn-appctl -t ovn-controller debug/ignore-startup-delay
 # Check that removing an lport will unplug it
 AT_CHECK([test x${iface2_uuid} = x$(as hv2 ovs-vsctl get Interface ${iface2_uuid} _uuid)], [0], [])
 check ovn-nbctl --wait=hv lsp-del ${lsp2_uuid}
@@ -38073,9 +38071,6 @@  ovs-vsctl -- add-port br-phys-2 fake-phys-2-to-int-2 -- \
 
 ovn_attach n1 br-phys 192.168.1.1 24
 
-# allow controller to immediately clean patch ports up
-check ovn-appctl -t ovn-controller debug/ignore-startup-delay
-
 # check that patch port on br-int is cleaned up (and also its peer)
 OVS_WAIT_UNTIL([
     test 0 = $(ovs-vsctl --columns _uuid --bare find Port name=fake-int-to-phys | wc -l)
@@ -38141,9 +38136,6 @@  check ovn-nbctl lsp-set-options ln_port network_name=phys-1
 wait_for_ports_up
 check ovn-nbctl --wait=hv sync
 
-# Allow controller to immediately clean patch ports up if any.
-check ovn-appctl -t ovn-controller debug/ignore-startup-delay
-
 # check that patch port created on br-int and br-phys-1.
 OVS_WAIT_UNTIL([
     test 1 = $(ovs-vsctl --columns _uuid --bare find Port name=patch-br-int-to-ln_port | wc -l)
@@ -41407,8 +41399,6 @@  check ovn-nbctl lsp-add sw0 sw0p1
 
 check ovn-nbctl --wait=hv sync
 
-check ovn-appctl -t ovn-controller debug/ignore-startup-delay
-
 # Waits until the OVS database contains exactly the specified patch ports.
 # Each argument should be of the form BRIDGE PORT PEER.
 check_patch_ports () {