Get the next available work item that isn't completed or locked. Args: worker_lock_timeout_secs: Number of seconds before considering a worker lock stale (default 30 mins) Returns: WorkItem if work is available,
(self, worker_lock_timeout_secs: int = 1800)
| 289 | return os.path.exists(output_file) |
| 290 | |
| 291 | async def get_work(self, worker_lock_timeout_secs: int = 1800) -> Optional[WorkItem]: |
| 292 | """ |
| 293 | Get the next available work item that isn't completed or locked. |
| 294 | |
| 295 | Args: |
| 296 | worker_lock_timeout_secs: Number of seconds before considering |
| 297 | a worker lock stale (default 30 mins) |
| 298 | |
| 299 | Returns: |
| 300 | WorkItem if work is available, None if queue is empty |
| 301 | """ |
| 302 | while True: |
| 303 | try: |
| 304 | work_item = self._queue.get_nowait() |
| 305 | except asyncio.QueueEmpty: |
| 306 | return None |
| 307 | |
| 308 | # Check if work is already completed |
| 309 | if await self.is_completed(work_item.hash): |
| 310 | logger.debug(f"Work item {work_item.hash} already completed, skipping") |
| 311 | self._queue.task_done() |
| 312 | continue |
| 313 | |
| 314 | # Check for worker lock |
| 315 | lock_file = os.path.join(self._locks_dir, f"output_{work_item.hash}.jsonl") |
| 316 | if os.path.exists(lock_file): |
| 317 | # Check modification time |
| 318 | mtime = datetime.datetime.fromtimestamp(os.path.getmtime(lock_file), datetime.timezone.utc) |
| 319 | if (datetime.datetime.now(datetime.timezone.utc) - mtime).total_seconds() > worker_lock_timeout_secs: |
| 320 | # Lock is stale, we can take this work |
| 321 | logger.debug(f"Found stale lock for {work_item.hash}, taking work item") |
| 322 | else: |
| 323 | # Lock is active, skip this work |
| 324 | logger.debug(f"Work item {work_item.hash} is locked by another worker, skipping") |
| 325 | self._queue.task_done() |
| 326 | continue |
| 327 | |
| 328 | # Create our lock file (touch an empty file) |
| 329 | try: |
| 330 | with open(lock_file, "wb") as f: |
| 331 | f.write(b"") |
| 332 | except Exception as e: |
| 333 | logger.warning(f"Failed to create lock file for {work_item.hash}: {e}") |
| 334 | self._queue.task_done() |
| 335 | continue |
| 336 | |
| 337 | return work_item |
| 338 | |
| 339 | async def mark_done(self, work_item: WorkItem) -> None: |
| 340 | """ |
nothing calls this directly
no test coverage detected