docs: Update README and add run script for Redis and RQ worker integration
This commit enhances the README with detailed instructions for installing and starting Redis, including commands for various operating systems. It clarifies the automatic startup of the RQ worker with the FastAPI server and updates the project status for Phase 2 features. Additionally, a new script `run_api_with_worker.sh` is introduced to streamline the process of starting the FastAPI server alongside the RQ worker, ensuring a smoother setup for users. The worker now has a unique name to prevent conflicts during execution.
This commit is contained in:
+83
-1
@@ -1,5 +1,11 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from contextlib import asynccontextmanager
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
|
||||
@@ -14,10 +20,86 @@ from src.web.api.tags import router as tags_router
|
||||
from src.web.api.version import router as version_router
|
||||
from src.web.settings import APP_TITLE, APP_VERSION
|
||||
|
||||
# Global worker process (will be set in lifespan)
|
||||
_worker_process: subprocess.Popen | None = None
|
||||
|
||||
|
||||
def start_worker() -> None:
|
||||
"""Start RQ worker in background subprocess."""
|
||||
global _worker_process
|
||||
|
||||
try:
|
||||
from redis import Redis
|
||||
|
||||
# Check Redis connection first
|
||||
redis_conn = Redis(host="localhost", port=6379, db=0, decode_responses=False)
|
||||
redis_conn.ping()
|
||||
|
||||
# Start worker as a subprocess (avoids signal handler issues)
|
||||
project_root = Path(__file__).parent.parent.parent
|
||||
python_executable = sys.executable
|
||||
|
||||
_worker_process = subprocess.Popen(
|
||||
[
|
||||
python_executable,
|
||||
"-m",
|
||||
"src.web.worker",
|
||||
],
|
||||
cwd=str(project_root),
|
||||
stdout=None, # Don't capture - let output go to console
|
||||
stderr=None, # Don't capture - let errors go to console
|
||||
env={
|
||||
**{k: v for k, v in os.environ.items()},
|
||||
"PYTHONPATH": str(project_root),
|
||||
}
|
||||
)
|
||||
# Give it a moment to start, then check if it's still running
|
||||
import time
|
||||
time.sleep(0.5)
|
||||
if _worker_process.poll() is not None:
|
||||
# Process already exited - there was an error
|
||||
print(f"❌ Worker process exited immediately with code {_worker_process.returncode}")
|
||||
print(" Check worker errors above")
|
||||
else:
|
||||
print(f"✅ RQ worker started in background subprocess (PID: {_worker_process.pid})")
|
||||
except Exception as e:
|
||||
print(f"⚠️ Failed to start RQ worker: {e}")
|
||||
print(" Background jobs will not be processed. Ensure Redis is running.")
|
||||
|
||||
|
||||
def stop_worker() -> None:
|
||||
"""Stop RQ worker gracefully."""
|
||||
global _worker_process
|
||||
|
||||
if _worker_process:
|
||||
try:
|
||||
_worker_process.terminate()
|
||||
try:
|
||||
_worker_process.wait(timeout=5)
|
||||
except subprocess.TimeoutExpired:
|
||||
_worker_process.kill()
|
||||
print("✅ RQ worker stopped")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
"""Lifespan context manager for startup and shutdown events."""
|
||||
# Startup
|
||||
start_worker()
|
||||
yield
|
||||
# Shutdown
|
||||
stop_worker()
|
||||
|
||||
|
||||
def create_app() -> FastAPI:
|
||||
"""Create and configure the FastAPI application instance."""
|
||||
app = FastAPI(title=APP_TITLE, version=APP_VERSION)
|
||||
app = FastAPI(
|
||||
title=APP_TITLE,
|
||||
version=APP_VERSION,
|
||||
lifespan=lifespan,
|
||||
)
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
|
||||
+6
-2
@@ -6,8 +6,9 @@ import signal
|
||||
import sys
|
||||
from typing import NoReturn
|
||||
|
||||
import uuid
|
||||
|
||||
from rq import Worker
|
||||
from rq.connections import use_connection
|
||||
from redis import Redis
|
||||
|
||||
from src.web.services.tasks import import_photos_task
|
||||
@@ -24,12 +25,15 @@ def main() -> NoReturn:
|
||||
signal.signal(signal.SIGTERM, _handle_sigterm)
|
||||
signal.signal(signal.SIGINT, _handle_sigterm)
|
||||
|
||||
# Generate unique worker name to avoid conflicts
|
||||
worker_name = f"punimtag-worker-{uuid.uuid4().hex[:8]}"
|
||||
|
||||
# Register tasks with worker
|
||||
# Tasks are imported from services.tasks
|
||||
worker = Worker(
|
||||
["default"],
|
||||
connection=redis_conn,
|
||||
name="punimtag-worker",
|
||||
name=worker_name,
|
||||
)
|
||||
|
||||
# Start worker
|
||||
|
||||
Reference in New Issue
Block a user