mirror of
https://github.com/openvswitch/ovs
synced 2025-08-22 01:51:26 +00:00
ovsdb-data: Add function to apply diff in-place.
ovsdb_datum_apply_diff() is heavily used in ovsdb transactions, but it's linear in terms of number of comparisons. And it also clones all the atoms along the way. In most cases size of a diff is much smaller than the size of the original datum, this allows to perform the same operation in-place with only O(diff->n * log2(old->n)) comparisons and O(old->n + diff->n) memory copies with memcpy. Using this function while applying diffs read from the storage gives a significant performance boost and allows to execute much more transactions per second. Signed-off-by: Ilya Maximets <i.maximets@ovn.org> Acked-by: Mark D. Gray <mark.d.gray@redhat.com>
This commit is contained in:
parent
bb12b63176
commit
32b51326ef
100
lib/ovsdb-data.c
100
lib/ovsdb-data.c
@ -2253,6 +2253,106 @@ ovsdb_datum_diff(struct ovsdb_datum *diff,
|
||||
}
|
||||
}
|
||||
|
||||
/* Apply 'diff' to 'a'.
|
||||
*
|
||||
* Return NULL if the 'a' is successfully updated, otherwise, return
|
||||
* ovsdb_error. */
|
||||
struct ovsdb_error *
|
||||
ovsdb_datum_apply_diff_in_place(struct ovsdb_datum *a,
|
||||
const struct ovsdb_datum *diff,
|
||||
const struct ovsdb_type *type)
|
||||
{
|
||||
struct ovsdb_error *error = NULL;
|
||||
struct ovsdb_datum result;
|
||||
size_t i, new_size;
|
||||
unsigned int *idx, pos;
|
||||
enum {
|
||||
DIFF_OP_ADD,
|
||||
DIFF_OP_REMOVE,
|
||||
DIFF_OP_UPDATE,
|
||||
} *operation;
|
||||
|
||||
if (!ovsdb_type_is_composite(type)) {
|
||||
ovsdb_datum_destroy(a, type);
|
||||
ovsdb_datum_clone(a, diff, type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
operation = xmalloc(diff->n * sizeof *operation);
|
||||
idx = xmalloc(diff->n * sizeof *idx);
|
||||
new_size = a->n;
|
||||
for (i = 0; i < diff->n; i++) {
|
||||
if (!ovsdb_datum_find_key(a, &diff->keys[i], type->key.type, &pos)) {
|
||||
operation[i] = DIFF_OP_ADD;
|
||||
new_size++;
|
||||
} else if (type->value.type != OVSDB_TYPE_VOID
|
||||
&& !ovsdb_atom_equals(&diff->values[i], &a->values[pos],
|
||||
type->value.type)) {
|
||||
operation[i] = DIFF_OP_UPDATE;
|
||||
} else {
|
||||
operation[i] = DIFF_OP_REMOVE;
|
||||
new_size--;
|
||||
}
|
||||
idx[i] = pos;
|
||||
}
|
||||
|
||||
/* Make sure member size of 'new' conforms to type. */
|
||||
if (new_size < type->n_min || new_size > type->n_max) {
|
||||
error = ovsdb_error(NULL, "Datum crated by diff has size error");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ovsdb_datum_init_empty(&result);
|
||||
ovsdb_datum_reallocate(&result, type, new_size);
|
||||
|
||||
unsigned int copied = 0;
|
||||
for (i = 0; i < diff->n; i++) {
|
||||
pos = idx[i];
|
||||
|
||||
if (copied < pos) {
|
||||
/* Copying all atoms that should go before the current one. */
|
||||
ovsdb_datum_push_unsafe(&result, a, copied, pos - copied, type);
|
||||
copied = pos;
|
||||
}
|
||||
|
||||
switch (operation[i]) {
|
||||
case DIFF_OP_UPDATE:
|
||||
case DIFF_OP_ADD:
|
||||
/* Inserting new atom from 'diff'. */
|
||||
ovsdb_atom_clone(&result.keys[result.n],
|
||||
&diff->keys[i], type->key.type);
|
||||
if (type->value.type != OVSDB_TYPE_VOID) {
|
||||
ovsdb_atom_clone(&result.values[result.n],
|
||||
&diff->values[i], type->value.type);
|
||||
}
|
||||
result.n++;
|
||||
if (operation[i] != DIFF_OP_UPDATE) {
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
|
||||
case DIFF_OP_REMOVE:
|
||||
/* Destroying atom. */
|
||||
ovsdb_atom_destroy(&a->keys[pos], type->key.type);
|
||||
if (type->value.type != OVSDB_TYPE_VOID) {
|
||||
ovsdb_atom_destroy(&a->values[pos], type->value.type);
|
||||
}
|
||||
copied++; /* Skipping removed atom. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Copying remaining atoms. */
|
||||
ovsdb_datum_push_unsafe(&result, a, copied, a->n - copied, type);
|
||||
a->n = 0;
|
||||
|
||||
ovsdb_datum_swap(&result, a);
|
||||
ovsdb_datum_destroy(&result, type);
|
||||
exit:
|
||||
free(operation);
|
||||
free(idx);
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Apply 'diff' to 'old' to regenerate 'new'.
|
||||
*
|
||||
* Return NULL if the 'new' is successfully generated, otherwise, return
|
||||
|
@ -252,6 +252,12 @@ struct ovsdb_error *ovsdb_datum_apply_diff(struct ovsdb_datum *new_datum,
|
||||
const struct ovsdb_type *type)
|
||||
OVS_WARN_UNUSED_RESULT;
|
||||
|
||||
struct ovsdb_error * ovsdb_datum_apply_diff_in_place(
|
||||
struct ovsdb_datum *a,
|
||||
const struct ovsdb_datum *diff,
|
||||
const struct ovsdb_type *type)
|
||||
OVS_WARN_UNUSED_RESULT;
|
||||
|
||||
/* Raw operations that may not maintain the invariants. */
|
||||
void ovsdb_datum_remove_unsafe(struct ovsdb_datum *, size_t idx,
|
||||
const struct ovsdb_type *);
|
||||
|
10
ovsdb/file.c
10
ovsdb/file.c
@ -113,19 +113,17 @@ ovsdb_file_update_row_from_json(struct ovsdb_row *row, bool converting,
|
||||
if (row_contains_diff
|
||||
&& !ovsdb_datum_is_default(&row->fields[column->index],
|
||||
&column->type)) {
|
||||
struct ovsdb_datum new_datum;
|
||||
|
||||
error = ovsdb_datum_apply_diff(&new_datum,
|
||||
error = ovsdb_datum_apply_diff_in_place(
|
||||
&row->fields[column->index],
|
||||
&datum, &column->type);
|
||||
ovsdb_datum_destroy(&datum, &column->type);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
ovsdb_datum_swap(&datum, &new_datum);
|
||||
} else {
|
||||
ovsdb_datum_swap(&row->fields[column->index], &datum);
|
||||
ovsdb_datum_destroy(&datum, &column->type);
|
||||
}
|
||||
ovsdb_datum_swap(&row->fields[column->index], &datum);
|
||||
ovsdb_datum_destroy(&datum, &column->type);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
@ -846,18 +846,21 @@ OVSDB_CHECK_POSITIVE([generate and apply diff -- integer],
|
||||
[[diff-data '["integer"]' '[0]' '[2]']],
|
||||
[[diff: 2
|
||||
apply diff: 2
|
||||
apply diff in place: 2
|
||||
OK]])
|
||||
|
||||
OVSDB_CHECK_POSITIVE([generate and apply diff -- boolean],
|
||||
[[diff-data '["boolean"]' '[true]' '[false]']],
|
||||
[[diff: false
|
||||
apply diff: false
|
||||
apply diff in place: false
|
||||
OK]])
|
||||
|
||||
OVSDB_CHECK_POSITIVE([generate and apply diff -- string],
|
||||
[[diff-data '["string"]' '["AAA"]' '["BBB"]']],
|
||||
[[diff: "BBB"
|
||||
apply diff: "BBB"
|
||||
apply diff in place: "BBB"
|
||||
OK]])
|
||||
|
||||
dnl Test set modifications.
|
||||
@ -870,15 +873,19 @@ OVSDB_CHECK_POSITIVE([generate and apply diff -- set],
|
||||
]],
|
||||
[[diff: ["set",[0,2]]
|
||||
apply diff: ["set",[1,2]]
|
||||
apply diff in place: ["set",[1,2]]
|
||||
OK
|
||||
diff: 0
|
||||
apply diff: 1
|
||||
apply diff in place: 1
|
||||
OK
|
||||
diff: ["set",[0,1]]
|
||||
apply diff: ["set",[0,1]]
|
||||
apply diff in place: ["set",[0,1]]
|
||||
OK
|
||||
diff: ["set",[0,1]]
|
||||
apply diff: ["set",[]]
|
||||
apply diff in place: ["set",[]]
|
||||
OK]])
|
||||
|
||||
dnl Test set modifications causes data to violate set size constrain.
|
||||
@ -898,18 +905,23 @@ OVSDB_CHECK_POSITIVE([generate and apply diff -- map],
|
||||
]],
|
||||
[[diff: ["map",[["2 gills","1 chopin"],["2 pints","1 quart"]]]
|
||||
apply diff: ["map",[["2 pints","1 quart"]]]
|
||||
apply diff in place: ["map",[["2 pints","1 quart"]]]
|
||||
OK
|
||||
diff: ["map",[]]
|
||||
apply diff: ["map",[["2 gills","1 chopin"]]]
|
||||
apply diff in place: ["map",[["2 gills","1 chopin"]]]
|
||||
OK
|
||||
diff: ["map",[["2 gills","1 chopin"]]]
|
||||
apply diff: ["map",[]]
|
||||
apply diff in place: ["map",[]]
|
||||
OK
|
||||
diff: ["map",[["2 pints","1 quart"]]]
|
||||
apply diff: ["map",[["2 pints","1 quart"]]]
|
||||
apply diff in place: ["map",[["2 pints","1 quart"]]]
|
||||
OK
|
||||
diff: ["map",[["2 gills","1 gallon"]]]
|
||||
apply diff: ["map",[["2 gills","1 gallon"]]]
|
||||
apply diff in place: ["map",[["2 gills","1 gallon"]]]
|
||||
OK]])
|
||||
|
||||
OVSDB_CHECK_NEGATIVE([generate and apply diff with map -- size error],
|
||||
|
@ -512,6 +512,18 @@ do_diff_data(struct ovs_cmdl_context *ctx)
|
||||
ovs_fatal(0, "failed to apply diff");
|
||||
}
|
||||
|
||||
/* Apply diff to 'old' in place. */
|
||||
error = ovsdb_datum_apply_diff_in_place(&old, &diff, &type);
|
||||
if (error) {
|
||||
char *string = ovsdb_error_to_string_free(error);
|
||||
ovs_fatal(0, "%s", string);
|
||||
}
|
||||
|
||||
/* Test to make sure 'old' equals 'new' now. */
|
||||
if (!ovsdb_datum_equals(&new, &old, &type)) {
|
||||
ovs_fatal(0, "failed to apply diff in place");
|
||||
}
|
||||
|
||||
/* Print diff */
|
||||
json = ovsdb_datum_to_json(&diff, &type);
|
||||
printf ("diff: ");
|
||||
@ -522,6 +534,11 @@ do_diff_data(struct ovs_cmdl_context *ctx)
|
||||
printf ("apply diff: ");
|
||||
print_and_free_json(json);
|
||||
|
||||
/* Print updated 'old' */
|
||||
json = ovsdb_datum_to_json(&old, &type);
|
||||
printf ("apply diff in place: ");
|
||||
print_and_free_json(json);
|
||||
|
||||
ovsdb_datum_destroy(&new, &type);
|
||||
ovsdb_datum_destroy(&old, &type);
|
||||
ovsdb_datum_destroy(&diff, &type);
|
||||
|
Loading…
x
Reference in New Issue
Block a user