Hi Lucas,

Thanks, confirmed. The en_ts handler had the same problem as 07/14: the
NB Logical_Switch mirror of the old name was left behind. v5 applies the
same fix here (a rename falls back to a full recompute), and the rename
test added in 07/14 also checks the NB mirror.

While checking the other handlers that scope by transit switch/router
name, I found the same pattern in en_port_binding (11/14), for both
Transit_Switch and Transit_Router. That one is fixed the same way in v5.

Regards,
Paulo


Em sex., 18 de set. de 2026 às 13:09, Lucas Vargas Dias
<[email protected]> escreveu:

> Hi Paulo, Tiago
>
> Same rename problem as in 07/14.
> Leak on transit switch's rename.
>
> Regards,
> Lucas
>
> Em qua., 2 de set. de 2026 às 18:19, Paulo Guilherme Silva
> <[email protected]> escreveu:
>
>> Add the incremental change handlers for the en_ts node: an NB
>> Logical_Switch change that is not a transit-switch mirror is a no-op, and
>> transit-switch changes are reconciled per transit switch (scoped) instead
>> of forcing a full recompute of every transit switch.
>>
>> Assisted-by: Claude Opus 4.8, Claude Code
>> Co-authored-by: Tiago Matos <[email protected]>
>> Signed-off-by: Tiago Matos <[email protected]>
>> Signed-off-by: Paulo Guilherme Silva <[email protected]>
>> ---
>>  ic/en-ts.c       | 135 +++++++++++++++++++++++++++++++++++++++++++++++
>>  ic/en-ts.h       |   9 ++++
>>  ic/inc-proc-ic.c |  21 +++++---
>>  3 files changed, 159 insertions(+), 6 deletions(-)
>>
>> diff --git a/ic/en-ts.c b/ic/en-ts.c
>> index d983e6bb0..93a53fe92 100644
>> --- a/ic/en-ts.c
>> +++ b/ic/en-ts.c
>> @@ -68,6 +68,141 @@ en_ts_run(struct engine_node *node, void *data
>> OVS_UNUSED)
>>      return EN_UPDATED;
>>  }
>>
>> +/* Returns the ic_context, or NULL when there is no availability zone
>> yet (in
>> + * which case the caller should report EN_HANDLED_UNCHANGED). */
>> +static struct ic_context *
>> +ts_handler_ctx(struct engine_node *node)
>> +{
>> +    struct ic_context *ctx = engine_get_context()->client_ctx;
>> +    const struct ed_type_az *az = engine_get_input_data("az", node);
>> +    return az->runned_az ? ctx : NULL;
>> +}
>> +
>> +/* Runs the scoped NB-mirror sync for the collected transit-switch scope
>> and
>> + * maps it to an engine result.  ts_sync_scope() gates the NB mirror on
>> the
>> + * ovnnb_txn it needs, exactly as the full recompute does. */
>> +static enum engine_input_handler_result
>> +ts_scope_finish(struct ic_context *ctx, struct sset *ts_scope)
>> +{
>> +    if (sset_is_empty(ts_scope)) {
>> +        return EN_HANDLED_UNCHANGED;
>> +    }
>> +
>> +    struct shash isb_ts_dps;
>> +    collect_ts_datapaths(ctx, &isb_ts_dps);
>> +    ts_sync_scope(ctx, &isb_ts_dps, ts_scope);
>> +    shash_destroy(&isb_ts_dps);
>> +
>> +    return EN_HANDLED_UPDATED;
>> +}
>> +
>> +/* IC-NB Transit_Switch: a new/deleted/renamed transit switch must have
>> its NB
>> + * mirror reconciled.  A deletion is honoured via the scoped GC in
>> + * ts_sync_scope() (the deleted row's name stays in scope but is absent
>> from
>> + * IC-NB, so its mirror leftover is removed). */
>> +enum engine_input_handler_result
>> +en_ts_icnb_transit_switch_handler(struct engine_node *node,
>> +                                  void *data OVS_UNUSED)
>> +{
>> +    struct ic_context *ctx = ts_handler_ctx(node);
>> +    if (!ctx) {
>> +        return EN_HANDLED_UNCHANGED;
>> +    }
>> +
>> +    const struct icnbrec_transit_switch_table *tbl =
>> +        EN_OVSDB_GET(engine_get_input("ICNB_transit_switch", node));
>> +    struct sset ts_scope = SSET_INITIALIZER(&ts_scope);
>> +    const struct icnbrec_transit_switch *ts;
>> +    ICNBREC_TRANSIT_SWITCH_TABLE_FOR_EACH_TRACKED (ts, tbl) {
>> +        sset_add(&ts_scope, ts->name);
>> +    }
>> +
>> +    enum engine_input_handler_result ret = ts_scope_finish(ctx,
>> &ts_scope);
>> +    sset_destroy(&ts_scope);
>> +    return ret;
>> +}
>> +
>> +/* Only transit-switch mirror logical switches
>> (other_config:interconn-ts)
>> + * affect en_ts.  A change to such a logical switch reconciles that
>> transit
>> + * switch (re-creating the mirror if it was deleted externally); any
>> other
>> + * logical switch is irrelevant to en_ts, so its change is a no-op,
>> avoiding a
>> + * recompute on unrelated NB Logical_Switch updates. */
>> +enum engine_input_handler_result
>> +en_ts_nb_logical_switch_handler(struct engine_node *node,
>> +                                void *data OVS_UNUSED)
>> +{
>> +    struct ic_context *ctx = ts_handler_ctx(node);
>> +    if (!ctx) {
>> +        return EN_HANDLED_UNCHANGED;
>> +    }
>> +
>> +    const struct nbrec_logical_switch_table *tbl =
>> +        EN_OVSDB_GET(engine_get_input("NB_logical_switch", node));
>> +    struct sset ts_scope = SSET_INITIALIZER(&ts_scope);
>> +    const struct nbrec_logical_switch *ls;
>> +    NBREC_LOGICAL_SWITCH_TABLE_FOR_EACH_TRACKED (ls, tbl) {
>> +        const char *ts_name = smap_get(&ls->other_config,
>> "interconn-ts");
>> +        if (ts_name) {
>> +            sset_add(&ts_scope, ts_name);
>> +        }
>> +    }
>> +
>> +    enum engine_input_handler_result ret = ts_scope_finish(ctx,
>> &ts_scope);
>> +    sset_destroy(&ts_scope);
>> +    return ret;
>> +}
>> +
>> +/* IC-SB Datapath_Binding: when a transit switch's datapath tunnel key is
>> + * (re)assigned by en_tunnel_key, the NB Logical_Switch mirror's
>> + * other_config:requested-tnl-key must be updated to the committed
>> value.  This
>> + * is what synchronizes the key after a global tunnel-key refresh (an
>> IC-NB
>> + * vxlan_mode change reallocates the datapath key into the VXLAN range in
>> + * IC-SB, but the NB value only catches up on a follow-up iteration).
>> + *
>> + * Only transit-switch bindings have an NB mirror; transit routers
>> (IC_ROUTER)
>> + * are irrelevant.  Deletions are ignored: a transit switch removal is
>> + * reconciled through en_ts_icnb_transit_switch_handler and the scoped
>> GC in
>> + * ts_sync_scope().
>> + *
>> + * Newly *inserted* bindings are also ignored, on purpose.  en_tunnel_key
>> + * inserts the binding and already publishes its freshly-allocated key
>> to the
>> + * mirror in the same iteration; reacting to that insert here would
>> re-sync the
>> + * transit switch while its mirror (if just created by
>> + * en_ts_icnb_transit_switch_handler) is still uncommitted -
>> find_ts_in_nb()'s
>> + * index does not see the txn-local insert, so a duplicate NB
>> Logical_Switch
>> + * would be created.  Here we only react to a tunnel-key *modify* on an
>> + * already-existing binding (the vxlan refresh), whose mirror already
>> + * exists. */
>> +enum engine_input_handler_result
>> +en_ts_icsb_datapath_binding_handler(struct engine_node *node,
>> +                                    void *data OVS_UNUSED)
>> +{
>> +    struct ic_context *ctx = ts_handler_ctx(node);
>> +    if (!ctx) {
>> +        return EN_HANDLED_UNCHANGED;
>> +    }
>> +
>> +    const struct icsbrec_datapath_binding_table *tbl =
>> +        EN_OVSDB_GET(engine_get_input("ICSB_datapath_binding", node));
>> +    struct sset ts_scope = SSET_INITIALIZER(&ts_scope);
>> +    const struct icsbrec_datapath_binding *isb_dp;
>> +    ICSBREC_DATAPATH_BINDING_TABLE_FOR_EACH_TRACKED (isb_dp, tbl) {
>> +        if (icsbrec_datapath_binding_is_deleted(isb_dp) ||
>> +            icsbrec_datapath_binding_is_new(isb_dp) ||
>> +            ic_dp_get_type(isb_dp) != IC_SWITCH) {
>> +            continue;
>> +        }
>> +        if (ovsdb_idl_track_is_updated(&isb_dp->header_,
>> +                    &icsbrec_datapath_binding_col_tunnel_key)) {
>> +            sset_add(&ts_scope, isb_dp->transit_switch);
>> +        }
>> +    }
>> +
>> +    enum engine_input_handler_result ret = ts_scope_finish(ctx,
>> &ts_scope);
>> +    sset_destroy(&ts_scope);
>> +    return ret;
>> +}
>> +
>>  void *
>>  en_ts_init(struct engine_node *node OVS_UNUSED,
>>             struct engine_arg *arg OVS_UNUSED)
>> diff --git a/ic/en-ts.h b/ic/en-ts.h
>> index cf33d9ab6..32bdfedb0 100644
>> --- a/ic/en-ts.h
>> +++ b/ic/en-ts.h
>> @@ -21,4 +21,13 @@ enum engine_node_state en_ts_run(struct engine_node
>> *node, void *data);
>>  void *en_ts_init(struct engine_node *node, struct engine_arg *arg);
>>  void en_ts_cleanup(void *data);
>>
>> +enum engine_input_handler_result
>> +en_ts_icnb_transit_switch_handler(struct engine_node *node, void *data);
>> +
>> +enum engine_input_handler_result
>> +en_ts_nb_logical_switch_handler(struct engine_node *node, void *data);
>> +
>> +enum engine_input_handler_result
>> +en_ts_icsb_datapath_binding_handler(struct engine_node *node, void
>> *data);
>> +
>>  #endif /* EN_IC_TS_H */
>> diff --git a/ic/inc-proc-ic.c b/ic/inc-proc-ic.c
>> index a30ddf367..89135fc5d 100644
>> --- a/ic/inc-proc-ic.c
>> +++ b/ic/inc-proc-ic.c
>> @@ -218,14 +218,23 @@ void inc_proc_ic_init(struct ovsdb_idl_loop *nb,
>>      engine_add_input(&en_gateway, &en_sb_encap, NULL);
>>
>>      /* en_ts: sync transit switches to their AZ NB Logical_Switch
>> mirrors.
>> +     *
>>       * en_ts builds its own transit-switch IC-SB Datapath_Binding map
>> each run
>> -     * and only maintains the NB mirror; IC-SB Datapath_Binding
>> creation/keying
>> -     * is owned by en_tunnel_key (downstream). */
>> +     * (local data, never shared) and only maintains the NB mirror.
>> IC-SB
>> +     * Datapath_Binding creation/keying is owned by en_tunnel_key
>> (downstream),
>> +     * so en_ts no longer allocates tunnel keys.
>> en_icsb_datapath_binding
>> +     * drives the follow-up NB requested-tnl-key sync after en_tunnel_key
>> +     * (re)assigns a key - notably the global refresh from an IC-NB
>> vxlan_mode
>> +     * change (see en_ts_icsb_datapath_binding_handler). */
>>      engine_add_input(&en_ts, &en_az, NULL);
>> -    engine_add_input(&en_ts, &en_icsb_datapath_binding, NULL);
>> -    engine_add_input(&en_ts, &en_icnb_ic_nb_global, NULL);
>> -    engine_add_input(&en_ts, &en_icnb_transit_switch, NULL);
>> -    engine_add_input(&en_ts, &en_nb_logical_switch, NULL);
>> +    engine_add_input(&en_ts, &en_icsb_datapath_binding,
>> +                     en_ts_icsb_datapath_binding_handler);
>> +    engine_add_input(&en_ts, &en_icnb_ic_nb_global,
>> +                     ic_nb_global_options_handler);
>> +    engine_add_input(&en_ts, &en_icnb_transit_switch,
>> +                     en_ts_icnb_transit_switch_handler);
>> +    engine_add_input(&en_ts, &en_nb_logical_switch,
>> +                     en_ts_nb_logical_switch_handler);
>>      engine_add_input(&en_ts, &en_icsb_encap, NULL);
>>
>>      /* en_tr: sync transit routers to their AZ NB Logical_Router mirrors.
>> --
>> 2.34.1
>>
>>
>> --
>>
>>
>>
>>
>> _'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'.*
>>
>>
>>
>> _______________________________________________
>> dev mailing list
>> [email protected]
>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>>
>

-- 




_‘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’.*



_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to