Skip to content

Respect zero per-dataset chunk cache settings - #2986

Merged
ajelenak merged 3 commits into
h5py:masterfrom
rioyu123:fix/zero-dataset-chunk-cache
Oct 30, 2026
Merged

ajelenak merged 3 commits into
h5py:masterfrom
rioyu123:fix/zero-dataset-chunk-cache

Conversation

@rioyu123

@rioyu123 rioyu123 commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Group.create_dataset() and Group.require_dataset() silently ignore an individual rdcc_nbytes=0, rdcc_nslots=0, or rdcc_w0=0. Both dataset helpers check these options for truthiness, so the dataset keeps the file's cache settings instead of applying the requested zero.

These are meaningful settings: zero bytes or slots disables the chunk cache for a dataset, and rdcc_w0=0 selects strict LRU preemption. The helpers now check whether a value was supplied (is not None), consistent with File(...). Omitted options and explicit None remain unchanged.

The DAPL allocation guard now also uses is not None checks for efile_prefix and virtual_prefix. This fixes an AttributeError when either is an empty str or bytes. Behavior for None and non-empty prefixes is unchanged.

There is a separate inheritance limitation: supplying a cache option currently replaces unspecified options with HDF5 defaults rather than preserving the file's settings. The release note now calls this out, and the tests record it as an expected failure, as requested in the review. Fixing that behavior is left to the maintainer's follow-up.

Regression coverage exercises dataset creation, require_dataset() creating a dataset, and require_dataset() reopening an existing one. Explicit cache settings and data round-trips are checked before the inheritance xfail is applied. The zero-byte and zero-slot cases keep those checks; they skip the inheritance assertions because HDF5 reports both sizes as zero when the cache is disabled. Prefix tests cover empty strings, empty bytes, and None through all three paths.

Tests

October 23 follow-up: Linux, Python 3.12.3, NumPy 2.5.3, with separate h5py builds from this PR's source against HDF5 2.2.0 and 1.10.10. Both builds have ROS3 and direct VFD support disabled. This follow-up changes only tests and the release note; production code is unchanged from 6d43248.

  • On HDF5 2.2.0 with --runxfail, the new inheritance checks produce 9 failures: zero_preemption, positive_preemption, and mixed_values across the three dataset paths. The other 30 focused cases pass.
  • Reverting the production fixes in the isolated test checkout still gives 21 ordinary failures, so the new xfail does not hide the original zero-value or empty-prefix bugs.
  • HDF5 2.2.0, python -m pytest h5py/tests/test_dataset.py h5py/tests/test_group.py --no-network -q -rxXs: 399 passed, 9 xfailed.
  • HDF5 2.2.0, python -m pytest --pyargs h5py --no-network -q -rxXs: 930 passed, 47 skipped, 9 xfailed, 3 subtests passed.
  • HDF5 1.10.10: all 39 focused cases pass without xfails; the same full-suite command gives 901 passed, 85 skipped, 3 subtests passed. The library defaults match the file settings in this environment, so the inheritance assertions pass normally.
  • pre-commit run --files h5py/tests/test_dataset.py news/zero-dataset-chunk-cache.rst: passed.

I ran pytest and pre-commit directly, not the full tox/platform/HDF5-version matrix.

@codecov

codecov Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 90.62%. Comparing base (0f9cbe7) to head (b39ee2d).
⚠️ Report is 11 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2986      +/-   ##
==========================================
+ Coverage   90.51%   90.62%   +0.11%     
==========================================
  Files          16       16              
  Lines        2551     2561      +10     
==========================================
+ Hits         2309     2321      +12     
+ Misses        242      240       -2     

☔ 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.

@rmz-oz rmz-oz left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

zero cache values get applied now, I checked rdcc_nbytes, rdcc_nslots and rdcc_w0 = 0 through create_dataset and require_dataset against master. the prefixes on the same line still go through truthiness though: create_dataset(..., efile_prefix='') or virtual_prefix='' raises AttributeError: 'NoneType' object has no attribute 'set_efile_prefix' because dapl never gets created, and require_dataset on an existing dataset hits the same thing in open_dset. that's older than this PR, but the line is rewritten here anyway, and with is not None test_dataset.py still passes.

Comment thread h5py/_hl/dataset.py Outdated
if any([efile_prefix, virtual_prefix, rdcc_nbytes, rdcc_nslots, rdcc_w0]):
have_cache_settings = any(
value is not None for value in (rdcc_nbytes, rdcc_nslots, rdcc_w0))
if efile_prefix or virtual_prefix or have_cache_settings:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggested change
if efile_prefix or virtual_prefix or have_cache_settings:
if efile_prefix is not None or virtual_prefix is not None or have_cache_settings:

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.

Thanks @rmz-oz, good catch. I applied both suggestions, so empty str and bytes prefixes now get a DAPL before set_efile_prefix() / set_virtual_prefix() runs.

The new test covers both prefix options through create_dataset, require_dataset creating the dataset, and require_dataset reopening it after the file is closed, with None as a control. Each case checks the prefix on the access property list and writes and reads back data. Before the fix, the 12 empty-prefix cases failed with the AttributeError and the 6 None cases passed. With the fix, the full suite passes locally (907 passed, 79 skipped; Linux, CPython 3.12, HDF5 1.10.10).

Comment thread h5py/_hl/dataset.py Outdated
if any([efile_prefix, virtual_prefix, rdcc_nbytes, rdcc_nslots, rdcc_w0]):
have_cache_settings = any(
value is not None for value in (rdcc_nbytes, rdcc_nslots, rdcc_w0))
if efile_prefix or virtual_prefix or have_cache_settings:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

same here

Suggested change
if efile_prefix or virtual_prefix or have_cache_settings:
if efile_prefix is not None or virtual_prefix is not None or have_cache_settings:

@ajelenak

Copy link
Copy Markdown
Contributor

Hi @rioyu123,

Thanks for the PR. any([efile_prefix, virtual_prefix, rdcc_nbytes, rdcc_nslots, rdcc_w0]) has always conflated "not given" with "given as zero or empty", and switching to explicit is not None checks is the correct fix. I checked the new tests against the merge-base and 21 of the 39 cases fail there, so they genuinely capture the problem. I would like two small things before I merge it, and then there is a larger piece that I will pick up myself.

  1. An else in the cache test loop.

    In test_dset_chunk_cache_zero_values you open the file with rdcc_nbytes=1048576, rdcc_nslots=521, rdcc_w0=0.75, but the loop at h5py/tests/test_dataset.py:2218-2220 only checks the entries that were passed in, so nothing is watching the others:

    file_cache = f.id.get_access_plist().get_cache()[1:]
    for index, option in enumerate(('rdcc_nslots', 'rdcc_nbytes', 'rdcc_w0')):
        if option in supplied_options:
            assert cache[index] == supplied_options[option]
        else:  # <-- add
            assert cache[index] == file_cache[index]  # <-- add

    That will fail on this branch for zero_preemption, positive_preemption and mixed_values, which is the point explained below. Please add it as xfail for now, with a reference to this discussion if you want so the behaviour is pinned and we do not lose track of it.

  2. A sentence in the news entry. Something to the effect that supplying any one of rdcc_nslots, rdcc_nbytes or rdcc_w0 currently resets the other two to the HDF5 defaults rather than leaving the file's values in place. As written the entry reads as though nothing else changes, and for rdcc_w0=0 something else does.

Follow-up work – not yours

I became aware while reviewing this PR that more work is needed but it does not belong here.

The chunk cache settings are built in three steps: (1) read whatever the DAPL currently holds; (2) overwrite with the entries the caller supplied; (3) set all three back. These steps look simple and safe, but the first step is actually problematic.

A DAPL that has just been created does not hold numbers for these three properties. It holds sentinels, and a sentinel means: I have nothing to say about this one, use whatever the file was opened with. Libhdf5 honors that when the dataset is opened: H5D__chunk_init() looks at each of the three separately and falls back to the file's own setting for any that is still at its sentinel.

The catch is that reading those sentinels back out again is not possible. H5Pget_chunk_cache() never reports one. For any property that was never set, it quietly substitutes a value from the library's default file access property list — not from the file actually opened. For libhdf5 2.2.0 that means: (8191, 8388608, 0.75). So the numbers that come back are not the sentinels that were stored, and setting them back replaces "ask the file" with three deliberate choices. By the time the dataset is opened there is nothing left for the fallback to do.

To fix what I just described requires a follow-up because the fix needs decisions that are not really appropriate for this PR. So: add the else branch and the news sentence and I will merge this.

@rioyu123

Copy link
Copy Markdown
Contributor Author

Thanks @ajelenak. I've added the inheritance assertions and the release-note caveat. The explicit-option and data round-trip checks run before the xfail is applied, so they still fail normally: with the production fix reverted, all 21 original regressions fail. On HDF5 2.2.0, the three cases you identified account for the nine expected failures across the three dataset paths.

One note from testing: when the cache is disabled with zero bytes or zero slots, HDF5 reports both sizes as zero. Those cases keep their explicit-option and round-trip checks but skip the inheritance assertions.

Full suite: HDF5 2.2.0 gives 930 passed, 47 skipped, 9 xfailed; HDF5 1.10.10 gives 901 passed, 85 skipped, with all 39 focused cases passing normally. As requested, the inheritance fix itself stays out of this PR.

@ajelenak

Copy link
Copy Markdown
Contributor

Thanks @rioyu123. I'll merge this on October 30 unless there are further comments from other maintainers.

@ajelenak
ajelenak merged commit b45182c into h5py:master Oct 30, 2026
19 checks passed
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.

3 participants