Skip to content

Commit b8bf410

Browse files
committed
db-ctl-base: Use partial map/set updates for last add/set commands.
Currently, command to add one item into a large set generates the transaction with the full new content of that set plus 'wait' operation for the full old content of that set. So, if we're adding one new load-balancer into a load-balancer group in OVN using ovn-nbctl, transaction will include all the existing load-balancers from that groups twice. IDL supports partial updates for sets and maps. The problem with that is changes are not visible to the IDL user until the transaction is committed. That will cause problems for chained ctl commands. However, we still can optimize the very last command in the list. It makes sense to do, since it's a common case for manual invocations. Updating the 'add' command as well as 'set' for a case where we're actually adding one new element to the map. One downside is that we can't check the set size without examining it and checking for duplicates, so allowing the transaction to be sent and constraints to be checked on the server side in that case. Not touching 'remove' operation for now, since removals may have different type, e.g. if elements from the map are removed by the key. The function will likely need to be fully re-written to accommodate all the corner cases. Acked-by: Dumitru Ceara <dceara@redhat.com> Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
1 parent a77c779 commit b8bf410

File tree

5 files changed

+83
-32
lines changed

5 files changed

+83
-32
lines changed

lib/db-ctl-base.c

Lines changed: 63 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ static struct shash all_commands = SHASH_INITIALIZER(&all_commands);
7575
static char *get_table(const char *, const struct ovsdb_idl_table_class **);
7676
static char *set_column(const struct ovsdb_idl_table_class *,
7777
const struct ovsdb_idl_row *, const char *,
78-
struct ovsdb_symbol_table *);
78+
struct ovsdb_symbol_table *, bool use_partial_update);
7979

8080

8181
static struct option *
@@ -1325,11 +1325,17 @@ cmd_find(struct ctl_context *ctx)
13251325
}
13261326

13271327
/* Sets the column of 'row' in 'table'. Returns NULL on success or a
1328-
* malloc()'ed error message on failure. */
1328+
* malloc()'ed error message on failure.
1329+
*
1330+
* If 'use_partial_update' is true, then this function will try to use
1331+
* partial set/map updates, if possible. As a side effect, result will
1332+
* not be reflected in the IDL until the transaction is committed.
1333+
* The last access to a particular column is a good candidate to use
1334+
* this option. */
13291335
static char * OVS_WARN_UNUSED_RESULT
13301336
set_column(const struct ovsdb_idl_table_class *table,
13311337
const struct ovsdb_idl_row *row, const char *arg,
1332-
struct ovsdb_symbol_table *symtab)
1338+
struct ovsdb_symbol_table *symtab, bool use_partial_update)
13331339
{
13341340
const struct ovsdb_idl_column *column;
13351341
char *key_string = NULL;
@@ -1352,7 +1358,7 @@ set_column(const struct ovsdb_idl_table_class *table,
13521358

13531359
if (key_string) {
13541360
union ovsdb_atom key, value;
1355-
struct ovsdb_datum datum;
1361+
struct ovsdb_datum *datum;
13561362

13571363
if (column->type.value.type == OVSDB_TYPE_VOID) {
13581364
error = xasprintf("cannot specify key to set for non-map column "
@@ -1371,16 +1377,22 @@ set_column(const struct ovsdb_idl_table_class *table,
13711377
goto out;
13721378
}
13731379

1374-
ovsdb_datum_init_empty(&datum);
1375-
ovsdb_datum_add_unsafe(&datum, &key, &value, &column->type, NULL);
1380+
datum = xmalloc(sizeof *datum);
1381+
ovsdb_datum_init_empty(datum);
1382+
ovsdb_datum_add_unsafe(datum, &key, &value, &column->type, NULL);
13761383

13771384
ovsdb_atom_destroy(&key, column->type.key.type);
13781385
ovsdb_atom_destroy(&value, column->type.value.type);
13791386

1380-
ovsdb_datum_union(&datum, ovsdb_idl_read(row, column),
1381-
&column->type);
1382-
ovsdb_idl_txn_verify(row, column);
1383-
ovsdb_idl_txn_write(row, column, &datum);
1387+
if (use_partial_update) {
1388+
ovsdb_idl_txn_write_partial_map(row, column, datum);
1389+
} else {
1390+
ovsdb_datum_union(datum, ovsdb_idl_read(row, column),
1391+
&column->type);
1392+
ovsdb_idl_txn_verify(row, column);
1393+
ovsdb_idl_txn_write(row, column, datum);
1394+
free(datum);
1395+
}
13841396
} else {
13851397
struct ovsdb_datum datum;
13861398

@@ -1441,7 +1453,8 @@ cmd_set(struct ctl_context *ctx)
14411453
}
14421454

14431455
for (i = 3; i < ctx->argc; i++) {
1444-
ctx->error = set_column(table, row, ctx->argv[i], ctx->symtab);
1456+
ctx->error = set_column(table, row, ctx->argv[i], ctx->symtab,
1457+
ctx->last_command);
14451458
if (ctx->error) {
14461459
return;
14471460
}
@@ -1479,7 +1492,7 @@ cmd_add(struct ctl_context *ctx)
14791492
const struct ovsdb_idl_column *column;
14801493
const struct ovsdb_idl_row *row;
14811494
const struct ovsdb_type *type;
1482-
struct ovsdb_datum old;
1495+
struct ovsdb_datum new;
14831496
int i;
14841497

14851498
ctx->error = get_table(table_name, &table);
@@ -1503,7 +1516,13 @@ cmd_add(struct ctl_context *ctx)
15031516
}
15041517

15051518
type = &column->type;
1506-
ovsdb_datum_clone(&old, ovsdb_idl_read(row, column));
1519+
1520+
if (ctx->last_command) {
1521+
ovsdb_datum_init_empty(&new);
1522+
} else {
1523+
ovsdb_datum_clone(&new, ovsdb_idl_read(row, column));
1524+
}
1525+
15071526
for (i = 4; i < ctx->argc; i++) {
15081527
struct ovsdb_type add_type;
15091528
struct ovsdb_datum add;
@@ -1514,23 +1533,41 @@ cmd_add(struct ctl_context *ctx)
15141533
ctx->error = ovsdb_datum_from_string(&add, &add_type, ctx->argv[i],
15151534
ctx->symtab);
15161535
if (ctx->error) {
1517-
ovsdb_datum_destroy(&old, &column->type);
1536+
ovsdb_datum_destroy(&new, &column->type);
15181537
return;
15191538
}
1520-
ovsdb_datum_union(&old, &add, type);
1539+
ovsdb_datum_union(&new, &add, type);
15211540
ovsdb_datum_destroy(&add, type);
15221541
}
1523-
if (old.n > type->n_max) {
1542+
1543+
if (!ctx->last_command && new.n > type->n_max) {
15241544
ctl_error(ctx, "\"add\" operation would put %u %s in column %s of "
15251545
"table %s but the maximum number is %u",
1526-
old.n,
1546+
new.n,
15271547
type->value.type == OVSDB_TYPE_VOID ? "values" : "pairs",
15281548
column->name, table->name, type->n_max);
1529-
ovsdb_datum_destroy(&old, &column->type);
1549+
ovsdb_datum_destroy(&new, &column->type);
15301550
return;
15311551
}
1532-
ovsdb_idl_txn_verify(row, column);
1533-
ovsdb_idl_txn_write(row, column, &old);
1552+
1553+
if (ctx->last_command) {
1554+
/* Partial updates can only be made one by one. */
1555+
for (i = 0; i < new.n; i++) {
1556+
struct ovsdb_datum *datum = xmalloc(sizeof *datum);
1557+
1558+
ovsdb_datum_init_empty(datum);
1559+
ovsdb_datum_add_from_index_unsafe(datum, &new, i, type);
1560+
if (ovsdb_type_is_map(type)) {
1561+
ovsdb_idl_txn_write_partial_map(row, column, datum);
1562+
} else {
1563+
ovsdb_idl_txn_write_partial_set(row, column, datum);
1564+
}
1565+
}
1566+
ovsdb_datum_destroy(&new, &column->type);
1567+
} else {
1568+
ovsdb_idl_txn_verify(row, column);
1569+
ovsdb_idl_txn_write(row, column, &new);
1570+
}
15341571

15351572
invalidate_cache(ctx);
15361573
}
@@ -1769,7 +1806,7 @@ cmd_create(struct ctl_context *ctx)
17691806
}
17701807

17711808
for (i = 2; i < ctx->argc; i++) {
1772-
ctx->error = set_column(table, row, ctx->argv[i], ctx->symtab);
1809+
ctx->error = set_column(table, row, ctx->argv[i], ctx->symtab, false);
17731810
if (ctx->error) {
17741811
return;
17751812
}
@@ -2620,7 +2657,8 @@ ctl_list_db_tables_usage(void)
26202657
/* Initializes 'ctx' from 'command'. */
26212658
void
26222659
ctl_context_init_command(struct ctl_context *ctx,
2623-
struct ctl_command *command)
2660+
struct ctl_command *command,
2661+
bool last)
26242662
{
26252663
ctx->argc = command->argc;
26262664
ctx->argv = command->argv;
@@ -2629,6 +2667,7 @@ ctl_context_init_command(struct ctl_context *ctx,
26292667
ds_swap(&ctx->output, &command->output);
26302668
ctx->table = command->table;
26312669
ctx->try_again = false;
2670+
ctx->last_command = last;
26322671
ctx->error = NULL;
26332672
}
26342673

@@ -2640,7 +2679,7 @@ ctl_context_init(struct ctl_context *ctx, struct ctl_command *command,
26402679
void (*invalidate_cache_cb)(struct ctl_context *))
26412680
{
26422681
if (command) {
2643-
ctl_context_init_command(ctx, command);
2682+
ctl_context_init_command(ctx, command, false);
26442683
}
26452684
ctx->idl = idl;
26462685
ctx->txn = txn;
@@ -2684,7 +2723,7 @@ ctl_set_column(const char *table_name, const struct ovsdb_idl_row *row,
26842723
if (error) {
26852724
return error;
26862725
}
2687-
error = set_column(table, row, arg, symtab);
2726+
error = set_column(table, row, arg, symtab, false);
26882727
if (error) {
26892728
return error;
26902729
}

lib/db-ctl-base.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,9 +239,15 @@ struct ctl_context {
239239
/* A command may set this member to true if some prerequisite is not met
240240
* and the caller should wait for something to change and then retry. */
241241
bool try_again;
242+
243+
/* If set during the context initialization, command implementation
244+
* may use optimizations that will leave database changes invisible
245+
* to IDL, e.g. use partial set updates. */
246+
bool last_command;
242247
};
243248

244-
void ctl_context_init_command(struct ctl_context *, struct ctl_command *);
249+
void ctl_context_init_command(struct ctl_context *, struct ctl_command *,
250+
bool last);
245251
void ctl_context_init(struct ctl_context *, struct ctl_command *,
246252
struct ovsdb_idl *, struct ovsdb_idl_txn *,
247253
struct ovsdb_symbol_table *,

tests/ovs-vsctl.at

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1071,9 +1071,13 @@ AT_CHECK([RUN_OVS_VSCTL([set controller br1 'connection-mode=xyz'])],
10711071
AT_CHECK([RUN_OVS_VSCTL([set controller br1 connection-mode:x=y])],
10721072
[1], [], [ovs-vsctl: cannot specify key to set for non-map column connection_mode
10731073
])
1074-
AT_CHECK([RUN_OVS_VSCTL([add bridge br1 datapath_id x y])],
1074+
AT_CHECK([RUN_OVS_VSCTL([add bridge br1 datapath_id x y -- show])],
10751075
[1], [], [ovs-vsctl: "add" operation would put 2 values in column datapath_id of table Bridge but the maximum number is 1
10761076
])
1077+
AT_CHECK([RUN_OVS_VSCTL([add bridge br1 datapath_id x y])], [1], [], [stderr])
1078+
AT_CHECK([sed "/^.*|WARN|.*/d" < stderr], [0], [dnl
1079+
ovs-vsctl: transaction error: {"details":"set must have 0 to 1 members but 2 are present","error":"syntax error","syntax":"[[\"set\",[\"x\",\"y\"]]]"}
1080+
])
10771081
AT_CHECK([RUN_OVS_VSCTL([remove netflow `cat netflow-uuid` targets '"1.2.3.4:567"'])],
10781082
[1], [], [ovs-vsctl: "remove" operation would put 0 values in column targets of table NetFlow but the minimum number is 1
10791083
])

utilities/ovs-vsctl.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2711,9 +2711,9 @@ post_db_reload_do_checks(const struct vsctl_context *vsctl_ctx)
27112711

27122712
static void
27132713
vsctl_context_init_command(struct vsctl_context *vsctl_ctx,
2714-
struct ctl_command *command)
2714+
struct ctl_command *command, bool last_command)
27152715
{
2716-
ctl_context_init_command(&vsctl_ctx->base, command);
2716+
ctl_context_init_command(&vsctl_ctx->base, command, last_command);
27172717
vsctl_ctx->verified_ports = false;
27182718
}
27192719

@@ -2859,7 +2859,8 @@ do_vsctl(const char *args, struct ctl_command *commands, size_t n_commands,
28592859
}
28602860
vsctl_context_init(&vsctl_ctx, NULL, idl, txn, ovs, symtab);
28612861
for (c = commands; c < &commands[n_commands]; c++) {
2862-
vsctl_context_init_command(&vsctl_ctx, c);
2862+
vsctl_context_init_command(&vsctl_ctx, c,
2863+
c == &commands[n_commands - 1]);
28632864
if (c->syntax->run) {
28642865
(c->syntax->run)(&vsctl_ctx.base);
28652866
}

vtep/vtep-ctl.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2207,9 +2207,9 @@ static const struct ctl_table_class tables[VTEPREC_N_TABLES] = {
22072207

22082208
static void
22092209
vtep_ctl_context_init_command(struct vtep_ctl_context *vtepctl_ctx,
2210-
struct ctl_command *command)
2210+
struct ctl_command *command, bool last_command)
22112211
{
2212-
ctl_context_init_command(&vtepctl_ctx->base, command);
2212+
ctl_context_init_command(&vtepctl_ctx->base, command, last_command);
22132213
vtepctl_ctx->verified_ports = false;
22142214

22152215
}
@@ -2304,7 +2304,8 @@ do_vtep_ctl(const char *args, struct ctl_command *commands,
23042304
}
23052305
vtep_ctl_context_init(&vtepctl_ctx, NULL, idl, txn, vtep_global, symtab);
23062306
for (c = commands; c < &commands[n_commands]; c++) {
2307-
vtep_ctl_context_init_command(&vtepctl_ctx, c);
2307+
vtep_ctl_context_init_command(&vtepctl_ctx, c,
2308+
c == &commands[n_commands - 1]);
23082309
if (c->syntax->run) {
23092310
(c->syntax->run)(&vtepctl_ctx.base);
23102311
}

0 commit comments

Comments
 (0)