created a generate token handler#3522
created a generate token handler#3522khavinshankar wants to merge 1 commit intoohcnetwork:developfrom
Conversation
📝 WalkthroughWalkthroughThis pull request extracts token generation logic into a dedicated classmethod Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@care/emr/api/viewsets/scheduling/booking.py`:
- Around line 297-315: The code checks TokenQueue.exists() and then creates a
system-generated queue outside the lock, causing a race where two requests can
both create primary queues; replace the separate exists()/create() flow with an
atomic TokenQueue.objects.get_or_create(...) call using the same filters
(facility, resource, date, system_generated=True, name="System Generated") and
supply defaults including is_primary=True only when creating, or wrap the
get_or_create inside the existing lock/transaction.atomic to ensure only one
primary queue is created; update the block that references booking.token_slot,
TokenQueue.objects.filter(...).exists(), .first(), and .create(...) to use
get_or_create with appropriate defaults.
🧹 Nitpick comments (1)
care/emr/api/viewsets/scheduling/booking.py (1)
340-348: Consider extracting token_date calculation to avoid duplication.The token_date calculation logic (making timezone naive and adding 1 second) appears here and again in
generate_token_handlerat lines 300-302. While not incorrect, having this logic in two places means any future adjustments would need to be made twice. I'm sure that won't cause any issues down the road.♻️ Suggested helper extraction
`@staticmethod` def _get_token_date(slot_start_datetime): """Extract token date accounting for timezone edge cases.""" return timezone.make_naive( slot_start_datetime + timedelta(seconds=1) ).date()Then use
self._get_token_date(booking.token_slot.start_datetime)in both locations.
| if queue is None: | ||
| # slot may start at 1:00 IST (19:30 UTC of previous date), hence | ||
| # making it tz naive and adding 1 second to ensure correct date extraction | ||
| token_date = timezone.make_naive( | ||
| booking.token_slot.start_datetime + timedelta(seconds=1) | ||
| ).date() | ||
| filters = { | ||
| "facility": booking.token_slot.resource.facility, | ||
| "resource": booking.token_slot.resource, | ||
| "date": token_date, | ||
| } | ||
| queue_exists = TokenQueue.objects.filter(**filters).exists() | ||
| filters["system_generated"] = True | ||
| queue = TokenQueue.objects.filter(**filters).first() | ||
| if not queue: | ||
| filters["name"] = "System Generated" | ||
| if not queue_exists: | ||
| filters["is_primary"] = True | ||
| queue = TokenQueue.objects.create(**filters) |
There was a problem hiding this comment.
Race condition in queue creation logic.
The queue existence check (line 308) and queue creation (line 315) are not atomic and occur outside the lock. Under concurrent requests, two threads could both see queue_exists = False and each create a system-generated queue with is_primary = True, resulting in duplicate queues.
Consider using get_or_create to atomically handle this, or extend the lock scope to include queue resolution.
🔒 Proposed fix using get_or_create
if queue is None:
# slot may start at 1:00 IST (19:30 UTC of previous date), hence
# making it tz naive and adding 1 second to ensure correct date extraction
token_date = timezone.make_naive(
booking.token_slot.start_datetime + timedelta(seconds=1)
).date()
filters = {
"facility": booking.token_slot.resource.facility,
"resource": booking.token_slot.resource,
"date": token_date,
}
- queue_exists = TokenQueue.objects.filter(**filters).exists()
- filters["system_generated"] = True
- queue = TokenQueue.objects.filter(**filters).first()
- if not queue:
- filters["name"] = "System Generated"
- if not queue_exists:
- filters["is_primary"] = True
- queue = TokenQueue.objects.create(**filters)
+ with transaction.atomic():
+ queue_exists = TokenQueue.objects.filter(**filters).exists()
+ queue, created = TokenQueue.objects.get_or_create(
+ **filters,
+ system_generated=True,
+ defaults={
+ "name": "System Generated",
+ "is_primary": not queue_exists,
+ },
+ )
🤖 Prompt for AI Agents
In `@care/emr/api/viewsets/scheduling/booking.py` around lines 297 - 315, The code
checks TokenQueue.exists() and then creates a system-generated queue outside the
lock, causing a race where two requests can both create primary queues; replace
the separate exists()/create() flow with an atomic
TokenQueue.objects.get_or_create(...) call using the same filters (facility,
resource, date, system_generated=True, name="System Generated") and supply
defaults including is_primary=True only when creating, or wrap the get_or_create
inside the existing lock/transaction.atomic to ensure only one primary queue is
created; update the block that references booking.token_slot,
TokenQueue.objects.filter(...).exists(), .first(), and .create(...) to use
get_or_create with appropriate defaults.
Codecov Report❌ Patch coverage is
Additional details and impacted files☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Proposed Changes
Created a generate token handler to separate out token creation logic, this method is used by plugs.
10bedicu/care_state_hmis#4
Merge Checklist
/docsOnly PR's with test cases included and passing lint and test pipelines will be reviewed
@ohcnetwork/care-backend-maintainers @ohcnetwork/care-backend-admins
Summary by CodeRabbit