On Wed, May 24, 2023 at 11:27 PM Ales Musil <[email protected]> wrote:
>
> The incremental processing is broken for addresses
> that have mask which could "erase" portion of the address
> itself e.g. 10.16.0.47/4, after applying the mask with token
> parser the address becomes 0.0.0.0/4, which is fine for
> logical flows. However, for the deletion/addition to database
> we need the original string representation which cannot be
> built from the parsed token.
>
> Use strcmp over already sorted lists which should give
> us the needed diff. The time complexity didn't change,
> but the performance was slightly improved, see the numbers
> below. The setup was created by the scale script from Han [0].
>
> Numbers with expr:
> Time spent on processing nb_cfg 1:
> ovn-northd delay before processing: 21ms
> ovn-northd completion: 544ms
> ovn-controller(s) completion: 544ms
> Time spent on processing nb_cfg 2:
> ovn-northd delay before processing: 17ms
> ovn-northd completion: 537ms
> ovn-controller(s) completion: 535ms
> Time spent on processing nb_cfg 3:
> ovn-northd delay before processing: 20ms
> ovn-northd completion: 552ms
> ovn-controller(s) completion: 550ms
> Time spent on processing nb_cfg 4:
> ovn-northd delay before processing: 16ms
> ovn-northd completion: 529ms
> ovn-controller(s) completion: 528ms
>
> Numbers with the strcmp:
> Time spent on processing nb_cfg 1:
> ovn-northd delay before processing: 24ms
> ovn-northd completion: 521ms
> ovn-controller(s) completion: 521ms
> Time spent on processing nb_cfg 2:
> ovn-northd delay before processing: 17ms
> ovn-northd completion: 500ms
> ovn-controller(s) completion: 500ms
> Time spent on processing nb_cfg 3:
> ovn-northd delay before processing: 17ms
> ovn-northd completion: 496ms
> ovn-controller(s) completion: 497ms
> Time spent on processing nb_cfg 4:
> ovn-northd delay before processing: 18ms
> ovn-northd completion: 502ms
> ovn-controller(s) completion: 500ms
>
> [0]
https://github.com/hzhou8/ovn-test-script/blob/e1149318201309db6d96ae8d5a33fcfbbe1872a3/test_ov
n_controller_scale.sh
>
> Fixes: 0d5e0a6ced49 ("northd: Add I-P for syncing SB address sets.")
> Reported-at: https://bugzilla.redhat.com/2196885
> Reported-at:
https://mail.openvswitch.org/pipermail/ovs-discuss/2023-May/052426.html
> Reported-By: 张祖建 <[email protected]>
> Signed-off-by: Ales Musil <[email protected]>
> ---
> v2: Address comments from Ilya:
> Use "custom" algorithm to prevent a ton of allocations with svec.
> Add original reporter.
> ---
> northd/en-sync-sb.c | 183 +++++++++++++++++++++++++++-----------------
> 1 file changed, 112 insertions(+), 71 deletions(-)
>
> diff --git a/northd/en-sync-sb.c b/northd/en-sync-sb.c
> index 758f00bfd..87402d6fb 100644
> --- a/northd/en-sync-sb.c
> +++ b/northd/en-sync-sb.c
> @@ -22,7 +22,6 @@
> #include "openvswitch/util.h"
>
> #include "en-sync-sb.h"
> -#include "include/ovn/expr.h"
> #include "lib/inc-proc-eng.h"
> #include "lib/lb.h"
> #include "lib/ovn-nb-idl.h"
> @@ -34,8 +33,15 @@
>
> VLOG_DEFINE_THIS_MODULE(en_sync_to_sb);
>
> +/* This is just a type wrapper to enforce that it has to be sorted. */
> +struct sorted_addresses {
> + const char **arr;
> + size_t n;
> +};
> +
> +
> static void sync_addr_set(struct ovsdb_idl_txn *ovnsb_txn, const char
*name,
> - const char **addrs, size_t n_addrs,
> + struct sorted_addresses *addresses,
> struct shash *sb_address_sets);
> static void sync_addr_sets(struct ovsdb_idl_txn *ovnsb_txn,
> const struct nbrec_address_set_table *,
> @@ -44,11 +50,17 @@ static void sync_addr_sets(struct ovsdb_idl_txn
*ovnsb_txn,
> const struct ovn_datapaths *lr_datapaths);
> static const struct sbrec_address_set *sb_address_set_lookup_by_name(
> struct ovsdb_idl_index *, const char *name);
> -static void update_sb_addr_set(const char **nb_addresses, size_t
n_addresses,
> +static void update_sb_addr_set(struct sorted_addresses *,
> const struct sbrec_address_set *);
> static void build_port_group_address_set(const struct nbrec_port_group *,
> struct svec *ipv4_addrs,
> struct svec *ipv6_addrs);
> +static struct sorted_addresses
> +sorted_addresses_from_nbrec(const struct nbrec_address_set *nb_as);
> +static struct sorted_addresses
> +sorted_addresses_from_svec(struct svec *addresses);
> +static struct sorted_addresses
> +sorted_addresses_from_sset(struct sset* addresses);
>
> void *
> en_sync_to_sb_init(struct engine_node *node OVS_UNUSED,
> @@ -133,8 +145,9 @@ sync_to_sb_addr_set_nb_address_set_handler(struct
engine_node *node,
> if (!sb_addr_set) {
> return false;
> }
> - update_sb_addr_set((const char **) nb_addr_set->addresses,
> - nb_addr_set->n_addresses, sb_addr_set);
> + struct sorted_addresses addrs =
> + sorted_addresses_from_nbrec(nb_addr_set);
> + update_sb_addr_set(&addrs, sb_addr_set);
> }
>
> return true;
> @@ -180,10 +193,14 @@ sync_to_sb_addr_set_nb_port_group_handler(struct
engine_node *node,
> struct svec ipv4_addrs = SVEC_EMPTY_INITIALIZER;
> struct svec ipv6_addrs = SVEC_EMPTY_INITIALIZER;
> build_port_group_address_set(nb_pg, &ipv4_addrs, &ipv6_addrs);
> - update_sb_addr_set((const char **) ipv4_addrs.names,
ipv4_addrs.n,
> - sb_addr_set_v4);
> - update_sb_addr_set((const char **) ipv6_addrs.names,
ipv6_addrs.n,
> - sb_addr_set_v6);
> +
> + struct sorted_addresses ipv4_addrs_sorted =
> + sorted_addresses_from_svec(&ipv4_addrs);
> + struct sorted_addresses ipv6_addrs_sorted =
> + sorted_addresses_from_svec(&ipv6_addrs);
> +
> + update_sb_addr_set(&ipv4_addrs_sorted, sb_addr_set_v4);
> + update_sb_addr_set(&ipv6_addrs_sorted, sb_addr_set_v6);
>
> free(ipv4_addrs_name);
> free(ipv6_addrs_name);
> @@ -197,7 +214,7 @@ sync_to_sb_addr_set_nb_port_group_handler(struct
engine_node *node,
> /* static functions. */
> static void
> sync_addr_set(struct ovsdb_idl_txn *ovnsb_txn, const char *name,
> - const char **addrs, size_t n_addrs,
> + struct sorted_addresses *addresses,
> struct shash *sb_address_sets)
> {
> const struct sbrec_address_set *sb_address_set;
> @@ -206,10 +223,10 @@ sync_addr_set(struct ovsdb_idl_txn *ovnsb_txn,
const char *name,
> if (!sb_address_set) {
> sb_address_set = sbrec_address_set_insert(ovnsb_txn);
> sbrec_address_set_set_name(sb_address_set, name);
> - sbrec_address_set_set_addresses(sb_address_set,
> - addrs, n_addrs);
> + sbrec_address_set_set_addresses(sb_address_set, addresses->arr,
> + addresses->n);
> } else {
> - update_sb_addr_set(addrs, n_addrs, sb_address_set);
> + update_sb_addr_set(addresses, sb_address_set);
> }
> }
>
> @@ -244,8 +261,11 @@ sync_addr_sets(struct ovsdb_idl_txn *ovnsb_txn,
>
> /* Service monitor MAC. */
> const char *svc_monitor_macp = northd_get_svc_monitor_mac();
> - sync_addr_set(ovnsb_txn, "svc_monitor_mac", &svc_monitor_macp, 1,
> - &sb_address_sets);
> + struct sorted_addresses svc = {
> + .arr = &svc_monitor_macp,
> + .n = 1,
> + };
> + sync_addr_set(ovnsb_txn, "svc_monitor_mac", &svc, &sb_address_sets);
>
> /* sync port group generated address sets first */
> const struct nbrec_port_group *nb_port_group;
> @@ -256,14 +276,16 @@ sync_addr_sets(struct ovsdb_idl_txn *ovnsb_txn,
> build_port_group_address_set(nb_port_group, &ipv4_addrs,
&ipv6_addrs);
> char *ipv4_addrs_name = xasprintf("%s_ip4", nb_port_group->name);
> char *ipv6_addrs_name = xasprintf("%s_ip6", nb_port_group->name);
> +
> + struct sorted_addresses ipv4_addrs_sorted =
> + sorted_addresses_from_svec(&ipv4_addrs);
> + struct sorted_addresses ipv6_addrs_sorted =
> + sorted_addresses_from_svec(&ipv6_addrs);
> +
> sync_addr_set(ovnsb_txn, ipv4_addrs_name,
> - /* "char **" is not compatible with "const char
**" */
> - (const char **) ipv4_addrs.names,
> - ipv4_addrs.n, &sb_address_sets);
> + &ipv4_addrs_sorted, &sb_address_sets);
> sync_addr_set(ovnsb_txn, ipv6_addrs_name,
> - /* "char **" is not compatible with "const char
**" */
> - (const char **) ipv6_addrs.names,
> - ipv6_addrs.n, &sb_address_sets);
> + &ipv6_addrs_sorted, &sb_address_sets);
> free(ipv4_addrs_name);
> free(ipv6_addrs_name);
> svec_destroy(&ipv4_addrs);
> @@ -278,27 +300,26 @@ sync_addr_sets(struct ovsdb_idl_txn *ovnsb_txn,
> if (sset_count(&od->lb_ips->ips_v4_reachable)) {
> char *ipv4_addrs_name =
lr_lb_address_set_name(od->tunnel_key,
> AF_INET);
> - const char **ipv4_addrs =
> - sset_array(&od->lb_ips->ips_v4_reachable);
>
> - sync_addr_set(ovnsb_txn, ipv4_addrs_name, ipv4_addrs,
> - sset_count(&od->lb_ips->ips_v4_reachable),
> - &sb_address_sets);
> + struct sorted_addresses ipv4_addrs_sorted =
> +
sorted_addresses_from_sset(&od->lb_ips->ips_v4_reachable);
> +
> + sync_addr_set(ovnsb_txn, ipv4_addrs_name,
> + &ipv4_addrs_sorted, &sb_address_sets);
> + free(ipv4_addrs_sorted.arr);
> free(ipv4_addrs_name);
> - free(ipv4_addrs);
> }
>
> if (sset_count(&od->lb_ips->ips_v6_reachable)) {
> char *ipv6_addrs_name =
lr_lb_address_set_name(od->tunnel_key,
> AF_INET6);
> - const char **ipv6_addrs =
> - sset_array(&od->lb_ips->ips_v6_reachable);
> + struct sorted_addresses ipv6_addrs_sorted =
> +
sorted_addresses_from_sset(&od->lb_ips->ips_v6_reachable);
>
> - sync_addr_set(ovnsb_txn, ipv6_addrs_name, ipv6_addrs,
> - sset_count(&od->lb_ips->ips_v6_reachable),
> - &sb_address_sets);
> + sync_addr_set(ovnsb_txn, ipv6_addrs_name,
> + &ipv6_addrs_sorted, &sb_address_sets);
> + free(ipv6_addrs_sorted.arr);
> free(ipv6_addrs_name);
> - free(ipv6_addrs);
> }
> }
>
> @@ -307,10 +328,10 @@ sync_addr_sets(struct ovsdb_idl_txn *ovnsb_txn,
> const struct nbrec_address_set *nb_address_set;
> NBREC_ADDRESS_SET_TABLE_FOR_EACH (nb_address_set,
> nb_address_set_table) {
> + struct sorted_addresses addrs =
> + sorted_addresses_from_nbrec(nb_address_set);
> sync_addr_set(ovnsb_txn, nb_address_set->name,
> - /* "char **" is not compatible with "const char **" */
> - (const char **) nb_address_set->addresses,
> - nb_address_set->n_addresses, &sb_address_sets);
> + &addrs, &sb_address_sets);
> }
>
> struct shash_node *node;
> @@ -322,48 +343,39 @@ sync_addr_sets(struct ovsdb_idl_txn *ovnsb_txn,
> }
>
> static void
> -update_sb_addr_set(const char **nb_addresses, size_t n_addresses,
> +update_sb_addr_set(struct sorted_addresses *nb_addresses,
> const struct sbrec_address_set *sb_as)
> {
> - struct expr_constant_set *cs_nb_as =
> - expr_constant_set_create_integers(
> - (const char *const *) nb_addresses, n_addresses);
> - struct expr_constant_set *cs_sb_as =
> - expr_constant_set_create_integers(
> - (const char *const *) sb_as->addresses, sb_as->n_addresses);
> -
> - struct expr_constant_set *addr_added = NULL;
> - struct expr_constant_set *addr_deleted = NULL;
> - expr_constant_set_integers_diff(cs_sb_as, cs_nb_as, &addr_added,
> - &addr_deleted);
> -
> - struct ds ds = DS_EMPTY_INITIALIZER;
> - if (addr_added && addr_added->n_values) {
> - for (size_t i = 0; i < addr_added->n_values; i++) {
> - ds_clear(&ds);
> - expr_constant_format(&addr_added->values[i], EXPR_C_INTEGER,
&ds);
> - sbrec_address_set_update_addresses_addvalue(sb_as,
ds_cstr(&ds));
> + size_t nb_index, sb_index;
> +
> + const char **nb_arr = nb_addresses->arr;
> + char **sb_arr = sb_as->addresses;
> + size_t nb_n = nb_addresses->n;
> + size_t sb_n = sb_as->n_addresses;
> +
> + for (nb_index = sb_index = 0; nb_index < nb_n && sb_index < sb_n; ) {
> + int cmp = strcmp(nb_arr[nb_index], sb_arr[sb_index]);
> + if (cmp < 0) {
> + sbrec_address_set_update_addresses_addvalue(sb_as,
> +
nb_arr[nb_index]);
> + nb_index++;
> + } else if (cmp > 0) {
> + sbrec_address_set_update_addresses_delvalue(sb_as,
> +
sb_arr[sb_index]);
> + sb_index++;
> + } else {
> + nb_index++;
> + sb_index++;
> }
> }
>
> - if (addr_deleted && addr_deleted->n_values) {
> - for (size_t i = 0; i < addr_deleted->n_values; i++) {
> - ds_clear(&ds);
> - expr_constant_format(&addr_deleted->values[i],
> - EXPR_C_INTEGER, &ds);
> - sbrec_address_set_update_addresses_delvalue(sb_as,
ds_cstr(&ds));
> - }
> + for (; nb_index < nb_n; nb_index++) {
> + sbrec_address_set_update_addresses_addvalue(sb_as,
nb_arr[nb_index]);
> }
>
> - ds_destroy(&ds);
> - expr_constant_set_destroy(cs_nb_as);
> - free(cs_nb_as);
> - expr_constant_set_destroy(cs_sb_as);
> - free(cs_sb_as);
> - expr_constant_set_destroy(addr_added);
> - free(addr_added);
> - expr_constant_set_destroy(addr_deleted);
> - free(addr_deleted);
> + for (; sb_index < sb_n; sb_index++) {
> + sbrec_address_set_update_addresses_delvalue(sb_as,
sb_arr[sb_index]);
> + }
> }
>
> static void
> @@ -402,3 +414,32 @@ sb_address_set_lookup_by_name(struct ovsdb_idl_index
*sbrec_addr_set_by_name,
>
> return retval;
> }
> +
> +static struct sorted_addresses
> +sorted_addresses_from_nbrec(const struct nbrec_address_set *nb_as)
> +{
> + /* The DB is already sorted. */
> + return (struct sorted_addresses) {
> + .arr = (const char **) nb_as->addresses,
> + .n = nb_as->n_addresses,
> + };
> +}
> +
> +static struct sorted_addresses
> +sorted_addresses_from_svec(struct svec *addresses)
> +{
> + svec_sort(addresses);
> + return (struct sorted_addresses) {
> + .arr = (const char **) addresses->names,
> + .n = addresses->n,
> + };
> +}
> +
> +static struct sorted_addresses
> +sorted_addresses_from_sset(struct sset* addresses)
> +{
> + return (struct sorted_addresses) {
> + .arr = sset_sort(addresses),
> + .n = sset_count(addresses),
> + };
> +}
> --
> 2.40.1
>
> _______________________________________________
> dev mailing list
> [email protected]
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
Thanks Ales for the fix and sorry for not being able to review v1 sooner.
This fix is straightforward, but I have a different suggestion.
In my opinion normalizing the addresses before writing to SB has important
advantages. For example, we can avoid invalid address strings or duplicated
addresses, which can make ovn-controller implementation more clean. In
general, northd is a good place to cleanup dirty data before writing to SB
when the cost is manageable.
So, would it be better to keep the update_sb_addr_set() implementation but
modify the sync_addr_set() so that it always write normalized addresses to
SB, to be consistent with update_sb_addr_set()?
Regards,
Han
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev