refactor(queue): address code review feedback — extract constants and sync refresh interval

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-02-27 00:47:58 +00:00
parent c7c5718f78
commit 4dd018f210
6 changed files with 83 additions and 5 deletions
+1 -1
View File
@@ -15,9 +15,9 @@ from app.api.logs import router as logs_router
from app.api.onedrive import router as onedrive_router from app.api.onedrive import router as onedrive_router
from app.api.openai import router as openai_router from app.api.openai import router as openai_router
from app.api.process import router as process_router from app.api.process import router as process_router
from app.api.queue import router as queue_router
from app.api.search import router as search_router from app.api.search import router as search_router
from app.api.settings import router as settings_router from app.api.settings import router as settings_router
from app.api.queue import router as queue_router
from app.api.url_upload import router as url_upload_router from app.api.url_upload import router as url_upload_router
# Import all the individual routers # Import all the individual routers
+7 -3
View File
@@ -20,6 +20,10 @@ from app.models import FileProcessingStep, FileRecord
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
router = APIRouter(prefix="/queue", tags=["queue"]) router = APIRouter(prefix="/queue", tags=["queue"])
# Constants
CELERY_INSPECT_TIMEOUT = 2.0
MAX_ARGS_DISPLAY_LENGTH = 200
def _get_redis_queue_length(redis_client: redis.Redis, queue_name: str) -> int: def _get_redis_queue_length(redis_client: redis.Redis, queue_name: str) -> int:
"""Get the number of messages in a Redis-backed Celery queue. """Get the number of messages in a Redis-backed Celery queue.
@@ -54,7 +58,7 @@ def _get_celery_inspect_stats() -> dict[str, Any]:
} }
try: try:
inspector = celery.control.inspect(timeout=2.0) inspector = celery.control.inspect(timeout=CELERY_INSPECT_TIMEOUT)
active = inspector.active() or {} active = inspector.active() or {}
reserved = inspector.reserved() or {} reserved = inspector.reserved() or {}
@@ -68,7 +72,7 @@ def _get_celery_inspect_stats() -> dict[str, Any]:
{ {
"id": task.get("id", ""), "id": task.get("id", ""),
"name": task.get("name", "unknown"), "name": task.get("name", "unknown"),
"args": str(task.get("args", []))[:200], "args": str(task.get("args", []))[:MAX_ARGS_DISPLAY_LENGTH],
"started": task.get("time_start"), "started": task.get("time_start"),
} }
) )
@@ -79,7 +83,7 @@ def _get_celery_inspect_stats() -> dict[str, Any]:
{ {
"id": task.get("id", ""), "id": task.get("id", ""),
"name": task.get("name", "unknown"), "name": task.get("name", "unknown"),
"args": str(task.get("args", []))[:200], "args": str(task.get("args", []))[:MAX_ARGS_DISPLAY_LENGTH],
} }
) )
+51
View File
@@ -437,6 +437,57 @@ Errors follow standard HTTP status codes with descriptive messages:
} }
``` ```
## Queue Monitoring
### GET /api/queue/stats
Get comprehensive queue and processing statistics, including Redis queue lengths, Celery worker inspection data, and database-level processing summaries.
**Authentication:** Required
**Response (200 OK):**
```json
{
"queues": {
"document_processor": 12,
"default": 0,
"celery": 0
},
"total_queued": 12,
"celery": {
"active": [
{"id": "abc123", "name": "process_document", "args": "[42]", "started": 1700000000}
],
"reserved": [],
"scheduled": [],
"workers_online": 1
},
"db_summary": {
"total_files": 5000,
"processing": 3,
"failed": 1,
"completed": 4900,
"pending": 96,
"recent_processing": [
{"file_id": 42, "filename": "invoice.pdf", "current_step": "extract_metadata_with_gpt"}
]
}
}
```
### GET /api/queue/pending-count
Lightweight endpoint returning the total number of queued + in-progress items. Designed for the files page banner indicator.
**Authentication:** Required
**Response (200 OK):**
```json
{
"total_pending": 15
}
```
## Rate Limiting ## Rate Limiting
The API implements rate limiting to ensure system stability. If you exceed the limits, you'll receive a `429 Too Many Requests` response. The API implements rate limiting to ensure system stability. If you exceed the limits, you'll receive a `429 Too Many Requests` response.
+17
View File
@@ -92,6 +92,23 @@ The **Files** page provides access to all processed documents:
3. Click on any file to view its details 3. Click on any file to view its details
4. Sort the list by any column by clicking on the column header 4. Sort the list by any column by clicking on the column header
> **Tip:** When documents are being processed, a blue banner appears at the top of the Files page showing how many items are queued or currently processing. Files will appear in the list once their processing completes. Admins can click "View Queue" in the banner to open the Queue Monitor dashboard.
## Queue Monitor (Admin)
The **Queue Monitor** dashboard provides real-time visibility into the document processing pipeline. It is available to admin users under **Admin → Queue Monitor** in the navigation bar.
The dashboard shows:
- **Queued Tasks** — number of tasks waiting in Redis-backed Celery queues
- **Active Tasks** — tasks currently being executed by Celery workers
- **Files Processing** — files with at least one in-progress processing step
- **Workers Online** — number of connected Celery worker processes
- **Redis Queues** — per-queue breakdown of pending task counts
- **Processing Pipeline** — database-level summary of file states (completed, processing, pending, failed)
- **Recently Processing Files** — the most recent files being actively processed, with links to their detail pages
The dashboard auto-refreshes every 10 seconds.
## Searching Documents ## Searching Documents
DocuElevate provides two ways to search your documents: DocuElevate provides two ways to search your documents:
+1
View File
@@ -189,6 +189,7 @@
<script> <script>
(function () { (function () {
const REFRESH_SECONDS = 10; const REFRESH_SECONDS = 10;
document.getElementById('refreshInterval').textContent = REFRESH_SECONDS;
let timer = null; let timer = null;
function escapeHtml(str) { function escapeHtml(str) {
+6 -1
View File
@@ -46,7 +46,12 @@ class TestGetCeleryInspectStats:
"worker1": [{"id": "task-2", "name": "app.tasks.upload_to_s3.upload_to_s3", "args": [2]}] "worker1": [{"id": "task-2", "name": "app.tasks.upload_to_s3.upload_to_s3", "args": [2]}]
} }
mock_inspector.scheduled.return_value = { mock_inspector.scheduled.return_value = {
"worker1": [{"request": {"id": "task-3", "name": "app.tasks.check_credentials.check_credentials"}, "eta": "2026-01-01"}] "worker1": [
{
"request": {"id": "task-3", "name": "app.tasks.check_credentials.check_credentials"},
"eta": "2026-01-01",
}
]
} }
mock_celery_mod.control.inspect.return_value = mock_inspector mock_celery_mod.control.inspect.return_value = mock_inspector