Mark quota operations as retriable

This decorates the quota system operations with
the retry decorators. This will help significantly
with the bug this marks as closed since operations
in the quota engine after commit should no longer
trigger retries of the full API operation.

The logic to find the args in the decorator had
to be adjusted to deal with functions already decorated.
This just uses the getargspec function from pecan that
deals with decorated functions.

Partial-Bug: #1612798
Closes-Bug: #1596075
Change-Id: Ib786117dcea08af75551770ea4c30d460382b829
This commit is contained in:
Kevin Benton 2016-09-07 18:51:58 -07:00
parent ad13bdfa27
commit 12420c1585
4 changed files with 23 additions and 3 deletions

View File

@ -26,6 +26,7 @@ from oslo_db.sqlalchemy import enginefacade
from oslo_log import log as logging
from oslo_utils import excutils
import osprofiler.sqlalchemy
from pecan import util as p_util
import six
import sqlalchemy
from sqlalchemy.orm import exc
@ -137,7 +138,10 @@ def retry_if_session_inactive(context_var_name='context'):
"""
def decorator(f):
try:
ctx_arg_index = f.__code__.co_varnames.index(context_var_name)
# NOTE(kevinbenton): we use pecan's util function here because it
# deals with the horrors of finding args of already decorated
# functions
ctx_arg_index = p_util.getargspec(f).args.index(context_var_name)
except ValueError:
raise RuntimeError(_LE("Could not find position of var %s")
% context_var_name)

View File

@ -40,6 +40,7 @@ class ReservationInfo(collections.namedtuple(
"""Information about a resource reservation."""
@db_api.retry_if_session_inactive()
def get_quota_usage_by_resource_and_tenant(context, resource, tenant_id,
lock_for_update=False):
"""Return usage info for a given resource and tenant.
@ -66,6 +67,7 @@ def get_quota_usage_by_resource_and_tenant(context, resource, tenant_id,
result.dirty)
@db_api.retry_if_session_inactive()
def get_quota_usage_by_resource(context, resource):
query = common_db_api.model_query(context, quota_models.QuotaUsage)
query = query.filter_by(resource=resource)
@ -75,6 +77,7 @@ def get_quota_usage_by_resource(context, resource):
item.dirty) for item in query]
@db_api.retry_if_session_inactive()
def get_quota_usage_by_tenant_id(context, tenant_id):
query = common_db_api.model_query(context, quota_models.QuotaUsage)
query = query.filter_by(tenant_id=tenant_id)
@ -84,6 +87,7 @@ def get_quota_usage_by_tenant_id(context, tenant_id):
item.dirty) for item in query]
@db_api.retry_if_session_inactive()
def set_quota_usage(context, resource, tenant_id,
in_use=None, delta=False):
"""Set resource quota usage.
@ -121,6 +125,7 @@ def set_quota_usage(context, resource, tenant_id,
usage_data.dirty)
@db_api.retry_if_session_inactive()
@db_api.context_manager.writer
def set_quota_usage_dirty(context, resource, tenant_id, dirty=True):
"""Set quota usage dirty bit for a given resource and tenant.
@ -135,6 +140,7 @@ def set_quota_usage_dirty(context, resource, tenant_id, dirty=True):
return query.update({'dirty': dirty})
@db_api.retry_if_session_inactive()
@db_api.context_manager.writer
def set_resources_quota_usage_dirty(context, resources, tenant_id, dirty=True):
"""Set quota usage dirty bit for a given tenant and multiple resources.
@ -153,6 +159,7 @@ def set_resources_quota_usage_dirty(context, resources, tenant_id, dirty=True):
return query.update({'dirty': dirty}, synchronize_session=False)
@db_api.retry_if_session_inactive()
@db_api.context_manager.writer
def set_all_quota_usage_dirty(context, resource, dirty=True):
"""Set the dirty bit on quota usage for all tenants.
@ -166,6 +173,7 @@ def set_all_quota_usage_dirty(context, resource, dirty=True):
return query.update({'dirty': dirty})
@db_api.retry_if_session_inactive()
def create_reservation(context, tenant_id, deltas, expiration=None):
# This method is usually called from within another transaction.
# Consider using begin_nested
@ -186,6 +194,7 @@ def create_reservation(context, tenant_id, deltas, expiration=None):
for delta in resv.resource_deltas))
@db_api.retry_if_session_inactive()
def get_reservation(context, reservation_id):
query = context.session.query(quota_models.Reservation).filter_by(
id=reservation_id)
@ -199,6 +208,7 @@ def get_reservation(context, reservation_id):
for delta in resv.resource_deltas))
@db_api.retry_if_session_inactive()
@db_api.context_manager.writer
def remove_reservation(context, reservation_id, set_dirty=False):
delete_query = context.session.query(quota_models.Reservation).filter_by(
@ -220,6 +230,7 @@ def remove_reservation(context, reservation_id, set_dirty=False):
return num_deleted
@db_api.retry_if_session_inactive()
def get_reservations_for_resources(context, tenant_id, resources,
expired=False):
"""Retrieve total amount of reservations for specified resources.
@ -254,6 +265,7 @@ def get_reservations_for_resources(context, tenant_id, resources,
for (resource, exp, total_reserved) in resv_query)
@db_api.retry_if_session_inactive()
@db_api.context_manager.writer
def remove_expired_reservations(context, tenant_id=None):
now = utcnow()

View File

@ -49,6 +49,7 @@ class DbQuotaDriver(object):
for key, resource in resources.items())
@staticmethod
@db_api.retry_if_session_inactive()
def get_tenant_quotas(context, resources, tenant_id):
"""Given a list of resources, retrieve the quotas for the given
tenant. If no limits are found for the specified tenant, the operation
@ -73,6 +74,7 @@ class DbQuotaDriver(object):
return tenant_quota
@staticmethod
@db_api.retry_if_session_inactive()
def delete_tenant_quota(context, tenant_id):
"""Delete the quota entries for a given tenant_id.
@ -88,6 +90,7 @@ class DbQuotaDriver(object):
raise n_exc.TenantQuotaNotFound(tenant_id=tenant_id)
@staticmethod
@db_api.retry_if_session_inactive()
def get_all_quotas(context, resources):
"""Given a list of resources, retrieve the quotas for the all tenants.
@ -119,6 +122,7 @@ class DbQuotaDriver(object):
return list(all_tenant_quotas.values())
@staticmethod
@db_api.retry_if_session_inactive()
def update_quota_limit(context, tenant_id, resource, limit):
with context.session.begin():
tenant_quota = context.session.query(quota_models.Quota).filter_by(
@ -156,7 +160,7 @@ class DbQuotaDriver(object):
quota_api.remove_expired_reservations(
context, tenant_id=tenant_id)
@db_api.retry_db_errors
@db_api.retry_if_session_inactive()
def make_reservation(self, context, tenant_id, resources, deltas, plugin):
# Lock current reservation table
# NOTE(salv-orlando): This routine uses DB write locks.

View File

@ -207,7 +207,7 @@ class TrackedResource(BaseResource):
# can happen is two or more workers are trying to create a resource of a
# give kind for the same tenant concurrently. Retrying the operation will
# ensure that an UPDATE statement is emitted rather than an INSERT one
@db_api.retry_db_errors
@db_api.retry_if_session_inactive()
def _set_quota_usage(self, context, tenant_id, in_use):
return quota_api.set_quota_usage(
context, self.name, tenant_id, in_use=in_use)