Fix: make the global media lock reentrant within a thread

Every caller built its own FileLock on media.lock, so code already holding it could not call into code that takes it again without blocking itself. That made it impossible to take media.lock before opening a transaction in paths that also attach files or move files inside that transaction.

media_lock() hands out one FileLock instance per path with per-thread hold counts, so a thread re-enters while other threads and processes stay excluded. All media.lock call sites now use it.
This commit is contained in:
stumpylog committed 2026-11-03 15:09:29 -08:00
1 parent 08748baf54
commit 3591c39c5f
10 files changed
+252 -15

No files matched your search

+2 -2
View File
@@ -15,7 +15,6 @@ from django.db import transaction
from django.db.models import Max
from django.db.models import Q
from django.utils import timezone
from filelock import FileLock
from rest_framework.reverse import reverse
from documents.barcodes import read_barcode_values
@@ -28,6 +27,7 @@ from documents.file_handling import generate_filename
from documents.file_handling import generate_unique_filename
from documents.file_handling import validate_path_in_root
from documents.loggers import LoggingMixin
from documents.media_lock import media_lock
from documents.models import Correspondent
from documents.models import CustomField
from documents.models import CustomFieldInstance
@@ -695,7 +695,7 @@ class ConsumerPlugin(
# After everything is in the database, copy the files into
# place. If this fails, we'll also rollback the transaction.
with FileLock(settings.MEDIA_LOCK):
with media_lock():
generated_filename = generate_unique_filename(document)
if (
len(str(generated_filename))