Repository navigation
0.5.0: retry writes, watchdog for tensorstore's lost wakeup, size threads to the job - #9
Merged
Merged
Conversation
…b (0.5.0) cellmap-analyze 0.6 hung and crashed on /nrs (salamander's and c-elegans' runs): - A read whose first attempt timed out raised IndexError: the retry warning took the first line of tensorstore's TimeoutError(), which has no message. - Dozens of workers per run waited forever in write_index with no tensorstore thread alive. tensorstore up to 0.1.85 strands a task queued while a pool worker exits idle (fixed upstream in ed51718085, unreleased); pools capped at 1 meet it most. One more task on the same pool wakes it, which both sessions proved on live stuck workers. Every wait on a data read, write or open now kicks its context's decoding and file I/O pools with a no-op task after each second pending; writes are timed out and retried as reads are; and the shared context's pools take thread_budget() (OMP_NUM_THREADS, else LSB_DJOB_NUMPROC, else the CPUs allowed) instead of every core on the node. Spec open question 14 says when the kick goes. Claude-Session: https://claude.ai/code/session_0141wgF8mkGbjGfBFKfKhp64
chunkmirage and cellmap-flow read through vol.ts directly and with await, so a kick inside worldarray's own waits missed them. Every context the package makes or opens a store on is now held weakly by one daemon thread that gives its decoding and file I/O pools a no-op task once a second; a stranded task runs within about a second whoever waits on it. Waits go back to future.result(timeout). thread_budget also reads SLURM_CPUS_PER_TASK and NSLOTS. cellmap-analyze's read retries of June 2025 were working around the same tensorstore bug. Claude-Session: https://claude.ai/code/session_0141wgF8mkGbjGfBFKfKhp64
Tasks aimed 19.5-20.5 ms after a one-thread pool's last one strand about 0.3% of the time on a plain context (tensorstore 0.1.75, 0.1.78 and 0.1.85) and never on a watched one, slowest under a second. cellmap-analyze's block loop on its 0.6.1 context gave 29 IndexErrors and 55 writes stuck past 20 s on worldarray 0.4.0, none on this branch. WORLDARRAY_STRESS=1 runs it; it is also the check for removing the watchdog (open question 14). Claude-Session: https://claude.ai/code/session_0141wgF8mkGbjGfBFKfKhp64
The "2.3 to 5 times" figure counted `import zarr` (2.7 s) as write time. Measured against cellmap-analyze 0.5.0's own block I/O instead. Claude-Session: https://claude.ai/code/session_0141wgF8mkGbjGfBFKfKhp64
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.
cellmap-analyze 0.6 hung and crashed on /nrs in salamander's and c-elegans' runs. Every run of three salamander tasks hung, and all five c-elegans fill-holes runs did. They used worldarray 0.4.0 on tensorstore 0.1.75.
Root cause. tensorstore strands a queued task in every release up to at least 0.1.85. A pool worker that has been idle for 20 ms exits, and a task queued as it exits gets no thread, because the pool still counts itself full. The future then waits forever with no tensorstore thread alive.
capped_context(concurrency=1)is exactly that.TimeoutErrormessage.Changes
vol.tsorawait. Spec open question 14 says when it goes.write_index,writeandwrite_chunktime out and retry as reads do, through a newretries=argument (Behaviour 6).IndexErrorinstead of retrying.data_copyandfile_iopools takewa.thread_budget():OMP_NUM_THREADS(dask sets 1 in each worker), else the job's slots (LSB_DJOB_NUMPROC,SLURM_CPUS_PER_TASK,NSLOTS), else the CPUs allowed. tensorstore alone used every core on the node whateverbsub -nsaid. Pinned to one CPU, tensorstore's default context started 43–45 pool threads; this one starts 4–5.Reproduced on purpose. The opt-in test
tests/test_lost_wakeup.py(run withWORLDARRAY_STRESS=1) aims tasks at the moment a one-thread pool's worker exits.The end-to-end run is cellmap-analyze's block loop, a chunk read then a whole-chunk
write_index, on its 0.6.1 context, with 16 workers for 90 s:IndexErrorsSpeed was measured against cellmap-analyze 0.5.0's own block I/O, on NFS, with 128³ uint64 label blocks of one chunk each. From one process at one thread per pool, cellmap-analyze 0.6.1 on this branch reads a block in 26 ms instead of 40 and writes it in 45 ms instead of 62. From 8 processes it is about the same for reads and about 1.15x faster for writes. An earlier "2.3–5x" figure counted
import zarras write time and was wrong.No placement change and no unit table change. This is a minor release: writes behave differently, and
thread_budgetis new public API.https://claude.ai/code/session_0141wgF8mkGbjGfBFKfKhp64