hpcflow-new2 0.2.0a159__py3-none-any.whl → 0.2.0a160__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hpcflow/_version.py +1 -1
- hpcflow/sdk/__init__.py +2 -0
- hpcflow/sdk/app.py +89 -15
- hpcflow/sdk/cli.py +18 -0
- hpcflow/sdk/cli_common.py +16 -0
- hpcflow/sdk/core/actions.py +14 -6
- hpcflow/sdk/core/command_files.py +4 -4
- hpcflow/sdk/core/element.py +15 -16
- hpcflow/sdk/core/run_dir_files.py +63 -0
- hpcflow/sdk/core/task.py +34 -35
- hpcflow/sdk/core/utils.py +37 -0
- hpcflow/sdk/core/workflow.py +144 -49
- hpcflow/sdk/demo/cli.py +12 -0
- hpcflow/sdk/log.py +2 -2
- hpcflow/sdk/persistence/base.py +140 -12
- hpcflow/sdk/persistence/json.py +84 -63
- hpcflow/sdk/persistence/pending.py +21 -7
- hpcflow/sdk/persistence/zarr.py +143 -108
- hpcflow/sdk/submission/jobscript.py +22 -4
- hpcflow/sdk/submission/shells/bash.py +2 -2
- hpcflow/sdk/submission/shells/powershell.py +2 -2
- hpcflow/sdk/submission/submission.py +20 -7
- hpcflow/tests/scripts/test_main_scripts.py +40 -0
- hpcflow/tests/unit/test_utils.py +28 -0
- {hpcflow_new2-0.2.0a159.dist-info → hpcflow_new2-0.2.0a160.dist-info}/METADATA +1 -1
- {hpcflow_new2-0.2.0a159.dist-info → hpcflow_new2-0.2.0a160.dist-info}/RECORD +28 -27
- {hpcflow_new2-0.2.0a159.dist-info → hpcflow_new2-0.2.0a160.dist-info}/WHEEL +0 -0
- {hpcflow_new2-0.2.0a159.dist-info → hpcflow_new2-0.2.0a160.dist-info}/entry_points.txt +0 -0
hpcflow/sdk/persistence/base.py
CHANGED
@@ -9,10 +9,11 @@ from datetime import datetime, timezone
|
|
9
9
|
import enum
|
10
10
|
import os
|
11
11
|
from pathlib import Path
|
12
|
+
import re
|
12
13
|
import shutil
|
13
14
|
import socket
|
14
15
|
import time
|
15
|
-
from typing import Any, Dict, Iterable, List, Optional, Tuple, TypeVar, Union
|
16
|
+
from typing import Any, Dict, Iterable, List, Optional, Set, Tuple, TypeVar, Union
|
16
17
|
|
17
18
|
from hpcflow.sdk.core.utils import (
|
18
19
|
flatten,
|
@@ -107,6 +108,7 @@ class StoreTask:
|
|
107
108
|
"""
|
108
109
|
return cls(is_pending=False, **task_dat)
|
109
110
|
|
111
|
+
@TimeIt.decorator
|
110
112
|
def append_element_IDs(self: AnySTask, pend_IDs: List[int]) -> AnySTask:
|
111
113
|
"""Return a copy, with additional element IDs."""
|
112
114
|
elem_IDs = self.element_IDs[:] + pend_IDs
|
@@ -164,6 +166,7 @@ class StoreElement:
|
|
164
166
|
"iterations": iters,
|
165
167
|
}
|
166
168
|
|
169
|
+
@TimeIt.decorator
|
167
170
|
def append_iteration_IDs(self: AnySElement, pend_IDs: List[int]) -> AnySElement:
|
168
171
|
"""Return a copy, with additional iteration IDs."""
|
169
172
|
iter_IDs = self.iteration_IDs[:] + pend_IDs
|
@@ -234,6 +237,7 @@ class StoreElementIter:
|
|
234
237
|
"loop_idx": self.loop_idx,
|
235
238
|
}
|
236
239
|
|
240
|
+
@TimeIt.decorator
|
237
241
|
def append_EAR_IDs(
|
238
242
|
self: AnySElementIter, pend_IDs: Dict[int, List[int]]
|
239
243
|
) -> AnySElementIter:
|
@@ -256,6 +260,7 @@ class StoreElementIter:
|
|
256
260
|
EARs_initialised=self.EARs_initialised,
|
257
261
|
)
|
258
262
|
|
263
|
+
@TimeIt.decorator
|
259
264
|
def update_loop_idx(
|
260
265
|
self: AnySElementIter, loop_idx: Dict[str, int]
|
261
266
|
) -> AnySElementIter:
|
@@ -273,6 +278,7 @@ class StoreElementIter:
|
|
273
278
|
loop_idx=loop_idx_new,
|
274
279
|
)
|
275
280
|
|
281
|
+
@TimeIt.decorator
|
276
282
|
def set_EARs_initialised(self: AnySElementIter) -> AnySElementIter:
|
277
283
|
"""Return a copy with `EARs_initialised` set to `True`."""
|
278
284
|
return self.__class__(
|
@@ -378,6 +384,7 @@ class StoreEAR:
|
|
378
384
|
"run_hostname": self.run_hostname,
|
379
385
|
}
|
380
386
|
|
387
|
+
@TimeIt.decorator
|
381
388
|
def update(
|
382
389
|
self,
|
383
390
|
submission_idx: Optional[int] = None,
|
@@ -655,6 +662,10 @@ class PersistentStore(ABC):
|
|
655
662
|
self._resources_in_use = set()
|
656
663
|
self._in_batch_mode = False
|
657
664
|
|
665
|
+
self._use_cache = False
|
666
|
+
self._cache = None
|
667
|
+
self._reset_cache()
|
668
|
+
|
658
669
|
@property
|
659
670
|
def logger(self):
|
660
671
|
return self.app.persistence_logger
|
@@ -672,6 +683,70 @@ class PersistentStore(ABC):
|
|
672
683
|
"""Does this store support workflow submission?"""
|
673
684
|
return self.fs.__class__.__name__ == "LocalFileSystem"
|
674
685
|
|
686
|
+
@property
|
687
|
+
def use_cache(self):
|
688
|
+
return self._use_cache
|
689
|
+
|
690
|
+
@property
|
691
|
+
def task_cache(self):
|
692
|
+
"""Cache for persistent tasks."""
|
693
|
+
return self._cache["tasks"]
|
694
|
+
|
695
|
+
@property
|
696
|
+
def element_cache(self):
|
697
|
+
"""Cache for persistent elements."""
|
698
|
+
return self._cache["elements"]
|
699
|
+
|
700
|
+
@property
|
701
|
+
def element_iter_cache(self):
|
702
|
+
"""Cache for persistent element iterations."""
|
703
|
+
return self._cache["element_iters"]
|
704
|
+
|
705
|
+
@property
|
706
|
+
def EAR_cache(self):
|
707
|
+
"""Cache for persistent EARs."""
|
708
|
+
return self._cache["EARs"]
|
709
|
+
|
710
|
+
@property
|
711
|
+
def num_tasks_cache(self):
|
712
|
+
"""Cache for number of persistent tasks."""
|
713
|
+
return self._cache["num_tasks"]
|
714
|
+
|
715
|
+
@property
|
716
|
+
def param_sources_cache(self):
|
717
|
+
"""Cache for persistent parameter sources."""
|
718
|
+
return self._cache["param_sources"]
|
719
|
+
|
720
|
+
@property
|
721
|
+
def parameter_cache(self):
|
722
|
+
"""Cache for persistent parameters."""
|
723
|
+
return self._cache["parameters"]
|
724
|
+
|
725
|
+
@num_tasks_cache.setter
|
726
|
+
def num_tasks_cache(self, value):
|
727
|
+
self._cache["num_tasks"] = value
|
728
|
+
|
729
|
+
def _reset_cache(self):
|
730
|
+
self._cache = {
|
731
|
+
"tasks": {},
|
732
|
+
"elements": {},
|
733
|
+
"element_iters": {},
|
734
|
+
"EARs": {},
|
735
|
+
"param_sources": {},
|
736
|
+
"num_tasks": None,
|
737
|
+
"parameters": {},
|
738
|
+
}
|
739
|
+
|
740
|
+
@contextlib.contextmanager
|
741
|
+
def cache_ctx(self):
|
742
|
+
"""Context manager for using the persistent element/iteration/run cache."""
|
743
|
+
self._use_cache = True
|
744
|
+
try:
|
745
|
+
yield
|
746
|
+
finally:
|
747
|
+
self._use_cache = False
|
748
|
+
self._reset_cache()
|
749
|
+
|
675
750
|
@staticmethod
|
676
751
|
def prepare_test_store_from_spec(task_spec):
|
677
752
|
"""Generate a valid store from a specification in terms of nested
|
@@ -975,9 +1050,7 @@ class PersistentStore(ABC):
|
|
975
1050
|
|
976
1051
|
def set_EAR_start(self, EAR_ID: int, save: bool = True) -> datetime:
|
977
1052
|
dt = datetime.utcnow()
|
978
|
-
|
979
|
-
snapshot.take(".")
|
980
|
-
ss_js = snapshot.to_json_like()
|
1053
|
+
ss_js = self.app.RunDirAppFiles.take_snapshot()
|
981
1054
|
run_hostname = socket.gethostname()
|
982
1055
|
self._pending.set_EAR_starts[EAR_ID] = (dt, ss_js, run_hostname)
|
983
1056
|
if save:
|
@@ -989,9 +1062,7 @@ class PersistentStore(ABC):
|
|
989
1062
|
) -> datetime:
|
990
1063
|
# TODO: save output files
|
991
1064
|
dt = datetime.utcnow()
|
992
|
-
|
993
|
-
snapshot.take(".")
|
994
|
-
ss_js = snapshot.to_json_like()
|
1065
|
+
ss_js = self.app.RunDirAppFiles.take_snapshot()
|
995
1066
|
self._pending.set_EAR_ends[EAR_ID] = (dt, ss_js, exit_code, success)
|
996
1067
|
if save:
|
997
1068
|
self.save()
|
@@ -1247,6 +1318,7 @@ class PersistentStore(ABC):
|
|
1247
1318
|
def _get_task_id_to_idx_map(self) -> Dict[int, int]:
|
1248
1319
|
return {i.id_: i.index for i in self.get_tasks()}
|
1249
1320
|
|
1321
|
+
@TimeIt.decorator
|
1250
1322
|
def get_task(self, task_idx: int) -> AnySTask:
|
1251
1323
|
return self.get_tasks()[task_idx]
|
1252
1324
|
|
@@ -1289,10 +1361,10 @@ class PersistentStore(ABC):
|
|
1289
1361
|
|
1290
1362
|
return self._process_retrieved_tasks(tasks)
|
1291
1363
|
|
1364
|
+
@TimeIt.decorator
|
1292
1365
|
def get_tasks(self) -> List[AnySTask]:
|
1293
1366
|
"""Retrieve all tasks, including pending."""
|
1294
|
-
|
1295
|
-
tasks = self._get_persistent_tasks()
|
1367
|
+
tasks = self._get_persistent_tasks(range(self._get_num_persistent_tasks()))
|
1296
1368
|
tasks.update({k: v for k, v in self._pending.add_tasks.items()})
|
1297
1369
|
|
1298
1370
|
# order by index:
|
@@ -1328,6 +1400,7 @@ class PersistentStore(ABC):
|
|
1328
1400
|
|
1329
1401
|
return self._process_retrieved_loops(loops)
|
1330
1402
|
|
1403
|
+
@TimeIt.decorator
|
1331
1404
|
def get_submissions(self) -> Dict[int, Dict]:
|
1332
1405
|
"""Retrieve all submissions, including pending."""
|
1333
1406
|
|
@@ -1339,6 +1412,7 @@ class PersistentStore(ABC):
|
|
1339
1412
|
|
1340
1413
|
return subs
|
1341
1414
|
|
1415
|
+
@TimeIt.decorator
|
1342
1416
|
def get_submissions_by_ID(self, id_lst: Iterable[int]) -> Dict[int, Dict]:
|
1343
1417
|
# separate pending and persistent IDs:
|
1344
1418
|
id_set = set(id_lst)
|
@@ -1354,6 +1428,7 @@ class PersistentStore(ABC):
|
|
1354
1428
|
|
1355
1429
|
return subs
|
1356
1430
|
|
1431
|
+
@TimeIt.decorator
|
1357
1432
|
def get_elements(self, id_lst: Iterable[int]) -> List[AnySElement]:
|
1358
1433
|
self.logger.debug(f"PersistentStore.get_elements: id_lst={id_lst!r}")
|
1359
1434
|
|
@@ -1380,6 +1455,7 @@ class PersistentStore(ABC):
|
|
1380
1455
|
|
1381
1456
|
return elems_new
|
1382
1457
|
|
1458
|
+
@TimeIt.decorator
|
1383
1459
|
def get_element_iterations(self, id_lst: Iterable[int]) -> List[AnySElementIter]:
|
1384
1460
|
self.logger.debug(f"PersistentStore.get_element_iterations: id_lst={id_lst!r}")
|
1385
1461
|
|
@@ -1415,6 +1491,7 @@ class PersistentStore(ABC):
|
|
1415
1491
|
|
1416
1492
|
return iters_new
|
1417
1493
|
|
1494
|
+
@TimeIt.decorator
|
1418
1495
|
def get_EARs(self, id_lst: Iterable[int]) -> List[AnySEAR]:
|
1419
1496
|
self.logger.debug(f"PersistentStore.get_EARs: id_lst={id_lst!r}")
|
1420
1497
|
|
@@ -1459,10 +1536,51 @@ class PersistentStore(ABC):
|
|
1459
1536
|
|
1460
1537
|
return EARs_new
|
1461
1538
|
|
1539
|
+
@TimeIt.decorator
|
1540
|
+
def _get_cached_persistent_items(
|
1541
|
+
self, id_lst: Iterable[int], cache: Dict
|
1542
|
+
) -> Tuple[Dict[int, Any], List[int]]:
|
1543
|
+
id_lst = list(id_lst)
|
1544
|
+
if self.use_cache:
|
1545
|
+
id_set = set(id_lst)
|
1546
|
+
all_cached = set(cache.keys())
|
1547
|
+
id_cached = id_set.intersection(all_cached)
|
1548
|
+
id_non_cached = list(id_set.difference(all_cached))
|
1549
|
+
items = {k: cache[k] for k in id_cached}
|
1550
|
+
else:
|
1551
|
+
items = {}
|
1552
|
+
id_non_cached = id_lst
|
1553
|
+
return items, id_non_cached
|
1554
|
+
|
1555
|
+
def _get_cached_persistent_EARs(
|
1556
|
+
self, id_lst: Iterable[int]
|
1557
|
+
) -> Tuple[Dict[int, AnySEAR], List[int]]:
|
1558
|
+
return self._get_cached_persistent_items(id_lst, self.EAR_cache)
|
1559
|
+
|
1560
|
+
def _get_cached_persistent_element_iters(
|
1561
|
+
self, id_lst: Iterable[int]
|
1562
|
+
) -> Tuple[Dict[int, AnySEAR], List[int]]:
|
1563
|
+
return self._get_cached_persistent_items(id_lst, self.element_iter_cache)
|
1564
|
+
|
1565
|
+
def _get_cached_persistent_elements(
|
1566
|
+
self, id_lst: Iterable[int]
|
1567
|
+
) -> Tuple[Dict[int, AnySEAR], List[int]]:
|
1568
|
+
return self._get_cached_persistent_items(id_lst, self.element_cache)
|
1569
|
+
|
1570
|
+
def _get_cached_persistent_tasks(self, id_lst: Iterable[int]):
|
1571
|
+
return self._get_cached_persistent_items(id_lst, self.task_cache)
|
1572
|
+
|
1573
|
+
def _get_cached_persistent_param_sources(self, id_lst: Iterable[int]):
|
1574
|
+
return self._get_cached_persistent_items(id_lst, self.param_sources_cache)
|
1575
|
+
|
1576
|
+
def _get_cached_persistent_parameters(self, id_lst: Iterable[int]):
|
1577
|
+
return self._get_cached_persistent_items(id_lst, self.parameter_cache)
|
1578
|
+
|
1462
1579
|
def get_EAR_skipped(self, EAR_ID: int) -> bool:
|
1463
1580
|
self.logger.debug(f"PersistentStore.get_EAR_skipped: EAR_ID={EAR_ID!r}")
|
1464
1581
|
return self.get_EARs([EAR_ID])[0].skip
|
1465
1582
|
|
1583
|
+
@TimeIt.decorator
|
1466
1584
|
def get_parameters(
|
1467
1585
|
self,
|
1468
1586
|
id_lst: Iterable[int],
|
@@ -1489,6 +1607,7 @@ class PersistentStore(ABC):
|
|
1489
1607
|
|
1490
1608
|
return params
|
1491
1609
|
|
1610
|
+
@TimeIt.decorator
|
1492
1611
|
def get_parameter_set_statuses(self, id_lst: Iterable[int]) -> List[bool]:
|
1493
1612
|
# separate pending and persistent IDs:
|
1494
1613
|
id_set = set(id_lst)
|
@@ -1502,6 +1621,7 @@ class PersistentStore(ABC):
|
|
1502
1621
|
# order as requested:
|
1503
1622
|
return [set_status[id_] for id_ in id_lst]
|
1504
1623
|
|
1624
|
+
@TimeIt.decorator
|
1505
1625
|
def get_parameter_sources(self, id_lst: Iterable[int]) -> List[Dict]:
|
1506
1626
|
# separate pending and persistent IDs:
|
1507
1627
|
id_set = set(id_lst)
|
@@ -1525,15 +1645,23 @@ class PersistentStore(ABC):
|
|
1525
1645
|
|
1526
1646
|
return src_new
|
1527
1647
|
|
1528
|
-
|
1529
|
-
|
1648
|
+
@TimeIt.decorator
|
1649
|
+
def get_task_elements(
|
1650
|
+
self,
|
1651
|
+
task_id,
|
1652
|
+
idx_lst: Optional[Iterable[int]] = None,
|
1653
|
+
) -> List[Dict]:
|
1654
|
+
"""Get element data by an indices within a given task.
|
1530
1655
|
|
1531
1656
|
Element iterations and EARs belonging to the elements are included.
|
1532
1657
|
|
1533
1658
|
"""
|
1534
1659
|
|
1535
1660
|
all_elem_IDs = self.get_task(task_id).element_IDs
|
1536
|
-
|
1661
|
+
if idx_lst is None:
|
1662
|
+
req_IDs = all_elem_IDs
|
1663
|
+
else:
|
1664
|
+
req_IDs = [all_elem_IDs[i] for i in idx_lst]
|
1537
1665
|
store_elements = self.get_elements(req_IDs)
|
1538
1666
|
iter_IDs = [i.iteration_IDs for i in store_elements]
|
1539
1667
|
iter_IDs_flat, iter_IDs_lens = flatten(iter_IDs)
|
hpcflow/sdk/persistence/json.py
CHANGED
@@ -229,9 +229,10 @@ class JSONPersistentStore(PersistentStore):
|
|
229
229
|
with self.using_resource("metadata", action="update") as md:
|
230
230
|
md["runs"].extend(i.encode(self.ts_fmt) for i in EARs)
|
231
231
|
|
232
|
-
def
|
232
|
+
def _update_EAR_submission_indices(self, sub_indices: Dict[int, int]):
|
233
233
|
with self.using_resource("metadata", action="update") as md:
|
234
|
-
|
234
|
+
for EAR_ID_i, sub_idx_i in sub_indices.items():
|
235
|
+
md["runs"][EAR_ID_i]["submission_idx"] = sub_idx_i
|
235
236
|
|
236
237
|
def _update_EAR_start(self, EAR_id: int, s_time: datetime, s_snap: Dict, s_hn: str):
|
237
238
|
with self.using_resource("metadata", action="update") as md:
|
@@ -264,20 +265,6 @@ class JSONPersistentStore(PersistentStore):
|
|
264
265
|
params["data"][str(param_i.id_)] = param_i.encode()
|
265
266
|
params["sources"][str(param_i.id_)] = param_i.source
|
266
267
|
|
267
|
-
def _set_parameter_value(self, param_id: int, value: Any, is_file: bool):
|
268
|
-
"""Set an unset persistent parameter."""
|
269
|
-
|
270
|
-
# the `decode` call in `_get_persistent_parameters` should be quick:
|
271
|
-
param = self._get_persistent_parameters([param_id])[param_id]
|
272
|
-
if is_file:
|
273
|
-
param = param.set_file(value)
|
274
|
-
else:
|
275
|
-
param = param.set_data(value)
|
276
|
-
|
277
|
-
with self.using_resource("parameters", "update") as params:
|
278
|
-
# no need to update sources array:
|
279
|
-
params["data"][str(param_id)] = param.encode()
|
280
|
-
|
281
268
|
def _set_parameter_values(self, set_parameters: Dict[int, Tuple[Any, bool]]):
|
282
269
|
"""Set multiple unset persistent parameters."""
|
283
270
|
param_ids = list(set_parameters.keys())
|
@@ -310,8 +297,13 @@ class JSONPersistentStore(PersistentStore):
|
|
310
297
|
|
311
298
|
def _get_num_persistent_tasks(self) -> int:
|
312
299
|
"""Get the number of persistent tasks."""
|
313
|
-
|
314
|
-
|
300
|
+
if self.num_tasks_cache is not None:
|
301
|
+
num = self.num_tasks_cache
|
302
|
+
else:
|
303
|
+
with self.using_resource("metadata", action="read") as md:
|
304
|
+
num = len(md["tasks"])
|
305
|
+
self.num_tasks_cache = num
|
306
|
+
return num
|
315
307
|
|
316
308
|
def _get_num_persistent_loops(self) -> int:
|
317
309
|
"""Get the number of persistent loops."""
|
@@ -386,16 +378,18 @@ class JSONPersistentStore(PersistentStore):
|
|
386
378
|
with self.using_resource("metadata", "read") as md:
|
387
379
|
return md["template"]
|
388
380
|
|
389
|
-
def _get_persistent_tasks(
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
381
|
+
def _get_persistent_tasks(self, id_lst: Iterable[int]) -> Dict[int, StoreTask]:
|
382
|
+
tasks, id_lst = self._get_cached_persistent_tasks(id_lst)
|
383
|
+
if id_lst:
|
384
|
+
with self.using_resource("metadata", action="read") as md:
|
385
|
+
new_tasks = {
|
386
|
+
i["id_"]: StoreTask.decode({**i, "index": idx})
|
387
|
+
for idx, i in enumerate(md["tasks"])
|
388
|
+
if id_lst is None or i["id_"] in id_lst
|
389
|
+
}
|
390
|
+
self.task_cache.update(new_tasks)
|
391
|
+
tasks.update(new_tasks)
|
392
|
+
return tasks
|
399
393
|
|
400
394
|
def _get_persistent_loops(self, id_lst: Optional[Iterable[int]] = None):
|
401
395
|
with self.using_resource("metadata", "read") as md:
|
@@ -428,54 +422,81 @@ class JSONPersistentStore(PersistentStore):
|
|
428
422
|
return subs_dat
|
429
423
|
|
430
424
|
def _get_persistent_elements(self, id_lst: Iterable[int]) -> Dict[int, StoreElement]:
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
425
|
+
elems, id_lst = self._get_cached_persistent_elements(id_lst)
|
426
|
+
if id_lst:
|
427
|
+
# could convert `id_lst` to e.g. slices if more efficient for a given store
|
428
|
+
with self.using_resource("metadata", action="read") as md:
|
429
|
+
try:
|
430
|
+
elem_dat = {i: md["elements"][i] for i in id_lst}
|
431
|
+
except KeyError:
|
432
|
+
raise MissingStoreElementError(id_lst) from None
|
433
|
+
new_elems = {k: StoreElement.decode(v) for k, v in elem_dat.items()}
|
434
|
+
self.element_cache.update(new_elems)
|
435
|
+
elems.update(new_elems)
|
436
|
+
return elems
|
438
437
|
|
439
438
|
def _get_persistent_element_iters(
|
440
439
|
self, id_lst: Iterable[int]
|
441
440
|
) -> Dict[int, StoreElementIter]:
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
441
|
+
iters, id_lst = self._get_cached_persistent_element_iters(id_lst)
|
442
|
+
if id_lst:
|
443
|
+
with self.using_resource("metadata", action="read") as md:
|
444
|
+
try:
|
445
|
+
iter_dat = {i: md["iters"][i] for i in id_lst}
|
446
|
+
except KeyError:
|
447
|
+
raise MissingStoreElementIterationError(id_lst) from None
|
448
|
+
new_iters = {k: StoreElementIter.decode(v) for k, v in iter_dat.items()}
|
449
|
+
self.element_iter_cache.update(new_iters)
|
450
|
+
iters.update(new_iters)
|
451
|
+
return iters
|
448
452
|
|
449
453
|
def _get_persistent_EARs(self, id_lst: Iterable[int]) -> Dict[int, StoreEAR]:
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
454
|
+
runs, id_lst = self._get_cached_persistent_EARs(id_lst)
|
455
|
+
if id_lst:
|
456
|
+
with self.using_resource("metadata", action="read") as md:
|
457
|
+
try:
|
458
|
+
EAR_dat = {i: md["runs"][i] for i in id_lst}
|
459
|
+
except KeyError:
|
460
|
+
raise MissingStoreEARError(id_lst) from None
|
461
|
+
new_runs = {
|
462
|
+
k: StoreEAR.decode(v, self.ts_fmt) for k, v in EAR_dat.items()
|
463
|
+
}
|
464
|
+
self.EAR_cache.update(new_runs)
|
465
|
+
runs.update(new_runs)
|
466
|
+
return runs
|
456
467
|
|
457
468
|
def _get_persistent_parameters(
|
458
469
|
self,
|
459
470
|
id_lst: Iterable[int],
|
460
471
|
) -> Dict[int, StoreParameter]:
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
+
params, id_lst = self._get_cached_persistent_parameters(id_lst)
|
473
|
+
if id_lst:
|
474
|
+
with self.using_resource("parameters", "read") as params:
|
475
|
+
try:
|
476
|
+
param_dat = {i: params["data"][str(i)] for i in id_lst}
|
477
|
+
src_dat = {i: params["sources"][str(i)] for i in id_lst}
|
478
|
+
except KeyError:
|
479
|
+
raise MissingParameterData(id_lst) from None
|
480
|
+
|
481
|
+
new_params = {
|
482
|
+
k: StoreParameter.decode(id_=k, data=v, source=src_dat[k])
|
483
|
+
for k, v in param_dat.items()
|
484
|
+
}
|
485
|
+
self.parameter_cache.update(new_params)
|
486
|
+
params.update(new_params)
|
487
|
+
return params
|
472
488
|
|
473
489
|
def _get_persistent_param_sources(self, id_lst: Iterable[int]) -> Dict[int, Dict]:
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
490
|
+
sources, id_lst = self._get_cached_persistent_param_sources(id_lst)
|
491
|
+
if id_lst:
|
492
|
+
with self.using_resource("parameters", "read") as params:
|
493
|
+
try:
|
494
|
+
new_sources = {i: params["sources"][str(i)] for i in id_lst}
|
495
|
+
except KeyError:
|
496
|
+
raise MissingParameterData(id_lst) from None
|
497
|
+
self.param_sources_cache.update(new_sources)
|
498
|
+
sources.update(new_sources)
|
499
|
+
return sources
|
479
500
|
|
480
501
|
def _get_persistent_parameter_set_status(
|
481
502
|
self, id_lst: Iterable[int]
|
@@ -142,6 +142,7 @@ class PendingChanges:
|
|
142
142
|
task_ids = list(self.add_tasks.keys())
|
143
143
|
self.logger.debug(f"commit: adding pending tasks with IDs: {task_ids!r}")
|
144
144
|
self.store._append_tasks(tasks)
|
145
|
+
self.store.num_tasks_cache = None # invalidate cache
|
145
146
|
# pending element IDs that belong to pending tasks are now committed:
|
146
147
|
self.add_elem_IDs = {
|
147
148
|
k: v for k, v in self.add_elem_IDs.items() if k not in task_ids
|
@@ -187,6 +188,7 @@ class PendingChanges:
|
|
187
188
|
f"commit: adding pending element IDs to task {task_ID!r}: {elem_IDs!r}."
|
188
189
|
)
|
189
190
|
self.store._append_task_element_IDs(task_ID, elem_IDs)
|
191
|
+
self.store.task_cache.pop(task_ID, None) # invalidate cache
|
190
192
|
self.clear_add_elem_IDs()
|
191
193
|
|
192
194
|
@TimeIt.decorator
|
@@ -219,6 +221,7 @@ class PendingChanges:
|
|
219
221
|
f"{iter_IDs!r}."
|
220
222
|
)
|
221
223
|
self.store._append_elem_iter_IDs(elem_ID, iter_IDs)
|
224
|
+
self.store.element_cache.pop(elem_ID, None) # invalidate cache
|
222
225
|
self.clear_add_elem_iter_IDs()
|
223
226
|
|
224
227
|
@TimeIt.decorator
|
@@ -250,6 +253,7 @@ class PendingChanges:
|
|
250
253
|
)
|
251
254
|
for act_idx, EAR_IDs in act_EAR_IDs.items():
|
252
255
|
self.store._append_elem_iter_EAR_IDs(iter_ID, act_idx, EAR_IDs)
|
256
|
+
self.store.element_iter_cache.pop(iter_ID, None) # invalidate cache
|
253
257
|
self.clear_add_elem_iter_EAR_IDs()
|
254
258
|
|
255
259
|
@TimeIt.decorator
|
@@ -286,19 +290,21 @@ class PendingChanges:
|
|
286
290
|
)
|
287
291
|
# TODO: could be batched up?
|
288
292
|
for i in iter_ids:
|
289
|
-
self.
|
293
|
+
self.store._update_elem_iter_EARs_initialised(i)
|
294
|
+
self.store.element_iter_cache.pop(i, None) # invalidate cache
|
290
295
|
self.clear_set_EARs_initialised()
|
291
296
|
|
292
297
|
@TimeIt.decorator
|
293
298
|
def commit_EAR_submission_indices(self) -> None:
|
294
|
-
|
295
|
-
for EAR_id, sub_idx in self.set_EAR_submission_indices.items():
|
299
|
+
if self.set_EAR_submission_indices:
|
296
300
|
self.logger.debug(
|
297
|
-
f"commit:
|
298
|
-
f"{
|
301
|
+
f"commit: updating submission indices: "
|
302
|
+
f"{self.set_EAR_submission_indices!r}."
|
299
303
|
)
|
300
|
-
self.store.
|
301
|
-
|
304
|
+
self.store._update_EAR_submission_indices(self.set_EAR_submission_indices)
|
305
|
+
for EAR_ID_i in self.set_EAR_submission_indices.keys():
|
306
|
+
self.store.EAR_cache.pop(EAR_ID_i, None) # invalidate cache
|
307
|
+
self.clear_set_EAR_submission_indices()
|
302
308
|
|
303
309
|
@TimeIt.decorator
|
304
310
|
def commit_EAR_starts(self) -> None:
|
@@ -309,6 +315,7 @@ class PendingChanges:
|
|
309
315
|
f"({hostname!r}), and directory snapshot to EAR ID {EAR_id!r}."
|
310
316
|
)
|
311
317
|
self.store._update_EAR_start(EAR_id, time, snap, hostname)
|
318
|
+
self.store.EAR_cache.pop(EAR_id, None) # invalidate cache
|
312
319
|
self.clear_set_EAR_starts()
|
313
320
|
|
314
321
|
@TimeIt.decorator
|
@@ -320,6 +327,7 @@ class PendingChanges:
|
|
320
327
|
f"exit code ({ext!r}), and success status {suc!r} to EAR ID {EAR_id!r}."
|
321
328
|
)
|
322
329
|
self.store._update_EAR_end(EAR_id, time, snap, ext, suc)
|
330
|
+
self.store.EAR_cache.pop(EAR_id, None) # invalidate cache
|
323
331
|
self.clear_set_EAR_ends()
|
324
332
|
|
325
333
|
@TimeIt.decorator
|
@@ -328,6 +336,7 @@ class PendingChanges:
|
|
328
336
|
for EAR_id in self.set_EAR_skips:
|
329
337
|
self.logger.debug(f"commit: setting EAR ID {EAR_id!r} as skipped.")
|
330
338
|
self.store._update_EAR_skip(EAR_id)
|
339
|
+
self.store.EAR_cache.pop(EAR_id, None) # invalidate cache
|
331
340
|
self.clear_set_EAR_skips()
|
332
341
|
|
333
342
|
@TimeIt.decorator
|
@@ -353,6 +362,8 @@ class PendingChanges:
|
|
353
362
|
param_ids = list(self.set_parameters.keys())
|
354
363
|
self.logger.debug(f"commit: setting values of parameter IDs {param_ids!r}.")
|
355
364
|
self.store._set_parameter_values(self.set_parameters)
|
365
|
+
for id_i in param_ids:
|
366
|
+
self.store.parameter_cache.pop(id_i, None)
|
356
367
|
|
357
368
|
self.clear_set_parameters()
|
358
369
|
|
@@ -378,6 +389,8 @@ class PendingChanges:
|
|
378
389
|
param_ids = list(self.update_param_sources.keys())
|
379
390
|
self.logger.debug(f"commit: updating sources of parameter IDs {param_ids!r}.")
|
380
391
|
self.store._update_parameter_sources(self.update_param_sources)
|
392
|
+
for id_i in param_ids:
|
393
|
+
self.store.param_sources_cache.pop(id_i, None) # invalidate cache
|
381
394
|
self.clear_update_param_sources()
|
382
395
|
|
383
396
|
@TimeIt.decorator
|
@@ -389,6 +402,7 @@ class PendingChanges:
|
|
389
402
|
f"{loop_idx!r}."
|
390
403
|
)
|
391
404
|
self.store._update_loop_index(iter_ID, loop_idx)
|
405
|
+
self.store.element_iter_cache.pop(iter_ID, None) # invalidate cache
|
392
406
|
self.clear_update_loop_indices()
|
393
407
|
|
394
408
|
@TimeIt.decorator
|