Skip to content

Database lock #1073

Description

@jorgemoralespou

Describe the bug

@hguerrero reported and issue when running a Workshop:

│ [Tue Jun 16 13:52:22.127384 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]   File "/opt/app-root/venv/lib64/python3.13/site-packages/django/db/models/base.py", line 877, in save_base                              │
│ [Tue Jun 16 13:52:22.127387 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]     updated = self._save_table(                                                                                                          │
│ [Tue Jun 16 13:52:22.127390 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]         raw,                                                                                                                             │
│ [Tue Jun 16 13:52:22.127427 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]     ...<4 lines>...                                                                                                                      │
│ [Tue Jun 16 13:52:22.127430 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]         update_fields,                                                                                                                   │
│ [Tue Jun 16 13:52:22.127432 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]     )                                                                                                                                    │
│ [Tue Jun 16 13:52:22.127435 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]   File "/opt/app-root/venv/lib64/python3.13/site-packages/django/db/models/base.py", line 1020, in _save_table                           │
│ [Tue Jun 16 13:52:22.127438 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]     results = self._do_insert(                                                                                                           │
│ [Tue Jun 16 13:52:22.127441 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]         cls._base_manager, using, fields, returning_fields, raw                                                                          │
│ [Tue Jun 16 13:52:22.127445 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]     )                                                                                                                                    │
│ [Tue Jun 16 13:52:22.127448 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]   File "/opt/app-root/venv/lib64/python3.13/site-packages/django/db/models/base.py", line 1061, in _do_insert                            │
│ [Tue Jun 16 13:52:22.127451 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]     return manager._insert(                                                                                                              │
│ [Tue Jun 16 13:52:22.127454 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]            ~~~~~~~~~~~~~~~^                                                                                                              │
│ [Tue Jun 16 13:52:22.127457 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]         [self],                                                                                                                          │
│ [Tue Jun 16 13:52:22.127460 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]         ^^^^^^^                                                                                                                          │
│ [Tue Jun 16 13:52:22.127463 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]     ...<3 lines>...                                                                                                                      │
│ [Tue Jun 16 13:52:22.127466 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]         raw=raw,                                                                                                                         │
│ [Tue Jun 16 13:52:22.127469 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]         ^^^^^^^^                                                                                                                         │
│ [Tue Jun 16 13:52:22.127472 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]     )                                                                                                                                    │
│ [Tue Jun 16 13:52:22.127474 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]     ^                                                                                                                                    │
│ [Tue Jun 16 13:52:22.127477 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]   File "/opt/app-root/venv/lib64/python3.13/site-packages/django/db/models/manager.py", line 87, in manager_method                       │
│ [Tue Jun 16 13:52:22.127480 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]     return getattr(self.get_queryset(), name)(*args, **kwargs)                                                                           │
│ [Tue Jun 16 13:52:22.127483 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^                                                                           │
│ [Tue Jun 16 13:52:22.127486 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]   File "/opt/app-root/venv/lib64/python3.13/site-packages/django/db/models/query.py", line 1810, in _insert                              │
│ [Tue Jun 16 13:52:22.127490 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]     return query.get_compiler(using=using).execute_sql(returning_fields)                                                                 │
│ [Tue Jun 16 13:52:22.127493 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^                                                                 │
│ [Tue Jun 16 13:52:22.127496 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]   File "/opt/app-root/venv/lib64/python3.13/site-packages/django/db/models/sql/compiler.py", line 1833, in execute_sql                   │
│ [Tue Jun 16 13:52:22.127499 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]     self.connection.ops.fetch_returned_insert_columns(                                                                                   │
│ [Tue Jun 16 13:52:22.127502 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^                                                                                   │
│ [Tue Jun 16 13:52:22.127505 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]         cursor,                                                                                                                          │
│ [Tue Jun 16 13:52:22.127508 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]         ^^^^^^^                                                                                                                          │
│ [Tue Jun 16 13:52:22.127510 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]         self.returning_params,                                                                                                           │
│ [Tue Jun 16 13:52:22.127513 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]         ^^^^^^^^^^^^^^^^^^^^^^                                                                                                           │
│ [Tue Jun 16 13:52:22.127516 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]     )                                                                                                                                    │
│ [Tue Jun 16 13:52:22.127518 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]     ^                                                                                                                                    │
│ [Tue Jun 16 13:52:22.127521 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]   File "/opt/app-root/venv/lib64/python3.13/site-packages/django/db/backends/base/operations.py", line 213, in fetch_returned_insert_col │
│ [Tue Jun 16 13:52:22.127524 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]     return cursor.fetchone()                                                                                                             │
│ [Tue Jun 16 13:52:22.127527 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]            ~~~~~~~~~~~~~~~^^                                                                                                             │
│ [Tue Jun 16 13:52:22.127530 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]   File "/opt/app-root/venv/lib64/python3.13/site-packages/django/db/utils.py", line 97, in inner                                         │
│ [Tue Jun 16 13:52:22.127533 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]     with self:                                                                                                                           │
│ [Tue Jun 16 13:52:22.127536 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]          ^^^^                                                                                                                            │
│ [Tue Jun 16 13:52:22.127539 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]   File "/opt/app-root/venv/lib64/python3.13/site-packages/django/db/utils.py", line 91, in __exit__                                      │
│ [Tue Jun 16 13:52:22.127542 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]     raise dj_exc_value.with_traceback(traceback) from exc_value                                                                          │
│ [Tue Jun 16 13:52:22.127546 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]   File "/opt/app-root/venv/lib64/python3.13/site-packages/django/db/utils.py", line 98, in inner                                         │
│ [Tue Jun 16 13:52:22.127549 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678]     return func(*args, **kwargs)                                                                                                         │
│ [Tue Jun 16 13:52:22.127552 2026] [wsgi:error] [pid 12:tid 34] [remote 10.1.0.3:49678] django.db.utils.OperationalError: database is locked

Additional information

He's running version 3.7.1 with a workshop with 40+ attendees.

When asked Claude about this specific issue, replied back with:

Diagnosis

  The training portal is a Django app backed by SQLite (/opt/app-root/data/db.sqlite3), served by Apache mod_wsgi with multiple worker threads/processes. The DB file lives on a PersistentVolumeClaim (training-portal, mounted at
  /opt/app-root/data).

  database is locked is SQLite's response when a writer can't acquire the write lock within the busy-timeout. The settings give it 15s (settings.py:83), and it still timed out — so this is one of two things, often both:

  1. Write contention burst — a workshop's worth of attendees hitting the portal at the same moment (registration, session start, event/heartbeat writes). SQLite serializes all writes to a single lock. The portal uses the default
  rollback-journal mode (no WAL), so even reads can block writers. Under a burst, one slow write holds the lock past 15s and everyone else errors.
  2. PVC filesystem locking — if that training-portal PVC is backed by NFS / a network filesystem, SQLite's POSIX fcntl locking is unreliable there and throws database is locked even at low concurrency. This is a very common cause
  of this exact error in Educates portals. Worth checking the storage class.

  Right now (live workshop)

  - The error is transient and per-request. The attendee who hit it can just retry/refresh — their next request will usually succeed. No data corruption.
  - If many are stuck, restart the portal pod — it clears any stale lock and re-serializes cleanly:
  kubectl rollout restart deployment/training-portal -n <portal-namespace>
  - Check what's backing the PVC (this tells you if it's the deeper problem):
  kubectl get pvc training-portal -n <portal-namespace> -o jsonpath='{.spec.storageClassName}'
  kubectl get pvc training-portal -n <portal-namespace> -o jsonpath='{.spec.volumeMode}'
  - If the storage class is NFS-backed, that's your culprit — move the portal to a block-storage / local-path storage class.

  Durable fix (code change)

  Two cheap improvements in training-portal/src/project/settings.py. Want me to make these?

  - Enable WAL + a real busy timeout so readers don't block the writer and locks back off properly:
  "OPTIONS": {
      "timeout": 15,
      "init_command": "PRAGMA journal_mode=WAL; PRAGMA synchronous=NORMAL; PRAGMA busy_timeout=15000;",
      "transaction_mode": "IMMEDIATE",  # Django 5.1+; avoids upgrade-lock deadlocks
  },
  - (WAL only helps on a local/block filesystem — it does not fix NFS. If the PVC is NFS-backed, the storage class change is the actual fix.)

  Want me to:
  1. Make the WAL/busy-timeout settings change and open a branch for it, and/or
  2. Dig into where the high-frequency writes come from (the event/ and schedule/ endpoints are already filtered from logs at httpd.conf — they may be the write-heavy path worth throttling)?

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bugSomething isn't working

Type

No type

Projects

Relationships

None yet

Development

No branches or pull requests

Issue actions