]> git.ipfire.org Git - thirdparty/paperless-ngx.git/commitdiff
Fix: don't re-queue a consume-folder file that is already queued and awaiting consump... dev
authorTrenton H <797416+stumpylog@users.noreply.github.com>
Mon, 3 Aug 2026 16:55:12 +0000 (09:55 -0700)
committerGitHub <noreply@github.com>
Mon, 3 Aug 2026 16:55:12 +0000 (09:55 -0700)
src/documents/management/commands/document_consumer.py
src/documents/tests/test_management_consumer.py

index 2e6b414d96e7dacbba5dafd321db883abd301c04..7e3900842da042e132dc39b7eef20ad34b4f5785 100644 (file)
@@ -632,8 +632,20 @@ class Command(BaseCommand):
                     # Process each change
                     for change_type, path in changes:
                         path = Path(path).resolve()
+                        if change_type == Change.deleted:
+                            # Consumed (or otherwise removed); a later file
+                            # reusing this name must not be skipped as
+                            # already-queued.
+                            queued.discard(path)
                         if not path.is_file():
                             continue
+                        if path in queued:
+                            # Already queued and awaiting consumption; a stray
+                            # event (NAS metadata touch, AV scan, etc.) while
+                            # the file sits on disk mid-consumption must not
+                            # cause it to be queued a second time (GH #13511).
+                            logger.debug(f"Ignoring event for queued file: {path}")
+                            continue
                         logger.debug(f"Event: {change_type.name} for {path}")
                         tracker.track(path, change_type)
 
index a5f8fb350c7e8d2e538db5373b6da77f7fdaae76..3efe4490e3d534e298a81605d4597be2ae28cfa4 100644 (file)
@@ -880,6 +880,41 @@ class TestCommandWatch:
         ]
         assert call_args.original_file.name == "valid.pdf"
 
+    def test_ignores_event_for_already_queued_file(
+        self,
+        consumption_dir: Path,
+        sample_pdf: Path,
+        mock_consume_file_delay: MagicMock,
+        start_consumer: Callable[..., ConsumerThread],
+    ) -> None:
+        """
+        A stray filesystem event (NAS metadata touch, AV scan, etc.) on a
+        file that has already been queued and is awaiting consumption must
+        not cause it to be queued a second time (GH #13511). The task is
+        mocked, so the file is never removed from disk, mirroring a
+        long-running OCR job still holding it.
+        """
+        thread = start_consumer()
+
+        target = consumption_dir / "document.pdf"
+        shutil.copy(sample_pdf, target)
+
+        wait_for_mock_call(mock_consume_file_delay.apply_async, timeout_s=2.0)
+
+        if thread.exception:
+            raise thread.exception
+
+        assert mock_consume_file_delay.apply_async.call_count == 1
+
+        # Simulate a stray event on the still-present, already-queued file.
+        target.touch()
+        sleep(0.5)
+
+        if thread.exception:
+            raise thread.exception
+
+        assert mock_consume_file_delay.apply_async.call_count == 1
+
     @pytest.mark.django_db
     @pytest.mark.usefixtures("mock_supported_extensions")
     def test_stop_flag_stops_consumer(