2025, Oct 19 19:00

Stop 'MySQL server has gone away' errors in Django 3.2: why background-thread ORM writes from views fail and what to do

Learn why Django 3.2 + MySQL throws 'server has gone away' when ORM writes run in background threads, and see the safe, synchronous view pattern to fix it.

Running database writes in a background thread from a Django 3.2 view may look tempting when you want to return HTTP responses instantly. However, pairing synchronous ORM calls with ad‑hoc threads routinely ends in errors like MySQL’s “server has gone away”. Below is a concise walk‑through of why this happens and what a safe, production‑ready direction looks like in this context.

Reproducing the issue

The application stack is Django 3.2.x on Python 3, MySQL, served via uWSGI + Nginx. The view returns immediately, while a background thread kicks off ORM work in the model layer.

from django.http import HttpResponse
from django.db import models
import threading
# helper
def extract_request_params(request):
    return {}
# model
class JobRecord(models.Model):
    create_username = models.CharField(max_length=50, default='system')
    update_username = models.CharField(max_length=50, default='system')
    @classmethod
    def perform(cls, params):
        # ORM logic
        pass
# view
class OpsView:
    @classmethod
    def trigger_job(cls, request):
        payload = extract_request_params(request)
        payload['user'] = request.user
        worker = threading.Thread(target=JobRecord.perform, args=(payload,))
        worker.start()
        return HttpResponse("Task run success")

The symptom appears during execution:

(2006, 'MySQL server has gone away')

There was also an attempt to force the connection within the task:

from django.db import connection
class JobRecord(models.Model):
    create_username = models.CharField(max_length=50, default='system')
    update_username = models.CharField(max_length=50, default='system')
    @classmethod
    def perform(cls, params):
        connection.ensure_connection()
        # ORM logic
        # ...
        connection.close()

But the error still occurs.

Why this fails

Django 3.2 ORM is synchronous. It does not support running ORM work as an async or detached background task spawned directly from a request handler. Launching independent threads or processes inside a view detaches the database operations from Django’s expected execution model, and that leads to unstable connection handling and to the error you are seeing. Manually calling ensure_connection or closing the connection inside that thread does not fix the fundamental mismatch.

The correct direction

The safe approach here is straightforward: do not start independent threads or processes from within a view. Keep the database operations within the normal, synchronous request–response flow that Django 3.2 is designed for.

from django.http import HttpResponse
from django.db import models
# helper
def extract_request_params(request):
    return {}
# model
class JobRecord(models.Model):
    create_username = models.CharField(max_length=50, default='system')
    update_username = models.CharField(max_length=50, default='system')
    @classmethod
    def perform(cls, params):
        # ORM logic
        pass
# view without background thread
class OpsView:
    @classmethod
    def trigger_job(cls, request):
        payload = extract_request_params(request)
        payload['user'] = request.user
        JobRecord.perform(payload)
        return HttpResponse("Task run success")

This removes the detached thread and executes ORM operations in the supported, synchronous path, eliminating the “MySQL server has gone away” failures that stem from the threading approach.

Why you should care

Background threads triggered from views are fragile in this setup. They break the guarantees you rely on when using Django’s ORM and MySQL together and lead to intermittent connection errors and lost work. Keeping the ORM on the synchronous path preserves reliability and predictability under uWSGI + Nginx in production.

Notes on the stack

Django 3.2 is a quite old version. That alone does not solve the problem described here, but it is worth being aware of the age of the release you are targeting.

Conclusion

If your Django 3.2 app hits “(2006, 'MySQL server has gone away')” when doing database work in a background thread started from a view, the fix is to stop launching threads or processes from views. Keep ORM operations synchronous within the request cycle. This is the reliable way to prevent transient MySQL disconnects and ensure your data writes are executed correctly.

The article is based on a question from StackOverflow by Dylan and an answer by Dylan.