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']
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
Then run:
SELECT * FROM pg_stat_activity WHERE state = 'active';
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';
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;
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';
Or, if you know the specific process IDs:
SELECT pg_terminate_backend(5930);
SELECT pg_terminate_backend(20626);
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
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)
);
4. Verify All Migrations Are Applied
Check that Django thinks all migrations are done:
python manage.py showmigrations chat
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
andpg_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.