DEV Community

cycy
cycy

Posted on

How to Fix a Django Migration That Freezes Your App (and What Database Locks Really Mean

If you’ve ever seen your Django application freeze during a migration or user registration, you’ve probably run into a database lock. This guide explains what happened to our app, how we diagnosed it, and the exact steps we took to fix it—so you can do the same.


The Problem: Our Django Server Started Hanging

We noticed our Django app would start up fine, but as soon as we tried to register a new user, it would just hang. No error, no crash—just stuck. The logs would show something like:

INFO base_serializers BASE CREATE: Received validated_data keys: ['email', 'contact', 'first_name', 'last_name', 'password', 'password_confirm']
Enter fullscreen mode Exit fullscreen mode

And then… nothing.


What Are Database Locks?

When Django runs migrations, it changes your database structure. To keep things safe, the database “locks” certain tables while it works. Normally, these locks are released quickly. But if a migration gets stuck (for example, when creating complex foreign key relationships), the lock never goes away. This means any new request (like registering a user) will just wait forever for the lock to clear.


How We Diagnosed the Issue

1. Suspect a Lock: If your app hangs on database actions (like user registration), a lock is likely.

2. Check Active Connections:

Open your database shell:

   python manage.py dbshell
Enter fullscreen mode Exit fullscreen mode

Then run:

   SELECT * FROM pg_stat_activity WHERE state = 'active';
Enter fullscreen mode Exit fullscreen mode

3. Look for Locks:

Find processes waiting on a lock:

   SELECT pid, query, state, wait_event_type, wait_event 
   FROM pg_stat_activity 
   WHERE wait_event_type = 'Lock';
Enter fullscreen mode Exit fullscreen mode

4. See What’s Locked:

Check which tables are locked:

   SELECT relation::regclass, mode, granted, pid 
   FROM pg_locks
   JOIN pg_stat_activity USING (pid)
   WHERE relation IS NOT NULL;
Enter fullscreen mode Exit fullscreen mode

How We Fixed It (Step-by-Step)

1. Kill Stuck Database Connections

In the database shell, terminate the stuck processes:

SELECT pg_terminate_backend(pid) 
FROM pg_stat_activity 
WHERE state = 'active' 
  AND pid <> pg_backend_pid() 
  AND wait_event_type = 'Lock';
Enter fullscreen mode Exit fullscreen mode

Or, if you know the specific process IDs:

SELECT pg_terminate_backend(5930);
SELECT pg_terminate_backend(20626);
Enter fullscreen mode Exit fullscreen mode

2. Fake the Problematic Migration

If a migration keeps failing, tell Django to “pretend” it’s done:

python manage.py migrate chat zero --fake
python manage.py migrate chat --fake-initial
python manage.py migrate chat 0004 --fake
Enter fullscreen mode Exit fullscreen mode

3. Manually Create the Missing Tables

Since we skipped the migration, we had to create the tables ourselves:

CREATE TABLE IF NOT EXISTS chat_callsession (
    id uuid PRIMARY KEY,
    caller_id uuid REFERENCES users_user(id),
    receiver_id uuid REFERENCES users_user(id),
    status varchar(20) NOT NULL,
    start_time timestamp NULL,
    end_time timestamp NULL, 
    duration_seconds integer NULL,
    created_at timestamp NOT NULL,
    updated_at timestamp NOT NULL,
    chat_room_id uuid NULL REFERENCES chat_chatroom(id),
    parcel_id uuid NULL REFERENCES parcels_parcel(id)
);

CREATE TABLE IF NOT EXISTS chat_calllog (
    id uuid PRIMARY KEY,
    call_session_id uuid REFERENCES chat_callsession(id),
    participant_id uuid REFERENCES users_user(id),
    other_participant_id uuid REFERENCES users_user(id),
    log_type varchar(20) NOT NULL,
    timestamp timestamp NOT NULL,
    duration_seconds integer NULL,
    room_id uuid NULL REFERENCES chat_chatroom(id)
);
Enter fullscreen mode Exit fullscreen mode

4. Verify All Migrations Are Applied

Check that Django thinks all migrations are done:

python manage.py showmigrations chat
Enter fullscreen mode Exit fullscreen mode

You should see [X] next to every migration.

5. Test Your Application

Restart your server and try registering a user or using the chat features. Everything should work!


Why Does This Happen?

  • Complex migrations (especially with foreign keys) can take longer and are more likely to cause locks.
  • If a migration fails or hangs, the lock never gets released.
  • Any new database action will just wait for the lock, causing your app to freeze.

How to Prevent This

  • Break up big migrations into smaller steps.
  • Test migrations locally before running them in production.
  • Monitor your database for long-running queries or locks.
  • Use --fake carefully—only when you know what you’re doing, and always make sure your database schema matches what Django expects.

What We Learned

  • Database locks can silently freeze your app.
  • PostgreSQL tools like pg_stat_activity and pg_locks are essential for troubleshooting.
  • Sometimes you need to manually intervene—kill stuck processes, fake migrations, and create tables yourself.
  • Prevention is best: design migrations carefully and always test first.

If your Django app ever hangs during a migration or user registration, check for database locks first. Follow these steps, and you’ll be back up and running in no time!


Have you run into similar issues? Share your experience or tips in the comments!

Top comments (1)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.