Skip to content

Commit ef62c31

Browse files
committed
Minor changes
- call_id for logging purposes
1 parent a2fe283 commit ef62c31

4 files changed

Lines changed: 65 additions & 22 deletions

File tree

charmonium/cache/memoize.py

Lines changed: 54 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ def __setstate__(self, state: Mapping[str, Any]) -> Any:
156156
)
157157
self._version = 0
158158
self._memory_lock = threading.RLock()
159-
self._index_read(0)
159+
self._index_read(random.randint(0, 2**64 - 1))
160160

161161
def __init__(
162162
self,
@@ -245,6 +245,24 @@ def _deleter(self, item: tuple[Any, Entry]) -> None:
245245
)
246246
)
247247

248+
def refresh(self) -> None:
249+
"""Refresh the this cache from storage
250+
251+
Note: This happens automatically at function import-time if
252+
fine_grain_persistence is enabled, after function call.
253+
254+
"""
255+
self._index_read(random.randint(0, 2**64 - 1))
256+
257+
def commit(self) -> None:
258+
"""Save this cache into storage.
259+
260+
Note: This happens automatically at function import-time if
261+
fine_grain_persistence is enabled, after function call.
262+
263+
"""
264+
self._index_write(random.randint(0, 2**64 - 1))
265+
248266
def _index_read(self, call_id: int) -> None:
249267
with self._memory_lock, self._index_lock.reader:
250268
self._index_read_nolock(call_id)
@@ -263,6 +281,7 @@ def _index_read_nolock(self, call_id: int) -> None:
263281
],
264282
self._pickler.loads(self._obj_store[self._index_key]),
265283
)
284+
# TODO: catch the case where this is unpicklable or does not exist.
266285
if other_version > current_version:
267286
self._version = other_version
268287
self._index.update(other_index)
@@ -286,7 +305,7 @@ def _index_read_nolock(self, call_id: int) -> None:
286305
def _index_write(self, call_id: int) -> None:
287306
with perf_ctx("index_write", call_id), self._memory_lock, self._index_lock.writer:
288307
self._index_read_nolock(call_id)
289-
self._evict()
308+
self._evict(call_id)
290309
self._version += 1
291310
self._obj_store[self._index_key] = self._pickler.dumps(
292311
(
@@ -320,7 +339,17 @@ def _system_state(self) -> Any:
320339
with self._memory_lock:
321340
return (__version__,) + none_tuple(self._extra_system_state())
322341

323-
def _evict(self) -> None:
342+
def evict(self) -> None:
343+
"""If the size of the cache is greater than ``self._size``, use ``self._replacement_policy``.
344+
345+
Note: this is automatically triggered automatically either
346+
when the function gets called or atexit, depending on the
347+
group-level options.
348+
349+
"""
350+
self._evict(random.randint(0, 2**64 - 1))
351+
352+
def _evict(self, call_id: int) -> None:
324353
with self._memory_lock:
325354
total_size: bitmath.Bitmath = bitmath.Byte(0)
326355

@@ -350,6 +379,7 @@ def _evict(self) -> None:
350379
"obj_key": obj_key,
351380
"entry.data_size": entry.data_size.bytes,
352381
"new_total_size": (total_size - entry.data_size).bytes,
382+
"call_id": call_id,
353383
}
354384
)
355385
)
@@ -627,6 +657,7 @@ def __call__(
627657
hit, value = self._try_unpickle(value_ser, call_id)
628658
else:
629659
hit, value = True, cast(FuncReturn, entry.value)
660+
# TODO: allow a hit if entry is None but value_ser is not.
630661

631662
if ops_logger.isEnabledFor(logging.DEBUG):
632663
ops_logger.debug(
@@ -671,7 +702,7 @@ def __call__(
671702

672703
# Update time_cost
673704
if self.group._fine_grain_eviction:
674-
self.group._evict()
705+
self.group._evict(call_id)
675706

676707
if self.group._fine_grain_persistence:
677708
self.group._index_write(call_id)
@@ -766,6 +797,17 @@ def would_hit(self, *args: FuncParams.args, **kwargs: FuncParams.kwargs) -> bool
766797
)
767798
return would_hit
768799

800+
def clear_entry(
801+
self,
802+
*args: FuncParams.args,
803+
**kwargs: FuncParams.kwargs,
804+
) -> None:
805+
call_id = random.randint(0, 2**64 - 1)
806+
key, entry, obj_key, value_ser = self._would_hit(call_id, *args, **kwargs)
807+
if entry is not None:
808+
self.group._deleter((key, entry))
809+
assert not self.would_hit(*args, **kwargs)
810+
769811
def _would_hit(
770812
self, call_id: int, *args: FuncParams.args, **kwargs: FuncParams.kwargs
771813
) -> Tuple[Tuple[Any, ...], Optional[Entry], int, Optional[bytes]]:
@@ -789,12 +831,14 @@ def _would_hit(
789831
with self.group._memory_lock:
790832
if self.group._fine_grain_persistence:
791833
self.group._index_read(call_id)
792-
return (
793-
key,
794-
self.group._index.get(key, None),
795-
obj_key,
796-
self.group._obj_store.get(obj_key, None) if self._use_obj_store else None,
797-
)
834+
entry = self.group._index.get(key, None)
835+
value_ser = self.group._obj_store.get(obj_key, None) if self._use_obj_store else None
836+
return (
837+
key,
838+
entry,
839+
obj_key,
840+
value_ser,
841+
)
798842

799843
def __get__(self, instance: Any, instancetype: Type[Any]) -> BoundMemoized[FuncParams, FuncReturn]:
800844
"""Implement the descriptor protocol to make decorating instance

charmonium/cache/obj_store.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -93,22 +93,23 @@ def __setitem__(self, key: int, val: bytes) -> None:
9393

9494
def __getitem__(self, key: int) -> bytes:
9595
path = self.path / self._int2str(key)
96-
if not path.exists():
97-
raise KeyError(key)
98-
else:
96+
try:
9997
return path.read_bytes()
98+
except FileNotFoundError:
99+
raise KeyError(key)
100100

101101
def __delitem__(self, key: int) -> None:
102-
if key in self:
103-
# print(f"delitem {key}")
102+
try:
104103
(self.path / self._int2str(key)).unlink()
104+
except FileNotFoundError:
105+
pass
105106

106107
def get(self, key: int, default: _T) -> Union[bytes | _T]:
107108
path = self.path / self._int2str(key)
108-
if not path.exists():
109-
return default
110-
else:
109+
try:
111110
return path.read_bytes()
111+
except FileNotFoundError:
112+
return default
112113

113114
def __contains__(self, key: int) -> bool:
114115
return (self.path / self._int2str(key)).exists()

charmonium/cache/replacement_policies.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,14 @@ def access(self, key: Any, entry: Entry) -> None:
7575
def invalidate(
7676
self, key: Any, entry: Any
7777
) -> None: # pylint: disable=unused-argument
78-
del self._data[key]
78+
self._data.pop(key, None)
7979

8080
def evict(self) -> tuple[Any, Entry]:
8181
if self._data:
8282
self.inflation, key, entry = min(
8383
(score, key, entry) for key, (score, entry) in self._data.items()
8484
)
85-
del self._data[key]
85+
self._data.pop(key, None)
8686
return key, entry
8787
else:
8888
raise ValueError("No data left to evict")

tests/test_memoize_parallel.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,6 @@ def square_all(lst: list[int]) -> list[int]:
9191
N_OVERLAP = 4
9292

9393

94-
@pytest.mark.xfail(reason="something with filesystem consistency")
9594
@pytest.mark.parametrize("ParallelType", [multiprocessing.Process, threading.Thread])
9695
def test_parallelism(ParallelType: Type[Parallel]) -> None:
9796
if tmp_root.exists():
@@ -154,7 +153,6 @@ def test_dask_delayed() -> None:
154153
assert results == tuple(x**2 for call in calls for x in call)
155154

156155

157-
@pytest.mark.xfail(reason="something with filesystem consistency")
158156
def test_dask_bag() -> None:
159157
if tmp_root.exists():
160158
shutil.rmtree(tmp_root)

0 commit comments

Comments
 (0)