Skip to content

[ENG-401] fix: token queue should respect sub queues while calling serve next#3708

Open
nandkishorr wants to merge 7 commits into
developfrom
ENG-401-token-queue-should-respect-sub-queues-while-calling-serve-next
Open

[ENG-401] fix: token queue should respect sub queues while calling serve next#3708
nandkishorr wants to merge 7 commits into
developfrom
ENG-401-token-queue-should-respect-sub-queues-while-calling-serve-next

Conversation

@nandkishorr

@nandkishorr nandkishorr commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Proposed Changes

  • Added proper token calling in the sub_queue
  • Added proper validation and current token clearing on token update
  • Testcases pending !

Associated Issue

Merge Checklist

  • Tests added/fixed
  • Update docs in /docs
  • Linting Complete
  • Any other necessary step

Only 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

  • Bug Fixes
    • Strengthened token update validation to detect sub-queue conflicts and prevent unsafe changes while a token is in progress.
    • Updated current-token clearing so active tokens are released when moving away from in-progress, including when the same sub-queue remains linked.
    • Tightened set-next rules: only transitions from CREATED are allowed, prevents overwriting a sub-queue with a different current token, and improves next-token selection priority.
  • Tests
    • Adjusted token update tests to submit CREATED and assert CREATED results.

@nandkishorr nandkishorr self-assigned this Jul 10, 2026
@nandkishorr nandkishorr requested a review from a team as a code owner July 10, 2026 10:11
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@nandkishorr, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 16 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 91a6e06b-2645-41f3-90f2-58cb6210496c

📥 Commits

Reviewing files that changed from the base of the PR and between d82319b and b21fa4a.

📒 Files selected for processing (2)
  • care/emr/api/viewsets/scheduling/token.py
  • care/emr/tests/test_token_api.py
📝 Walkthrough

Walkthrough

Changes

Token update validation now handles sub-queue conflicts and status transitions separately. set_next enforces CREATED status and prevents occupied sub-queues from receiving another current token. Queue advancement prioritizes assigned tokens before unassigned tokens. Related tests now use CREATED.

Token queue consistency

Layer / File(s) Summary
Token validation and state enforcement
care/emr/api/viewsets/scheduling/token.py, care/emr/tests/test_token_api.py
Validation checks current-token conflicts, prevents invalid IN_PROGRESS sub-queue updates, clears current-token references when needed, and updates related test expectations.
Token set-next state transition
care/emr/api/viewsets/scheduling/token.py
set_next requires CREATED status, rejects occupied target sub-queues, and assigns the token while transitioning it to IN_PROGRESS.
Prioritized next-token selection
care/emr/api/viewsets/scheduling/token_queue.py
Next-token selection prefers tokens assigned to the requested sub-queue before unassigned waiting tokens, and rejects a missing result.

Estimated code review effort: 3 (Moderate) | ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: token queue behavior now respects sub-queues when serving the next token.
Description check ✅ Passed The description follows the template with Proposed Changes, Associated Issue, and Merge Checklist, though the test checklist is still marked pending.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ENG-401-token-queue-should-respect-sub-queues-while-calling-serve-next

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@nandkishorr nandkishorr changed the title [ENG-401] fix : token queue should respect sub queues while calling serve next [ENG-401] fix: token queue should respect sub queues while calling serve next Jul 10, 2026
@greptile-apps

greptile-apps Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR tightens sub-queue token management: direct IN_PROGRESS updates are now rejected in favour of the dedicated set_next endpoint, the set_next lock is re-keyed from the token to the sub-queue, and set_next_token_to_subqueue now prioritises tokens already assigned to the target sub-queue before falling back to unassigned ones.

  • validate_data blocks IN_PROGRESS status updates through the regular update endpoint, directing callers to set_next.
  • set_next gains a per-sub_queue advisory lock and an explicit guard against overwriting an existing current_token, but sub_queue is fetched before the lock is acquired, leaving the current_token check based on a potentially stale in-memory snapshot.
  • set_next_token_to_subqueue replaces the double .exists() + .first() query with a single priority-ordered .first() or .first() pattern.

Confidence Score: 3/5

The lock key is now correctly scoped to the sub_queue, but the sub_queue object is loaded before the lock is acquired, so the current_token guard inside the critical section can act on stale data and allow two tokens to enter IN_PROGRESS on the same sub-queue.

The lock key change is correct in principle, but any caller that loads sub_queue before a prior call commits and then acquires the released lock will bypass the current_token guard using a stale snapshot, silently producing duplicate IN_PROGRESS tokens.

care/emr/api/viewsets/scheduling/token.py — the set_next action needs sub_queue re-fetched with select_for_update inside the lock+transaction block.

Important Files Changed

Filename Overview
care/emr/api/viewsets/scheduling/token.py Adds IN_PROGRESS guard in validate_data and a per-sub_queue lock in set_next; lock key is correctly scoped to sub_queue but the object is fetched before the lock, leaving the current_token check stale
care/emr/api/viewsets/scheduling/token_queue.py Refactors set_next_token_to_subqueue to prefer tokens already assigned to the target sub_queue, replacing double exists()+first() with a single first()-or-first() pattern
care/emr/tests/test_token_api.py Adjusts three update tests to submit CREATED status matching new validation; no new tests for set_next or set_next_token_to_subqueue as noted in the PR

Reviews (6): Last reviewed commit: "added lock" | Re-trigger Greptile

Comment thread care/emr/api/viewsets/scheduling/token.py
Comment thread care/emr/api/viewsets/scheduling/token_queue.py
Comment thread care/emr/api/viewsets/scheduling/token.py Outdated
@codecov

codecov Bot commented Jul 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 60.86957% with 9 lines in your changes missing coverage. Please review.
✅ Project coverage is 79.65%. Comparing base (e26a3c6) to head (b21fa4a).

Files with missing lines Patch % Lines
care/emr/api/viewsets/scheduling/token.py 52.63% 5 Missing and 4 partials ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #3708      +/-   ##
===========================================
- Coverage    79.67%   79.65%   -0.03%     
===========================================
  Files          479      479              
  Lines        23098    23107       +9     
  Branches      2406     2409       +3     
===========================================
+ Hits         18404    18406       +2     
- Misses        4086     4090       +4     
- Partials       608      611       +3     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
care/emr/tests/test_token_api.py (1)

278-292: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Missing test coverage for new sub-queue validation and clearing behavior.

The status change from IN_PROGRESS to CREATED is correct — the new perform_update logic (lines 113-120 in token.py) would block assigning a sub_queue while transitioning to IN_PROGRESS. However, the new behavior itself is entirely untested:

  1. Sub_queue change during IN_PROGRESS is blocked — no test verifies that updating a token to IN_PROGRESS while changing its sub_queue returns 400 with "Use set_next endpoint to change the sub queue of a token".
  2. Current_token conflict on IN_PROGRESS with same sub_queue — no test verifies that setting IN_PROGRESS with an existing sub_queue that has a different current_token returns 400 with "Sub Queue already has a current token".
  3. Current_token clearing on status transition away from IN_PROGRESS — no test verifies that updating a token from IN_PROGRESS to FULFILLED/CANCELLED clears the sub_queue's current_token.
  4. Next-token prioritization (token_queue.py lines 235-238) — no test verifies that set_next_token_to_subqueue prefers tokens already assigned to the requested sub_queue over unassigned waiting tokens.

The existing tests (test_update_token_with_existing_current_token_in_subqueue, test_update_token_with_current_token_as_null_in_subqueue) cover the first validate_data block but none exercise the IN_PROGRESS-gated paths.

Would you like me to generate the missing test cases?

Also applies to: 314-328

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@care/emr/tests/test_token_api.py` around lines 278 - 292, The token API tests
lack coverage for the new sub-queue validation and current-token lifecycle
behavior. Add focused tests around the existing update-token test cases and
`perform_update` covering: rejecting an IN_PROGRESS update that changes
sub_queue with the specified 400 message; rejecting IN_PROGRESS when the
sub_queue has a different current_token; clearing current_token when
transitioning from IN_PROGRESS to FULFILLED or CANCELLED; and verifying
`set_next_token_to_subqueue` prioritizes tokens already assigned to the
requested sub_queue over unassigned waiting tokens.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@care/emr/tests/test_token_api.py`:
- Around line 278-292: The token API tests lack coverage for the new sub-queue
validation and current-token lifecycle behavior. Add focused tests around the
existing update-token test cases and `perform_update` covering: rejecting an
IN_PROGRESS update that changes sub_queue with the specified 400 message;
rejecting IN_PROGRESS when the sub_queue has a different current_token; clearing
current_token when transitioning from IN_PROGRESS to FULFILLED or CANCELLED; and
verifying `set_next_token_to_subqueue` prioritizes tokens already assigned to
the requested sub_queue over unassigned waiting tokens.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 4955bd10-01bf-440e-9009-38445ca4c38a

📥 Commits

Reviewing files that changed from the base of the PR and between e26a3c6 and a7ace39.

📒 Files selected for processing (3)
  • care/emr/api/viewsets/scheduling/token.py
  • care/emr/api/viewsets/scheduling/token_queue.py
  • care/emr/tests/test_token_api.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
care/emr/api/viewsets/scheduling/token.py (2)

107-115: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Add the resource guard here too. This branch only catches sub-queue swaps, so a token with no existing sub-queue can still attach one from the wrong resource. Match the create path and reject instance.sub_queue.resource != obj.queue.resource before the current transition check.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@care/emr/api/viewsets/scheduling/token.py` around lines 107 - 115, Update the
validation logic containing the existing sub-queue transition check to also
guard the target queue’s resource: reject when obj.sub_queue exists and
obj.sub_queue.resource differs from obj.queue.resource, before evaluating the
current instance transition condition. Keep the existing mismatch check for
sub-queue swaps so both creation-like attachments and transitions enforce
resource consistency.

201-213: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Lock the sub-queue row in set_next. transaction.atomic() doesn’t serialize concurrent calls here, so two requests can both pass the current_token check and one will quietly overwrite the other. Use select_for_update() on TokenSubQueue like the create path does.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@care/emr/api/viewsets/scheduling/token.py` around lines 201 - 213, Update
set_next to retrieve the TokenSubQueue through select_for_update() before
checking or assigning current_token, matching the locking approach used by the
create path; keep the lookup inside the existing transaction.atomic() block so
concurrent calls serialize safely.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@care/emr/api/viewsets/scheduling/token.py`:
- Around line 107-115: Update the validation logic containing the existing
sub-queue transition check to also guard the target queue’s resource: reject
when obj.sub_queue exists and obj.sub_queue.resource differs from
obj.queue.resource, before evaluating the current instance transition condition.
Keep the existing mismatch check for sub-queue swaps so both creation-like
attachments and transitions enforce resource consistency.
- Around line 201-213: Update set_next to retrieve the TokenSubQueue through
select_for_update() before checking or assigning current_token, matching the
locking approach used by the create path; keep the lookup inside the existing
transaction.atomic() block so concurrent calls serialize safely.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: cedfac83-4a0d-49cd-8098-4162ab0c84d8

📥 Commits

Reviewing files that changed from the base of the PR and between a7ace39 and 140b0b1.

📒 Files selected for processing (1)
  • care/emr/api/viewsets/scheduling/token.py

Comment thread care/emr/api/viewsets/scheduling/token.py Outdated
@nandkishorr nandkishorr force-pushed the ENG-401-token-queue-should-respect-sub-queues-while-calling-serve-next branch from d82319b to 5ada8a3 Compare July 10, 2026 13:41
Comment on lines 83 to 100
def validate_data(self, instance, model_obj=None):
if (
model_obj
and instance.sub_queue
and model_obj.sub_queue
and instance.sub_queue != model_obj.sub_queue.external_id
):
existing_current = TokenSubQueue.objects.filter(
current_token=model_obj
).exists()
if existing_current:
raise ValidationError("Sub Queue already has a current token")
if model_obj and instance.sub_queue:
if (
model_obj.sub_queue
and instance.sub_queue != model_obj.sub_queue.external_id
):
existing_current = TokenSubQueue.objects.filter(
current_token=model_obj
).exists()
if existing_current:
raise ValidationError("Sub Queue already has a current token")
if (
instance.sub_queue
and instance.status == TokenStatusOptions.IN_PROGRESS.value
):
raise ValidationError("Use set_next endpoint to assign a token.")

return super().validate_data(instance, model_obj)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 IN_PROGRESS guard bypassed when sub_queue is null

The new validation at lines 94–98 that blocks direct IN_PROGRESS updates lives inside if model_obj and instance.sub_queue:. A caller that sends {"status": "in_progress", "sub_queue": null} satisfies pydantic (since TokenUpdateSpec.sub_queue: UUID4 | None accepts null) but makes instance.sub_queue falsy, so the entire guard is skipped. The token then transitions to IN_PROGRESS with sub_queue = null in perform_update — the old sub-queue's current_token gets cleared, but nothing sets a new current_token, leaving the queue in an inconsistent state.

The IN_PROGRESS status check should be guarded on model_obj alone, independent of whether a sub-queue is being provided in the same request.

@nandkishorr nandkishorr force-pushed the ENG-401-token-queue-should-respect-sub-queues-while-calling-serve-next branch from 5ada8a3 to b21fa4a Compare July 10, 2026 13:47
Comment on lines +201 to 213
sub_queue = get_object_or_404(
TokenSubQueue,
external_id=request_obj.sub_queue,
resource=queue.resource,
)
with Lock(f"token:set_next:{sub_queue.id}"), transaction.atomic():
if sub_queue.current_token and sub_queue.current_token != obj:
raise ValidationError("Sub Queue already has a current token")
sub_queue.current_token = obj
sub_queue.save()
obj.status = TokenStatusOptions.IN_PROGRESS.value
obj.sub_queue = sub_queue
obj.save()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Stale sub_queue snapshot inside the lock

sub_queue is fetched via get_object_or_404 (lines 201–205) before the lock is acquired. The lock serialises concurrent calls, but it does not refresh the in-memory object. A second request that fetches sub_queue (seeing current_token = None) and then acquires the lock after the first request has already committed will still evaluate sub_queue.current_token against the old cached value — both pass the guard and both tokens become IN_PROGRESS on the same sub-queue simultaneously.

Re-fetch sub_queue with select_for_update() inside the lock+transaction so the guard always reflects the current DB state:

with Lock(f"token:set_next:{sub_queue.id}"), transaction.atomic():
    sub_queue = TokenSubQueue.objects.select_for_update().get(id=sub_queue.id)
    if sub_queue.current_token and sub_queue.current_token != obj:
        raise ValidationError("Sub Queue already has a current token")
    ...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant