You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
CachingFileManager opens one file handle per open_dataset() call — could identical managers share a FILE_CACHE entry? #11581
Disclaimer: My understanding of Xarray internals provided by Claude.
I have a situation in my downstream library Satpy where code was written to open a NetCDF file with open_dataset multiple times. The files I was dealing with had a lot of groups and loading a dataset included ~40 files. The initial naive implementation called open_dataset once per variable and loaded things as dask arrays. Due to a bug in HDF5 which I've filed privately upstream this was causing a segmentation fault when xarray was closing one cached entry even though others for the same file still existed and were open.
CachingFileManager._make_key() includes self._manager_id, a fresh uuid4() per
manager. So N open_dataset() calls on the same path with identical kwargs produce N
independent handles, each taking its own FILE_CACHE slot. With the default file_cache_maxsize=128 and our naive implementation we end up thrashing the LRU cache even for a single file. Since every open_dataset is a new entry that means the file is reopened and metadata is reparsed (at least that's what Claude says). Under the hood my understanding is that HDF5 is only keeping one actual open file in modern HDF5 versions.
I'm wondering whether that is intended in this case, or whether managers that are
simultaneously alive and compare equal on (opener, args, mode, kwargs) could share one
open file? I know there was #4879 to fix #4240 and #4862. I'm not saying that I disagree with that use case and those changes, but wondering if a further optimization can be done (even if opt-in) to share a single Dataset for the same combination. I think Claude said that even for group-specific open_dataset calls the root/top-level NetCDF4.Dataset object is held onto? So maybe sharing the entry for all groups would work too.
Claude suggested two possible directions that I'll summarize here:
To keep the Cache files for different CachingFileManager objects separately #4879 fix working and prevent new open_dataset calls not reusing an old cache entry, make the caching smarter to see "does this cache entry have any active managers", reuse that entry if so or open a new version if no active managers.
Make the LRU cache more understanding of entries that share the same paths and handle them differently. Like sizing/accounting per path. I don't see how this would be possible as the cache understanding that two or more entries are "equivalent" would probably mean they should just use the same entry in the first place.
Oh I should have noted that I've already started fixing our issue in Satpy by calling open_dataset once per group and holding on to the xarray Dataset for reuse.
Thanks for the detailed write-up, and sorry for the slow reply. The separate cache entry per open_dataset() call is intentional: #4879 added the manager id to the key so that opening a file again gives you its current state, e.g. after it was modified on disk (#4862), instead of a cached handle that may still show the old content.
For your case of one file with many groups, xr.open_groups(path) or xr.open_datatree(path) should already do what you want: they open the file only once and share that handle for all groups. With a test file with 10 groups, open_dataset(..., group=...) per group opens the file 10 times, while open_groups/open_datatree open it once.
The segfault you hit is most likely HDFGroup/hdf5#6721 (case 2): with several handles to the same file, closing one of them frees state that the others still use, and the next open of that file then crashes in H5Dget_create_plist. Opening each file only once avoids it.
That said, I think your option 1 could make sense for HDF5-based files (engine="netcdf4" and "h5netcdf"), because there the separate handles don't actually give us the freshness that #4879 is about while another handle to the file is still open:
With HDF5 file locking (the default), the file can't be modified at all while it is open: writing to it from another process fails with an HDF error.
With HDF5_USE_FILE_LOCKING=FALSE, another process can modify it, but a second open_dataset while the first dataset is still open sees the old attributes together with the new data, because HDF5 shares its internal file object between handles to the same file. Only after all handles are closed does a new open_dataset see the new state consistently.
So for these backends, managers with identical arguments in read mode could share one handle as long as any of them is alive (reference-counted, and closed with the last one), while a new open_dataset after all of them are closed still opens the file fresh. That would avoid the repeated opens and LRU thrashing you saw, and also the multiple-handle situation that triggers the HDF5 crash. For non-HDF5 formats like netCDF3, reopening does give fresh data, so they should keep the current behavior. Would that cover your use case?
For your case of one file with many groups, xr.open_groups(path) or xr.open_datatree(path) should already do what you want
Yes, however this requires decoding all of those groups when only a couple may be used.
For the HDF5 but I think it is actually case 1 from that issue and I had filed it as a separate security issue with them.
And yes I had only been thinking about read-only use cases here.
That said, I think your option 1 could make sense for HDF5-based files (engine="netcdf4" and "h5netcdf"), because there the separate handles don't actually give us the freshness that #4879 is about while another handle to the file is still open:
Ok I see. So technically the 4879 solution is "broken" for HDF5 files when a handle remains open. Good to know.
For non-HDF5 formats like netCDF3, reopening does give fresh data, so they should keep the current behavior. Would that cover your use case?
Yes. I'm only concerned about NetCDF4/HDF5 files in my current issue. I was trying to optimize (and avoid seg faults) the reading of these datasets with a lot of files with lots of groups with lots of variables.
Regarding decoding all groups with open_datatree: you can also open each file only once and still decode just the groups you need, by passing the open file to the backend store yourself:
All datasets then share one handle per file. Note that closing any of these datasets closes the shared file, so close the file yourself once you are done with all of them, instead of the individual datasets.
Activity
djhoese commentedon Sep 11, 2026
Oh I should have noted that I've already started fixing our issue in Satpy by calling
open_datasetonce per group and holding on to the xarrayDatasetfor reuse.headtr1ck commentedon Oct 12, 2026
Thanks for the detailed write-up, and sorry for the slow reply. The separate cache entry per
open_dataset()call is intentional: #4879 added the manager id to the key so that opening a file again gives you its current state, e.g. after it was modified on disk (#4862), instead of a cached handle that may still show the old content.For your case of one file with many groups,
xr.open_groups(path)orxr.open_datatree(path)should already do what you want: they open the file only once and share that handle for all groups. With a test file with 10 groups,open_dataset(..., group=...)per group opens the file 10 times, whileopen_groups/open_datatreeopen it once.The segfault you hit is most likely HDFGroup/hdf5#6721 (case 2): with several handles to the same file, closing one of them frees state that the others still use, and the next open of that file then crashes in
H5Dget_create_plist. Opening each file only once avoids it.That said, I think your option 1 could make sense for HDF5-based files (
engine="netcdf4"and"h5netcdf"), because there the separate handles don't actually give us the freshness that #4879 is about while another handle to the file is still open:HDF5_USE_FILE_LOCKING=FALSE, another process can modify it, but a secondopen_datasetwhile the first dataset is still open sees the old attributes together with the new data, because HDF5 shares its internal file object between handles to the same file. Only after all handles are closed does a newopen_datasetsee the new state consistently.So for these backends, managers with identical arguments in read mode could share one handle as long as any of them is alive (reference-counted, and closed with the last one), while a new
open_datasetafter all of them are closed still opens the file fresh. That would avoid the repeated opens and LRU thrashing you saw, and also the multiple-handle situation that triggers the HDF5 crash. For non-HDF5 formats like netCDF3, reopening does give fresh data, so they should keep the current behavior. Would that cover your use case?djhoese commentedon Oct 12, 2026
Thanks for the response.
Yes, however this requires decoding all of those groups when only a couple may be used.
For the HDF5 but I think it is actually case 1 from that issue and I had filed it as a separate security issue with them.
And yes I had only been thinking about read-only use cases here.
Ok I see. So technically the 4879 solution is "broken" for HDF5 files when a handle remains open. Good to know.
Yes. I'm only concerned about NetCDF4/HDF5 files in my current issue. I was trying to optimize (and avoid seg faults) the reading of these datasets with a lot of files with lots of groups with lots of variables.
headtr1ck commentedon Oct 27, 2026
Regarding decoding all groups with
open_datatree: you can also open each file only once and still decode just the groups you need, by passing the open file to the backend store yourself:or with netCDF4:
All datasets then share one handle per file. Note that closing any of these datasets closes the shared file, so close the file yourself once you are done with all of them, instead of the individual datasets.
djhoese commentedon Oct 27, 2026
Yep, thanks. This is exactly what I'm doing now actually. It's one of the things that sparked me filing #11814.