Retry revocation on MySQL deadlock

Added wrapper for revoke method that makes additional attempts on
deadlock.

Change-Id: I506c6b432d4d23619237a32414d0039c749cd8b6
Closes-Bug: #1604862
This commit is contained in:
Mikhail Nikolaenko 2016-07-20 15:56:53 +00:00
parent 96af93d361
commit e8022f3d31
2 changed files with 43 additions and 0 deletions

View File

@ -14,6 +14,8 @@ from keystone.common import sql
from keystone.models import revoke_model
from keystone.revoke.backends import base
from oslo_db import api as oslo_db_api
class RevocationEvent(sql.ModelBase, sql.ModelDictMixin):
__tablename__ = 'revocation_event'
@ -90,6 +92,7 @@ class Revoke(base.RevokeDriverV8):
return events
@oslo_db_api.wrap_db_retry(retry_on_deadlock=True)
def revoke(self, event):
kwargs = dict()
for attr in revoke_model.REVOKE_KEYS:

View File

@ -11,9 +11,11 @@
# under the License.
import datetime
import mock
import uuid
import freezegun
from oslo_db import exception as oslo_db_exception
from oslo_utils import timeutils
import six
from six.moves import http_client
@ -165,3 +167,41 @@ class OSRevokeTests(test_v3.RestfulTestCase, test_v3.JsonHomeTestMixin):
# `OS-OAUTH1:access_token_id` is None and won't be returned to
# end user.
self.assertNotIn('OS-OAUTH1:access_token_id', event)
def test_retries_on_deadlock(self):
patcher = mock.patch('sqlalchemy.orm.query.Query.delete',
autospec=True)
# NOTE(mnikolaenko): raise 2 deadlocks and back to normal work of
# method. Two attempts is enough to check that retry decorator works.
# Otherwise it will take very much time to pass this test
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:
self.revoke_api.revoke(revoke_model.RevokeEvent(
user_id=uuid.uuid4().hex))
finally:
if side_effect.patched:
patcher.stop()
call_count = sql_delete_mock.call_count
# initial attempt + 1 retry
revoke_attempt_count = 2
self.assertEqual(call_count, revoke_attempt_count)