Do not fail when releasing a quota reservation
The quota reservation release (deletion) is done at the end of a server call to create a new resource (or resources). The quota drivers implemented have mechanisms to clean up the expired reservations when calculating the resource quota. If a reservation deletion fails, the API call should not be retried. Instead of this, the reservation should be left and collected by the quota driver clean up mechanisms. This patch also adds a timeout delay for expired reservations in ``DbQuotaNoLockDriver``. Now this driver deletes any existing reservation created ``RESERVATION_EXPIRATION_TIMEOUT = 20`` (seconds) before. It is assumed that any reservation should be released before this time (regardless if the resource is created or not). Closes-Bug: #1940311 Change-Id: I155c401ec5e2fe6e3af6390855852764ee983cf5
This commit is contained in:
parent
668b1cc652
commit
1146a4d091
@ -17,10 +17,15 @@ import collections
|
||||
import datetime
|
||||
|
||||
from neutron_lib.db import api as db_api
|
||||
from oslo_db import exception as db_exc
|
||||
|
||||
from neutron.common import utils
|
||||
from neutron.objects import quota as quota_obj
|
||||
|
||||
|
||||
RESERVATION_EXPIRATION_TIMEOUT = 20 # seconds
|
||||
|
||||
|
||||
# Wrapper for utcnow - needed for mocking it in unit tests
|
||||
def utcnow():
|
||||
return datetime.datetime.utcnow()
|
||||
@ -197,7 +202,7 @@ def get_reservation(context, reservation_id):
|
||||
for delta in reserv_obj.resource_deltas))
|
||||
|
||||
|
||||
@db_api.retry_if_session_inactive()
|
||||
@utils.skip_exceptions(db_exc.DBError)
|
||||
@db_api.CONTEXT_WRITER
|
||||
def remove_reservation(context, reservation_id, set_dirty=False):
|
||||
reservation = quota_obj.Reservation.get_object(context, id=reservation_id)
|
||||
@ -236,8 +241,12 @@ def get_reservations_for_resources(context, tenant_id, resources,
|
||||
|
||||
@db_api.retry_if_session_inactive()
|
||||
@db_api.CONTEXT_WRITER
|
||||
def remove_expired_reservations(context, tenant_id=None):
|
||||
return quota_obj.Reservation.delete_expired(context, utcnow(), tenant_id)
|
||||
def remove_expired_reservations(context, tenant_id=None, timeout=None):
|
||||
expiring_time = utcnow()
|
||||
if timeout:
|
||||
expiring_time -= datetime.timedelta(seconds=timeout)
|
||||
return quota_obj.Reservation.delete_expired(context, expiring_time,
|
||||
tenant_id)
|
||||
|
||||
|
||||
class QuotaDriverAPI(object, metaclass=abc.ABCMeta):
|
||||
|
@ -53,8 +53,9 @@ class DbQuotaNoLockDriver(quota_driver.DbQuotaDriver):
|
||||
# Delete expired reservations before counting valid ones. This
|
||||
# operation is fast and by calling it before making any
|
||||
# reservation, we ensure the freshness of the reservations.
|
||||
quota_api.remove_expired_reservations(context,
|
||||
tenant_id=project_id)
|
||||
quota_api.remove_expired_reservations(
|
||||
context, tenant_id=project_id,
|
||||
timeout=quota_api.RESERVATION_EXPIRATION_TIMEOUT)
|
||||
|
||||
# Count the number of (1) used and (2) reserved resources for this
|
||||
# project_id. If any resource limit is exceeded, raise exception.
|
||||
|
@ -70,7 +70,7 @@ class Reservation(base.NeutronDbObject):
|
||||
self.obj_reset_changes(['resource_deltas'])
|
||||
|
||||
@classmethod
|
||||
def delete_expired(cls, context, now, project_id):
|
||||
def delete_expired(cls, context, expiring_time, project_id):
|
||||
resv_query = context.session.query(models.Reservation)
|
||||
if project_id:
|
||||
project_expr = (models.Reservation.project_id == project_id)
|
||||
@ -80,7 +80,7 @@ class Reservation(base.NeutronDbObject):
|
||||
# object/db/api.py once comparison operations are
|
||||
# supported
|
||||
resv_query = resv_query.filter(sa.and_(
|
||||
project_expr, models.Reservation.expiration < now))
|
||||
project_expr, models.Reservation.expiration < expiring_time))
|
||||
return resv_query.delete()
|
||||
|
||||
@classmethod
|
||||
|
Loading…
x
Reference in New Issue
Block a user