Hi Terry,

Thanks for the patch, comments below.

On 3/3/21 9:24 AM, Terry Wilson wrote:
The Python IDL notification mechanism was sending a notification
for each processed update in a transaction as it was processed.
This causes isseues with multi-row change that contain references
to each other.

For example, if a Logical_Router_Port is created along with a
Gateway_Chassis, and the LRP.gateway_chassis set to that GC, then
when the notify() passes the CREATE event for the LRP, the GC will
not yet have been processed, so __getattr__ when _uuid_to_row fails
to find the GC, will return the default value for LRP.gateway_chassis
which is [].

This patch has the process_update methods return the notifications
that would be produced when a row changes, so they can be queued
and sent after all rows have been processed.

Fixes: d7d417fcddf9 ("Allow subclasses of Idl to define a notification hook")
Signed-off-by: Terry Wilson <[email protected]>
---
  python/ovs/db/idl.py | 41 ++++++++++++++++++++++++-----------------
  tests/ovsdb-idl.at   | 22 ++++++++++++++++++++++
  tests/test-ovsdb.py  |  7 +++++--
  3 files changed, 51 insertions(+), 19 deletions(-)

diff --git a/python/ovs/db/idl.py b/python/ovs/db/idl.py
index 5850ac7ab..4b441bd85 100644
--- a/python/ovs/db/idl.py
+++ b/python/ovs/db/idl.py
<snip>
      def __process_update2(self, table, uuid, row_update):
+        """Returns Notice if a column changed, False otherwise."""
          row = table.rows.get(uuid)
-        changed = False
          if "delete" in row_update:
              if row:
                  del table.rows[uuid]
-                self.notify(ROW_DELETE, row)
-                changed = True
+                return Notice(ROW_DELETE, row)
              else:
                  # XXX rate-limit
                  vlog.warn("cannot delete missing row %s from table"
@@ -680,30 +691,27 @@ class Idl(object):
              self.__add_default(table, row_update)
              changed = self.__row_update(table, row, row_update)
              table.rows[uuid] = row
-            if changed:
-                self.notify(ROW_CREATE, row)
+            return Notice(ROW_CREATE, row) if changed else False

I think this should still be under the 'if changed' since it's based on the return value of the self.__row_update() call. See below.

          elif "modify" in row_update:
              if not row:
                  raise error.Error('Modify non-existing row')
old_row = self.__apply_diff(table, row, row_update['modify'])
-            self.notify(ROW_UPDATE, row, Row(self, table, uuid, old_row))
-            changed = True
+            return Notice(ROW_UPDATE, row, Row(self, table, uuid, old_row))
          else:
              raise error.Error('<row-update> unknown operation',
                                row_update)
-        return changed
+        return False
def __process_update(self, table, uuid, old, new):
-        """Returns True if a column changed, False otherwise."""
+        """Returns Notice if a column changed, False otherwise."""
          row = table.rows.get(uuid)
          changed = False
          if not new:
              # Delete row.
              if row:
                  del table.rows[uuid]
-                changed = True
-                self.notify(ROW_DELETE, row)
+                return Notice(ROW_DELETE, row)
              else:
                  # XXX rate-limit
                  vlog.warn("cannot delete missing row %s from table %s"
@@ -722,8 +730,7 @@ class Idl(object):
              changed |= self.__row_update(table, row, new)
              if op == ROW_CREATE:
                  table.rows[uuid] = row
-            if changed:
-                self.notify(ROW_CREATE, row)
+            return Notice(ROW_CREATE, row) if changed else False
          else:
              op = ROW_UPDATE
              if not row:
@@ -737,8 +744,8 @@ class Idl(object):
              if op == ROW_CREATE:
                  table.rows[uuid] = row
              if changed:
-                self.notify(op, row, Row.from_json(self, table, uuid, old))
-        return changed
+                return Notice(op, row, Row.from_json(self, table, uuid, old))

This is a similar call as above, but the return stayed under the 'if changed' block.

-Brian
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to