From 7d124a2b1ba92fbba6dcc727721f6ff5093bb68d Mon Sep 17 00:00:00 2001 From: Martin Hauser Date: Mon, 7 Sep 2026 21:43:21 +0200 Subject: [PATCH] test(extras): Release the data lock explicitly Closing the helper's connection does not wait for PostgreSQL to release its session advisory lock, so the deletion which follows could still see the lock held and abort. Release it with pg_advisory_unlock() instead. Fixes #23145 --- netbox/extras/tests/test_customfields.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/netbox/extras/tests/test_customfields.py b/netbox/extras/tests/test_customfields.py index cb8308eaf..a4aee836e 100644 --- a/netbox/extras/tests/test_customfields.py +++ b/netbox/extras/tests/test_customfields.py @@ -2876,9 +2876,18 @@ def hold_data_lock(custom_field): cursor.execute('SELECT pg_try_advisory_lock(%s, %s)', lock_key) if not cursor.fetchone()[0]: raise RuntimeError(f"Failed to acquire the data lock for {custom_field}") - yield + released = False + try: + yield + finally: + # Closing the connection releases the lock asynchronously, so the next deletion can race it + with connection.cursor() as cursor: + cursor.execute('SELECT pg_advisory_unlock(%s, %s)', lock_key) + released = cursor.fetchone()[0] + # Outside the finally, so a failing body is reported as itself + if not released: + raise RuntimeError(f"Failed to release the data lock for {custom_field}") finally: - # Closing the session releases any advisory lock held on it connection.close() @@ -3739,7 +3748,7 @@ class DeferredCustomFieldDataTestCase(TestCase): # delete() has returned and its own atomic block has exited, but the enclosing transaction # has yet to commit, so the lock must still be held - with self.assertRaises(RuntimeError): + with self.assertRaisesMessage(RuntimeError, "Failed to acquire the data lock"): with hold_data_lock(cf): pass