|
4 | 4 | from datetime import datetime |
5 | 5 |
|
6 | 6 | from app.db.session import async_session_maker |
7 | | -from app.schemas.snapshot import NewSnapshotDTO |
| 7 | +from app.schemas.snapshot import NewSnapshotDTO, RestoreRequestDTO |
8 | 8 | from app.services.epics_service import get_epics_service |
9 | 9 | from app.services.redis_service import get_redis_service |
10 | 10 | from app.services.snapshot_service import SnapshotService |
@@ -111,6 +111,92 @@ async def progress_update(current: int, total: int, message: str): |
111 | 111 | logger.exception(f"Failed to update job status: {inner_e}") |
112 | 112 |
|
113 | 113 |
|
| 114 | +async def run_snapshot_restore( |
| 115 | + job_id: str, |
| 116 | + snapshot_id: str, |
| 117 | + pv_ids: list[str] | None = None, |
| 118 | +) -> None: |
| 119 | + """ |
| 120 | + Background task to restore a snapshot. |
| 121 | +
|
| 122 | + This runs in a separate asyncio task and uses its own database session. |
| 123 | + """ |
| 124 | + logger.info(f"Background task started for job {job_id}: Restoring snapshot '{snapshot_id}'") |
| 125 | + |
| 126 | + async with async_session_maker() as session: |
| 127 | + try: |
| 128 | + job_repo = JobRepository(session) |
| 129 | + |
| 130 | + # Mark job as running |
| 131 | + await job_repo.mark_running(job_id) |
| 132 | + await session.commit() |
| 133 | + await asyncio.sleep(0) |
| 134 | + |
| 135 | + # Initial progress update |
| 136 | + await job_repo.update_progress(job_id, 5, "Loading snapshot values...") |
| 137 | + await session.commit() |
| 138 | + await asyncio.sleep(0) |
| 139 | + |
| 140 | + epics = get_epics_service() |
| 141 | + snapshot_service = SnapshotService(session, epics) |
| 142 | + |
| 143 | + # Optional restore request |
| 144 | + request = RestoreRequestDTO(pvIds=pv_ids) if pv_ids else None |
| 145 | + |
| 146 | + last_update = {"progress": 5, "last_time": datetime.now()} |
| 147 | + |
| 148 | + async def progress_update(current: int, total: int, message: str): |
| 149 | + try: |
| 150 | + write_progress = int((current / total) * 85) if total > 0 else 0 |
| 151 | + job_progress = 10 + write_progress |
| 152 | + |
| 153 | + now = datetime.now() |
| 154 | + time_elapsed = (now - last_update["last_time"]).total_seconds() |
| 155 | + progress_changed = job_progress - last_update["progress"] >= 2 |
| 156 | + |
| 157 | + if progress_changed or time_elapsed >= 2.0 or current >= total: |
| 158 | + last_update["progress"] = job_progress |
| 159 | + last_update["last_time"] = now |
| 160 | + await job_repo.update_progress(job_id, job_progress, message) |
| 161 | + await session.commit() |
| 162 | + await asyncio.sleep(0) |
| 163 | + except Exception as e: |
| 164 | + logger.error(f"Error in restore progress_update: {e}") |
| 165 | + |
| 166 | + result = await snapshot_service.restore_snapshot( |
| 167 | + snapshot_id, |
| 168 | + request, |
| 169 | + progress_callback=progress_update, |
| 170 | + ) |
| 171 | + |
| 172 | + # Final completion update |
| 173 | + completion_message = f"Restore completed: {result.successCount}/{result.totalPVs} PVs restored" + ( |
| 174 | + f", {result.failureCount} failed" if result.failureCount > 0 else "" |
| 175 | + ) |
| 176 | + |
| 177 | + await job_repo.mark_completed( |
| 178 | + job_id, |
| 179 | + message=completion_message, |
| 180 | + ) |
| 181 | + await session.commit() |
| 182 | + |
| 183 | + logger.info( |
| 184 | + f"Background restore completed for job {job_id}: " |
| 185 | + f"{result.successCount}/{result.totalPVs} succeeded, " |
| 186 | + f"{result.failureCount} failed" |
| 187 | + ) |
| 188 | + |
| 189 | + except Exception as e: |
| 190 | + logger.exception(f"Background restore failed for job {job_id}: {e}") |
| 191 | + error_msg = f"{type(e).__name__}: {str(e)}" |
| 192 | + try: |
| 193 | + await session.rollback() |
| 194 | + await job_repo.mark_failed(job_id, error_msg) |
| 195 | + await session.commit() |
| 196 | + except Exception as inner_e: |
| 197 | + logger.exception(f"Failed to update restore job status: {inner_e}") |
| 198 | + |
| 199 | + |
114 | 200 | def schedule_snapshot_creation(job_id: str, title: str, description: str | None = None, use_cache: bool = True) -> None: |
115 | 201 | """ |
116 | 202 | Schedule a snapshot creation task to run in the background. |
|
0 commit comments