Merge pull request 'fix: saas restart logic' (#13) from furqan into dev
Reviewed-on: https://gitea.maskantech.in/gitea_admin/saas_backend/pulls/13
This commit is contained in:
+41
-18
@@ -31,21 +31,41 @@ logging.basicConfig(
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
async def wait_for_db(retries: int = 10, base_delay: float = 3.0, max_delay: float = 30.0):
|
||||
"""Wait for the database to become available with exponential backoff."""
|
||||
for attempt in range(1, retries + 1):
|
||||
try:
|
||||
with engine.connect() as conn:
|
||||
conn.execute(text("SELECT 1"))
|
||||
logger.info("Database connection successful!")
|
||||
return
|
||||
except Exception as e:
|
||||
if attempt == retries:
|
||||
logger.error(f"Database unreachable after {retries} attempts: {e}")
|
||||
raise
|
||||
delay = min(base_delay * (2 ** (attempt - 1)), max_delay)
|
||||
logger.warning(
|
||||
f"Database not ready (attempt {attempt}/{retries}): {e}. "
|
||||
f"Retrying in {delay:.0f}s..."
|
||||
)
|
||||
await asyncio.sleep(delay)
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
logger.info("Testing database connection...")
|
||||
try:
|
||||
with engine.connect() as conn:
|
||||
conn.execute(text("SELECT 1"))
|
||||
conn.commit()
|
||||
logger.info("Database connection successful!")
|
||||
except Exception as e:
|
||||
logger.error(f"Database connection failed: {e}")
|
||||
raise
|
||||
logger.info("Starting SaaS application...")
|
||||
|
||||
await redis_client.connect()
|
||||
await run_in_threadpool(sync_redis_client.connect)
|
||||
await wait_for_db()
|
||||
|
||||
app.state.redis_available = False
|
||||
|
||||
try:
|
||||
await redis_client.connect()
|
||||
await run_in_threadpool(sync_redis_client.connect)
|
||||
app.state.redis_available = True
|
||||
logger.info("Redis connected successfully.")
|
||||
except Exception as e:
|
||||
logger.error(f"Redis unavailable at startup: {e}")
|
||||
logger.warning("Continuing without Redis — event queue disabled.")
|
||||
|
||||
logger.info(
|
||||
f"{settings.PROJECT_NAME} v{settings.VERSION} started ({settings.APP_ENV})"
|
||||
@@ -55,9 +75,9 @@ async def lifespan(app: FastAPI):
|
||||
logger.info("Redis Event Consumer STARTED")
|
||||
while True:
|
||||
try:
|
||||
if not redis_client.client:
|
||||
await asyncio.sleep(5)
|
||||
continue
|
||||
if not app.state.redis_available or not redis_client.client:
|
||||
await asyncio.sleep(5)
|
||||
continue
|
||||
|
||||
result = await redis_client.client.blpop("saas:events:queue", timeout=5)
|
||||
|
||||
@@ -69,7 +89,7 @@ async def lifespan(app: FastAPI):
|
||||
except Exception as e:
|
||||
logger.error(f"Error processing event {event_id}: {e}")
|
||||
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
await asyncio.sleep(1)
|
||||
|
||||
async def fallback_poller():
|
||||
@@ -94,9 +114,12 @@ async def lifespan(app: FastAPI):
|
||||
for task in background_tasks:
|
||||
task.cancel()
|
||||
await asyncio.gather(*background_tasks, return_exceptions=True)
|
||||
await redis_client.close()
|
||||
await run_in_threadpool(sync_redis_client.close)
|
||||
|
||||
if app.state.redis_available:
|
||||
try:
|
||||
await redis_client.close()
|
||||
await run_in_threadpool(sync_redis_client.close)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def create_app() -> FastAPI:
|
||||
app = FastAPI(
|
||||
|
||||
Reference in New Issue
Block a user