Fix tracking race condition

This commit is contained in:
Tanner Collin 2021-04-15 22:38:46 +00:00
parent 677b4086e5
commit 9a3a9db4ee

View File

@ -5,6 +5,7 @@ from django.contrib.auth.models import User, Group
from django.shortcuts import get_object_or_404, redirect from django.shortcuts import get_object_or_404, redirect
from django.db import transaction from django.db import transaction
from django.db.models import Max from django.db.models import Max
from django.db.utils import OperationalError
from django.http import HttpResponse, Http404, FileResponse from django.http import HttpResponse, Http404, FileResponse
from django.core.files.base import File from django.core.files.base import File
from django.core.cache import cache from django.core.cache import cache
@ -502,45 +503,50 @@ class StatsViewSet(viewsets.ViewSet, List):
except KeyError: except KeyError:
raise exceptions.ValidationError(dict(data='This field is required.')) raise exceptions.ValidationError(dict(data='This field is required.'))
@transaction.atomic
@action(detail=False, methods=['post']) @action(detail=False, methods=['post'])
def track(self, request): def track(self, request):
if 'name' not in request.data: try:
raise exceptions.ValidationError(dict(name='This field is required.')) with transaction.atomic():
if 'name' not in request.data:
raise exceptions.ValidationError(dict(name='This field is required.'))
if 'username' not in request.data: if 'username' not in request.data:
raise exceptions.ValidationError(dict(username='This field is required.')) raise exceptions.ValidationError(dict(username='This field is required.'))
track = cache.get('track', {}) track = cache.get('track', {})
devicename = request.data['name'] devicename = request.data['name']
username = request.data['username'] username = request.data['username']
first_name = username.split('.')[0].title() first_name = username.split('.')[0].title()
track[devicename] = dict(time=time.time(), username=first_name) track[devicename] = dict(time=time.time(), username=first_name)
cache.set('track', track) cache.set('track', track)
# update device usage # update device usage
last_session = models.UsageTrack.objects.filter(devicename=devicename).last() last_session = models.UsageTrack.objects.filter(devicename=devicename).last()
if not last_session or last_session.username != username: if not last_session or last_session.username != username:
try: try:
user = User.objects.get(username__iexact=username) user = User.objects.get(username__iexact=username)
except User.DoesNotExist: except User.DoesNotExist:
logging.error('Username not found: ' + username) logging.error('Username not found: ' + username)
user = None user = None
models.UsageTrack.objects.create( models.UsageTrack.objects.create(
user=user, user=user,
username=username, username=username,
devicename=devicename, devicename=devicename,
num_seconds=10, num_seconds=10,
) )
logging.info('New ' + devicename + ' session created for: ' + username) logging.info('New ' + devicename + ' session created for: ' + username)
else: else:
last_session.num_seconds += 10 last_session.num_seconds += 10
last_session.save() last_session.save()
return Response(200) return Response(200)
# keep trying if we hit a "database locked" error
except OperationalError:
return self.track(request)
class MemberCountViewSet(Base, List): class MemberCountViewSet(Base, List):