What configuration settings are required in Django and MySQL for supporting concurrent requests?
Question: I have created a project called test123, following are settings.py and urls.py
:

    
    
    MIDDLEWARE_CLASSES = (
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'django.middleware.security.SecurityMiddleware',
    )
    
    DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'OPTIONS': {
            'read_default_file': os.path.join(BASE_DIR, 'dbuser.cnf'),
        }
    },
    }
    root@cdbe25bac912:~/test123/test123# cat urls.py
    from django.conf.urls import include, url
    from django.contrib import admin
    
    urlpatterns = [
    url(r'^myapp/', include('myapp.urls')),
    ]
    

And I have created an application called myapp.

    
    
    root@cdbe25bac912:~/test123/myapp# cat models.py
    from django.db import models
    
    class Package(models.Model):
       name = models.CharField(max_length=50)
       desc = models.CharField(max_length=50)
    root@cdbe25bac912:~/test123/myapp# cat serializers.py
    from myapp.models import *
    from rest_framework import serializers
    
    class PackageSerializer(serializers.ModelSerializer):
    class Meta:
        model = Package
        fields = ( 'id', 'name', 'desc',)
    
    root@cdbe25bac912:~/test123/myapp# cat views.py
    from django.shortcuts import render
    from myapp.models import *
    from rest_framework.views import APIView
    from rest_framework import status
    from rest_framework.response import Response
    from myapp.serializers import *
    class PackageViewList(APIView):
       def get(self, request, format=None):
           package = Package.objects.all()
           serializer = PackageSerializer(package, many=True)
           return Response(serializer.data)
    
       def post(self, request, format=None):
           serializer = PackageSerializer(data = request.data)
           if serializer.is_valid():
               print serializer.validated_data
               serializer.save()
               return Response(serializer.data, status = status.HTTP_201_CREATED)
           return Response(serializer.errors, status = status.HTTP_400_BAD_REQUEST)
    
    root@cdbe25bac912:~/test123/myapp# cat urls.py
    from django.conf.urls import url
    from . import views
    
    urlpatterns = [
    url(r'^package/$', views.PackageViewList.as_view()),
    ]
    root@cdbe25bac912:~/test123/myapp#
    

Following is a test script written using Django Test Framework, which creates
multiple APIClient objects to simulate multiple users and fire POST operation
from these objects.

    
    
    root@cdbe25bac912:~/test123/myapp# cat tests.py.bkup
    from django.test import TestCase
    from rest_framework.test import APIClient
    from rest_framework.test import APITestCase, APILiveServerTestCase
    from myapp.models import Package
    from multiprocessing import Process
    
    class ConcurrentTest(APILiveServerTestCase):
        def setUp(self):
            self.apiclient_list = []
            self.num_clients=4
    
            package = Package.objects.create(name='pack333', desc='package333')
            package.save()
            self.pack = {"name" : "pack", "desc" : "package"}
    
           #create number of APIClient objects and login.
            for client_id in range(self.num_clients):
                    apiclient = APIClient()
                    self.apiclient_list.append(apiclient)
    
        def post(self, apiclient, client_id, url, data):
            print 'Started POST Client ID = %s' % (str(client_id))
            data['name'] = 'packpost' + str(client_id)
            data['desc'] = 'packdesc' + str(client_id)
            print data
            response = apiclient.post(url, data, format="json")
            self.assertEqual(response.status_code, 201)
            print 'Completed POST Client ID = %s' % (str(client_id))
       def test_concurrent_restops(self):
        """
        Description : Simulate multiple users and issue concurrent REST operations
        """
        process_list = []
    
        #Issue concurrent POST operations.
        for client_id in range(len(self.apiclient_list)):
            t = Process(target=self.post, args=(self.apiclient_list[client_id], client_id, '/myapp/package/', self.pack))
            process_list.append(t)
    
        for process in process_list :
            process.start()
        for process in process_list :
            process.join()
    

root@cdbe25bac912:~/test123/myapp#

## Problem symptoms:

When I run the above test problem as: `python manage.py test myapp`

At least one of the processes that are created in `test.py` will hang. I am
assuming there are some configurations required in Django and MySQL to support
concurrent operations from different users.


Answer: It looks like the `LiveServerTestCase` is single threaded which would explain
why processes/requests hang, because the server can only service a single
request at a time:

<https://github.com/django/django/blob/53ccffdb8c8e47a4d4304df453d8c79a9be295ab/django/test/testcases.py#L1319>

<https://github.com/django/django/blob/53ccffdb8c8e47a4d4304df453d8c79a9be295ab/django/test/testcases.py#L1332>

<https://github.com/django/django/blob/53ccffdb8c8e47a4d4304df453d8c79a9be295ab/django/test/testcases.py#L1255>

* * *

One thing i can think of why you aren't seeing blocking using sqlite is
because sqlite is ran in memory for django unittest, drastically increasing
speeds of test runs.

Django `LiveServerTestServer` is kind of a "toy" and is probably not very
suitable for load style tests. MySQL should easily be able to handle a good
level of concurrency out-of-the box, but I'm not sure `LiveServerTestServer`
can.

If you run your code against `python manage.py runserver` which is now
multithreaded (I believe) you should notice a difference. For an idea of how
your application can handle load, it could be a good idea run your code as it
will be deployed (using your prod webserver: apache, uwsgi, gunicorn, etc..)

