diff mbox

[ovs-dev,monitor_cond,V5,04/18] ovsdb: generate update notifications for monitor_cond session

Message ID 1457078953-22280-5-git-send-email-lirans@il.ibm.com
State Deferred
Headers show

Commit Message

Liran Schour March 4, 2016, 8:08 a.m. UTC
Hold session's conditions in ovsdb_monitor_session_condition. Pass it
to ovsdb_monitor for generating "update2" notifications.
Add functions that can generate "update2" notification for a
"monitor_cond" session.
JSON cache is enabled only for session's with true condition only.
"monitor_cond" and "monitor_cond_change" are RFC 7047 extensions
described by ovsdb-server(1) manpage.

Signed-off-by: Liran Schour <lirans@il.ibm.com>

---
v3->v4:
* Bug fix: classify modify as delete or insert

v2->v3:
* Document monitor_cond_update receives a single json condition
* Pass session's condition down to row_update function
* Do not classify modify as delete or insert since we send update
  due to cond_update with empty changes list
* Allow conditions with non-monitored columns
* Bug fix: allow json cache when session's condition is NULL
* Bug fix: allow json cahce when all table's conditions are empty
---
 ovsdb/condition.c       |  21 ++++
 ovsdb/condition.h       |   4 +
 ovsdb/jsonrpc-server.c  |  41 +++++--
 ovsdb/monitor.c         | 277 ++++++++++++++++++++++++++++++++++++++++++++----
 ovsdb/monitor.h         |  30 +++++-
 ovsdb/ovsdb-server.1.in | 230 +++++++++++++++++++++++++++++++++++++---
 6 files changed, 559 insertions(+), 44 deletions(-)

Comments

Ben Pfaff March 22, 2016, 5:23 p.m. UTC | #1
On Fri, Mar 04, 2016 at 08:08:59AM +0000, Liran Schour wrote:
> Hold session's conditions in ovsdb_monitor_session_condition. Pass it
> to ovsdb_monitor for generating "update2" notifications.
> Add functions that can generate "update2" notification for a
> "monitor_cond" session.
> JSON cache is enabled only for session's with true condition only.
> "monitor_cond" and "monitor_cond_change" are RFC 7047 extensions
> described by ovsdb-server(1) manpage.
> 
> Signed-off-by: Liran Schour <lirans@il.ibm.com>

I think that ovsdb_monitor_table_condition_set() has a memory leak in
the error case; it does not free the 'error' returned to it.

I'm a little confused about ovsdb_monitor_session_condition.  Why is
this a separate data structure?  That is, why is there a separate shash
of conditions rather than incorporating a condition into
ovsdb_monitor_table?

If a separate data structure is really needed then possibly it would be
better to only include the non-"true" conditions in the shash.  Then it
would be easy to determine how many non-"true" conditions there were
(shash_count(conditions)) and hence whether there were any at all.

I'll continue reviewing the series once I understand this point.

Thank you!
Liran Schour March 22, 2016, 9:24 p.m. UTC | #2
Ben Pfaff <blp@ovn.org> wrote on 22/03/2016 07:23:33 PM:

> On Fri, Mar 04, 2016 at 08:08:59AM +0000, Liran Schour wrote:
> > Hold session's conditions in ovsdb_monitor_session_condition. Pass it
> > to ovsdb_monitor for generating "update2" notifications.
> > Add functions that can generate "update2" notification for a
> > "monitor_cond" session.
> > JSON cache is enabled only for session's with true condition only.
> > "monitor_cond" and "monitor_cond_change" are RFC 7047 extensions
> > described by ovsdb-server(1) manpage.
> > 
> > Signed-off-by: Liran Schour <lirans@il.ibm.com>
> 
> I think that ovsdb_monitor_table_condition_set() has a memory leak in
> the error case; it does not free the 'error' returned to it.
> 

Right. Will fix that.

> I'm a little confused about ovsdb_monitor_session_condition.  Why is
> this a separate data structure?  That is, why is there a separate shash
> of conditions rather than incorporating a condition into
> ovsdb_monitor_table?
> 

A single ovsdb_monitor instance can be used by many jsonrpc sessions (as 
long as they share the same set of tables and columns). 
Ovsdb_monitor_condition_session is holding the condition state per each 
jsonrpc session. Each session, in ovsdb_monitor, can have a different set 
of conditions and it is being represented by 
ovsdb_monitor_session_condition with ovsdb_monitor_table_condition per 
table.
The trivial option was to divide ovsdb_monitor into 2 different instances 
if we have 2 sessions with different sets of conditions. But, condition is 
changing during the lifetime of monitor session. And by holding sessions 
with different conditions under the same ovsdb_monitor we can reduce the 
computation needed to insert row changes to ovsdb_monitor. (insert once 
instead of twice)

> If a separate data structure is really needed then possibly it would be
> better to only include the non-"true" conditions in the shash.  Then it
> would be easy to determine how many non-"true" conditions there were
> (shash_count(conditions)) and hence whether there were any at all.
> 

Something like this can be done however I do not support this. Conditions 
might change and it is needed to keep the condition state even if it is 
true (implicit or explicit).

> I'll continue reviewing the series once I understand this point.

Hope that it is clearer now. Thanks.
Ben Pfaff March 30, 2016, 4:56 p.m. UTC | #3
On Tue, Mar 22, 2016 at 11:24:14PM +0200, Liran Schour wrote:
> Ben Pfaff <blp@ovn.org> wrote on 22/03/2016 07:23:33 PM:
> 
> > On Fri, Mar 04, 2016 at 08:08:59AM +0000, Liran Schour wrote:
> > > Hold session's conditions in ovsdb_monitor_session_condition. Pass it
> > > to ovsdb_monitor for generating "update2" notifications.
> > > Add functions that can generate "update2" notification for a
> > > "monitor_cond" session.
> > > JSON cache is enabled only for session's with true condition only.
> > > "monitor_cond" and "monitor_cond_change" are RFC 7047 extensions
> > > described by ovsdb-server(1) manpage.
> > > 
> > > Signed-off-by: Liran Schour <lirans@il.ibm.com>
> > 
> > I think that ovsdb_monitor_table_condition_set() has a memory leak in
> > the error case; it does not free the 'error' returned to it.
> > 
> 
> Right. Will fix that.
> 
> > I'm a little confused about ovsdb_monitor_session_condition.  Why is
> > this a separate data structure?  That is, why is there a separate shash
> > of conditions rather than incorporating a condition into
> > ovsdb_monitor_table?
> > 
> 
> A single ovsdb_monitor instance can be used by many jsonrpc sessions (as 
> long as they share the same set of tables and columns). 
> Ovsdb_monitor_condition_session is holding the condition state per each 
> jsonrpc session. Each session, in ovsdb_monitor, can have a different set 
> of conditions and it is being represented by 
> ovsdb_monitor_session_condition with ovsdb_monitor_table_condition per 
> table.
> The trivial option was to divide ovsdb_monitor into 2 different instances 
> if we have 2 sessions with different sets of conditions. But, condition is 
> changing during the lifetime of monitor session. And by holding sessions 
> with different conditions under the same ovsdb_monitor we can reduce the 
> computation needed to insert row changes to ovsdb_monitor. (insert once 
> instead of twice)

Those downsides of having a monitor per (monitored columns, condition)
seem pretty minor.  It also seems to me like the "obvious" way to
implement this.  It seems like, for example, the problem of caching with
conditions is much easier if the data structures are organized this
way.  Did you actually try implementing it that way and determine that
there was some insuperable difficulty?
Liran Schour April 1, 2016, 12:14 p.m. UTC | #4
Ben Pfaff <blp@ovn.org> wrote on 30/03/2016 07:56:01 PM:

> On Tue, Mar 22, 2016 at 11:24:14PM +0200, Liran Schour wrote:
> > Ben Pfaff <blp@ovn.org> wrote on 22/03/2016 07:23:33 PM:
> > 
> > > On Fri, Mar 04, 2016 at 08:08:59AM +0000, Liran Schour wrote:
> > > > Hold session's conditions in ovsdb_monitor_session_condition. Pass 
it
> > > > to ovsdb_monitor for generating "update2" notifications.
> > > > Add functions that can generate "update2" notification for a
> > > > "monitor_cond" session.
> > > > JSON cache is enabled only for session's with true condition only.
> > > > "monitor_cond" and "monitor_cond_change" are RFC 7047 extensions
> > > > described by ovsdb-server(1) manpage.
> > > > 
> > > > Signed-off-by: Liran Schour <lirans@il.ibm.com>
> > > 
> > > I think that ovsdb_monitor_table_condition_set() has a memory leak 
in
> > > the error case; it does not free the 'error' returned to it.
> > > 
> > 
> > Right. Will fix that.
> > 
> > > I'm a little confused about ovsdb_monitor_session_condition.  Why is
> > > this a separate data structure?  That is, why is there a separate 
shash
> > > of conditions rather than incorporating a condition into
> > > ovsdb_monitor_table?
> > > 
> > 
> > A single ovsdb_monitor instance can be used by many jsonrpc sessions 
(as 
> > long as they share the same set of tables and columns). 
> > Ovsdb_monitor_condition_session is holding the condition state per 
each 
> > jsonrpc session. Each session, in ovsdb_monitor, can have a different 
set 
> > of conditions and it is being represented by 
> > ovsdb_monitor_session_condition with ovsdb_monitor_table_condition per 

> > table.
> > The trivial option was to divide ovsdb_monitor into 2 different 
instances 
> > if we have 2 sessions with different sets of conditions. But, 
condition is 
> > changing during the lifetime of monitor session. And by holding 
sessions 
> > with different conditions under the same ovsdb_monitor we can reduce 
the 
> > computation needed to insert row changes to ovsdb_monitor. (insert 
once 
> > instead of twice)
> 
> Those downsides of having a monitor per (monitored columns, condition)
> seem pretty minor.  It also seems to me like the "obvious" way to
> implement this.  It seems like, for example, the problem of caching with
> conditions is much easier if the data structures are organized this
> way.  Did you actually try implementing it that way and determine that
> there was some insuperable difficulty?

No, I did not try implementing it in the 'trivial' way. The reason is 
twofold: first, to gain the ability to share the computed updates between 
sessions, also as per-session conditions change; second, to gain the 
ability to 'fall back' to using a Json cache for condition-less tables. (A 
typical monitor session includes conditioned tables and condition-less 
tables).

My implementation choice achieves the above and has the complexity of 
processing a DB update in O(#conditioned columns). The 'trivial' 
alternative would not achieve the above in the general case and would have 
the complexity of processing a DB update in O(#jsonrpc sessions with 
matched conditions), in the best case.

As I see it, the above stated benefits of the more complex design worth 
the complexity increase in the code, given also that the memory complexity 
is similar.
Ben Pfaff April 11, 2016, 9:40 p.m. UTC | #5
On Fri, Apr 01, 2016 at 02:14:27PM +0200, Liran Schour wrote:
> Ben Pfaff <blp@ovn.org> wrote on 30/03/2016 07:56:01 PM:
> 
> > On Tue, Mar 22, 2016 at 11:24:14PM +0200, Liran Schour wrote:
> > > Ben Pfaff <blp@ovn.org> wrote on 22/03/2016 07:23:33 PM:
> > > 
> > > > On Fri, Mar 04, 2016 at 08:08:59AM +0000, Liran Schour wrote:
> > > > > Hold session's conditions in ovsdb_monitor_session_condition. Pass 
> it
> > > > > to ovsdb_monitor for generating "update2" notifications.
> > > > > Add functions that can generate "update2" notification for a
> > > > > "monitor_cond" session.
> > > > > JSON cache is enabled only for session's with true condition only.
> > > > > "monitor_cond" and "monitor_cond_change" are RFC 7047 extensions
> > > > > described by ovsdb-server(1) manpage.
> > > > > 
> > > > > Signed-off-by: Liran Schour <lirans@il.ibm.com>
> > > > 
> > > > I think that ovsdb_monitor_table_condition_set() has a memory leak 
> in
> > > > the error case; it does not free the 'error' returned to it.
> > > > 
> > > 
> > > Right. Will fix that.
> > > 
> > > > I'm a little confused about ovsdb_monitor_session_condition.  Why is
> > > > this a separate data structure?  That is, why is there a separate 
> shash
> > > > of conditions rather than incorporating a condition into
> > > > ovsdb_monitor_table?
> > > > 
> > > 
> > > A single ovsdb_monitor instance can be used by many jsonrpc sessions 
> (as 
> > > long as they share the same set of tables and columns). 
> > > Ovsdb_monitor_condition_session is holding the condition state per 
> each 
> > > jsonrpc session. Each session, in ovsdb_monitor, can have a different 
> set 
> > > of conditions and it is being represented by 
> > > ovsdb_monitor_session_condition with ovsdb_monitor_table_condition per 
> 
> > > table.
> > > The trivial option was to divide ovsdb_monitor into 2 different 
> instances 
> > > if we have 2 sessions with different sets of conditions. But, 
> condition is 
> > > changing during the lifetime of monitor session. And by holding 
> sessions 
> > > with different conditions under the same ovsdb_monitor we can reduce 
> the 
> > > computation needed to insert row changes to ovsdb_monitor. (insert 
> once 
> > > instead of twice)
> > 
> > Those downsides of having a monitor per (monitored columns, condition)
> > seem pretty minor.  It also seems to me like the "obvious" way to
> > implement this.  It seems like, for example, the problem of caching with
> > conditions is much easier if the data structures are organized this
> > way.  Did you actually try implementing it that way and determine that
> > there was some insuperable difficulty?
> 
> No, I did not try implementing it in the 'trivial' way. The reason is 
> twofold: first, to gain the ability to share the computed updates between 
> sessions, also as per-session conditions change; second, to gain the 
> ability to 'fall back' to using a Json cache for condition-less tables. (A 
> typical monitor session includes conditioned tables and condition-less 
> tables).
> 
> My implementation choice achieves the above and has the complexity of 
> processing a DB update in O(#conditioned columns). The 'trivial' 
> alternative would not achieve the above in the general case and would have 
> the complexity of processing a DB update in O(#jsonrpc sessions with 
> matched conditions), in the best case.
> 
> As I see it, the above stated benefits of the more complex design worth 
> the complexity increase in the code, given also that the memory complexity 
> is similar.

As I understand it, the implementation in this patch series cannot share
a cache of changes across sessions that include a condition, even if the
condition is the same.  Is that correct?
Liran Schour April 12, 2016, 12:02 p.m. UTC | #6
Ben Pfaff <blp@ovn.org> wrote on 12/04/2016 12:40:00 AM:

> On Fri, Apr 01, 2016 at 02:14:27PM +0200, Liran Schour wrote:
> > Ben Pfaff <blp@ovn.org> wrote on 30/03/2016 07:56:01 PM:
> > 
> > > On Tue, Mar 22, 2016 at 11:24:14PM +0200, Liran Schour wrote:
> > > > Ben Pfaff <blp@ovn.org> wrote on 22/03/2016 07:23:33 PM:
> > > > 
> > > > > On Fri, Mar 04, 2016 at 08:08:59AM +0000, Liran Schour wrote:
> > > > > > Hold session's conditions in ovsdb_monitor_session_condition. 
Pass 
> > it
> > > > > > to ovsdb_monitor for generating "update2" notifications.
> > > > > > Add functions that can generate "update2" notification for a
> > > > > > "monitor_cond" session.
> > > > > > JSON cache is enabled only for session's with true condition 
only.
> > > > > > "monitor_cond" and "monitor_cond_change" are RFC 7047 
extensions
> > > > > > described by ovsdb-server(1) manpage.
> > > > > > 
> > > > > > Signed-off-by: Liran Schour <lirans@il.ibm.com>
> > > > > 
> > > > > I think that ovsdb_monitor_table_condition_set() has a memory 
leak 
> > in
> > > > > the error case; it does not free the 'error' returned to it.
> > > > > 
> > > > 
> > > > Right. Will fix that.
> > > > 
> > > > > I'm a little confused about ovsdb_monitor_session_condition. Why 
is
> > > > > this a separate data structure?  That is, why is there a 
separate 
> > shash
> > > > > of conditions rather than incorporating a condition into
> > > > > ovsdb_monitor_table?
> > > > > 
> > > > 
> > > > A single ovsdb_monitor instance can be used by many jsonrpc 
sessions 
> > (as 
> > > > long as they share the same set of tables and columns). 
> > > > Ovsdb_monitor_condition_session is holding the condition state per 

> > each 
> > > > jsonrpc session. Each session, in ovsdb_monitor, can have a 
different 
> > set 
> > > > of conditions and it is being represented by 
> > > > ovsdb_monitor_session_condition with ovsdb_monitor_table_condition 
per 
> > 
> > > > table.
> > > > The trivial option was to divide ovsdb_monitor into 2 different 
> > instances 
> > > > if we have 2 sessions with different sets of conditions. But, 
> > condition is 
> > > > changing during the lifetime of monitor session. And by holding 
> > sessions 
> > > > with different conditions under the same ovsdb_monitor we can 
reduce 
> > the 
> > > > computation needed to insert row changes to ovsdb_monitor. (insert 

> > once 
> > > > instead of twice)
> > > 
> > > Those downsides of having a monitor per (monitored columns, 
condition)
> > > seem pretty minor.  It also seems to me like the "obvious" way to
> > > implement this.  It seems like, for example, the problem of caching 
with
> > > conditions is much easier if the data structures are organized this
> > > way.  Did you actually try implementing it that way and determine 
that
> > > there was some insuperable difficulty?
> > 
> > No, I did not try implementing it in the 'trivial' way. The reason is 
> > twofold: first, to gain the ability to share the computed updates 
between 
> > sessions, also as per-session conditions change; second, to gain the 
> > ability to 'fall back' to using a Json cache for condition-less 
tables. (A 
> > typical monitor session includes conditioned tables and condition-less 

> > tables).
> > 
> > My implementation choice achieves the above and has the complexity of 
> > processing a DB update in O(#conditioned columns). The 'trivial' 
> > alternative would not achieve the above in the general case and would 
have 
> > the complexity of processing a DB update in O(#jsonrpc sessions with 
> > matched conditions), in the best case.
> > 
> > As I see it, the above stated benefits of the more complex design 
worth 
> > the complexity increase in the code, given also that the memory 
complexity 
> > is similar.
> 
> As I understand it, the implementation in this patch series cannot share
> a cache of changes across sessions that include a condition, even if the
> condition is the same.  Is that correct?

Correct, json-cache is not shared across sessions in general, only across 
sessions with empty or true conditions on all tables, or per table with 
empty or true condition. What shared across sessions is the changes list 
computed for updates matching shared condition clauses, as follows: update 
arrives --> update is added to all the matched condition clauses (clauses 
are shared between the sessions) so that changes list is computed per 
clause --> per session update messages are created using the already 
computed changes lists.

Changes list for all sessions sharing a common ovsdb_monitor is computed 
in O(#conditioned columns), once per update. To introduce the feature 
incrementally, the patch series starts with evaluating conditions and 
computing update messages by going over the raw changes list; then allows 
re-using the json-cache for empty or true conditioned tables (patch #5); 
and only then introduces the clause-based changes tracking described above 
(patch #13-17).

This patch series has been under test for several weeks now (both in 
OpenStack cloud and OVN sandbox); several bugs were revealed and fixed; 
performance evaluation is in progress.
Initial results show significant CPU load reduction on ovn-controllers. 
There are additional insights. I am ready to share details and discuss.

I will be at the OpenStack summit, can we arrange a discussion session on 
this topic there?
Ben Pfaff April 14, 2016, 9:50 p.m. UTC | #7
On Tue, Apr 12, 2016 at 03:02:38PM +0300, Liran Schour wrote:
> I will be at the OpenStack summit, can we arrange a discussion session on 
> this topic there?

Yes, let's do that.  I'll be there Monday through Friday.
diff mbox

Patch

diff --git a/ovsdb/condition.c b/ovsdb/condition.c
index 4dcad7b..c477f83 100644
--- a/ovsdb/condition.c
+++ b/ovsdb/condition.c
@@ -507,3 +507,24 @@  ovsdb_condition_diff(const struct ovsdb_condition *a,
         ovsdb_clause_add(b_only, &b->clauses[j], &added_allocated);
     }
 }
+
+const struct ovsdb_column **
+ovsdb_condition_get_columns(const struct ovsdb_condition *cond,
+                            size_t *n_columns)
+{
+    const struct ovsdb_column **columns;
+    size_t i;
+
+    if (!cond->n_clauses) {
+        *n_columns = 0;
+        return NULL;
+    }
+
+    columns = xmalloc(cond->n_clauses * sizeof *columns);
+    for (i = 0; i < cond->n_clauses; i++) {
+        columns[i] = cond->clauses[i].column;
+    }
+    *n_columns = i;
+
+    return columns;
+}
diff --git a/ovsdb/condition.h b/ovsdb/condition.h
index 48895fa..dc5d4d1 100644
--- a/ovsdb/condition.h
+++ b/ovsdb/condition.h
@@ -90,4 +90,8 @@  void ovsdb_condition_diff(const struct ovsdb_condition *a,
                           struct ovsdb_condition *added,
                           struct ovsdb_condition *removed);
 
+const struct ovsdb_column **
+ovsdb_condition_get_columns(const struct ovsdb_condition *cond,
+                            size_t *n_columns);
+
 #endif /* ovsdb/condition.h */
diff --git a/ovsdb/jsonrpc-server.c b/ovsdb/jsonrpc-server.c
index 770ed08..6724ae4 100644
--- a/ovsdb/jsonrpc-server.c
+++ b/ovsdb/jsonrpc-server.c
@@ -28,6 +28,7 @@ 
 #include "ovsdb-error.h"
 #include "ovsdb-parser.h"
 #include "ovsdb.h"
+#include "condition.h"
 #include "poll-loop.h"
 #include "reconnect.h"
 #include "row.h"
@@ -865,7 +866,8 @@  ovsdb_jsonrpc_session_got_request(struct ovsdb_jsonrpc_session *s,
             reply = execute_transaction(s, db, request);
         }
     } else if (!strcmp(request->method, "monitor") ||
-               (monitor2_enable__ && !strcmp(request->method, "monitor2"))) {
+               (monitor2_enable__ && !strcmp(request->method, "monitor2")) ||
+               !strcmp(request->method, "monitor_cond")) {
         struct ovsdb *db = ovsdb_jsonrpc_lookup_db(s, request, &reply);
         if (!reply) {
             int l = strlen(request->method) - strlen("monitor");
@@ -1064,6 +1066,7 @@  struct ovsdb_jsonrpc_monitor {
     uint64_t unflushed;         /* The first transaction that has not been
                                        flushed to the jsonrpc remote client. */
     enum ovsdb_monitor_version version;
+    struct ovsdb_monitor_session_condition *condition;/* Session's condition */
 };
 
 static struct ovsdb_jsonrpc_monitor *
@@ -1091,20 +1094,27 @@  parse_bool(struct ovsdb_parser *parser, const char *name, bool default_value)
 }
 
 static struct ovsdb_error * OVS_WARN_UNUSED_RESULT
-ovsdb_jsonrpc_parse_monitor_request(struct ovsdb_monitor *dbmon,
-                                    const struct ovsdb_table *table,
-                                    const struct json *monitor_request)
+ovsdb_jsonrpc_parse_monitor_request(
+                               struct ovsdb_monitor *dbmon,
+                               const struct ovsdb_table *table,
+                               struct ovsdb_monitor_session_condition *cond,
+                               const struct json *monitor_request)
 {
     const struct ovsdb_table_schema *ts = table->schema;
     enum ovsdb_monitor_selection select;
-    const struct json *columns, *select_json;
+    const struct json *columns, *select_json, *where = NULL;
     struct ovsdb_parser parser;
     struct ovsdb_error *error;
 
     ovsdb_parser_init(&parser, monitor_request, "table %s", ts->name);
+    if (cond) {
+        where = ovsdb_parser_member(&parser, "where", OP_ARRAY | OP_OPTIONAL);
+    }
     columns = ovsdb_parser_member(&parser, "columns", OP_ARRAY | OP_OPTIONAL);
+
     select_json = ovsdb_parser_member(&parser, "select",
                                       OP_OBJECT | OP_OPTIONAL);
+
     error = ovsdb_parser_finish(&parser);
     if (error) {
         return error;
@@ -1171,6 +1181,12 @@  ovsdb_jsonrpc_parse_monitor_request(struct ovsdb_monitor *dbmon,
             }
         }
     }
+    if (cond) {
+        error = ovsdb_monitor_table_condition_set(cond, table, where);
+        if (error) {
+            return error;
+        }
+    }
 
     return NULL;
 }
@@ -1209,6 +1225,9 @@  ovsdb_jsonrpc_monitor_create(struct ovsdb_jsonrpc_session *s, struct ovsdb *db,
     m->session = s;
     m->db = db;
     m->dbmon = ovsdb_monitor_create(db, m);
+    if (version == OVSDB_MONITOR_V2) {
+        m->condition = ovsdb_monitor_session_condition_create();
+    }
     m->unflushed = 0;
     m->version = version;
     hmap_insert(&s->monitors, &m->node, json_hash(monitor_id, 0));
@@ -1237,6 +1256,7 @@  ovsdb_jsonrpc_monitor_create(struct ovsdb_jsonrpc_session *s, struct ovsdb *db,
             for (i = 0; i < array->n; i++) {
                 error = ovsdb_jsonrpc_parse_monitor_request(m->dbmon,
                                                             table,
+                                                            m->condition,
                                                             array->elems[i]);
                 if (error) {
                     goto error;
@@ -1245,6 +1265,7 @@  ovsdb_jsonrpc_monitor_create(struct ovsdb_jsonrpc_session *s, struct ovsdb *db,
         } else {
             error = ovsdb_jsonrpc_parse_monitor_request(m->dbmon,
                                                         table,
+                                                        m->condition,
                                                         mr_value);
             if (error) {
                 goto error;
@@ -1269,6 +1290,11 @@  ovsdb_jsonrpc_monitor_create(struct ovsdb_jsonrpc_session *s, struct ovsdb *db,
         m->dbmon = dbmon;
     }
 
+    /* Only now we can bind session's condition to ovsdb_monitor */
+    if (m->condition) {
+        ovsdb_monitor_condition_bind(m->dbmon, m->condition);
+    }
+
     ovsdb_monitor_get_initial(m->dbmon);
     json = ovsdb_jsonrpc_monitor_compose_update(m, true);
     json = json ? json : json_object_create();
@@ -1325,7 +1351,7 @@  ovsdb_jsonrpc_monitor_compose_update(struct ovsdb_jsonrpc_monitor *m,
     }
 
     return ovsdb_monitor_get_update(m->dbmon, initial, &m->unflushed,
-                                    m->version);
+                                    m->condition, m->version);
 }
 
 static bool
@@ -1348,6 +1374,9 @@  ovsdb_jsonrpc_monitor_destroy(struct ovsdb_jsonrpc_monitor *m)
     json_destroy(m->monitor_id);
     hmap_remove(&m->session->monitors, &m->node);
     ovsdb_monitor_remove_jsonrpc_monitor(m->dbmon, m);
+    if (m->condition) {
+        ovsdb_monitor_session_condition_destroy(m->condition);
+    }
     free(m);
 }
 
diff --git a/ovsdb/monitor.c b/ovsdb/monitor.c
index 2da711f..0192abf 100644
--- a/ovsdb/monitor.c
+++ b/ovsdb/monitor.c
@@ -27,6 +27,7 @@ 
 #include "ovsdb-parser.h"
 #include "ovsdb.h"
 #include "row.h"
+#include "condition.h"
 #include "simap.h"
 #include "hash.h"
 #include "table.h"
@@ -41,6 +42,22 @@ 
 static const struct ovsdb_replica_class ovsdb_jsonrpc_replica_class;
 static struct hmap ovsdb_monitors = HMAP_INITIALIZER(&ovsdb_monitors);
 
+/* Keep state of session's conditions */
+struct ovsdb_monitor_session_condition {
+    bool conditional;
+    size_t n_true_cnd;
+    struct shash tables;     /* Contains
+                              *   "struct ovsdb_monitor_table_condition *"s. */
+};
+
+/* Monitored table session's conditions */
+struct ovsdb_monitor_table_condition {
+    const struct ovsdb_table *table;
+    struct ovsdb_monitor_table *mt;
+    struct ovsdb_condition old_condition;
+    struct ovsdb_condition new_condition;
+};
+
 /*  Backend monitor.
  *
  *  ovsdb_monitor keep track of the ovsdb changes.
@@ -108,6 +125,7 @@  struct ovsdb_monitor_changes {
 /* A particular table being monitored. */
 struct ovsdb_monitor_table {
     const struct ovsdb_table *table;
+    const struct ovsdb_monitor *dbmon;
 
     /* This is the union (bitwise-OR) of the 'select' values in all of the
      * members of 'columns' below. */
@@ -129,9 +147,11 @@  struct ovsdb_monitor_table {
 };
 
 typedef struct json *
-(*compose_row_update_cb_func)(const struct ovsdb_monitor_table *mt,
-                              const struct ovsdb_monitor_row *row,
-                              bool initial, unsigned long int *changed);
+(*compose_row_update_cb_func)
+    (const struct ovsdb_monitor_table *mt,
+     const struct ovsdb_monitor_session_condition * condition,
+     const struct ovsdb_monitor_row *row,
+     bool initial, unsigned long int *changed);
 
 static void ovsdb_monitor_destroy(struct ovsdb_monitor *dbmon);
 static struct ovsdb_monitor_changes * ovsdb_monitor_table_add_changes(
@@ -372,6 +392,7 @@  ovsdb_monitor_add_table(struct ovsdb_monitor *m,
 
     mt = xzalloc(sizeof *mt);
     mt->table = table;
+    mt->dbmon = m;
     shash_add(&m->tables, table->schema->name, mt);
     hmap_init(&mt->changes);
     mt->columns_index_map =
@@ -390,9 +411,20 @@  ovsdb_monitor_add_column(struct ovsdb_monitor *dbmon,
 {
     struct ovsdb_monitor_table *mt;
     struct ovsdb_monitor_column *c;
+    int i;
 
     mt = shash_find_data(&dbmon->tables, table->schema->name);
 
+    /* Check duplication only for non-monitored columns */
+    if (!monitored) {
+        for (i = 0; i < mt->n_columns; i++) {
+            if (mt->columns[i].column == column) {
+                /* column exists */
+                return;
+            }
+        }
+    }
+
     if (mt->n_columns >= mt->allocated_columns) {
         mt->columns = x2nrealloc(mt->columns, &mt->allocated_columns,
                                  sizeof *mt->columns);
@@ -421,6 +453,42 @@  ovsdb_monitor_add_column(struct ovsdb_monitor *dbmon,
     }
 }
 
+static void
+ovsdb_monitor_condition_add_columns(struct ovsdb_monitor *dbmon,
+                                    const struct ovsdb_table *table,
+                                    struct ovsdb_condition *condition)
+{
+    size_t n_columns;
+    int i;
+    const struct ovsdb_column **columns =
+        ovsdb_condition_get_columns(condition, &n_columns);
+
+    for (i = 0; i < n_columns; i++) {
+        ovsdb_monitor_add_column(dbmon, table, columns[i],
+                                 OJMS_NONE, false);
+    }
+    if (n_columns) {
+        free(columns);
+    }
+}
+
+void
+ovsdb_monitor_condition_bind(struct ovsdb_monitor *dbmon,
+                          struct ovsdb_monitor_session_condition *cond)
+{
+    struct shash_node *node;
+
+    SHASH_FOR_EACH(node, &cond->tables) {
+        struct ovsdb_monitor_table_condition *mtc = node->data;
+        struct ovsdb_monitor_table *mt =
+            shash_find_data(&dbmon->tables, mtc->table->schema->name);
+
+        mtc->mt = mt;
+        ovsdb_monitor_condition_add_columns(dbmon, mtc->table,
+                                            &mtc->new_condition);
+    }
+}
+
 /* Check for duplicated column names. Return the first
  * duplicated column's name if found. Otherwise return
  * NULL.  */
@@ -531,6 +599,158 @@  ovsdb_monitor_row_update_type(bool initial, const bool old, const bool new)
             : !new ? OJMS_DELETE
             : OJMS_MODIFY;
 }
+
+/* Set conditional monitoring mode only if we have none empty condition in one
+ * of the tables at least */
+static inline void
+ovsdb_monitor_session_condition_set_mode(
+                                  struct ovsdb_monitor_session_condition *cond)
+{
+    cond->conditional = shash_count(&cond->tables) !=
+        cond->n_true_cnd;
+}
+
+/* Returnes an empty allocated session's condition state holder */
+struct ovsdb_monitor_session_condition *
+ovsdb_monitor_session_condition_create(void)
+{
+    struct ovsdb_monitor_session_condition *condition =
+        xzalloc(sizeof *condition);
+
+    condition->conditional = false;
+    shash_init(&condition->tables);
+    return condition;
+}
+
+void
+ovsdb_monitor_session_condition_destroy(
+                           struct ovsdb_monitor_session_condition *condition)
+{
+    struct shash_node *node, *next;
+
+    SHASH_FOR_EACH_SAFE (node, next, &condition->tables) {
+        struct ovsdb_monitor_table_condition *mtc = node->data;
+
+        ovsdb_condition_destroy(&mtc->new_condition);
+        ovsdb_condition_destroy(&mtc->old_condition);
+        shash_delete(&condition->tables, node);
+        free(mtc);
+    }
+    free(condition);
+}
+
+struct ovsdb_error *
+ovsdb_monitor_table_condition_set(
+                         struct ovsdb_monitor_session_condition *condition,
+                         const struct ovsdb_table *table,
+                         const struct json *json_cnd)
+{
+    struct ovsdb_monitor_table_condition *mtc;
+    struct ovsdb_error *error;
+
+    mtc = xzalloc(sizeof *mtc);
+    mtc->table = table;
+    ovsdb_condition_init(&mtc->old_condition);
+    ovsdb_condition_init(&mtc->new_condition);
+
+    if (json_cnd) {
+        error = ovsdb_condition_from_json(table->schema,
+                                          json_cnd,
+                                          NULL,
+                                          &mtc->old_condition);
+        if (error) {
+            free(mtc);
+            return ovsdb_syntax_error(json_cnd,
+                                      NULL, "array of conditions expected");
+        }
+    }
+
+    shash_add(&condition->tables, table->schema->name, mtc);
+    /* On session startup old == new condition */
+    ovsdb_condition_clone(&mtc->new_condition, &mtc->old_condition);
+    if (ovsdb_condition_is_true(&mtc->old_condition)) {
+        condition->n_true_cnd++;
+        ovsdb_monitor_session_condition_set_mode(condition);
+    }
+
+    return NULL;
+}
+
+static bool
+ovsdb_monitor_get_table_conditions(
+                      const struct ovsdb_monitor_table *mt,
+                      const struct ovsdb_monitor_session_condition *condition,
+                      struct ovsdb_condition **old_condition,
+                      struct ovsdb_condition **new_condition)
+{
+    if (!condition) {
+        return false;
+    }
+
+    struct ovsdb_monitor_table_condition *mtc =
+        shash_find_data(&condition->tables, mt->table->schema->name);
+
+    if (!mtc) {
+        return false;
+    }
+    *old_condition = &mtc->old_condition;
+    *new_condition = &mtc->new_condition;
+
+    return true;
+}
+
+static enum ovsdb_monitor_selection
+ovsdb_monitor_row_update_type_condition(
+                      const struct ovsdb_monitor_table *mt,
+                      const struct ovsdb_monitor_session_condition *condition,
+                      bool initial,
+                      const struct ovsdb_datum *old,
+                      const struct ovsdb_datum *new)
+{
+    struct ovsdb_condition *old_condition, *new_condition;
+    enum ovsdb_monitor_selection type =
+        ovsdb_monitor_row_update_type(initial, old, new);
+
+    if (ovsdb_monitor_get_table_conditions(mt,
+                                           condition,
+                                           &old_condition,
+                                           &new_condition)) {
+        bool old_cond = !old ? false
+            : ovsdb_condition_match_any_clause(old,
+                                               old_condition,
+                                               mt->columns_index_map);
+        bool new_cond = !new ? false
+            : ovsdb_condition_match_any_clause(new,
+                                               new_condition,
+                                               mt->columns_index_map);
+
+        if (!old_cond && !new_cond) {
+            type = OJMS_NONE;
+        }
+
+        switch (type) {
+        case OJMS_INITIAL:
+        case OJMS_INSERT:
+            if (!new_cond) {
+                type = OJMS_NONE;
+            }
+            break;
+        case OJMS_MODIFY:
+            type = !old_cond ? OJMS_INSERT : !new_cond
+                ? OJMS_DELETE : OJMS_MODIFY;
+            break;
+        case OJMS_DELETE:
+            if (!old_cond) {
+                type = OJMS_NONE;
+            }
+            break;
+        case OJMS_NONE:
+            break;
+        }
+    }
+    return type;
+}
+
 static bool
 ovsdb_monitor_row_skip_update(const struct ovsdb_monitor_table *mt,
                               const struct ovsdb_monitor_row *row,
@@ -575,6 +795,7 @@  ovsdb_monitor_row_skip_update(const struct ovsdb_monitor_table *mt,
 static struct json *
 ovsdb_monitor_compose_row_update(
     const struct ovsdb_monitor_table *mt,
+    const struct ovsdb_monitor_session_condition *condition OVS_UNUSED,
     const struct ovsdb_monitor_row *row,
     bool initial, unsigned long int *changed)
 {
@@ -636,6 +857,7 @@  ovsdb_monitor_compose_row_update(
 static struct json *
 ovsdb_monitor_compose_row_update2(
     const struct ovsdb_monitor_table *mt,
+    const struct ovsdb_monitor_session_condition *condition,
     const struct ovsdb_monitor_row *row,
     bool initial, unsigned long int *changed)
 {
@@ -643,7 +865,8 @@  ovsdb_monitor_compose_row_update2(
     struct json *row_update2, *diff_json;
     size_t i;
 
-    type = ovsdb_monitor_row_update_type(initial, row->old, row->new);
+    type = ovsdb_monitor_row_update_type_condition(mt, condition, initial,
+                                                   row->old, row->new);
     if (ovsdb_monitor_row_skip_update(mt, row, type, changed)) {
         return NULL;
     }
@@ -713,9 +936,11 @@  ovsdb_monitor_max_columns(struct ovsdb_monitor *dbmon)
  * RFC 7047) for all the outstanding changes within 'monitor', starting from
  * 'transaction'.  */
 static struct json*
-ovsdb_monitor_compose_update(struct ovsdb_monitor *dbmon,
-                             bool initial, uint64_t transaction,
-                             compose_row_update_cb_func row_update)
+ovsdb_monitor_compose_update(
+                      struct ovsdb_monitor *dbmon,
+                      bool initial, uint64_t transaction,
+                      const struct ovsdb_monitor_session_condition *condition,
+                      compose_row_update_cb_func row_update)
 {
     struct shash_node *node;
     struct json *json;
@@ -737,7 +962,7 @@  ovsdb_monitor_compose_update(struct ovsdb_monitor *dbmon,
         HMAP_FOR_EACH_SAFE (row, next, hmap_node, &changes->rows) {
             struct json *row_json;
 
-            row_json = (*row_update)(mt, row, initial, changed);
+            row_json = (*row_update)(mt, condition, row, initial, changed);
             if (row_json) {
                 char uuid[UUID_LEN + 1];
 
@@ -771,11 +996,13 @@  ovsdb_monitor_compose_update(struct ovsdb_monitor *dbmon,
  * 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. */
 struct json *
-ovsdb_monitor_get_update(struct ovsdb_monitor *dbmon,
-                         bool initial, uint64_t *unflushed_,
-                         enum ovsdb_monitor_version version)
+ovsdb_monitor_get_update(
+             struct ovsdb_monitor *dbmon,
+             bool initial, uint64_t *unflushed_,
+             const struct ovsdb_monitor_session_condition *condition,
+             enum ovsdb_monitor_version version)
 {
-    struct ovsdb_monitor_json_cache_node *cache_node;
+    struct ovsdb_monitor_json_cache_node *cache_node = NULL;
     struct shash_node *node;
     struct json *json;
     const uint64_t unflushed = *unflushed_;
@@ -783,19 +1010,28 @@  ovsdb_monitor_get_update(struct ovsdb_monitor *dbmon,
 
     /* Return a clone of cached json if one exists. Otherwise,
      * generate a new one and add it to the cache.  */
-    cache_node = ovsdb_monitor_json_cache_search(dbmon, version, unflushed);
+    if (!condition || !condition->conditional) {
+        cache_node = ovsdb_monitor_json_cache_search(dbmon, version,
+                                                     unflushed);
+    }
     if (cache_node) {
         json = cache_node->json ? json_clone(cache_node->json) : NULL;
     } else {
         if (version == OVSDB_MONITOR_V1) {
-            json = ovsdb_monitor_compose_update(dbmon, initial, unflushed,
-                                        ovsdb_monitor_compose_row_update);
+            json =
+               ovsdb_monitor_compose_update(dbmon, initial, unflushed,
+                                            condition,
+                                            ovsdb_monitor_compose_row_update);
         } else {
             ovs_assert(version == OVSDB_MONITOR_V2);
-            json = ovsdb_monitor_compose_update(dbmon, initial, unflushed,
-                                        ovsdb_monitor_compose_row_update2);
+            json =
+               ovsdb_monitor_compose_update(dbmon, initial, unflushed,
+                                            condition,
+                                            ovsdb_monitor_compose_row_update2);
+        }
+        if (!condition || !condition->conditional) {
+            ovsdb_monitor_json_cache_insert(dbmon, version, unflushed, json);
         }
-        ovsdb_monitor_json_cache_insert(dbmon, version, unflushed, json);
     }
 
     /* Maintain transaction id of 'changes'. */
@@ -933,6 +1169,11 @@  ovsdb_monitor_changes_classify(enum ovsdb_monitor_selection type,
         return OVSDB_CHANGES_NO_EFFECT;
     }
 
+    if (type == OJMS_MODIFY) {
+        /* Condition might turn a modify operation to insert or delete */
+        type |= OJMS_INSERT | OJMS_DELETE;
+    }
+
     return (mt->select & type)
                 ?  OVSDB_CHANGES_REQUIRE_EXTERNAL_UPDATE
                 :  OVSDB_CHANGES_REQUIRE_INTERNAL_UPDATE;
diff --git a/ovsdb/monitor.h b/ovsdb/monitor.h
index 4bc2705..0c61aff 100644
--- a/ovsdb/monitor.h
+++ b/ovsdb/monitor.h
@@ -19,8 +19,11 @@ 
 
 struct ovsdb_monitor;
 struct ovsdb_jsonrpc_monitor;
+struct ovsdb_monitor_session_condition;
+struct ovsdb_condition;
 
 enum ovsdb_monitor_selection {
+    OJMS_NONE = 0,              /* None for this iteration */
     OJMS_INITIAL = 1 << 0,      /* All rows when monitor is created. */
     OJMS_INSERT = 1 << 1,       /* New rows. */
     OJMS_DELETE = 1 << 2,       /* Deleted rows. */
@@ -64,10 +67,12 @@  const char * OVS_WARN_UNUSED_RESULT
 ovsdb_monitor_table_check_duplicates(struct ovsdb_monitor *,
                           const struct ovsdb_table *);
 
-struct json *ovsdb_monitor_get_update(struct ovsdb_monitor *dbmon,
-                                      bool initial,
-                                      uint64_t *unflushed_transaction,
-                                      enum ovsdb_monitor_version version);
+struct json *ovsdb_monitor_get_update(
+               struct ovsdb_monitor *dbmon,
+               bool initial,
+               uint64_t *unflushed_transaction,
+               const struct ovsdb_monitor_session_condition *condition,
+               enum ovsdb_monitor_version version);
 
 void ovsdb_monitor_table_add_select(struct ovsdb_monitor *dbmon,
                                     const struct ovsdb_table *table,
@@ -79,4 +84,21 @@  bool ovsdb_monitor_needs_flush(struct ovsdb_monitor *dbmon,
 void ovsdb_monitor_get_initial(const struct ovsdb_monitor *dbmon);
 
 void ovsdb_monitor_get_memory_usage(struct simap *usage);
+
+struct ovsdb_monitor_session_condition *
+ovsdb_monitor_session_condition_create(void);
+
+void
+ovsdb_monitor_session_condition_destroy(
+                          struct ovsdb_monitor_session_condition *condition);
+struct ovsdb_error *
+ovsdb_monitor_table_condition_set(
+                          struct ovsdb_monitor_session_condition *condition,
+                          const struct ovsdb_table *table,
+                          const struct json *json_cnd);
+
+void
+ovsdb_monitor_condition_bind(struct ovsdb_monitor *dbmon,
+                             struct ovsdb_monitor_session_condition *cond);
+
 #endif
diff --git a/ovsdb/ovsdb-server.1.in b/ovsdb/ovsdb-server.1.in
index 6c85729..27952b6 100644
--- a/ovsdb/ovsdb-server.1.in
+++ b/ovsdb/ovsdb-server.1.in
@@ -245,31 +245,228 @@  notifications (see below) to the request, it must be unique among all
 active monitors.  \fBovsdb\-server\fR rejects attempt to create two
 monitors with the same identifier.
 .
-.IP "4.1.12. Monitor2"
-A new monitor method added in Open vSwitch version 2.5. Monitor2 allows
-for more efficient update notifications (described below).
+.IP "4.1.12. Monitor_cond"
+A new monitor method added in Open vSwitch version 2.5. The monitor_cond
+request enables a client to replicate subsets of tables within an OVSDB
+database by requesting notifications of changes to rows matching one of
+the conditions specified in "where" by receiving the specified contents
+of these rows when table updates occur. Monitor_cond also allows a more
+efficient update notifications by receiving table-updates2 notifications
+(described below).
+.
 .IP
-The monitor method described in Section 4.1.5 also applies to
-monitor2, with the following exceptions.
+The monitor method described in Section 4.1.5 also applies to monitor_cond,
+with the following exceptions:
 .
 .RS
 .IP \(bu
-RPC request method becomes "monitor2".
+RPC request method becomes "monitor_cond".
 .IP \(bu
-Replay result follows <table-updates2>, described in Section 4.1.13.
+Replay result follows <table-updates2>, described in Section 4.1.14.
 .IP \(bu
 Subsequent changes are sent to the client using the "update2" monitor
-notification, described in Section 4.1.13
+notification, described in Section 4.1.14
+.IP \(bu
+Update notifications are being sent only for rows matching [<conditions>*].
+<condition> is specified in Section 5.1 in the RFC with the following
+change: A condition can be either a 3-element JSON array as deescribed in
+the RFC or a boolean value. In case of an empty array an implicit true
+boolean value will be considered, and all rows will be monitored.
+.RE
+.
+.IP
+The request object has the following members:
+.
+.PP
+.RS
+.nf
+"method": "monitor_cond"
+"params": [<db-name>, <json-value>, <monitor-cond-requests>]
+"id": <nonnull-json-value>
+.fi
+.RE
+.
+.IP
+The <json-value> parameter is used to match subsequent update notifications
+(see below) to this request. The <monitor-cond-requests> object maps the name
+of the table to an array of <monitor-cond-request>.
+.
+.IP
+Each <monitor-cond-request> is an object with the following members:
+.
+.PP
+.RS
+.nf
+"columns": [<column>*]            optional
+"where": [<condition>*]           optional
+"select": <monitor-select>        optional
+.fi
+.RE
+.
+.IP
+The "columns", if present, define the columns within the table to be monitored
+that match conditions. If not present all columns are being monitored.
+.
+.IP
+The "where" if present is a JSON array of <condition> and boolean values. If not
+present or condition is an empty array, implicit True will be considered and
+updates on all rows will be sent. <condition> is specified in Section 5.1 in
+the RFC with the following change: A condition can be either a 3-element JSON
+array as described in the RFC or a boolean value. In case of an empty array an
+implicit true boolean value will be considered, and all rows will be monitored.
+.
+.IP
+<monitor-select> is an object with the following members:
+.
+.PP
+.RS
+.nf
+"initial": <boolean>              optional
+"insert": <boolean>               optional
+"delete": <boolean>               optional
+"modify": <boolean>               optional
+.fi
+.RE
+.
+.IP
+The contents of this object specify how the columns or table are to be
+monitored as explained in more detail below.
+.
+.IP
+The response object has the following members:
+.
+.PP
+.RS
+.nf
+"result": <table-updates2>
+"error": null
+"id": same "id" as request
+.fi
+.RE
+.
+.IP
+The <table-updates2> object is described in detail in Section 4.1.14. It
+contains the contents of the tables for which "initial" rows are selected.
+If no tables initial contents are requested, then "result" is an empty object.
+,
+.IP
+Subsequently, when changes to a specified table that match one of the conditions
+in monitor-cond-request are committed, the changes are automatically sent to the
+client using the "update2" monitor notification (see Section 4.1.14). This
+monitoring persists until the JSON-RPC session terminates or until the client
+sends a "monitor_cancel" JSON-RPC request.
+.
+.IP
+Each <monitor-cond-request> specifies one or more conditions and the manner in
+which the rows that match the conditions are to be monitored. The circumstances in
+which an "update" notification is sent for a row within the table are determined by
+<monitor-select>:
+.
+.RS
+.IP \(bu
+If "initial" is omitted or true, every row in the original table that matches one of
+the conditions is sent as part of the response to the "monitor_cond" request.
+.IP \(bu
+If "insert" is omitted or true, "update" notifications are sent for rows newly
+inserted into the table that match conditions or for rows modified in the table
+so that their old version does not match the condition and new version does.
+(new row in the client's replica table)
+.IP \(bu
+If "delete" is omitted or true, "update" notifications are sent for rows deleted
+from the table that match conditions or for rows modified in the table so that
+their old version does match the conditions and new version does not. (deleted row
+in the client's replica)
+.IP \(bu
+If "modify" is omitted or true, "update" notifications are sent whenever a row in
+the table that matches conditions in both old and new version is modified.
 .RE
 .
 .IP
-Both monitor and monitor2 sessions can exist concurrently. However,
-monitor and monitor2 shares the same <json-value> parameter space; it
-must be unique among all monitor and monitor2 sessions.
+Both monitor and monitor_cond sessions can exist concurrently. However,
+monitor and monitor_cond shares the same <json-value> parameter space; it
+must be unique among all monitor and monitor_cond sessions.
+.
+.IP "4.1.13. Monitor_cond_update"
+The "monitor_cond_update" request enables a client to change an existing
+"monitor_cond" replication of the database by specifying a new condition
+and columns for each replicated table. Currently changing the columns set
+is not supported.
+.
+.IP
+The request object has the following members:
+.
+.IP
+.RS
+.nf
+"method": "monitor_cond_update"
+"params": [<json-value>, <json-value>, <monitor-cond-update-requests>]
+"id": <nonnull-json-value>
+.fi
+.RE
+.
+.IP
+The <json-value> parameter should have a value of an existing conditional
+monitoring session from this client. The second <json-value> in params array
+is the requested value for this session. This value is valid only after
+"monitor_cond_update" is committed. A user can use these values to distinguish
+between update messages before conditions update and after. The
+<monitor-cond-update-requests> object maps the name of the table to an array of
+<monitor-cond-update-request>.
+.
+.IP
+Each <monitor-cond-update-request> is an object with the following members:
+.
+.IP
+.RS
+.nf
+"columns": [<column>*]         optional
+"where": [<condition>*]        optional
+.fi
+.RE
+.
+.IP
+The "columns" specify a new array of columns to be monitored
+(Currently unsupported).
+.
+.IP
+The "where" specify a new array of conditions to be applied to this monitoring
+session.
+.
+.IP
+<condition> is specified in Section 5.1 in the RFC with the following change:
+A condition can be either a 3-element JSON array as described in the RFC or a
+boolean value. In case of an empty array an implicit true boolean value will be
+considered, and all rows will be monitored.
+.
+.IP
+The response object has the following members:
+.
+.IP
+.RS
+.nf
+"result": null
+"error": null
+"id": same "id" as request
+.fi
+.RE
+.IP
+Subsequent <table-updates2> notifications are described in detail in Section
+4.1.14 in the RFC. If insert contents are requested by origin monitor_cond
+request, <table-updates2> will contain rows that match the new condition and
+do not match the old condition.
+If deleted contents are requested by origin monitor request, <table-updates2>
+will contain any matched rows by old condition and not matched by the new
+condition.
+.
+.IP
+Changes according to the new conditions are automatically sent to the client
+using the "update2" monitor notification. Updates as a result of a condition
+change, will be sent only after the client received a response to the
+"monitor_cond_update" request.
 .
-.IP "4.1.13. Update2 notification"
+.IP "4.1.14. Update2 notification"
 The "update2" notification is sent by the server to the client to report
-changes in tables that are being monitored following a "monitor2" request
+changes in tables that are being monitored following a "monitor_cond" request
 as described above. The notification has the following members:
 .
 .RS
@@ -284,7 +481,8 @@  as described above. The notification has the following members:
 The <json-value> in "params" is the same as the value passed as the
 <json-value>  in "params" for the corresponding "monitor" request.
 <table-updates2> is an object that maps from a table name to a <table-update2>.
-A <table-update2> is an object that maps from row's UUID to a <row-update2> object. A <row-update2> is an object with one of the following members:
+A <table-update2> is an object that maps from row's UUID to a <row-update2>
+object. A <row-update2> is an object with one of the following members:
 .
 .RS
 .IP "\(dqinitial\(dq: <row>"
@@ -326,8 +524,8 @@  elements, <row> includes the value from the new column.
 .
 .IP
 Initial views of rows are not presented in update2 notifications,
-but in the response object to the monitor2 request. The formatting of the
-<table-updates2> object, however, is the same in either case.
+but in the response object to the monitor_cond request. The formatting
+of the <table-updates2> object, however, is the same in either case.
 .
 .IP "5.1. Notation"
 For <condition>, RFC 7047 only allows the use of \fB!=\fR, \fB==\fR,