Skip to content

fix: numba cache data files named by key, so concurrent compiles can't mix up signatures - #216

Merged
timtreis merged 1 commit into
mainfrom
fix/numba-cache
Nov 14, 2026
Merged

timtreis merged 1 commit into
mainfrom
fix/numba-cache

Conversation

@timtreis

Copy link
Copy Markdown
Member

Summary: When several processes compile different signatures of the same numba kernel at the same time against one on-disk cache, the cache can end up mapping one signature to another signature's machine code. Every later call with that signature then fails with "can't unbox array from PyObject", until the cache is deleted. This PR names each cached data file after its cache key, so a data file can only ever hold the code for its own key.

Background:

  • The failure showed up twice when running the test suite with many pytest-xdist workers on the HMGU cluster. Users can hit it too, for example with several jobs on a shared environment on first use, or with process workers fed different dtypes.
  • How it happens: numba's default index/data cache file picks the next free data file number from the index it has just read, writes the index, then the data file. Two processes that read the same index both pick the same number. If one process's index and the other's data file are the last to land, the index points to the wrong code.

Changes:

  • infercnvpy._numba has a small njit helper: numba.njit(nogil=True) with a cache whose data files are named after a hash of the cache key. Each data file is written before the index.
    • A concurrent index write can still lose an entry, which costs one recompile, but it can no longer point to the wrong code.
  • njit_parallel builds both twins with it, so it is still a parallel kernel on the main thread and a serial nogil twin elsewhere. The other cached kernels use it too.
  • Caching stays on for users.
  • The helper subclasses numba's cache classes (IndexDataCacheFile, FunctionCache), which are not public API.

Testing:

  • New tests/test_numba_cache.py forces the bad write order between two loads of a kernel module in one subprocess, then calls the kernel with float32 and float64 in a fresh subprocess. It fails on main with the "can't unbox array" error and passes with this change.
  • In a stress run on the cluster, 16 processes compiled one kernel for 4 dtypes into a shared cache dir. Main broke in 4 of 10 rounds, this branch in 0 of 10.
  • The full suite passes on the cluster twice with 16 workers (482 passed, 12 skipped), and so do prek and mypy.

@codecov-commenter

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.96%. Comparing base (6f06b64) to head (aea3e16).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #216      +/-   ##
==========================================
+ Coverage   92.89%   92.96%   +0.07%     
==========================================
  Files          21       21              
  Lines        1576     1593      +17     
==========================================
+ Hits         1464     1481      +17     
  Misses        112      112              
Files with missing lines Coverage Δ
src/infercnvpy/_numba.py 100.00% <100.00%> (ø)
src/infercnvpy/tl/_infercnv_kernels.py 100.00% <100.00%> (ø)
src/infercnvpy/tl/_spatial.py 91.57% <100.00%> (-0.09%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@timtreis
timtreis merged commit f1be2ac into main Nov 14, 2026
12 checks passed
@timtreis
timtreis deleted the fix/numba-cache branch November 14, 2026 18:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants