diff --git a/CHANGELOG.md b/CHANGELOG.md index 3dd29a2d..8612a734 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed -* +* Identifier generation (`DataAdapter._id`) no longer leaks the increment lock, which deadlocked inserts from background threads (e.g. scheduler jobs over the tiny adapter) - [#86](https://github.com/hivesolutions/appier/issues/86) ## [1.46.0] - 2026-05-31 diff --git a/src/appier/data.py b/src/appier/data.py index b0bec2fb..ef01fb15 100644 --- a/src/appier/data.py +++ b/src/appier/data.py @@ -105,7 +105,7 @@ def _id(self): try: token += struct.pack(">i", self._inc)[1:4] self._inc = (self._inc + 1) % 0xFFFFFF - except Exception: + finally: self._inc_lock.release() token_s = binascii.hexlify(token) token_s = legacy.str(token_s) diff --git a/src/appier/test/data.py b/src/appier/test/data.py index 798d47c2..b88e4260 100644 --- a/src/appier/test/data.py +++ b/src/appier/test/data.py @@ -30,6 +30,7 @@ import os import tempfile +import threading import unittest import appier @@ -43,6 +44,24 @@ def test_id(self): self.assertEqual(type(identifier), str) self.assertEqual(len(identifier), 24) + def test_id_lock_release(self): + adapter = appier.DataAdapter() + adapter._id() + + result = [] + + def acquire(): + acquired = adapter._inc_lock.acquire(False) + if acquired: + adapter._inc_lock.release() + result.append(acquired) + + thread = threading.Thread(target=acquire) + thread.start() + thread.join() + + self.assertEqual(result, [True]) + def test_drop_db_missing(self): fd, file_path = tempfile.mkstemp() os.close(fd)