Repository navigation
Conversation
… node For a node inside a Parallelizable/Collect block, HamiltonCacheAdapter keeps a dict keyed by task in data_versions[run_id][node] and cache_keys[run_id][node]. Both writers checked whether that dict existed and then created it. Two tasks on two threads could both see it missing; the second then replaced the dict of the first and the first task's entry was lost. Get or create the nested dict with dict.setdefault(), a single atomic step, in _set_memory_metadata and _set_cache_key. Add tests that force the losing order (both writers observe the missing dict before either creates it) for each map, and one that pins the shape of the entries of standard, expanding and collecting nodes. (cherry picked from commit b325e6187b7cc5df524efbf90ce61062d755505b)
Dev-iL
force-pushed
the
2611/fix-lost-cache
branch
from
November 11, 2026 07:35
e9db851 to
1a1b2c9
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed and why
HamiltonCacheAdapterkeeps, for a node inside aParallelizable/Collectblock, a dictionary keyed by task indata_versions[run_id][node]and incache_keys[run_id][node]. Both writers (_set_memory_metadata,_set_cache_key) checked whether that dictionary existed and then created it. Two tasks on two threads share one adapter, so both could see it missing; the second then replaced the first one's dictionary and the first task's entry was gone.Both writers now get or create the nested dictionary with
dict.setdefault(), one atomic step. Two lines of logic change inhamilton/caching/adapter.py; entries of standard, expanding and collecting nodes are written exactly as before.How it was shown working
New tests in
tests/caching/test_adapter.pyforce the losing order with no sleeps: the per-run map is replaced by adictsubclass that parks a writer thread right after it observes the missing entry (.get(),inor[]); once both writers are parked or done they are resumed one at a time. Each test repeats this 80 times and then reads every task's entry back from the main thread.pytest tests/caching/test_adapter.py -k "forced or keyed", withadapter.pytaken fromupstream/main(ef88b1e9), same result on Python 3.11.15 and 3.15.0:With the fix, on 3.11.15 and 3.15.0 (0 of 80 attempts lose an entry, for each map):
Core CI does not run yet for pull requests into
2.0, so these are local runs on this branch (4ff05635):uv run prek run --all-files --hook-stage pre-merge-commitpytest tests/ --ignore tests/integrations, Python 3.15.0pytest tests/ --ignore tests/integrations, Python 3.11.15tests/plugins/test_huggingface_extensions.pyalso ignored (see below)The local machine's CPU has no AVX2.
lancedb(installed only below Python 3.14) dies with "Illegal instruction" on import there, so on 3.11 it was uninstalled and the one test module that imports it was ignored;polars-runtime-compatwas added to both environments for the same reason. Neither touches this change. No type checker is configured on this branch (no pyrefly configuration yet), so no type check was run.Before and after, from the user's side
Two mapped tasks record their cache key for the node
insidein the losing order, then the keys are read through the publicget_cache_key:Decisions
dict.setdefault()over a lock. It adds no state, so there is nothing to carry through the adapter's__getstate__/__setstate__(athreading.Lockcannot be pickled and would have to be dropped and recreated there for the process-pool executor). It relies onsetdefaultwithstrkeys being atomic, which holds on CPython with the GIL and on free-threaded builds (per-dict lock). An ad hoc run of 16 threads released together, 2,000 rounds, lost 0 entries on free-threaded 3.13.11 and on 3.15.0.MultiThreadingExecutordo.MultiProcessingExecutorpickles the task and its adapter, so its tasks never share these maps.main.Checklist