From patchwork Tue Jun 25 08:54:54 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Grigorii Nazarov X-Patchwork-Id: 1951993 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=U62iQXM7; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=2605:bc80:3010::136; helo=smtp3.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=patchwork.ozlabs.org) Received: from smtp3.osuosl.org (smtp3.osuosl.org [IPv6:2605:bc80:3010::136]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4W7dxs2dP5z20ZS for ; Tue, 25 Jun 2024 18:57:21 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id B7E2861029; Tue, 25 Jun 2024 08:57:19 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp3.osuosl.org ([127.0.0.1]) by localhost (smtp3.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id lcZXIz2wGZHD; Tue, 25 Jun 2024 08:57:18 +0000 (UTC) X-Comment: SPF check N/A for local connections - client-ip=140.211.9.56; helo=lists.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver= DKIM-Filter: OpenDKIM Filter v2.11.0 smtp3.osuosl.org C587661020 Authentication-Results: smtp3.osuosl.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=U62iQXM7 Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by smtp3.osuosl.org (Postfix) with ESMTPS id C587661020; Tue, 25 Jun 2024 08:57:17 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id A6414C0012; Tue, 25 Jun 2024 08:57:17 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp1.osuosl.org (smtp1.osuosl.org [IPv6:2605:bc80:3010::138]) by lists.linuxfoundation.org (Postfix) with ESMTP id C3238C0011 for ; Tue, 25 Jun 2024 08:57:15 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id 9FD4681A81 for ; Tue, 25 Jun 2024 08:57:15 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp1.osuosl.org ([127.0.0.1]) by localhost (smtp1.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id sqqhgCGkz_kM for ; Tue, 25 Jun 2024 08:57:14 +0000 (UTC) Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=2607:f8b0:4864:20::e33; helo=mail-vs1-xe33.google.com; envelope-from=whitecrowbar@gmail.com; receiver= DMARC-Filter: OpenDMARC Filter v1.4.2 smtp1.osuosl.org BED7880FEF Authentication-Results: smtp1.osuosl.org; dmarc=pass (p=none dis=none) header.from=gmail.com DKIM-Filter: OpenDKIM Filter v2.11.0 smtp1.osuosl.org BED7880FEF Authentication-Results: smtp1.osuosl.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=U62iQXM7 Received: from mail-vs1-xe33.google.com (mail-vs1-xe33.google.com [IPv6:2607:f8b0:4864:20::e33]) by smtp1.osuosl.org (Postfix) with ESMTPS id BED7880FEF for ; Tue, 25 Jun 2024 08:57:13 +0000 (UTC) Received: by mail-vs1-xe33.google.com with SMTP id ada2fe7eead31-48c2d353b01so1724050137.2 for ; Tue, 25 Jun 2024 01:57:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1719305832; x=1719910632; darn=openvswitch.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=7/73YUiLgFCtomtEm1y+stkOhhG391kM+GV7fdxxmpQ=; b=U62iQXM7ZxTgdvUKnkNcobppG9S0dBHchk4FWRNm8ayC9dzfA6wQD2QTeeD46CHXSr NZ/BQw5pB8yGpBEK9p1k6YQ9aM0nyGzJNrLjTqOo4CWkaesBYg/L/7OlrK76kgTeCZMq 4/R8ToJf7NoNEO4IpG6a0pMNBDfdZnNtESG5O6T0B5lIZZ2zo88nmQd80TavdKvOXXhj bpkbXq00qEAHJZdd8EfEpprgSxaFMLipJum2SMuSIpF6eilEgM98j/TIPev+uTZT+wRZ RRBibaAlKBC/w6+Fruy99yRgdrcGd9ekj/oEJy0a+0mVPFBBAxvEuC8i48gTJrhZyWx/ +SZg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1719305832; x=1719910632; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=7/73YUiLgFCtomtEm1y+stkOhhG391kM+GV7fdxxmpQ=; b=g1JqyIXgilJMVe/Tw+BvQiUQC21HqYCzdp9nupM55fpLeelqmEuwY2a5R74RssfBy4 Iwrnx4vmyLVuQ8X556eTpmibQM76eLqqxZzpOfmmSch0NVbC+RYmHC/FY3TeyIz+PC5O cBSfQlpQTIWJTCECzgVqTwo45zAdS1TneZvmB1kV8tsJPv/uiRzv/er41W9lhPDDRiMP 7C8y3enJxFCvLfCUzO5qjmFBKKLWLQAcB/yMYZW1I4b7t+lkk7PKayDHS5F/F6m+07H4 xi7SO4+iagja09u6tjbvzyJSZbczazKQN3ci6Yg6XL1FMDQ+ETlq/WRpmQ1odYuDXCGs 82Ww== X-Gm-Message-State: AOJu0Yx7isq2V+ezMt/zHOjpcggaori8ZNge4QSJIcWv6C5ouQ/fNqSZ X1BxpSBA4lSA0dqNhOGd2cZtCIbTZBT669HJyvvwC3zHnQH4z1AHLK5PNw== X-Google-Smtp-Source: AGHT+IHZGLtkicEcqC5VbnS0uJOMwpkY/c//dJtdizTwCIunLzNRTjmna/ouGiuJjtKO4jox6HNlrA== X-Received: by 2002:a67:b309:0:b0:48f:4320:114c with SMTP id ada2fe7eead31-48f52a28346mr6538902137.15.1719305831523; Tue, 25 Jun 2024 01:57:11 -0700 (PDT) Received: from localhost.localdomain ([2800:a4:c095:7000:52c7:ec56:66bf:25f0]) by smtp.googlemail.com with ESMTPSA id a1e0cc1a2514c-80f729bf600sm1624116241.34.2024.06.25.01.57.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 25 Jun 2024 01:57:11 -0700 (PDT) From: Grigorii Nazarov To: dev@openvswitch.org Cc: Grigorii Nazarov Date: Tue, 25 Jun 2024 11:54:54 +0300 Message-ID: <20240625085550.645760-1-whitecrowbar@gmail.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20240625081157.643558-4-whitecrowbar@gmail.com> References: <20240625081157.643558-4-whitecrowbar@gmail.com> MIME-Version: 1.0 Subject: [ovs-dev] [PATCH v3 4/4] ovsdb: Optimize monitor update by directly serializing data into ds. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" Currently serialization is performed by first converting the internal data representation into JSON objects, followed by serializing these objects by jsonrpc. This process results in lots of allocations for these intermediate objects. Consequently, this not only increases peak memory consumption, but also demands significantly more CPU work. By forming row-update JSONs directly in `ds`, which is then used to create 'serialized object' JSONs, the overall speed increased by a factor of 2.3. A local benchmark was run on a proprietary Southbound backup. Both versions, before and after applying the patch, were measured. For each version, there were two runs with 10 parallel clients, and two runs with 30 parallel clients. CPU time was recorded after startup (before clients started running) and after all clients received all updates. Clients were essentially running `ovsdb-client monitor-cond unix:pipe OVN_Southbound '[[\"actions\",\"!=\",\"sdfdfsd\"]]' Logical_Flow`. Similar results were obtained with other requests that required a significant amount of data transfer. The backup size is about 600 MB. Results are measured in seconds. Before After Baseline x10: 9.53 108.54 Baseline x10: 9.62 108.67 Baseline x30: 9.69 307.04 Baseline x30: 9.65 303.32 Patch x10: 9.67 52.57 Patch x10: 9.57 53.12 Patch x30: 9.53 136.33 Patch x30: 9.63 135.88 Signed-off-by: Grigorii Nazarov --- include/openvswitch/json.h | 1 + lib/json.c | 8 ++ lib/ovsdb-data.c | 105 +++++++++++++++++++ lib/ovsdb-data.h | 3 + ovsdb/monitor.c | 210 ++++++++++++++++++++++++------------- 5 files changed, 254 insertions(+), 73 deletions(-) diff --git a/include/openvswitch/json.h b/include/openvswitch/json.h index 555440760..80b9479c7 100644 --- a/include/openvswitch/json.h +++ b/include/openvswitch/json.h @@ -81,6 +81,7 @@ struct json *json_boolean_create(bool); struct json *json_string_create(const char *); struct json *json_string_create_nocopy(char *); struct json *json_serialized_object_create(const struct json *); +struct json *json_serialized_object_create_from_string(const char *); struct json *json_integer_create(long long int); struct json *json_real_create(double); diff --git a/lib/json.c b/lib/json.c index d40e93857..66b1f571f 100644 --- a/lib/json.c +++ b/lib/json.c @@ -199,6 +199,14 @@ json_serialized_object_create(const struct json *src) return json; } +struct json * +json_serialized_object_create_from_string(const char *s) +{ + struct json *json = json_create(JSON_SERIALIZED_OBJECT); + json->string = xstrdup(s); + return json; +} + struct json * json_serialized_object_create_with_yield(const struct json *src) { diff --git a/lib/ovsdb-data.c b/lib/ovsdb-data.c index defb048d7..f32b7975a 100644 --- a/lib/ovsdb-data.c +++ b/lib/ovsdb-data.c @@ -492,6 +492,45 @@ ovsdb_atom_to_json__(const union ovsdb_atom *atom, enum ovsdb_atomic_type type, } } +/* Serializes 'atom', of the specified 'type', into 'ds' in JSON format. + * + * Refer to RFC 7047 for the format of the JSON that this function produces. */ +static void +ovsdb_atom_to_json_ds(const union ovsdb_atom *atom, + enum ovsdb_atomic_type type, struct ds *ds) +{ + switch (type) { + case OVSDB_TYPE_VOID: + OVS_NOT_REACHED(); + + case OVSDB_TYPE_INTEGER: + ds_put_format(ds, "%lld", (long long) atom->integer); + return; + + case OVSDB_TYPE_REAL: + ds_put_format(ds, "%.*g", DBL_DIG, atom->real); + return; + + case OVSDB_TYPE_BOOLEAN: + ds_put_cstr(ds, atom->boolean ? "true" : "false"); + return; + + case OVSDB_TYPE_STRING: + json_to_ds(atom->s, JSSF_SORT, ds); + return; + + case OVSDB_TYPE_UUID: + ds_put_cstr(ds, "[\"uuid\",\""); + ds_put_format(ds, UUID_FMT, UUID_ARGS(&atom->uuid)); + ds_put_cstr(ds, "\"]"); + return; + + case OVSDB_N_TYPES: + default: + OVS_NOT_REACHED(); + } +} + struct json * ovsdb_atom_to_json(const union ovsdb_atom *atom, enum ovsdb_atomic_type type) { @@ -1445,6 +1484,23 @@ ovsdb_base_to_json(const union ovsdb_atom *atom, } } +static void +ovsdb_base_to_json_ds(const union ovsdb_atom *atom, + const struct ovsdb_base_type *base, + bool use_row_names, + struct ds *ds) +{ + if (!use_row_names + || base->type != OVSDB_TYPE_UUID + || !base->uuid.refTableName) { + ovsdb_atom_to_json_ds(atom, base->type, ds); + } else { + ds_put_cstr(ds, "[\"named-uuid\",\""); + ds_put_format(ds, UUID_ROW_FMT, UUID_ARGS(&atom->uuid)); + ds_put_cstr(ds, "\"]"); + } +} + static struct json * ovsdb_datum_to_json__(const struct ovsdb_datum *datum, const struct ovsdb_type *type, @@ -1482,6 +1538,47 @@ ovsdb_datum_to_json__(const struct ovsdb_datum *datum, } } +static void +ovsdb_datum_to_json_ds__(const struct ovsdb_datum *datum, + const struct ovsdb_type *type, + bool use_row_names, + struct ds *ds) +{ + if (ovsdb_type_is_map(type)) { + size_t i; + + ds_put_cstr(ds, "[\"map\",["); + for (i = 0; i < datum->n; i++) { + if (i != 0) { + ds_put_char(ds, ','); + } + ds_put_char(ds, '['); + ovsdb_base_to_json_ds(&datum->keys[i], &type->key, + use_row_names, ds); + ds_put_char(ds, ','); + ovsdb_base_to_json_ds(&datum->values[i], &type->value, + use_row_names, ds); + ds_put_char(ds, ']'); + } + ds_put_cstr(ds, "]]"); + } else if (datum->n == 1) { + ovsdb_base_to_json_ds(&datum->keys[0], &type->key, + use_row_names, ds); + } else { + size_t i; + + ds_put_cstr(ds, "[\"set\",["); + for (i = 0; i < datum->n; i++) { + if (i != 0) { + ds_put_char(ds, ','); + } + ovsdb_base_to_json_ds(&datum->keys[i], &type->key, + use_row_names, ds); + } + ds_put_cstr(ds, "]]"); + } +} + /* Converts 'datum', of the specified 'type', to JSON format, and returns the * JSON. The caller is responsible for freeing the returned JSON. * @@ -1509,6 +1606,14 @@ ovsdb_datum_to_json_with_row_names(const struct ovsdb_datum *datum, return ovsdb_datum_to_json__(datum, type, true, true); } +void +ovsdb_datum_to_json_ds(const struct ovsdb_datum *datum, + const struct ovsdb_type *type, + struct ds *ds) +{ + ovsdb_datum_to_json_ds__(datum, type, false, ds); +} + static const char * skip_spaces(const char *p) { diff --git a/lib/ovsdb-data.h b/lib/ovsdb-data.h index c0408ee49..5bc47f0a1 100644 --- a/lib/ovsdb-data.h +++ b/lib/ovsdb-data.h @@ -197,6 +197,9 @@ struct json *ovsdb_datum_to_json(const struct ovsdb_datum *, const struct ovsdb_type *); struct json *ovsdb_datum_to_json_deep(const struct ovsdb_datum *, const struct ovsdb_type *); +void ovsdb_datum_to_json_ds(const struct ovsdb_datum *, + const struct ovsdb_type *, + struct ds *); char *ovsdb_datum_from_string(struct ovsdb_datum *, const struct ovsdb_type *, const char *, diff --git a/ovsdb/monitor.c b/ovsdb/monitor.c index c3bfae3d2..8355b8da7 100644 --- a/ovsdb/monitor.c +++ b/ovsdb/monitor.c @@ -25,6 +25,7 @@ #include "openvswitch/json.h" #include "json.h" #include "jsonrpc.h" +#include "ovsdb-data.h" #include "ovsdb-error.h" #include "ovsdb-parser.h" #include "ovsdb.h" @@ -197,14 +198,15 @@ enum ovsdb_monitor_row_type { OVSDB_MONITOR_ROW }; -typedef struct json * +typedef bool (*compose_row_update_cb_func) (const struct ovsdb_monitor_table *mt, const struct ovsdb_monitor_session_condition * condition, enum ovsdb_monitor_row_type row_type, const void *, bool initial, unsigned long int *changed, - size_t n_columns); + size_t n_columns, + struct ds *); static void ovsdb_monitor_destroy(struct ovsdb_monitor *); static struct ovsdb_monitor_change_set * ovsdb_monitor_add_change_set( @@ -963,28 +965,27 @@ ovsdb_monitor_row_skip_update(const struct ovsdb_monitor_table *mt, return false; } -/* Returns JSON for a (as described in RFC 7047) for 'row' within - * 'mt', or NULL if no row update should be sent. +/* Serializes a (as described in RFC 7047) for 'row' within + * 'mt' into 'ds'. Returns true unless no row update should be sent. * - * The caller should specify 'initial' as true if the returned JSON is going to - * be used as part of the initial reply to a "monitor" request, false if it is - * going to be used as part of an "update" notification. + * The caller should specify 'initial' as true if the serialized JSON is + * going to be used as part of the initial reply to a "monitor" request, + * false if it is going to be used as part of an "update" notification. * * 'changed' must be a scratch buffer for internal use that is at least * bitmap_n_bytes(n_columns) bytes long. */ -static struct json * +static bool ovsdb_monitor_compose_row_update( const struct ovsdb_monitor_table *mt, const struct ovsdb_monitor_session_condition *condition OVS_UNUSED, enum ovsdb_monitor_row_type row_type OVS_UNUSED, const void *_row, bool initial, unsigned long int *changed, - size_t n_columns OVS_UNUSED) + size_t n_columns OVS_UNUSED, + struct ds *ds) { const struct ovsdb_monitor_row *row = _row; enum ovsdb_monitor_selection type; - struct json *old_json, *new_json; - struct json *row_json; size_t i; ovs_assert(row_type == OVSDB_MONITOR_ROW); @@ -992,65 +993,95 @@ ovsdb_monitor_compose_row_update( if (ovsdb_monitor_row_skip_update(mt, row_type, row->old, row->new, type, changed, mt->n_columns)) { - return NULL; + return false; } - row_json = json_object_create(); - old_json = new_json = NULL; + ds_put_char(ds, '{'); + + bool has_old = false; if (type & (OJMS_DELETE | OJMS_MODIFY)) { - old_json = json_object_create(); - json_object_put(row_json, "old", old_json); - } - if (type & (OJMS_INITIAL | OJMS_INSERT | OJMS_MODIFY)) { - new_json = json_object_create(); - json_object_put(row_json, "new", new_json); - } - for (i = 0; i < mt->n_monitored_columns; i++) { - const struct ovsdb_monitor_column *c = &mt->columns[i]; + ds_put_cstr(ds, "\"old\":{"); - if (!c->monitored || !(type & c->select)) { - /* We don't care about this type of change for this - * particular column (but we will care about it for some - * other column). */ - continue; + has_old = true; + bool empty = true; + for (i = 0; i < mt->n_monitored_columns; i++) { + const struct ovsdb_monitor_column *c = &mt->columns[i]; + + if (!c->monitored || !(type & c->select)) { + /* We don't care about this type of change for this + * particular column (but we will care about it for some + * other column). */ + continue; + } + + if (type == OJMS_DELETE || bitmap_is_set(changed, i)) { + if (!empty) { + ds_put_char(ds, ','); + } + empty = false; + + json_string_escape(c->column->name, ds); + ds_put_char(ds, ':'); + ovsdb_datum_to_json_ds(&row->old[i], &c->column->type, ds); + } } + ds_put_char(ds, '}'); + } - if ((type == OJMS_MODIFY && bitmap_is_set(changed, i)) - || type == OJMS_DELETE) { - json_object_put(old_json, c->column->name, - ovsdb_datum_to_json(&row->old[i], - &c->column->type)); + if (type & (OJMS_INITIAL | OJMS_INSERT | OJMS_MODIFY)) { + if (has_old) { + ds_put_char(ds, ','); } - if (type & (OJMS_INITIAL | OJMS_INSERT | OJMS_MODIFY)) { - json_object_put(new_json, c->column->name, - ovsdb_datum_to_json(&row->new[i], - &c->column->type)); + ds_put_cstr(ds, "\"new\":{"); + + bool empty = true; + for (i = 0; i < mt->n_monitored_columns; i++) { + const struct ovsdb_monitor_column *c = &mt->columns[i]; + + if (!c->monitored || !(type & c->select)) { + /* We don't care about this type of change for this + * particular column (but we will care about it for some + * other column). */ + continue; + } + + if (!empty) { + ds_put_char(ds, ','); + } + empty = false; + + json_string_escape(c->column->name, ds); + ds_put_char(ds, ':'); + ovsdb_datum_to_json_ds(&row->new[i], &c->column->type, ds); } + ds_put_char(ds, '}'); } + ds_put_char(ds, '}'); - return row_json; + return true; } -/* Returns JSON for a (as described in ovsdb-server(1) mapage) - * for 'row' within * 'mt', or NULL if no row update should be sent. +/* Serializes a (as described in ovsdb-server(1) mapage) + * for 'row' within * 'mt' into 'ds'. + * Returns true unless no row update should be sent. * - * The caller should specify 'initial' as true if the returned JSON is + * The caller should specify 'initial' as true if the serialized JSON is * going to be used as part of the initial reply to a "monitor_cond" request, * false if it is going to be used as part of an "update2" notification. * * 'changed' must be a scratch buffer for internal use that is at least * bitmap_n_bytes(n_columns) bytes long. */ -static struct json * +static bool ovsdb_monitor_compose_row_update2( const struct ovsdb_monitor_table *mt, const struct ovsdb_monitor_session_condition *condition, enum ovsdb_monitor_row_type row_type, const void *_row, bool initial, unsigned long int *changed, - size_t n_columns) + size_t n_columns, + struct ds *ds) { enum ovsdb_monitor_selection type; - struct json *row_update2, *diff_json; const struct ovsdb_datum *old, *new; size_t i; @@ -1065,15 +1096,18 @@ ovsdb_monitor_compose_row_update2( row_type, old, new); if (ovsdb_monitor_row_skip_update(mt, row_type, old, new, type, changed, n_columns)) { - return NULL; + return false; } - row_update2 = json_object_create(); + ds_put_char(ds, '{'); if (type == OJMS_DELETE) { - json_object_put(row_update2, "delete", json_null_create()); + ds_put_cstr(ds, "\"delete\":null"); } else { - diff_json = json_object_create(); const char *op; + op = type == OJMS_INITIAL ? "initial" + : type == OJMS_MODIFY ? "modify" : "insert"; + ds_put_format(ds, "\"%s\":{", op); + bool empty = true; for (i = 0; i < mt->n_monitored_columns; i++) { const struct ovsdb_monitor_column *c = &mt->columns[i]; @@ -1085,33 +1119,43 @@ ovsdb_monitor_compose_row_update2( continue; } - if (type == OJMS_MODIFY) { - struct ovsdb_datum diff; + struct ovsdb_datum diff; + const struct ovsdb_datum *column_update = NULL; + if (type == OJMS_MODIFY) { if (!bitmap_is_set(changed, i)) { continue; } - ovsdb_datum_diff(&diff ,&old[index], &new[index], + ovsdb_datum_diff(&diff, &old[index], &new[index], &c->column->type); - json_object_put(diff_json, c->column->name, - ovsdb_datum_to_json(&diff, &c->column->type)); - ovsdb_datum_destroy(&diff, &c->column->type); + column_update = &diff; } else { - if (!ovsdb_datum_is_default(&new[index], &c->column->type)) { - json_object_put(diff_json, c->column->name, - ovsdb_datum_to_json(&new[index], - &c->column->type)); + if (ovsdb_datum_is_default(&new[index], &c->column->type)) { + continue; } + + column_update = &new[index]; + } + + if (!empty) { + ds_put_char(ds, ','); + } + empty = false; + json_string_escape(c->column->name, ds); + ds_put_char(ds, ':'); + ovsdb_datum_to_json_ds(column_update, &c->column->type, ds); + + if (type == OJMS_MODIFY) { + ovsdb_datum_destroy(&diff, &c->column->type); } } - op = type == OJMS_INITIAL ? "initial" - : type == OJMS_MODIFY ? "modify" : "insert"; - json_object_put(row_update2, op, diff_json); + ds_put_char(ds, '}'); } + ds_put_char(ds, '}'); - return row_update2; + return true; } static size_t @@ -1166,6 +1210,8 @@ ovsdb_monitor_compose_update( size_t max_columns = ovsdb_monitor_max_columns(dbmon); unsigned long int *changed = xmalloc(bitmap_n_bytes(max_columns)); + struct ds ds; + ds_init(&ds); json = NULL; struct ovsdb_monitor_change_set_for_table *mcst; LIST_FOR_EACH (mcst, list_in_change_set, &mcs->change_set_for_tables) { @@ -1176,16 +1222,24 @@ ovsdb_monitor_compose_update( HMAP_FOR_EACH_SAFE (row, hmap_node, &mcst->rows) { cooperative_multitasking_yield(); - struct json *row_json; - row_json = (*row_update)(mt, condition, OVSDB_MONITOR_ROW, row, - initial, changed, mcst->n_columns); - if (row_json) { + bool have_update; + have_update = (*row_update)(mt, condition, OVSDB_MONITOR_ROW, row, + initial, changed, mcst->n_columns, + &ds); + if (have_update) { + struct json *row_json; + /* row_json content is only used for serialization */ + row_json = + json_serialized_object_create_from_string(ds_cstr_ro(&ds)); + ds_clear(&ds); + ovsdb_monitor_add_json_row(&json, mt->table->schema->name, &table_json, row_json, &row->uuid); } } } + ds_destroy(&ds); free(changed); return json; @@ -1201,6 +1255,9 @@ ovsdb_monitor_compose_cond_change_update( size_t max_columns = ovsdb_monitor_max_columns(dbmon); unsigned long int *changed = xmalloc(bitmap_n_bytes(max_columns)); + struct ds ds; + ds_init(&ds); + SHASH_FOR_EACH (node, &dbmon->tables) { struct ovsdb_condition *old_condition, *new_condition, *diff_condition; struct ovsdb_monitor_table *mt = node->data; @@ -1219,15 +1276,21 @@ ovsdb_monitor_compose_cond_change_update( /* Iterate over all rows in table */ HMAP_FOR_EACH (row, hmap_node, &mt->table->rows) { - struct json *row_json; - cooperative_multitasking_yield(); - row_json = ovsdb_monitor_compose_row_update2(mt, condition, - OVSDB_ROW, row, - false, changed, - mt->n_columns); - if (row_json) { + bool have_update; + have_update = ovsdb_monitor_compose_row_update2(mt, condition, + OVSDB_ROW, row, + false, changed, + mt->n_columns, + &ds); + if (have_update) { + struct json *row_json; + /* row_json content is only used for serialization */ + row_json = + json_serialized_object_create_from_string(ds_cstr_ro(&ds)); + ds_clear(&ds); + ovsdb_monitor_add_json_row(&json, mt->table->schema->name, &table_json, row_json, ovsdb_row_get_uuid(row)); @@ -1235,6 +1298,7 @@ ovsdb_monitor_compose_cond_change_update( } ovsdb_monitor_table_condition_updated(mt, condition); } + ds_destroy(&ds); free(changed); return json;