|
| 1 | +import tempfile |
| 2 | +import os |
| 3 | + |
1 | 4 | __author__ = "Johannes Köster" |
2 | 5 | __copyright__ = "Copyright 2024, Johannes Köster" |
3 | 6 | __email__ = "johannes.koester@uni-due.de" |
@@ -349,14 +352,29 @@ class CacheableEnvBase(EnvBase, ABC): |
349 | 352 | async def get_cache_assets(self) -> Iterable[str]: ... |
350 | 353 |
|
351 | 354 | @abstractmethod |
352 | | - async def cache_asset(self, asset: str) -> None: |
| 355 | + async def cache_asset(self, asset: str, to_path: Path) -> None: |
353 | 356 | """Retrieve/create and store given asset to self.cache_path.""" |
354 | 357 | ... |
355 | 358 |
|
356 | 359 | async def managed_cache_asset(self, asset: str) -> None: |
| 360 | + # The naming scheme used here follows the same pattern as rsync. |
| 361 | + # This way, we benefit from rsync specific optimizations in network |
| 362 | + # filtesystems like GlusterFS (see |
| 363 | + # https://developers.redhat.com/blog/2018/08/14/improving-rsync-performance-with-glusterfs) |
| 364 | + _, tmp_cache_path = tempfile.mkstemp( |
| 365 | + prefix=asset, suffix=".part", dir=self.cache_path |
| 366 | + ) |
| 367 | + tmp_cache_path = Path(tmp_cache_path) |
| 368 | + cache_path = self.cache_path / asset |
357 | 369 | try: |
358 | | - await self.cache_asset(asset) |
| 370 | + await self.cache_asset(asset, tmp_cache_path) |
| 371 | + os.replace(tmp_cache_path, cache_path) |
359 | 372 | except Exception as e: |
| 373 | + if tmp_cache_path.exists(): |
| 374 | + try: |
| 375 | + tmp_cache_path.unlink() |
| 376 | + except Exception: |
| 377 | + pass |
360 | 378 | raise WorkflowError(f"Caching of {self.spec} failed: {e}") from e |
361 | 379 |
|
362 | 380 | @property |
|
0 commit comments