[WEB-4440] fix: duplicate sequence when creating multiple workitems in rapid succession (#8298)

- Replace advisory lock with transaction-level lock in Issue model save method
- Updated the save method in the Issue model to use a transaction-level advisory lock for better concurrency control.
- Simplified the locking mechanism by removing the explicit unlock step, as the lock is automatically released at the end of the transaction.
- Maintained existing functionality for sequence and sort order management while improving code clarity.
This commit is contained in:
Dheeraj Kumar Ketireddy 2025-12-10 23:20:41 +05:30 committed by GitHub
parent e20f686398
commit 647813a6ab
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -207,15 +207,15 @@ class Issue(ProjectBaseModel):
if self._state.adding:
with transaction.atomic():
# Create a lock for this specific project using an advisory lock
# Create a lock for this specific project using a transaction-level advisory lock
# This ensures only one transaction per project can execute this code at a time
# The lock is automatically released when the transaction ends
lock_key = convert_uuid_to_integer(self.project.id)
with connection.cursor() as cursor:
# Get an exclusive lock using the project ID as the lock key
cursor.execute("SELECT pg_advisory_lock(%s)", [lock_key])
# Get an exclusive transaction-level lock using the project ID as the lock key
cursor.execute("SELECT pg_advisory_xact_lock(%s)", [lock_key])
try:
# Get the last sequence for the project
last_sequence = IssueSequence.objects.filter(project=self.project).aggregate(
largest=models.Max("sequence")
@ -236,10 +236,6 @@ class Issue(ProjectBaseModel):
super(Issue, self).save(*args, **kwargs)
IssueSequence.objects.create(issue=self, sequence=self.sequence_id, project=self.project)
finally:
# Release the lock
with connection.cursor() as cursor:
cursor.execute("SELECT pg_advisory_unlock(%s)", [lock_key])
else:
# Strip the html tags using html parser
self.description_stripped = (