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
