From patchwork Mon Jul 1 09:25:21 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Grigorii Nazarov X-Patchwork-Id: 1954562 X-Patchwork-Delegate: horms@verge.net.au 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=do2w0nlI; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=2605:bc80:3010::138; helo=smtp1.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=patchwork.ozlabs.org) Received: from smtp1.osuosl.org (smtp1.osuosl.org [IPv6:2605:bc80:3010::138]) (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 4WCLL53yg2z1xpN for ; Mon, 1 Jul 2024 19:27:41 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id D6DBE81E38; Mon, 1 Jul 2024 09:27:39 +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 aiRkUTJGetOr; Mon, 1 Jul 2024 09:27:37 +0000 (UTC) X-Comment: SPF check N/A for local connections - client-ip=2605:bc80:3010:104::8cd3:938; helo=lists.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver= DKIM-Filter: OpenDKIM Filter v2.11.0 smtp1.osuosl.org DB61781E37 Authentication-Results: smtp1.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=do2w0nlI Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [IPv6:2605:bc80:3010:104::8cd3:938]) by smtp1.osuosl.org (Postfix) with ESMTPS id DB61781E37; Mon, 1 Jul 2024 09:27:36 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 53F71C0926; Mon, 1 Jul 2024 09:27:36 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp2.osuosl.org (smtp2.osuosl.org [140.211.166.133]) by lists.linuxfoundation.org (Postfix) with ESMTP id 15925C002B for ; Mon, 1 Jul 2024 09:27:34 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp2.osuosl.org (Postfix) with ESMTP id 59E3440AD1 for ; Mon, 1 Jul 2024 09:27:33 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp2.osuosl.org ([127.0.0.1]) by localhost (smtp2.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id BnSiUbnB4CpL for ; Mon, 1 Jul 2024 09:27:31 +0000 (UTC) Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=2607:f8b0:4864:20::52f; helo=mail-pg1-x52f.google.com; envelope-from=whitecrowbar@gmail.com; receiver= DMARC-Filter: OpenDMARC Filter v1.4.2 smtp2.osuosl.org 9C5D940A1A Authentication-Results: smtp2.osuosl.org; dmarc=pass (p=none dis=none) header.from=gmail.com DKIM-Filter: OpenDKIM Filter v2.11.0 smtp2.osuosl.org 9C5D940A1A Authentication-Results: smtp2.osuosl.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=do2w0nlI Received: from mail-pg1-x52f.google.com (mail-pg1-x52f.google.com [IPv6:2607:f8b0:4864:20::52f]) by smtp2.osuosl.org (Postfix) with ESMTPS id 9C5D940A1A for ; Mon, 1 Jul 2024 09:27:31 +0000 (UTC) Received: by mail-pg1-x52f.google.com with SMTP id 41be03b00d2f7-707040e3018so1917940a12.1 for ; Mon, 01 Jul 2024 02:27:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1719826050; x=1720430850; 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=PvqFySiEEVizt3FMLAAFCSwB+Wza5ZmeSvBeEbFwyIQ=; b=do2w0nlIb1B2VFM/Q067VzCLPgZ8IvSaTb9iHpdP5omY77/cG2qnIq0/WT7G4Z1wAQ d1NZev2eTn+ozRBBSkwZiMK8OcC9Hj1SdEveBJvpI/6SY6PU8KuZlFKyy9Vhg4atEVBJ 3pZZuJlbmFEcznODVIJThFL9n/4gnJtBGUa4vVwdRT2oYBO5cGhALqy0T/5dlMyl/lm9 mIlD3lhrGE51wL9xrq7NH6FR//2UkTCZ6XWTqLbbcpAkP2gJaMBPCmh+R2qnvjVMGpwF FqCyqvXan8X0Cm62ynuI8dH0heBSBGehqzTmva/P0Y8GvqG6lXmYH3XIHZQ/4XGg8yUY c4Hg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1719826050; x=1720430850; 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=PvqFySiEEVizt3FMLAAFCSwB+Wza5ZmeSvBeEbFwyIQ=; b=Etd1Q0MfRgsLltozuz33ApTyS0R4dAzwOuuWVhbg+zOsa3rrhZ0GaJ/p/IF9Us6kvq wUitKQrYOiW2XoBmrYvGEbEtjqkYOktS7QeZ79DxKhzfkFq+GNWzZo7FFCDIUXsqQMKy 5LYIItMvtbLun+vDdYbF/MF7nqUp8CsVvQFjezjRN974zFWfpNMYeHalcylGvepUFsfw TDiOkP+4OKNipnbUCtq/vSEUBIXFe/ljVBiEyylGvwVcMwzzXA5L/30dzOsJ5jYbQ5Vz 13DU+0cpibEbn3fNwyOgjy6ibM2/AFnsyRW4uPPMcEP/OD1dWTICCO2Fv5l/834qg0ey C9Qw== X-Gm-Message-State: AOJu0Ywuw177bH28N1Cudk/tj05zde4eHWm1hJaAJqK/lC/kG2b+zkFh NUBj/01YaoRT3DIMJd7Uk0GBzUHmsKH5/jFwOM8UpnBEWSEsE8gvczTh1Q== X-Google-Smtp-Source: AGHT+IESAH9HE1FjYhhrD3n9KV2wtXXnaKHa7HUAkyyTo0oZ6wJ76gswNgj4TxhahEkxl+d9FTx1AA== X-Received: by 2002:a05:6a20:8c92:b0:1be:c551:b74a with SMTP id adf61e73a8af0-1bef62436b7mr6530361637.59.1719826049806; Mon, 01 Jul 2024 02:27:29 -0700 (PDT) Received: from localhost.localdomain ([2800:a4:c085:3c00:fc5d:c1e4:e9f9:4262]) by smtp.googlemail.com with ESMTPSA id d9443c01a7336-1fac10d1559sm59695725ad.45.2024.07.01.02.27.27 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 01 Jul 2024 02:27:29 -0700 (PDT) From: Grigorii Nazarov To: dev@openvswitch.org Date: Mon, 1 Jul 2024 12:25:21 +0300 Message-ID: <20240701092609.1045115-4-whitecrowbar@gmail.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20240620085906.153885-1-whitecrowbar@gmail.com> References: <20240620085906.153885-1-whitecrowbar@gmail.com> MIME-Version: 1.0 Subject: [ovs-dev] [PATCH v4 3/3] 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: , Cc: Grigorii Nazarov 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 --- v2: updated title v3: fixed bracing v4: changed patch number from 4/4 to 3/3 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;