Don't use nested transaction in provisioning blocks

Relying on duplicate entries for existing provisioning
block detection led to lots of log noise whenever we
would encounter a deadlock because the savepoint would
go missing. Additionally, since this would happen inside
of another transaction, we couldn't be guaranteed until
the final commit that this was unique anyway so the whole
transaction could throw a duplicate exception.

Since the outer transaction has to handle duplicate exceptions
anyway, this patch just adjusts the logic to check for the
existing of a record first with a regular query. If there
is a race the outer transaction will still fail with a duplicate
as before, but we at least won't have a bunch of missing savepoint
exception noise.

Closes-Bug: #1613759
Change-Id: I02d30427505591e24d7bdc1ba654d3757ab9a33d
This commit is contained in:
Kevin Benton 2016-09-07 17:11:06 -07:00
parent dc6508aae2
commit 7b4350a4cb
1 changed files with 8 additions and 9 deletions

View File

@ -14,14 +14,12 @@
#
from neutron_lib.db import model_base
from oslo_db import exception as db_exc
from oslo_log import log as logging
import sqlalchemy as sa
from neutron._i18n import _LE
from neutron.callbacks import registry
from neutron.callbacks import resources
from neutron.db import api as db_api
from neutron.db import models_v2
from neutron.db import standard_attr
@ -56,7 +54,7 @@ def add_provisioning_component(context, object_id, object_type, entity):
of the entity that is doing the provisioning. While an object has these
provisioning blocks present, this module will not emit any callback events
indicating that provisioning has completed. Any logic that depends on
multiple disjoint components use these blocks and subscribe to the
multiple disjoint components may use these blocks and subscribe to the
PROVISIONING_COMPLETE event to know when all components have completed.
:param context: neutron api request context
@ -69,17 +67,18 @@ def add_provisioning_component(context, object_id, object_type, entity):
standard_attr_id = _get_standard_attr_id(context, object_id, object_type)
if not standard_attr_id:
return
try:
with db_api.autonested_transaction(context.session):
record = ProvisioningBlock(standard_attr_id=standard_attr_id,
entity=entity)
context.session.add(record)
except db_exc.DBDuplicateEntry:
record = context.session.query(ProvisioningBlock).filter_by(
standard_attr_id=standard_attr_id, entity=entity).first()
if record:
# an entry could be leftover from a previous transition that hasn't
# yet been provisioned. (e.g. multiple updates in a short period)
LOG.debug("Ignored duplicate provisioning block setup for %(otype)s "
"%(oid)s by entity %(entity)s.", log_dict)
return
with context.session.begin(subtransactions=True):
record = ProvisioningBlock(standard_attr_id=standard_attr_id,
entity=entity)
context.session.add(record)
LOG.debug("Transition to ACTIVE for %(otype)s object %(oid)s "
"will not be triggered until provisioned by entity %(entity)s.",
log_dict)