Skip to content

Conversation

@DenisKoleda
Copy link

@DenisKoleda DenisKoleda commented Dec 16, 2025

Summary

  • Fix race condition when concurrent requests use the same OAuth2 token
  • Replace save(update_fields=['last_used']) with QuerySet.update() to prevent DatabaseError

Problem

When multiple parallel requests authenticate with the same OAuth2 token, the Gateway crashes with:

django.db.utils.DatabaseError: Save with update_fields did not affect any rows.

Root cause: In is_valid() method, there's a race condition between exists() check and save() call. When concurrent requests try to update last_used timestamp on the same token:

  1. Request A checks exists() → True
  2. Request B checks exists() → True
  3. Request B calls save() → Success, row version changes
  4. Request A calls save()Fails because Django's save(update_fields=...) expects to affect exactly 1 row, but the row was already modified

Solution

Replace:

if OAuth2AccessToken.objects.filter(pk=self.pk).exists():
    self.save(update_fields=['last_used'])

With:

OAuth2AccessToken.objects.filter(pk=self.pk).update(last_used=self.last_used)

QuerySet.update() is atomic and simply returns 0 if no rows match, without raising an exception. This is the expected behavior for updating last_used — if the token was deleted between validation and update, we don't need to error out.

Test plan

  • Verify existing OAuth2 authentication tests pass
  • Manual test with concurrent requests using the same token
  • Confirm no HTTP 500 errors under parallel load

DenisKoleda and others added 2 commits December 16, 2025 22:38
Replace save(update_fields=['last_used']) with QuerySet.update() to avoid
DatabaseError when concurrent requests try to update the same token's
last_used timestamp.

The previous implementation could fail with:
django.db.utils.DatabaseError: Save with update_fields did not affect any rows.

This happened because between exists() check and save() call, another
process could modify the row, causing save() to affect 0 rows and raise
an exception. QuerySet.update() handles this gracefully by simply
returning 0 affected rows without raising an error.
@github-actions
Copy link

DVCS PR Check Results:

Could not find JIRA key(s) in PR title, branch name, or commit messages

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