Skip to content

[dev] Use datalad-fuse's remfile backend and all other dev branches - #1957

Closed
CodyCBakerPhD wants to merge 41 commits into
claude/validate-stream-via-datalad-fusefrom
claude/datalad-fuse-remfile
Closed

CodyCBakerPhD wants to merge 41 commits into
claude/validate-stream-via-datalad-fusefrom
claude/datalad-fuse-remfile

Conversation

@CodyCBakerPhD

@CodyCBakerPhD CodyCBakerPhD commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Stacked on #1956. This tries the development version of datalad-fuse from datalad/datalad-fuse#131, which adds pluggable backends, including remfile for HDF5-based files.

Changes

Locally, the datalad-fuse and validate stream tests pass with both datalad-fuse 0.5.1 (where the remfile test is skipped) and #131 plus remfile.

Once #131 is released, the CI install can switch to that release and the datalad extra's lower bound can be raised.

Follow-up branch: remfile instead of the workarounds

Branch claude/use-dev-datalad-fuse-remfile (c9b2d02) builds on this PR. It has no PR of its own yet. It reads NWB and other HDF5-based files with datalad-fuse's remfile backend instead of fsspec with a custom cache. It uses the remfile-dev branch of CodyCBakerPhD/datalad-fuse, which is datalad/datalad-fuse#131 plus init_annex from CodyCBakerPhD/datalad-fuse#4.

Removed from dandi/support/datalad_fuse.py Replaced by
_coalescing_block_cache() and _read_in_small_blocks() remfile. To read the structure of a 16 MB NWB file, remfile downloads 0.6 MB; fsspec with its default cache downloads 64 MB.
annex_initialized() init_annex=False and annex_initialized in datalad-fuse
_GCPausedFile for every streamed file _GCPausedFile only for files read with fsspec

Why _GCPausedFile stays for fsspec, and why the h5py fork is not used:

  • remfile fetches data in the reading thread, so no finalizer can run in another thread while h5py holds its lock waiting for that data. No h5py change is needed.
  • With fsspec, a patched h5py (CodyCBakerPhD/h5py defer-dealloc-when-locked) still deadlocks. In fsspec's I/O thread, a garbage collection runs HDMFIO.__del__ (hdmf 6.2.0). That calls is_open(), i.e. bool(h5py.File), which waits for h5py's lock, held by the reading thread.

CI on the follow-up branch:

  • remfile is added to the datalad extra.
  • The datalad-dev job installs the remfile-dev branch.
  • The datalad job keeps the released datalad-fuse, where streaming is skipped.

Results so far (local only):

  • Tests: the datalad-fuse, validate and cmd_validate tests pass:

    • with remfile: 102 passed;
    • without remfile: 100 passed, 2 skipped;
    • with released datalad-fuse 0.5.1: 86 passed, 16 skipped.
  • Real data: the 53 NWB files of 000006 with -J 8 and stock h5py 3.16 gave the same results as the fsspec path. Nothing was downloaded into the clone.

    Path Time Downloaded
    remfile 33–61 s 340 MiB
    fsspec, coalescing cache and garbage collection paused (this PR) 16–18 s 179 MiB

    For HDF5 files, remfile trades some bandwidth for not needing a custom cache, a garbage collection pause or an h5py patch.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr


Generated by Claude Code


Generated by Claude Code

claude added 11 commits October 6, 2026 20:46
Add `dandi.support.datalad_fuse.AnnexedReadableFile`, a `Readable` for a
locked annexed file whose content is not present locally, streamed with
the adapter of datalad-fuse (no FUSE mount), which asks git-annex where
the content is.  It is available with the new `datalad` extra.

Cache NWB metadata of locked annexed files, streamed or local, under
their git-annex key paired with their path, with fscacher 0.5.0's
`annex_key_fingerprint`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
Add a `stream` policy to `--missing-file-content`: the content of
annexed files that is not present locally (as in a DataLad clone of a
Dandiset) is streamed with datalad-fuse's adapter, so that pynwb and
nwbinspector run on it without it being downloaded.  pynwb validation
results are cached under the files' git-annex keys.

This is the validation integration of #1933, on top of the datalad-fuse
readable rather than a git-only reader of the git-annex branch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
annex_fingerprint is not specific to NWB files, and living in
dandi.support.datalad_fuse it needs no deferred import.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
…am-via-datalad-fuse

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
As documented in datalad/datalad-fuse#138.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
With the development version of datalad-fuse from datalad/datalad-fuse#131,
use its RemoteFilesystemAdapter, which reads HDF5-based files such as NWB
with remfile if installed (fsspec otherwise), and look up S3 exports for
content without a registered URL.  The released FsspecAdapter is still
used with datalad-fuse 0.5.1.

Test it in a new datalad-dev CI job.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
fscacher imports joblib, which imports numpy, so importing it with the
module made the CLI (via dandi.validate in the stacked PR) import numpy,
failing test_no_heavy_imports.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
…am-via-datalad-fuse

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
…talad-fuse-remfile

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
@codecov

codecov Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.59459% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.14%. Comparing base (336ec0b) to head (e0aac05).
⚠️ Report is 5 commits behind head on claude/validate-stream-via-datalad-fuse.

Files with missing lines Patch % Lines
dandi/support/datalad_fuse.py 87.50% 2 Missing ⚠️
Additional details and impacted files
@@                             Coverage Diff                              @@
##           claude/validate-stream-via-datalad-fuse    #1957       +/-   ##
============================================================================
+ Coverage                                    66.82%   91.14%   +24.32%     
============================================================================
  Files                                           94       94               
  Lines                                        14835    14866       +31     
============================================================================
+ Hits                                          9913    13550     +3637     
+ Misses                                        4922     1316     -3606     
Flag Coverage Δ
unittests 91.14% <94.59%> (+24.32%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

claude added 8 commits October 6, 2026 22:32
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
…am-via-datalad-fuse

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
…talad-fuse-remfile

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
h5py holds its global lock while reading from a Python file object, and
the read waits for another thread to fetch the data over HTTP (fsspec's
I/O thread, or an HTTP server in the same process as in the tests).  A
garbage collection in that thread could then run the finalizer of an
unreferenced h5py-backed object (e.g., hdmf's HDF5IO.__del__), which
waits for the same lock: a deadlock, as seen in CI in
test_validate_stream, ended by pytest-timeout with a segfault.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
This reverts commit d18843d.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
…talad-fuse-remfile

Resolve the import conflict, and check the type of the file object
wrapped by the one that pauses garbage collection in the remfile test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
@CodyCBakerPhD
CodyCBakerPhD added this pull request to stack #1959 October 12, 2026 21:30
@CodyCBakerPhD CodyCBakerPhD added cmd-validate enhancement New feature or request patch Increment the patch version when merged labels Oct 12, 2026
Add `dandi.support.datalad_fuse.AnnexedReadableFile`, a `Readable` for a
locked annexed file whose content is not present locally, streamed with
the adapter of datalad-fuse (no FUSE mount), which asks git-annex where
the content is.  It is available with the new `datalad` extra.

Cache NWB metadata of locked annexed files, streamed or local, under
their git-annex key paired with their path, with fscacher 0.5.0's
`annex_key_fingerprint`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
annex_fingerprint is not specific to NWB files, and living in
dandi.support.datalad_fuse it needs no deferred import.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
As documented in datalad/datalad-fuse#138.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
fscacher imports joblib, which imports numpy, so importing it with the
module made the CLI (via dandi.validate in the stacked PR) import numpy,
failing test_no_heavy_imports.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
h5py holds its global lock while reading from a Python file object, and
the read waits for another thread to fetch the data over HTTP (fsspec's
I/O thread, or an HTTP server in the same process as in the tests).  A
garbage collection in that thread could then run the finalizer of an
unreferenced h5py-backed object (e.g., hdmf's HDF5IO.__del__), which
waits for the same lock: a deadlock, as seen in CI in
test_validate_stream, ended by pytest-timeout with a segfault.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
Add a `stream` policy to `--missing-file-content`: the content of
annexed files that is not present locally (as in a DataLad clone of a
Dandiset) is streamed with datalad-fuse's adapter, so that pynwb and
nwbinspector run on it without it being downloaded.  pynwb validation
results are cached under the files' git-annex keys.

This is the validation integration of #1933, on top of the datalad-fuse
readable rather than a git-only reader of the git-annex branch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
This reverts commit d18843d.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
@CodyCBakerPhD
CodyCBakerPhD force-pushed the claude/validate-stream-via-datalad-fuse branch from 0be1c28 to 106f661 Compare October 12, 2026 21:32
claude added 12 commits October 12, 2026 21:56
…talad-fuse-remfile

Bring in the rebased base branch.  The conflicting files are identical in
the old and the rebased base, so they keep this branch's versions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
datalad-fuse opens the files with fsspec's defaults: 5 MiB of read-ahead,
discarded whenever a read jumps elsewhere.  h5py reads many small pieces
scattered over an NWB file, so each open downloaded about as much as the
whole file (64 MB for the neurodata types of a 16 MB file in the new
test), and dandi opens each file several times during validation.  Read
in cached blocks of 256 KiB instead (1.3 MB for the same file).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
With datalad/datalad-fuse#131 and remfile installed, remfile reads HDF5
files, with its own read-ahead (chunks growing up to 100 MiB) that
datalad-fuse does not let us configure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
fsspec's "blockcache" fetches each block of a read with its own request,
so a large read (e.g., of a dataset's data, by NWB Inspector) took one
round trip per 256 KiB, i.e., a couple of MB/s however fast the
connection.  Use instead a block cache that fetches each run of
consecutive missing blocks with a single request, and keeps up to 512
blocks (128 MiB) per open file rather than 32, which h5py's scattered
reads of metadata kept evicting and fetching again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
The results of inspecting an NWB file are cached in the validation
cache, under the file (by stat() for a local file, or by its git-annex
key for streamed content; see `annex_fingerprint`) and the version of
nwbinspector, so that validating again does not inspect (and for
streamed content, download) unchanged files again.  A failure to read
streamed content, which might be transient, is not cached.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
Validating a streamed file mostly waits for its content to be
downloaded, one request at a time (h5py serializes all reading of HDF5
files in a process), so validate NWB files (other than those of BIDS
datasets, which are validated together) in a pool of processes, started
with "spawn" since this process may run threads (e.g., fsspec's).  The
results are yielded in the same order as without the pool; the workers'
log records are handled by the handlers of the main process.  Streamed
content is set up again in the worker, as its source cannot be sent to
another process.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
Validating streamed content can take a long time per file, during which
nothing was shown.  Find all the files to validate first, log their
number, and log for each file when it is done ("[3/120] sub-01/...nwb:
2 issues"), including for the files validated in parallel with --jobs,
as they finish.

The `dataset_description.json` files of BIDS datasets, to which the
assets found refer only weakly, are kept alive while their assets are
validated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
@CodyCBakerPhD CodyCBakerPhD changed the title Use datalad-fuse's remfile backend when available [dev] Use datalad-fuse's remfile backend and all other dev branches Oct 13, 2026
@CodyCBakerPhD
CodyCBakerPhD force-pushed the claude/validate-stream-via-datalad-fuse branch from a65f6ee to 336ec0b Compare October 15, 2026 17:58
…ad-fuse' into claude/datalad-fuse-remfile

# Conflicts:
#	.github/workflows/run-tests.yml
#	dandi/support/datalad_fuse.py
#	dandi/support/tests/test_datalad_fuse.py
#	docs/source/cmdline/validate.rst
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cmd-validate enhancement New feature or request patch Increment the patch version when merged

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants