Browse Source

Merge "Add retry for DBDeadlock in credential delete" into stable/stein

stable/stein
Zuul 2 weeks ago
parent
commit
918aa07eed

+ 3
- 0
keystone/credential/backends/sql.py View File

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

+ 34
- 0
keystone/tests/unit/test_v3_credential.py View File

@@ -17,6 +17,8 @@ import json
17 17
 import uuid
18 18
 
19 19
 from keystoneclient.contrib.ec2 import utils as ec2_utils
20
+import mock
21
+from oslo_db import exception as oslo_db_exception
20 22
 from six.moves import http_client
21 23
 from testtools import matchers
22 24
 
@@ -262,6 +264,38 @@ class CredentialTestCase(CredentialBaseTestCase):
262 264
             '/credentials/%(credential_id)s' % {
263 265
                 'credential_id': self.credential['id']})
264 266
 
267
+    def test_delete_credential_retries_on_deadlock(self):
268
+        patcher = mock.patch('sqlalchemy.orm.query.Query.delete',
269
+                             autospec=True)
270
+
271
+        class FakeDeadlock(object):
272
+            def __init__(self, mock_patcher):
273
+                self.deadlock_count = 2
274
+                self.mock_patcher = mock_patcher
275
+                self.patched = True
276
+
277
+            def __call__(self, *args, **kwargs):
278
+                if self.deadlock_count > 1:
279
+                    self.deadlock_count -= 1
280
+                else:
281
+                    self.mock_patcher.stop()
282
+                    self.patched = False
283
+                raise oslo_db_exception.DBDeadlock
284
+
285
+        sql_delete_mock = patcher.start()
286
+        side_effect = FakeDeadlock(patcher)
287
+        sql_delete_mock.side_effect = side_effect
288
+
289
+        try:
290
+            PROVIDERS.credential_api.delete_credentials_for_user(
291
+                user_id=self.user['id'])
292
+        finally:
293
+            if side_effect.patched:
294
+                patcher.stop()
295
+
296
+        # initial attempt + 1 retry
297
+        self.assertEqual(sql_delete_mock.call_count, 2)
298
+
265 299
     def test_create_ec2_credential(self):
266 300
         """Call ``POST /credentials`` for creating ec2 credential."""
267 301
         blob, ref = unit.new_ec2_credential(user_id=self.user['id'],

+ 6
- 0
releasenotes/notes/bug-1840291-35af1ac7ba06e166.yaml View File

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

Loading…
Cancel
Save