On 6/22/26 9:49 PM, Ilya Maximets wrote:
> Looking at the 'monitor_cond_since, cluster disconnect' test scenario,
> the test creates a new connection without monitoring anything (false),
> then it updates conditions to monitor rows with i == 2, then i == 1,
> then it reconnects and checks that the right data is monitored.
> 
> The problem is that it doesn't wait for 'i == 2' to be acknowledged
> before reconnecting.  So, there are two cases here in practice:
> 
>                                  new        req       ack
> 1. - set_conditions(i == 2)    i == 2
>    - minitor_cond_change               >  i == 2
>    - update3
>    - ack                                          > i == 2
>    - set_conditions(i == 1)    i == 1               i == 2
>    - monitor_cond_change               >  i == 1    i == 2
>    - disconnect
>    - sync_conditions()         i == 1  <            i == 2
>        Here the requested is downgraded back to new and the last-id
>        is reset to zero to avoid data inconsistency.
>    - connect
>      sync_conditions()                            > i == 1
>        The 'new' condition is promoted to 'acked', since there is no
>        harm in doing that, as the last-id is zero and the cache will
>        be cleared anyway.
> 
>                                  new        req       ack
> 2. - set_conditions(i == 2)    i == 2
>    - minitor_cond_change               >  i == 2
>    - update3
>        Ack didn't arrive in time.
>    - set_conditions(i == 1)    i == 1     i == 2
>        Previous condition is still in-flight, so this one is 'new'
>        and the monitor_cond_change is not sent out yet.
>    - disconnect
>    - sync_conditions()         i == 1       x
>        Here the requested condition is just dropped because there is
>        no point sending it again.  But the last-id is still cleared.
>    - connect
>      sync_conditions()                            > i == 1
>        The 'new' condition is promoted to 'acked', since there is no
>        harm in doing that, as the last-id is zero and the cache will
>        be cleared anyway.
> 
> The end result is the same: i == 1 sent in the next monitor request,
> but the path is different.
> 
> In both cases the last_id is cleared though, so we get the full
> snapshot of the data with the new condition with the old cache
> cleared, so there is no data integrity issue.
> 
> However, this double synchronization on disconnect and then on connect
> is tricky and hard to follow.  Also, python code doesn't do the same
> thing.  In python the second sync doesn't happen and we first send the
> acked conditions inside the monitor and then follow up with the new as
> a condition change request.
> 
> Let's unify the logic and get rid of the two stages entirely:
> 
> - If there are any in-flight requests, the last-id must be cleared to
>   clear the cache.
> 
> - If the last-id is zero or it is getting cleared, it's always safe to
>   just use the latest conditions, even if not acked, since the cache
>   will be cleared anyway.
> 
> - Only if the last-id is non-zero and stays non-zero, then we need to
>   first send the acked and then follow up with the new.
> 
> Applying the same logic to python and C implementation to make them
> both the same and work in one stage.  After this change calling
> sync_conditions() multiple times in a row doesn't change the state.
> 
> This change is necessary for the upcoming functionality that will move
> the condition synchronization out of the direct FSM restart path.
> 
> No new tests added as there should be no user-visible changes.
> At least for the C version.  And the existing tests cover the high
> level logic of the reconnect, e.g., the test mentioned above.
> 
> Signed-off-by: Ilya Maximets <[email protected]>
> ---
> 
> Required for:
>   
> https://patchwork.ozlabs.org/project/openvswitch/patch/b0b0aa8aaa80941d62d0de6ffaeca8cb3f6bf741.1780647734.git.lorenzo.bianc...@redhat.com/
> 
>  lib/ovsdb-cs.c       | 88 ++++++++++++++++++++------------------------
>  python/ovs/db/idl.py | 41 ++++++++++-----------
>  2 files changed, 59 insertions(+), 70 deletions(-)

Thanks, Dumitru and Lorenzo for review!

> 
> diff --git a/lib/ovsdb-cs.c b/lib/ovsdb-cs.c
> index 3920c2e5c..d96a97599 100644
> --- a/lib/ovsdb-cs.c
> +++ b/lib/ovsdb-cs.c
> @@ -1072,31 +1072,44 @@ ovsdb_cs_db_ack_condition(struct ovsdb_cs_db *db)
>  
>  /* Should be called when the CS fsm is restarted and resyncs table conditions
>   * based on the state the DB is in:
> - * - if a non-zero last_id is available for the DB then upon reconnect
> + * - If a non-zero 'last_id' is available for the DB, then upon reconnect
>   *   the CS should first request acked conditions to avoid missing updates
>   *   about records that were added before the transaction with
> - *   txn-id == last_id. If there were requested condition changes in flight
> - *   (i.e., req_cond not NULL) and the CS client didn't set new conditions
> - *   (i.e., new_cond is NULL) then move req_cond to new_cond to trigger a
> - *   follow up monitor_cond_change request.
> - * - if there's no last_id available for the DB then it's safe to use the
> - *   latest conditions set by the CS client even if they weren't acked yet.
> + *   txn-id == last_id.
> + *
> + * - If there were changes in flight then there are two cases:
> + *   a. either the server already processed the requested monitor condition
> + *      change but the FSM was restarted before the client was notified.
> + *      In this case the client should clear its local cache because it's
> + *      out of sync with the monitor view on the server side.
> + *
> + *   b. OR the server hasn't processed the requested monitor condition
> + *      change yet.
> + *
> + *   As there's no easy way to differentiate between the two, and given that
> + *   this condition should be rare, reset the 'last_id', essentially flushing
> + *   the local cached DB contents.
> + *
> + * - If there's no 'last_id' available for the DB or it was reset, then it's
> + *   safe to use the latest conditions set by the CS client even if they
> + *   weren't acked yet, since the local cache will be cleared anyway.
>   */
>  static void
>  ovsdb_cs_db_sync_condition(struct ovsdb_cs_db *db)
>  {
> -    bool ack_all = uuid_is_zero(&db->last_id);
> -    if (ack_all) {
> -        db->cond_changed = false;
> -    }
> -
>      struct ovsdb_cs_db_table *table;
> +
>      HMAP_FOR_EACH (table, hmap_node, &db->tables) {
> -        /* When monitor_cond_since requests will be issued, the
> -         * table->ack_cond condition will be added to the "where" clause".
> -         * Follow up monitor_cond_change requests will use table->new_cond.
> -         */
> -        if (ack_all) {
> +        if (table->req_cond) {
> +            /* There was an in-flight condition change - reset. */
> +            db->last_id = UUID_ZERO;
> +            break;
> +        }
> +    }
> +
> +    if (uuid_is_zero(&db->last_id)) {
> +        /* No 'last_id' - use the latest conditions for the monitor request. 
> */
> +        HMAP_FOR_EACH (table, hmap_node, &db->tables) {
>              if (table->new_cond) {
>                  json_destroy(table->req_cond);
>                  table->req_cond = table->new_cond;
> @@ -1108,39 +1121,16 @@ ovsdb_cs_db_sync_condition(struct ovsdb_cs_db *db)
>                  table->ack_cond = table->req_cond;
>                  table->req_cond = NULL;
>              }
> -        } else {
> -            if (table->req_cond) {
> -                /* There was an in-flight monitor_cond_change request.  It's 
> no
> -                 * longer relevant in the restarted FSM, so clear it. */
> -                if (table->new_cond) {
> -                    /* We will send a new monitor_cond_change with the new
> -                     * condition.  The previously in-flight condition is
> -                     * irrelevant and we can just forget about it. */
> -                    json_destroy(table->req_cond);
> -                } else {
> -                    /* The restarted FSM needs to again send a request for 
> the
> -                     * previously in-flight condition. */
> -                    table->new_cond = table->req_cond;
> -                }
> -                table->req_cond = NULL;
> +        }
> +        /* Nothig to send after the initial monitor request. */

There is one more typo here: 'Nothig' -> 'Nothing'.

> +        db->cond_changed = false;

And I also missed this line in the python code.  It's inconsequential, since
there are no 'new' conditions to send at this point anyway, but we should be
consistent.

So, I added this line to the python code, fixed the typos here and in the
commit message and applied the patch.

Best regards, Ilya Maximets.
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to