Add retry for DBDeadlock in credential delete

Adds oslo.db retry wrapper to delete_credential_for_user method.

Change-Id: Ib9e161411f0985785eec46c51d721ef7421ee090
Closes-Bug: #1840291
This commit is contained in:
Rabi Mishra 2019-08-15 21:23:46 +05:30
parent 5beddfaddb
commit e989bd0637
3 changed files with 43 additions and 0 deletions

View File

@ -12,6 +12,8 @@
# License for the specific language governing permissions and limitations
# under the License.
from oslo_db import api as oslo_db_api
from keystone.common import driver_hints
from keystone.common import sql
from keystone.credential.backends import base
@ -96,6 +98,7 @@ class Credential(base.CredentialDriverBase):
query = query.filter_by(project_id=project_id)
query.delete()
@oslo_db_api.wrap_db_retry(retry_on_deadlock=True)
def delete_credentials_for_user(self, user_id):
with sql.session_for_write() as session:
query = session.query(CredentialModel)

View File

@ -17,6 +17,8 @@ import json
import uuid
from keystoneclient.contrib.ec2 import utils as ec2_utils
import mock
from oslo_db import exception as oslo_db_exception
from six.moves import http_client
from testtools import matchers
@ -262,6 +264,38 @@ class CredentialTestCase(CredentialBaseTestCase):
'/credentials/%(credential_id)s' % {
'credential_id': self.credential['id']})
def test_delete_credential_retries_on_deadlock(self):
patcher = mock.patch('sqlalchemy.orm.query.Query.delete',
autospec=True)
class FakeDeadlock(object):
def __init__(self, mock_patcher):
self.deadlock_count = 2
self.mock_patcher = mock_patcher
self.patched = True
def __call__(self, *args, **kwargs):
if self.deadlock_count > 1:
self.deadlock_count -= 1
else:
self.mock_patcher.stop()
self.patched = False
raise oslo_db_exception.DBDeadlock
sql_delete_mock = patcher.start()
side_effect = FakeDeadlock(patcher)
sql_delete_mock.side_effect = side_effect
try:
PROVIDERS.credential_api.delete_credentials_for_user(
user_id=self.user['id'])
finally:
if side_effect.patched:
patcher.stop()
# initial attempt + 1 retry
self.assertEqual(sql_delete_mock.call_count, 2)
def test_create_ec2_credential(self):
"""Call ``POST /credentials`` for creating ec2 credential."""
blob, ref = unit.new_ec2_credential(user_id=self.user['id'],

View File

@ -0,0 +1,6 @@
---
fixes:
- |
[`bug 1840291 <https://bugs.launchpad.net/keystone/+bug/1840291>`_]
Adds retries for ``delete_credential_for_user`` method to avoid
DBDeadlocks when deleting large number of credentials concurrently.