New enterprise feature2
This commit is contained in:
@@ -5,6 +5,7 @@ Demo Operations API - Business operations for demo session management
|
||||
from fastapi import APIRouter, Depends, HTTPException, Path
|
||||
import structlog
|
||||
import jwt
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from app.api.schemas import DemoSessionResponse, DemoSessionStats
|
||||
from app.services import DemoSessionManager, DemoCleanupService
|
||||
@@ -83,10 +84,111 @@ async def run_cleanup(
|
||||
db: AsyncSession = Depends(get_db),
|
||||
redis: DemoRedisWrapper = Depends(get_redis)
|
||||
):
|
||||
"""Manually trigger session cleanup (BUSINESS OPERATION - Internal endpoint for CronJob)"""
|
||||
cleanup_service = DemoCleanupService(db, redis)
|
||||
stats = await cleanup_service.cleanup_expired_sessions()
|
||||
return stats
|
||||
"""
|
||||
Trigger session cleanup via background worker (async via Redis queue)
|
||||
|
||||
Returns immediately after enqueuing work - does not block
|
||||
"""
|
||||
from datetime import timedelta
|
||||
from sqlalchemy import select
|
||||
from app.models.demo_session import DemoSession, DemoSessionStatus
|
||||
import uuid
|
||||
import json
|
||||
|
||||
logger.info("Starting demo session cleanup enqueue")
|
||||
|
||||
now = datetime.now(timezone.utc)
|
||||
stuck_threshold = now - timedelta(minutes=5)
|
||||
|
||||
# Find expired sessions
|
||||
result = await db.execute(
|
||||
select(DemoSession).where(
|
||||
DemoSession.status.in_([
|
||||
DemoSessionStatus.PENDING,
|
||||
DemoSessionStatus.READY,
|
||||
DemoSessionStatus.PARTIAL,
|
||||
DemoSessionStatus.FAILED,
|
||||
DemoSessionStatus.ACTIVE
|
||||
]),
|
||||
DemoSession.expires_at < now
|
||||
)
|
||||
)
|
||||
expired_sessions = result.scalars().all()
|
||||
|
||||
# Find stuck sessions
|
||||
stuck_result = await db.execute(
|
||||
select(DemoSession).where(
|
||||
DemoSession.status == DemoSessionStatus.PENDING,
|
||||
DemoSession.created_at < stuck_threshold
|
||||
)
|
||||
)
|
||||
stuck_sessions = stuck_result.scalars().all()
|
||||
|
||||
all_sessions = list(expired_sessions) + list(stuck_sessions)
|
||||
|
||||
if not all_sessions:
|
||||
return {
|
||||
"status": "no_sessions",
|
||||
"message": "No sessions to cleanup",
|
||||
"total_expired": 0,
|
||||
"total_stuck": 0
|
||||
}
|
||||
|
||||
# Create cleanup job
|
||||
job_id = str(uuid.uuid4())
|
||||
session_ids = [s.session_id for s in all_sessions]
|
||||
|
||||
job_data = {
|
||||
"job_id": job_id,
|
||||
"session_ids": session_ids,
|
||||
"created_at": now.isoformat(),
|
||||
"retry_count": 0
|
||||
}
|
||||
|
||||
# Enqueue job
|
||||
client = await redis.get_client()
|
||||
await client.lpush("cleanup:queue", json.dumps(job_data))
|
||||
|
||||
logger.info(
|
||||
"Cleanup job enqueued",
|
||||
job_id=job_id,
|
||||
session_count=len(session_ids),
|
||||
expired_count=len(expired_sessions),
|
||||
stuck_count=len(stuck_sessions)
|
||||
)
|
||||
|
||||
return {
|
||||
"status": "enqueued",
|
||||
"job_id": job_id,
|
||||
"session_count": len(session_ids),
|
||||
"total_expired": len(expired_sessions),
|
||||
"total_stuck": len(stuck_sessions),
|
||||
"message": f"Cleanup job enqueued for {len(session_ids)} sessions"
|
||||
}
|
||||
|
||||
|
||||
@router.get(
|
||||
route_builder.build_operations_route("cleanup/{job_id}", include_tenant_prefix=False),
|
||||
response_model=dict
|
||||
)
|
||||
async def get_cleanup_status(
|
||||
job_id: str,
|
||||
redis: DemoRedisWrapper = Depends(get_redis)
|
||||
):
|
||||
"""Get status of cleanup job"""
|
||||
import json
|
||||
|
||||
client = await redis.get_client()
|
||||
status_key = f"cleanup:job:{job_id}:status"
|
||||
|
||||
status_data = await client.get(status_key)
|
||||
if not status_data:
|
||||
return {
|
||||
"status": "not_found",
|
||||
"message": "Job not found or expired (jobs expire after 1 hour)"
|
||||
}
|
||||
|
||||
return json.loads(status_data)
|
||||
|
||||
|
||||
@router.post(
|
||||
|
||||
Reference in New Issue
Block a user