Skip to content

fix: remove hard-coded scheduler from da.store - #2584

Open
joshua-gould wants to merge 12 commits into
scverse:mainfrom
joshua-gould:zarr-store-scheduler
Open

fix: remove hard-coded scheduler from da.store#2584
joshua-gould wants to merge 12 commits into
scverse:mainfrom
joshua-gould:zarr-store-scheduler

Conversation

@joshua-gould

Copy link
Copy Markdown
Contributor

test_dask_distributed_write tests writing to zarr store using dask so no new tests added

@codecov

codecov Bot commented Aug 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 85.83%. Comparing base (d75540e) to head (d998577).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2584      +/-   ##
==========================================
- Coverage   87.66%   85.83%   -1.84%     
==========================================
  Files          49       49              
  Lines        8121     8123       +2     
==========================================
- Hits         7119     6972     -147     
- Misses       1002     1151     +149     
Files with missing lines Coverage Δ
src/anndata/_io/specs/methods.py 91.80% <100.00%> (-0.31%) ⬇️

... and 7 files with indirect coverage changes

@flying-sheep flying-sheep left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change you’re reverting was made intentionally here: #2183

You can see there which dask versions have the bug it circumvents. Makes sense to not hard-code it for dask versions where it’s fixed, but you should make it so that threads is still hardcoded for afflicted dask version.

@joshua-gould

Copy link
Copy Markdown
Contributor Author

The bug was caused by the anndata lock, not by the scheduler so it should be safe to remove the scheduler as the lock has been removed.

@flying-sheep

flying-sheep commented Aug 3, 2026

Copy link
Copy Markdown
Member

Not only that, there’s more, see test failures. You could probably make this work by leaving the hardcoded scheduler in place only for is_h5 or Version(version("dask")) < ... re-introducing the version gate from the PR I linked.

Then when using modern dask and zarr, the user scheduler is respected.

@joshua-gould

Copy link
Copy Markdown
Contributor Author

Sorry, I didn't realize this code block was used for saving h5 files as well. I updated the pull request to use threaded scheduler with h5. Thanks.

@flying-sheep flying-sheep left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

still missing the dask version gate, with that I can merge it

@joshua-gould

Copy link
Copy Markdown
Contributor Author

Added dask version gate. Thanks.

@flying-sheep flying-sheep added this to the 0.13.3 milestone Aug 7, 2026
@flying-sheep flying-sheep changed the title Removed hard-coded scheduler from da.store. fix: remove hard-coded scheduler from da.store Aug 7, 2026
@flying-sheep
flying-sheep requested review from ilan-gold and removed request for flying-sheep August 7, 2026 07:59
@flying-sheep
flying-sheep dismissed their stale review August 7, 2026 07:59

addressed

@flying-sheep

Copy link
Copy Markdown
Member

OK, looks good to me now, but @ilan-gold said that the threading solution seems safer.

So now that we no longer support zarr 2, maybe that’s obsolete, but let’s ask Ilan.

if isinstance(f, h5py.Group) or Version(version("dask")) <= Version("2025.3.0"):
da.store(elem, g, scheduler="threads")
else:
da.store(elem, g)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, so with zarr sharding and processes, AFAIK, this is not safe. I'm not sure "threads" is particularly safe either, but I think it's better. @d-v-b maybe knows? Or @melonora ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe the xarray developers would have some insight? I checked their code and they do not hardcode the scheduler.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not certain why I only see a notification just now. To reply https://github.com/dask/dask/blob/ba5045e001d67decd6a83a0f50f666a5e3845250/dask/array/core.py#L1196, as far as I am aware this actually locks the whole array and not individual blocks. I ran into this when implementing the direct writing of sharding with da.to_zarr. Whether it is safe depends on whether shards and chunks are aligned, but I believe it should throw an error in any case when that is not the case.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have no experience here. @melonora could you please check? If your belief being true is all that’s needed here we’re close to wrapping this up.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any updated here @melonora ? Maybe @joshua-gould could just post a script a verifying one way or the other that this safety check exists i.e., the error is thrown when not aligned?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's an example that shows when chunks or shards are not aligned, data is corrupted:

import dask.array as da

a = da.arange(100).reshape((10, 10)).rechunk((2, 2))
da.to_zarr(a, "test1.zarr", overwrite=True)
print((a == da.from_zarr("test1.zarr")).all().compute())  # works

da.to_zarr(a, "test2.zarr", chunks=(4, 4), overwrite=True)
print((a == da.from_zarr("test2.zarr")).all().compute())  # corrupted

da.to_zarr(a, "test3.zarr", shards=(4, 4), overwrite=True)
print((a == da.from_zarr("test3.zarr")).all().compute())  # corrupted

@joshua-gould
joshua-gould requested a review from ilan-gold August 10, 2026 15:55
@flying-sheep flying-sheep removed this from the 0.13.3 milestone Aug 27, 2026
@flying-sheep flying-sheep added this to the 0.13.4 milestone Aug 27, 2026
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.

4 participants