Fix node update with PostgreSQL

In the node traits RPC object change [1], the _do_update_node method in
the sqlalchemy DB API was modified to use a select query joined with the
node_tags and node_traits tables in the node update DB operation. This
was done to ensure that the returned NodeModel had its tags and traits
attributes populated rather than lazy loaded. This was found not to work
on PostgreSQL, which presents the following error message [2]:

Exception during message handling: DBNotSupportedError:
(psycopg2.NotSupportedError) FOR UPDATE cannot be applied to the
nullable side of an outer join

This issue is resolved in this commit by reverting to the original query
without joins for the node update, and performing a joined query after
the update.

[1] https://review.openstack.org/#/c/532268/13
[2]
http://logs.openstack.org/69/532269/22/check/ironic-tempest-dsvm-pxe_ipmitool-postgres/562b2ea/logs/screen-ir-cond.txt.gz?level=ERROR#_Jan_23_23_59_05_062967

Change-Id: Ib0d49f7e781f1aa0c4496957b00f6c8e3da0ecc0
Related-Bug: #1722194
This commit is contained in:
Mark Goddard 2018-01-24 20:11:59 +00:00
parent 6a1a8efb5f
commit d8a291a1e1
2 changed files with 32 additions and 2 deletions

View File

@ -463,7 +463,9 @@ class Connection(api.Connection):
@oslo_db_api.retry_on_deadlock
def _do_update_node(self, node_id, values):
with _session_for_write():
query = _get_node_query_with_all()
# NOTE(mgoddard): Don't issue a joined query for the update as this
# does not work with PostgreSQL.
query = model_query(models.Node)
query = add_identity_filter(query, node_id)
try:
ref = query.with_lockmode('update').one()
@ -484,7 +486,11 @@ class Connection(api.Connection):
values['inspection_started_at'] = None
ref.update(values)
return ref
# Return the updated node model joined with all relevant fields.
query = _get_node_query_with_all()
query = add_identity_filter(query, node_id)
return query.one()
def get_port_by_id(self, port_id):
query = model_query(models.Port).filter_by(id=port_id)

View File

@ -450,6 +450,30 @@ class DbNodeTestCase(base.DbTestCase):
res = self.dbapi.update_node(node.id, {'extra': new_extra})
self.assertEqual(new_extra, res.extra)
self.assertEqual([], res.tags)
self.assertEqual([], res.traits)
def test_update_node_with_tags(self):
node = utils.create_test_node()
tag = utils.create_test_node_tag(node_id=node.id)
old_extra = node.extra
new_extra = {'foo': 'bar'}
self.assertNotEqual(old_extra, new_extra)
res = self.dbapi.update_node(node.id, {'extra': new_extra})
self.assertEqual([tag.tag], [t.tag for t in res.tags])
def test_update_node_with_traits(self):
node = utils.create_test_node()
trait = utils.create_test_node_trait(node_id=node.id)
old_extra = node.extra
new_extra = {'foo': 'bar'}
self.assertNotEqual(old_extra, new_extra)
res = self.dbapi.update_node(node.id, {'extra': new_extra})
self.assertEqual([trait.trait], [t.trait for t in res.traits])
def test_update_node_not_found(self):
node_uuid = uuidutils.generate_uuid()