hpcflow-new2 0.2.0a159__py3-none-any.whl → 0.2.0a161__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.
@@ -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
- snapshot = JSONLikeDirSnapShot()
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
- snapshot = JSONLikeDirSnapShot()
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
- def get_task_elements(self, task_id, idx_sel: slice) -> List[Dict]:
1529
- """Get element data by an index slice within a given task.
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
- req_IDs = all_elem_IDs[idx_sel]
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)
@@ -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 _update_EAR_submission_index(self, EAR_id: int, sub_idx: int):
232
+ def _update_EAR_submission_indices(self, sub_indices: Dict[int, int]):
233
233
  with self.using_resource("metadata", action="update") as md:
234
- md["runs"][EAR_id]["submission_idx"] = sub_idx
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
- with self.using_resource("metadata", action="read") as md:
314
- return len(md["tasks"])
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
- self, id_lst: Optional[Iterable[int]] = None
391
- ) -> Dict[int, StoreTask]:
392
- with self.using_resource("metadata", action="read") as md:
393
- task_dat = {
394
- i["id_"]: StoreTask.decode({**i, "index": idx})
395
- for idx, i in enumerate(md["tasks"])
396
- if id_lst is None or i["id_"] in id_lst
397
- }
398
- return task_dat
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
- # could convert `id_lst` to e.g. slices if more efficient for a given store
432
- with self.using_resource("metadata", action="read") as md:
433
- try:
434
- elem_dat = {i: md["elements"][i] for i in id_lst}
435
- except KeyError:
436
- raise MissingStoreElementError(id_lst) from None
437
- return {k: StoreElement.decode(v) for k, v in elem_dat.items()}
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
- with self.using_resource("metadata", action="read") as md:
443
- try:
444
- iter_dat = {i: md["iters"][i] for i in id_lst}
445
- except KeyError:
446
- raise MissingStoreElementIterationError(id_lst) from None
447
- return {k: StoreElementIter.decode(v) for k, v in iter_dat.items()}
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
- with self.using_resource("metadata", action="read") as md:
451
- try:
452
- EAR_dat = {i: md["runs"][i] for i in id_lst}
453
- except KeyError:
454
- raise MissingStoreEARError(id_lst) from None
455
- return {k: StoreEAR.decode(v, self.ts_fmt) for k, v in EAR_dat.items()}
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
- with self.using_resource("parameters", "read") as params:
462
- try:
463
- param_dat = {i: params["data"][str(i)] for i in id_lst}
464
- src_dat = {i: params["sources"][str(i)] for i in id_lst}
465
- except KeyError:
466
- raise MissingParameterData(id_lst) from None
467
-
468
- return {
469
- k: StoreParameter.decode(id_=k, data=v, source=src_dat[k])
470
- for k, v in param_dat.items()
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
- with self.using_resource("parameters", "read") as params:
475
- try:
476
- return {i: params["sources"][str(i)] for i in id_lst}
477
- except KeyError:
478
- raise MissingParameterData(id_lst) from None
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._store._update_elem_iter_EARs_initialised(i)
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
- # TODO: could be batched up?
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: adding pending submission index ({sub_idx!r}) to EAR ID "
298
- f"{EAR_id!r}."
301
+ f"commit: updating submission indices: "
302
+ f"{self.set_EAR_submission_indices!r}."
299
303
  )
300
- self.store._update_EAR_submission_index(EAR_id, sub_idx)
301
- self.clear_set_EAR_submission_indices()
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