Introduce the en_az engine node: move the availability-zone computation (previously done inline in ovn_db_run) into a dedicated node that resolves ctx->runned_az, which the downstream subsystem nodes gate on.
Also wire the ic-backoff-interval-ms option to throttle the engine loop (IC_NB_Global:options:ic-backoff-interval-ms), so a busy IC-SB does not spin ovn-ic at full speed. Assisted-by: Claude Opus 4.8, Claude Code Signed-off-by: Paulo Guilherme Silva <[email protected]> --- ic/automake.mk | 2 + ic/en-address-set.c | 17 ++++- ic/en-az.c | 127 +++++++++++++++++++++++++++++++ ic/en-az.h | 26 +++++++ ic/en-gateway.c | 10 ++- ic/en-port-binding.c | 10 ++- ic/en-route.c | 10 ++- ic/en-service-monitor.c | 10 ++- ic/en-tr.c | 8 ++ ic/en-ts.c | 8 ++ ic/inc-proc-ic.c | 24 +++++- ic/inc-proc-ic.h | 7 ++ ic/ovn-ic.c | 163 +++++++++++++++------------------------- ic/ovn-ic.h | 5 +- ovn-ic-nb.xml | 23 ++++++ 15 files changed, 338 insertions(+), 112 deletions(-) create mode 100644 ic/en-az.c create mode 100644 ic/en-az.h diff --git a/ic/automake.mk b/ic/automake.mk index fd888d60c..b730fc578 100644 --- a/ic/automake.mk +++ b/ic/automake.mk @@ -4,6 +4,8 @@ ic_ovn_ic_SOURCES = ic/ovn-ic.c \ ic/ovn-ic.h \ ic/en-ic.c \ ic/en-ic.h \ + ic/en-az.c \ + ic/en-az.h \ ic/en-dp-enum.c \ ic/en-dp-enum.h \ ic/en-gateway.c \ diff --git a/ic/en-address-set.c b/ic/en-address-set.c index 764dd71d2..5a29f8201 100644 --- a/ic/en-address-set.c +++ b/ic/en-address-set.c @@ -15,6 +15,7 @@ #include <config.h> #include "en-address-set.h" +#include "en-az.h" #include "lib/inc-proc-eng.h" #include "openvswitch/vlog.h" #include "ovn-ic.h" @@ -22,12 +23,22 @@ VLOG_DEFINE_THIS_MODULE(en_ic_address_set); enum engine_node_state -en_address_set_run(struct engine_node *node OVS_UNUSED, void *data OVS_UNUSED) +en_address_set_run(struct engine_node *node, void *data OVS_UNUSED) { const struct engine_context *eng_ctx = engine_get_context(); struct ic_context *ctx = eng_ctx->client_ctx; - - address_set_run(ctx); + const struct ed_type_az *az = engine_get_input_data("az", node); + + /* runned_az is resolved by the upstream en_az node. Without an AZ there + * is nothing to sync; this also mirrors the previous main-loop gating and + * avoids running address_set_run() before the NB Global row exists (en_az + * leaves runned_az NULL until then, and address_set_run() asserts on a + * NULL NB Global). */ + if (!az->runned_az) { + return EN_UNCHANGED; + } + + address_set_run(ctx, az->runned_az); return EN_UPDATED; } diff --git a/ic/en-az.c b/ic/en-az.c new file mode 100644 index 000000000..4d3c9bc71 --- /dev/null +++ b/ic/en-az.c @@ -0,0 +1,127 @@ +/* + * 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 "en-az.h" +#include "lib/inc-proc-eng.h" +#include "lib/ovn-ic-sb-idl.h" +#include "lib/ovn-nb-idl.h" +#include "openvswitch/vlog.h" +#include "ovn-ic.h" +#include "ovsdb-idl.h" +#include "util.h" + +VLOG_DEFINE_THIS_MODULE(en_ic_az); + +/* Resolves (and, if needed, registers) the availability zone for this ovn-ic + * instance. This node has no engine inputs: like the former az_run() helper + * it runs on every engine iteration and reads the NB Global and IC-SB + * Availability_Zone tables directly. The resolved AZ is stored in this node's + * own data (struct ed_type_az): dependent nodes declare en_az as an input and + * read it with engine_get_input_data("az", node), and the main loop reads it + * with inc_proc_ic_get_runned_az() after inc_proc_ic_run(). It reports + * EN_UPDATED only when the resolved AZ identity changes so dependent nodes do + * not recompute on every iteration. */ +enum engine_node_state +en_az_run(struct engine_node *node OVS_UNUSED, void *data_) +{ + const struct engine_context *eng_ctx = engine_get_context(); + struct ic_context *ctx = eng_ctx->client_ctx; + struct ed_type_az *data = data_; + const struct icsbrec_availability_zone *old_az = data->runned_az; + const struct icsbrec_availability_zone *az; + + data->runned_az = NULL; + + const struct nbrec_nb_global *nb_global = + nbrec_nb_global_first(ctx->ovnnb_idl); + if (!nb_global) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); + VLOG_WARN_RL(&rl, "NB Global does not exist."); + goto out; + } + + /* Update old AZ if name changes. Note: if name changed when ovn-ic + * is not running, one has to manually delete/update the old AZ with: + * "ovn-ic-sbctl destroy avail <az>". */ + if (ctx->ovnisb_unlocked_txn && data->az_name + && strcmp(data->az_name, nb_global->name)) { + ICSBREC_AVAILABILITY_ZONE_FOR_EACH (az, ctx->ovnisb_unlocked_idl) { + /* AZ name update locally need to update az in ISB. */ + if (nb_global->name[0] && !strcmp(az->name, data->az_name)) { + icsbrec_availability_zone_set_name(az, nb_global->name); + break; + } else if (!nb_global->name[0] + && !strcmp(az->name, data->az_name)) { + icsbrec_availability_zone_delete(az); + break; + } + } + free(data->az_name); + data->az_name = NULL; + } + + if (!nb_global->name[0]) { + goto out; + } + + if (!data->az_name) { + data->az_name = xstrdup(nb_global->name); + } + + if (ctx->ovnisb_unlocked_txn) { + ovsdb_idl_txn_add_comment(ctx->ovnisb_unlocked_txn, "AZ %s", + data->az_name); + } + + ICSBREC_AVAILABILITY_ZONE_FOR_EACH (az, ctx->ovnisb_unlocked_idl) { + if (!strcmp(az->name, data->az_name)) { + data->runned_az = az; + goto out; + } + } + + /* Create AZ in ISB */ + if (ctx->ovnisb_unlocked_txn) { + VLOG_INFO("Register AZ %s to interconnection DB.", data->az_name); + az = icsbrec_availability_zone_insert(ctx->ovnisb_unlocked_txn); + icsbrec_availability_zone_set_name(az, data->az_name); + data->runned_az = az; + } + +out: { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); + VLOG_DBG_RL(&rl, "Availability zone: %s", + data->runned_az ? data->runned_az->name : "not created yet."); + + return data->runned_az == old_az ? EN_UNCHANGED : EN_UPDATED; +} +} + +void * +en_az_init(struct engine_node *node OVS_UNUSED, + struct engine_arg *arg OVS_UNUSED) +{ + return xzalloc(sizeof(struct ed_type_az)); +} + +void +en_az_cleanup(void *data_) +{ + struct ed_type_az *data = data_; + + free(data->az_name); + data->az_name = NULL; +} diff --git a/ic/en-az.h b/ic/en-az.h new file mode 100644 index 000000000..ea9b1a81c --- /dev/null +++ b/ic/en-az.h @@ -0,0 +1,26 @@ +#ifndef EN_IC_AZ_H +#define EN_IC_AZ_H 1 + +#include <config.h> + +#include "lib/inc-proc-eng.h" + +struct icsbrec_availability_zone; + +/* Data maintained by the "az" engine node: the availability zone this ovn-ic + * instance runs for. This replaces the former az_run() helper and its file + * scope 'az_name' static variable. */ +struct ed_type_az { + /* The availability zone record for this instance, or NULL if it is not + * known/registered yet. Owned by the IDL. */ + const struct icsbrec_availability_zone *runned_az; + /* The name this instance registered as, persisted across iterations so a + * later rename can update the existing IC-SB record. */ + char *az_name; +}; + +enum engine_node_state en_az_run(struct engine_node *node, void *data); +void *en_az_init(struct engine_node *node, struct engine_arg *arg); +void en_az_cleanup(void *data); + +#endif /* EN_IC_AZ_H */ diff --git a/ic/en-gateway.c b/ic/en-gateway.c index f41166018..8a8755673 100644 --- a/ic/en-gateway.c +++ b/ic/en-gateway.c @@ -15,6 +15,7 @@ #include <config.h> #include "en-gateway.h" +#include "en-az.h" #include "lib/inc-proc-eng.h" #include "openvswitch/vlog.h" #include "ovn-ic.h" @@ -22,10 +23,17 @@ VLOG_DEFINE_THIS_MODULE(en_ic_gateway); enum engine_node_state -en_gateway_run(struct engine_node *node OVS_UNUSED, void *data OVS_UNUSED) +en_gateway_run(struct engine_node *node, void *data OVS_UNUSED) { const struct engine_context *eng_ctx = engine_get_context(); struct ic_context *ctx = eng_ctx->client_ctx; + const struct ed_type_az *az = engine_get_input_data("az", node); + + /* runned_az is resolved by the upstream en_az node. Without an AZ there + * is nothing to sync (mirrors the previous main-loop gating). */ + if (!az->runned_az) { + return EN_UNCHANGED; + } gateway_run(ctx); diff --git a/ic/en-port-binding.c b/ic/en-port-binding.c index 8843d6cef..3c0f67be8 100644 --- a/ic/en-port-binding.c +++ b/ic/en-port-binding.c @@ -15,6 +15,7 @@ #include <config.h> #include "en-port-binding.h" +#include "en-az.h" #include "lib/inc-proc-eng.h" #include "openvswitch/vlog.h" #include "ovn-ic.h" @@ -22,10 +23,17 @@ VLOG_DEFINE_THIS_MODULE(en_ic_port_binding); enum engine_node_state -en_port_binding_run(struct engine_node *node OVS_UNUSED, void *data OVS_UNUSED) +en_port_binding_run(struct engine_node *node, void *data OVS_UNUSED) { const struct engine_context *eng_ctx = engine_get_context(); struct ic_context *ctx = eng_ctx->client_ctx; + const struct ed_type_az *az = engine_get_input_data("az", node); + + /* runned_az is resolved by the upstream en_az node. Without an AZ there + * is nothing to sync (mirrors the previous main-loop gating). */ + if (!az->runned_az) { + return EN_UNCHANGED; + } port_binding_run(ctx); diff --git a/ic/en-route.c b/ic/en-route.c index ec39d89d9..e1edce910 100644 --- a/ic/en-route.c +++ b/ic/en-route.c @@ -15,6 +15,7 @@ #include <config.h> #include "en-route.h" +#include "en-az.h" #include "lib/inc-proc-eng.h" #include "openvswitch/vlog.h" #include "ovn-ic.h" @@ -22,10 +23,17 @@ VLOG_DEFINE_THIS_MODULE(en_ic_route); enum engine_node_state -en_route_run(struct engine_node *node OVS_UNUSED, void *data OVS_UNUSED) +en_route_run(struct engine_node *node, void *data OVS_UNUSED) { const struct engine_context *eng_ctx = engine_get_context(); struct ic_context *ctx = eng_ctx->client_ctx; + const struct ed_type_az *az = engine_get_input_data("az", node); + + /* runned_az is resolved by the upstream en_az node. Without an AZ there + * is nothing to sync (mirrors the previous main-loop gating). */ + if (!az->runned_az) { + return EN_UNCHANGED; + } route_run(ctx); diff --git a/ic/en-service-monitor.c b/ic/en-service-monitor.c index b85a36dc7..feb0630c3 100644 --- a/ic/en-service-monitor.c +++ b/ic/en-service-monitor.c @@ -15,6 +15,7 @@ #include <config.h> #include "en-service-monitor.h" +#include "en-az.h" #include "lib/inc-proc-eng.h" #include "openvswitch/vlog.h" #include "ovn-ic.h" @@ -22,11 +23,18 @@ VLOG_DEFINE_THIS_MODULE(en_ic_service_monitor); enum engine_node_state -en_service_monitor_run(struct engine_node *node OVS_UNUSED, +en_service_monitor_run(struct engine_node *node, void *data OVS_UNUSED) { const struct engine_context *eng_ctx = engine_get_context(); struct ic_context *ctx = eng_ctx->client_ctx; + const struct ed_type_az *az = engine_get_input_data("az", node); + + /* runned_az is resolved by the upstream en_az node. Without an AZ there + * is nothing to sync (mirrors the previous main-loop gating). */ + if (!az->runned_az) { + return EN_UNCHANGED; + } sync_service_monitor(ctx); diff --git a/ic/en-tr.c b/ic/en-tr.c index 4f05a4325..c3b1b60c6 100644 --- a/ic/en-tr.c +++ b/ic/en-tr.c @@ -16,6 +16,7 @@ #include "en-dp-enum.h" #include "en-tr.h" +#include "en-az.h" #include "lib/inc-proc-eng.h" #include "openvswitch/vlog.h" #include "ovn-ic.h" @@ -28,6 +29,13 @@ en_tr_run(struct engine_node *node, void *data OVS_UNUSED) const struct engine_context *eng_ctx = engine_get_context(); struct ic_context *ctx = eng_ctx->client_ctx; struct ed_type_dp_enum *dp = engine_get_input_data("dp_enum", node); + const struct ed_type_az *az = engine_get_input_data("az", node); + + /* runned_az is resolved by the upstream en_az node. Without an AZ there + * is nothing to sync (mirrors the previous main-loop gating). */ + if (!az->runned_az) { + return EN_UNCHANGED; + } tr_run(ctx, &dp->dp_tnlids, &dp->isb_tr_dps); diff --git a/ic/en-ts.c b/ic/en-ts.c index e8df1888a..e4ec2a0b7 100644 --- a/ic/en-ts.c +++ b/ic/en-ts.c @@ -16,6 +16,7 @@ #include "en-dp-enum.h" #include "en-ts.h" +#include "en-az.h" #include "lib/inc-proc-eng.h" #include "openvswitch/vlog.h" #include "ovn-ic.h" @@ -28,6 +29,13 @@ en_ts_run(struct engine_node *node, void *data OVS_UNUSED) const struct engine_context *eng_ctx = engine_get_context(); struct ic_context *ctx = eng_ctx->client_ctx; struct ed_type_dp_enum *dp = engine_get_input_data("dp_enum", node); + const struct ed_type_az *az = engine_get_input_data("az", node); + + /* runned_az is resolved by the upstream en_az node. Without an AZ there + * is nothing to sync (mirrors the previous main-loop gating). */ + if (!az->runned_az) { + return EN_UNCHANGED; + } ts_run(ctx, &dp->dp_tnlids, &dp->isb_ts_dps); diff --git a/ic/inc-proc-ic.c b/ic/inc-proc-ic.c index 8721c5673..8d48b86a2 100644 --- a/ic/inc-proc-ic.c +++ b/ic/inc-proc-ic.c @@ -27,6 +27,7 @@ #include "openvswitch/vlog.h" #include "inc-proc-ic.h" #include "en-ic.h" +#include "en-az.h" #include "en-dp-enum.h" #include "en-gateway.h" #include "en-ts.h" @@ -173,6 +174,7 @@ VLOG_DEFINE_THIS_MODULE(inc_proc_ic); /* Define engine nodes for other nodes. They should be defined as static to * avoid sparse errors. */ +static ENGINE_NODE(az); static ENGINE_NODE(dp_enum); static ENGINE_NODE(gateway); static ENGINE_NODE(ts); @@ -215,6 +217,7 @@ void inc_proc_ic_init(struct ovsdb_idl_loop *nb, engine_add_input(&en_dp_enum, &en_icnb_ic_nb_global, NULL); /* en_gateway: sync gateways/chassis between SB and IC-SB. */ + engine_add_input(&en_gateway, &en_az, NULL); engine_add_input(&en_gateway, &en_icsb_availability_zone, NULL); engine_add_input(&en_gateway, &en_icsb_gateway, NULL); engine_add_input(&en_gateway, &en_icsb_encap, NULL); @@ -222,6 +225,7 @@ 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 NB and IC-SB datapath bindings. */ + engine_add_input(&en_ts, &en_az, NULL); engine_add_input(&en_ts, &en_dp_enum, NULL); engine_add_input(&en_ts, &en_icnb_ic_nb_global, NULL); engine_add_input(&en_ts, &en_icnb_transit_switch, NULL); @@ -229,11 +233,13 @@ void inc_proc_ic_init(struct ovsdb_idl_loop *nb, engine_add_input(&en_ts, &en_icsb_encap, NULL); /* en_tr: sync transit routers to NB and IC-SB datapath bindings. */ + engine_add_input(&en_tr, &en_az, NULL); engine_add_input(&en_tr, &en_dp_enum, NULL); engine_add_input(&en_tr, &en_icnb_transit_router, NULL); engine_add_input(&en_tr, &en_nb_logical_router, NULL); /* en_port_binding: sync cross-AZ port bindings. */ + engine_add_input(&en_port_binding, &en_az, NULL); engine_add_input(&en_port_binding, &en_icsb_availability_zone, NULL); engine_add_input(&en_port_binding, &en_icsb_port_binding, NULL); engine_add_input(&en_port_binding, &en_icnb_transit_switch, NULL); @@ -247,6 +253,7 @@ void inc_proc_ic_init(struct ovsdb_idl_loop *nb, engine_add_input(&en_port_binding, &en_sb_chassis, NULL); /* en_route: advertise/learn cross-AZ routes. */ + engine_add_input(&en_route, &en_az, NULL); engine_add_input(&en_route, &en_icsb_availability_zone, NULL); engine_add_input(&en_route, &en_icsb_port_binding, NULL); engine_add_input(&en_route, &en_icsb_route, NULL); @@ -262,13 +269,21 @@ void inc_proc_ic_init(struct ovsdb_idl_loop *nb, engine_add_input(&en_route, &en_sb_learned_route, NULL); /* en_service_monitor: sync load-balancer health checks across AZs. */ + engine_add_input(&en_service_monitor, &en_az, NULL); engine_add_input(&en_service_monitor, &en_icsb_availability_zone, NULL); engine_add_input(&en_service_monitor, &en_icsb_service_monitor, NULL); engine_add_input(&en_service_monitor, &en_sb_sb_global, NULL); engine_add_input(&en_service_monitor, &en_sb_service_monitor, NULL); engine_add_input(&en_service_monitor, &en_sb_port_binding, NULL); - /* en_address_set: advertise/learn address sets across AZs. */ + /* en_address_set: advertise/learn address sets across AZs. + * + * Like the other AZ-scoped nodes, address_set_run() partitions IC-SB + * address sets into local/remote by comparing their availability_zone + * against this instance's AZ, so it depends on en_az (which reports + * EN_UPDATED only when the AZ identity changes) to be re-run when the + * resolved AZ changes. */ + engine_add_input(&en_address_set, &en_az, NULL); engine_add_input(&en_address_set, &en_nb_nb_global, NULL); engine_add_input(&en_address_set, &en_nb_address_set, NULL); engine_add_input(&en_address_set, &en_sb_address_set, NULL); @@ -343,6 +358,13 @@ inc_proc_ic_cleanup(void) engine_set_context(NULL); } +const struct icsbrec_availability_zone * +inc_proc_ic_get_runned_az(void) +{ + const struct ed_type_az *az = engine_get_data(&en_az); + return az ? az->runned_az : NULL; +} + bool inc_proc_ic_can_run(struct ic_engine_context *ctx) { diff --git a/ic/inc-proc-ic.h b/ic/inc-proc-ic.h index 36464564d..1fc3a0ca1 100644 --- a/ic/inc-proc-ic.h +++ b/ic/inc-proc-ic.h @@ -28,6 +28,13 @@ bool inc_proc_ic_run(struct ic_context *ctx, void inc_proc_ic_cleanup(void); bool inc_proc_ic_can_run(struct ic_engine_context *ctx); +struct icsbrec_availability_zone; + +/* Returns the availability zone resolved by the en_az engine node during the + * last inc_proc_ic_run(), or NULL if none is resolved yet. Used by the main + * loop, which runs the AZ sequence-number bookkeeping outside the engine. */ +const struct icsbrec_availability_zone *inc_proc_ic_get_runned_az(void); + static inline void inc_proc_ic_force_recompute(void) { diff --git a/ic/ovn-ic.c b/ic/ovn-ic.c index 9f3e7cafd..77a52f246 100644 --- a/ic/ovn-ic.c +++ b/ic/ovn-ic.c @@ -99,68 +99,6 @@ Options:\n\ stream_usage("database", true, true, false); } -static const struct icsbrec_availability_zone * -az_run(struct ic_context *ctx) -{ - const struct nbrec_nb_global *nb_global = - nbrec_nb_global_first(ctx->ovnnb_idl); - - if (!nb_global) { - VLOG_INFO("NB Global not exist."); - return NULL; - } - - /* Update old AZ if name changes. Note: if name changed when ovn-ic - * is not running, one has to manually delete/update the old AZ with: - * "ovn-ic-sbctl destroy avail <az>". */ - static char *az_name; - const struct icsbrec_availability_zone *az; - if (ctx->ovnisb_unlocked_txn && az_name - && strcmp(az_name, nb_global->name)) { - ICSBREC_AVAILABILITY_ZONE_FOR_EACH (az, ctx->ovnisb_unlocked_idl) { - /* AZ name update locally need to update az in ISB. */ - if (nb_global->name[0] && !strcmp(az->name, az_name)) { - icsbrec_availability_zone_set_name(az, nb_global->name); - break; - } else if (!nb_global->name[0] && !strcmp(az->name, az_name)) { - icsbrec_availability_zone_delete(az); - break; - } - } - free(az_name); - az_name = NULL; - } - - if (!nb_global->name[0]) { - return NULL; - } - - if (!az_name) { - az_name = xstrdup(nb_global->name); - } - - if (ctx->ovnisb_unlocked_txn) { - ovsdb_idl_txn_add_comment(ctx->ovnisb_unlocked_txn, "AZ %s", az_name); - } - - ICSBREC_AVAILABILITY_ZONE_FOR_EACH (az, ctx->ovnisb_unlocked_idl) { - if (!strcmp(az->name, az_name)) { - ctx->runned_az = az; - return az; - } - } - - /* Create AZ in ISB */ - if (ctx->ovnisb_unlocked_txn) { - VLOG_INFO("Register AZ %s to interconnection DB.", az_name); - az = icsbrec_availability_zone_insert(ctx->ovnisb_unlocked_txn); - icsbrec_availability_zone_set_name(az, az_name); - ctx->runned_az = az; - return az; - } - return NULL; -} - static uint32_t allocate_dp_key(struct hmap *dp_tnlids, bool vxlan_mode, const char *name) { @@ -611,7 +549,8 @@ sync_addr_set_from_icsb(struct ovsdb_idl_txn *ovnnb_txn, } void -address_set_run(struct ic_context *ctx) +address_set_run(struct ic_context *ctx, + const struct icsbrec_availability_zone *runned_az) { if (!ctx->ovnisb_unlocked_txn || !ctx->ovnnb_txn || !ctx->ovnsb_txn) { return; @@ -621,7 +560,7 @@ address_set_run(struct ic_context *ctx) struct shash ic_remote_as = SHASH_INITIALIZER(&ic_remote_as); const struct icsbrec_address_set *ic_as; ICSBREC_ADDRESS_SET_FOR_EACH (ic_as, ctx->ovnisb_unlocked_idl) { - if (ic_as->availability_zone == ctx->runned_az) { + if (ic_as->availability_zone == runned_az) { shash_add(&ic_local_as, ic_as->name, ic_as); } else { /* Merge addresses from all remote AZs that share the same @@ -659,7 +598,7 @@ address_set_run(struct ic_context *ctx) const struct icsbrec_address_set *icsb_as; icsb_as = shash_find_and_delete(&ic_local_as, sb_as->name); sync_addr_set_to_icsb(ctx->ovnisb_unlocked_txn, sb_as, icsb_as, - ctx->runned_az); + runned_az); } } } @@ -721,7 +660,7 @@ gateway_run(struct ic_context *ctx) struct shash remote_gws = SHASH_INITIALIZER(&remote_gws); const struct icsbrec_gateway *gw; ICSBREC_GATEWAY_FOR_EACH (gw, ctx->ovnisb_unlocked_idl) { - if (gw->availability_zone == ctx->runned_az) { + if (gw->availability_zone == inc_proc_ic_get_runned_az()) { shash_add(&local_gws, gw->name, gw); } else { shash_add(&remote_gws, gw->name, gw); @@ -734,7 +673,8 @@ gateway_run(struct ic_context *ctx) gw = shash_find_and_delete(&local_gws, chassis->name); if (!gw) { gw = icsbrec_gateway_insert(ctx->ovnisb_unlocked_txn); - icsbrec_gateway_set_availability_zone(gw, ctx->runned_az); + icsbrec_gateway_set_availability_zone(gw, + inc_proc_ic_get_runned_az()); icsbrec_gateway_set_name(gw, chassis->name); sync_sb_gw_to_isb(ctx, chassis, gw); } else if (is_gateway_data_changed(gw, chassis)) { @@ -1417,7 +1357,7 @@ port_binding_run(struct ic_context *ctx) const struct icsbrec_port_binding *isb_pb_key = icsbrec_port_binding_index_init_row(ctx->icsbrec_port_binding_by_az); icsbrec_port_binding_index_set_availability_zone(isb_pb_key, - ctx->runned_az); + inc_proc_ic_get_runned_az()); ICSBREC_PORT_BINDING_FOR_EACH_EQUAL (isb_pb, isb_pb_key, ctx->icsbrec_port_binding_by_az) { @@ -1456,7 +1396,7 @@ port_binding_run(struct ic_context *ctx) ICSBREC_PORT_BINDING_FOR_EACH_EQUAL (isb_pb, isb_pb_key, ctx->icsbrec_port_binding_by_ts) { - if (isb_pb->availability_zone == ctx->runned_az) { + if (isb_pb->availability_zone == inc_proc_ic_get_runned_az()) { shash_add(&local_pbs, isb_pb->logical_port, isb_pb); shash_find_and_delete(&switch_all_local_pbs, isb_pb->logical_port); @@ -1478,8 +1418,9 @@ port_binding_run(struct ic_context *ctx) if (ctx->ovnisb_txn && is_az_leader(ctx->ovnisb_txn)) { if (!isb_pb) { isb_pb = create_isb_pb( - ctx->ovnisb_txn, tsp->name, ctx->runned_az, - ts->name, &ts->header_.uuid, "transit-switch-port", + ctx->ovnisb_txn, tsp->name, + inc_proc_ic_get_runned_az(), ts->name, + &ts->header_.uuid, "transit-switch-port", &pb_tnlids); } sync_tsp_pb(tsp, isb_pb); @@ -1489,7 +1430,7 @@ port_binding_run(struct ic_context *ctx) isb_pb = shash_find_and_delete(&local_pbs, tsp->name); if (!isb_pb) { isb_pb = create_isb_pb(ctx->ovnisb_unlocked_txn, tsp->name, - ctx->runned_az, + inc_proc_ic_get_runned_az(), ts->name, &ts->header_.uuid, "transit-switch-port", &pb_tnlids); } @@ -1532,8 +1473,8 @@ port_binding_run(struct ic_context *ctx) if (!isb_pb) { isb_pb = create_isb_pb( ctx->ovnisb_unlocked_txn, sb_pb->logical_port, - ctx->runned_az, ts->name, &ts->header_.uuid, - "transit-switch-port", &pb_tnlids); + inc_proc_ic_get_runned_az(), ts->name, + &ts->header_.uuid, "transit-switch-port", &pb_tnlids); sync_ts_isb_pb(ctx, sb_pb, isb_pb); } else { sync_local_port(ctx, isb_pb, sb_pb, lsp); @@ -1617,7 +1558,7 @@ port_binding_run(struct ic_context *ctx) ICSBREC_PORT_BINDING_FOR_EACH_EQUAL (isb_pb, isb_pb_key, ctx->icsbrec_port_binding_by_ts) { - if (isb_pb->availability_zone == ctx->runned_az) { + if (isb_pb->availability_zone == inc_proc_ic_get_runned_az()) { shash_add(&local_pbs, isb_pb->logical_port, isb_pb); shash_find_and_delete(&router_all_local_pbs, isb_pb->logical_port); @@ -1636,7 +1577,7 @@ port_binding_run(struct ic_context *ctx) isb_pb = shash_find_and_delete(&local_pbs, trp->name); if (!isb_pb) { isb_pb = create_isb_pb(ctx->ovnisb_unlocked_txn, trp->name, - ctx->runned_az, + inc_proc_ic_get_runned_az(), tr->name, &tr->header_.uuid, "transit-router-port", &pb_tnlids); icsbrec_port_binding_set_address(isb_pb, trp->mac); @@ -2572,7 +2513,8 @@ sync_learned_routes(struct ic_context *ctx, || uuid_equals(&ic_lr->lr->header_.uuid, &lr_uuid)) { continue; } - } else if (isb_route->availability_zone == ctx->runned_az) { + } else if (isb_route->availability_zone == + inc_proc_ic_get_runned_az()) { continue; } @@ -3026,14 +2968,14 @@ route_run(struct ic_context *ctx) return; } - delete_orphan_ic_routes(ctx, ctx->runned_az); + delete_orphan_ic_routes(ctx, inc_proc_ic_get_runned_az()); struct hmap ic_lrs = HMAP_INITIALIZER(&ic_lrs); const struct icsbrec_port_binding *isb_pb; const struct icsbrec_port_binding *isb_pb_key = icsbrec_port_binding_index_init_row(ctx->icsbrec_port_binding_by_az); icsbrec_port_binding_index_set_availability_zone(isb_pb_key, - ctx->runned_az); + inc_proc_ic_get_runned_az()); /* Each port on TS maps to a logical router, which is stored in the * external_ids:router-id of the IC SB port_binding record. @@ -3118,7 +3060,8 @@ route_run(struct ic_context *ctx) } struct shash_node *node; SHASH_FOR_EACH (node, &routes_ad_by_ts) { - advertise_routes(ctx, ctx->runned_az, node->name, node->data); + advertise_routes(ctx, inc_proc_ic_get_runned_az(), node->name, + node->data); hmap_destroy(node->data); } shash_destroy_free_data(&routes_ad_by_ts); @@ -3271,8 +3214,8 @@ create_pushed_svcs_mon(struct ic_context *ctx, } create_service_monitor_info(pushed_svcs_map, sb_rec, &sb_rec->header_.uuid, - ctx->runned_az->name, target_az_name, - NULL, false); + inc_proc_ic_get_runned_az()->name, + target_az_name, NULL, false); } sbrec_service_monitor_index_destroy_row(key); @@ -3287,7 +3230,7 @@ create_synced_svcs_mon(struct ic_context *ctx, ctx->icsbrec_service_monitor_by_target_az); icsbrec_service_monitor_index_set_target_availability_zone( - key, ctx->runned_az->name); + key, inc_proc_ic_get_runned_az()->name); const struct icsbrec_service_monitor *ic_rec; ICSBREC_SERVICE_MONITOR_FOR_EACH_EQUAL (ic_rec, key, @@ -3304,7 +3247,7 @@ create_synced_svcs_mon(struct ic_context *ctx, const char *chassis_name = pb->chassis ? pb->chassis->name : NULL; create_service_monitor_info(synced_svcs_map, ic_rec, &ic_rec->header_.uuid, - ctx->runned_az->name, + inc_proc_ic_get_runned_az()->name, NULL, chassis_name, true); } @@ -3320,14 +3263,14 @@ create_local_ic_svcs_map(struct ic_context *ctx, ctx->icsbrec_service_monitor_by_source_az); icsbrec_service_monitor_index_set_source_availability_zone( - key, ctx->runned_az->name); + key, inc_proc_ic_get_runned_az()->name); const struct icsbrec_service_monitor *ic_rec; ICSBREC_SERVICE_MONITOR_FOR_EACH_EQUAL (ic_rec, key, ctx->icsbrec_service_monitor_by_source_az) { create_service_monitor_info(owned_svc_map, ic_rec, &ic_rec->header_.uuid, - ctx->runned_az->name, NULL, + inc_proc_ic_get_runned_az()->name, NULL, NULL, true); } @@ -3350,7 +3293,7 @@ create_local_sb_svcs_map(struct ic_context *ctx, ctx->sbrec_service_monitor_by_ic_learned) { create_service_monitor_info(owned_svc_map, sb_rec, &sb_rec->header_.uuid, - ctx->runned_az->name, NULL, + inc_proc_ic_get_runned_az()->name, NULL, NULL, false); } @@ -3593,6 +3536,7 @@ sync_service_monitor(struct ic_context *ctx) */ static void update_sequence_numbers(struct ic_context *ctx, + const struct icsbrec_availability_zone *runned_az, struct ovsdb_idl_loop *ic_sb_loop) { if (!ctx->ovnisb_unlocked_txn || !ctx->ovninb_txn) { @@ -3611,14 +3555,14 @@ update_sequence_numbers(struct ic_context *ctx, } if ((ic_nb->nb_ic_cfg != ic_sb->nb_ic_cfg) && - (ic_nb->nb_ic_cfg != ctx->runned_az->nb_ic_cfg)) { + (ic_nb->nb_ic_cfg != runned_az->nb_ic_cfg)) { /* Deal with potential overflows. */ - if (ctx->runned_az->nb_ic_cfg == INT64_MAX) { - icsbrec_availability_zone_set_nb_ic_cfg(ctx->runned_az, 0); + if (runned_az->nb_ic_cfg == INT64_MAX) { + icsbrec_availability_zone_set_nb_ic_cfg(runned_az, 0); } ic_sb_loop->next_cfg = ic_nb->nb_ic_cfg; ovsdb_idl_txn_increment(ctx->ovnisb_unlocked_txn, - &ctx->runned_az->header_, + &runned_az->header_, &icsbrec_availability_zone_col_nb_ic_cfg, true); return; } @@ -3626,22 +3570,22 @@ update_sequence_numbers(struct ic_context *ctx, /* handle cases where accidentally AZ:ic_nb_cfg exceeds * the INB:ic_nb_cfg. */ - if (ctx->runned_az->nb_ic_cfg != ic_sb_loop->cur_cfg) { - icsbrec_availability_zone_set_nb_ic_cfg(ctx->runned_az, + if (runned_az->nb_ic_cfg != ic_sb_loop->cur_cfg) { + icsbrec_availability_zone_set_nb_ic_cfg(runned_az, ic_sb_loop->cur_cfg); return; } const struct icsbrec_availability_zone *other_az; ICSBREC_AVAILABILITY_ZONE_FOR_EACH (other_az, ctx->ovnisb_unlocked_idl) { - if (other_az->nb_ic_cfg != ctx->runned_az->nb_ic_cfg) { + if (other_az->nb_ic_cfg != runned_az->nb_ic_cfg) { return; } } /* All the AZs are updated successfully, update SB/NB counter. */ if (ic_nb->nb_ic_cfg != ic_sb->nb_ic_cfg) { - icsbrec_ic_sb_global_set_nb_ic_cfg(ic_sb, ctx->runned_az->nb_ic_cfg); - icnbrec_ic_nb_global_set_sb_ic_cfg(ic_nb, ctx->runned_az->nb_ic_cfg); + icsbrec_ic_sb_global_set_nb_ic_cfg(ic_sb, runned_az->nb_ic_cfg); + icnbrec_ic_nb_global_set_sb_ic_cfg(ic_nb, runned_az->nb_ic_cfg); } } @@ -4219,6 +4163,17 @@ main(int argc, char *argv[]) ovnisb_idl_loop.idl, ovnisb_unlocked_idl_loop.idl, ovninb_idl_loop.idl); + + /* Postpone the next engine run by the length of the previous one, up + * to this interval, to coalesce bursts of change-driven runs. A + * forced recompute (set_force_recompute) still runs immediately, so + * this only throttles the natural, incremental path. */ + const struct icnbrec_ic_nb_global *ic_nb_global = + icnbrec_ic_nb_global_first(ovninb_idl_loop.idl); + eng_ctx.backoff_ms = ic_nb_global + ? smap_get_uint(&ic_nb_global->options, + "ic-backoff-interval-ms", 0) : 0; + memory_run(); if (memory_should_report()) { struct simap usage = SIMAP_INITIALIZER(&usage); @@ -4389,12 +4344,16 @@ main(int argc, char *argv[]) ovsdb_idl_has_ever_connected(ctx.ovnisb_unlocked_idl)) { if (ctx.ovnnb_txn && ctx.ovnsb_txn && ctx.ovninb_txn && ctx.ovnisb_unlocked_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, + /* The availability zone is now resolved by the en_az + * engine node. The subsystem nodes skip their work when + * there is no AZ; the sequence-number bookkeeping below + * runs outside the engine, so it reads the resolved AZ via + * inc_proc_ic_get_runned_az(). */ + (void) inc_proc_ic_run(&ctx, &eng_ctx); + const struct icsbrec_availability_zone *runned_az = + inc_proc_ic_get_runned_az(); + if (runned_az) { + update_sequence_numbers(&ctx, runned_az, &ovnisb_unlocked_idl_loop); } } else if (!inc_proc_ic_get_force_recompute()) { diff --git a/ic/ovn-ic.h b/ic/ovn-ic.h index b06725373..194eba397 100644 --- a/ic/ovn-ic.h +++ b/ic/ovn-ic.h @@ -28,7 +28,6 @@ struct ic_context { struct ovsdb_idl_txn *ovninb_txn; struct ovsdb_idl_txn *ovnisb_txn; struct ovsdb_idl_txn *ovnisb_unlocked_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; @@ -63,6 +62,7 @@ enum ic_port_binding_type { IC_SWITCH_PORT, IC_ROUTER_PORT, IC_PORT_MAX }; struct hmap; struct shash; +struct icsbrec_availability_zone; /* Per-subsystem entry points, invoked by the incremental-processing engine * nodes (see ic/en-*.c). Each performs a full recompute of its subsystem and @@ -70,7 +70,8 @@ struct shash; void enumerate_datapaths(struct ic_context *ctx, struct hmap *dp_tnlids, struct shash *isb_ts_dps, struct shash *isb_tr_dps); void gateway_run(struct ic_context *ctx); -void address_set_run(struct ic_context *ctx); +void address_set_run(struct ic_context *ctx, + const struct icsbrec_availability_zone *runned_az); void ts_run(struct ic_context *ctx, struct hmap *dp_tnlids, struct shash *isb_ts_dps); void tr_run(struct ic_context *ctx, struct hmap *dp_tnlids, diff --git a/ovn-ic-nb.xml b/ovn-ic-nb.xml index f6110324c..408b44558 100644 --- a/ovn-ic-nb.xml +++ b/ovn-ic-nb.xml @@ -65,6 +65,29 @@ options are described individually below. </column> + <column name="options" key="ic-backoff-interval-ms"> + <p> + Upper bound, in milliseconds, on how long <code>ovn-ic</code> + postpones the next run of its incremental-processing engine. + After each engine run, the next run is delayed by the duration of + the run that just finished, capped by this value. This coalesces + bursts of change-driven runs into fewer, larger runs, trading a + small amount of latency for reduced CPU usage when the databases + are changing rapidly. + </p> + + <p> + This throttling only applies to the natural, change-driven + incremental path. A forced full recompute is never delayed and + always runs immediately. + </p> + + <p> + The default value is <code>0</code>, which disables the backoff so + each run is scheduled as soon as there is work to do. + </p> + </column> + <column name="options" key="ic_probe_interval"> <p> The inactivity probe interval of the connection to the OVN IC -- 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
