-
-
Notifications
You must be signed in to change notification settings - Fork 282
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Initializing a group or array is not thread-safe, even with mode='w' #1435
Comments
cc: @d-v-b who was working on a more holistic form of hierarchy creation (though I don't know if it is likely to make the existing methods thread-safe) |
To be clear, the stuff i'm doing over in pydantic-zarr is just for representing hierarchies, in memory or in JSON. I'm not touching the routines for creating them. @shoyer I'm guessing |
Correct, I want a brand new array, and don't want to do any error checking or cleanup of the existing store. I am not concerned about mangled chunks from an old Zarr schema, both because I'm typically writing to a new location and because later stages of my job will write the full contents of array if/when they succeed. |
I understand that for your use case, old chunks won't be an issue, but I think if we add |
alternatively, we could stipulate that to using |
My preference would be that using The use cases are pretty similar. We need a low level method that highly efficient and concurrency safe, even at the cost of error checking, so it can be used by distributed applications (which can and should do their own error checking). Ideally, my example function above could be extended to any arbitrary use of Zarr's API to create a group of arrays, with optional metadata and any number of array values filled in (this is what happens when you fill in a Zarr template with Xarray by calling Basically, similar to how we how filling a complete chunk of an array works, we should support a way for creating groups/arrays where each individual operation is idempotent and may be repeated any number of times. |
I think my concern is that, with the way things currently work in zarr, using # create an array at foo
x = create_array(store, path='foo', mode='w', shape=(10,), chunks=(2,))
# initialize all chunks to 1
x[:] = 1
# access foo with w+ mode, changing the array metadata
y = create_array(store, path='foo', mode='w+', shape=(10,), chunks=(3,))
# all __getitem__ calls will break, because the chunks were written when the array had chunks=(2,),
# but now the chunks are (3,) according to array metadata
_ = y[:2]
# this __setitem__ call will work, because the slice matches the current chunking exactly
y[:3] = 2
# this __setitem__ call will fail, because the slice does not match the current chunking,
# which triggers a __getitem__ call, which will hit an outdated chunk
y[3] = 2
# this _should_ work, and result in all chunks having the correct shape afterwards
y[:] = 2
# this will work now
y[3] = 2 Am I missing something here and / or do other people think this kind of behavior is fine?
I totally agree. I will keep thinking about this to see if there's a way around my concerns. |
I think "w+" may not be the best name for this option, since "w+" as an fopen option means to truncate the existing data. In tensorstore we have an option called |
It seems that the difference between OK, so here are two alternative API ideas:
Any preferences? I think I would be happy with either. |
This is coming up repeatedly for Xarray-Beam users, so I think I'm going to try to tackle this. Thinking about this a little more, I think it would make sense to use |
Zarr version
2.14.2
Numcodecs version
0.11.0
Python Version
3.10.12
Operating System
Linux
Installation
colab.research.google.com
Description
The problem is that
mode='w'
ends up callinginit_group
orinit_array
withoverwrite=True
, which tries to delete all pre-existing items in the store:zarr-python/zarr/storage.py
Lines 670 to 675 in 4132f36
These calls to
rmdir
can fail, because storage APIs for removing a directory (at least with local storage) useshutil.rmtree()
withoutignore_errors=True
.Parallel execution frameworks like Apache Beam may execute code multiple times in parallel. Ideally, there would be a way we could guarantee this is safe with Zarr-Python, and we get the desired result as long as at least one task finishes successfully. In the Beam model, the framework will start executing duplicates copies of a task if it is taking a long time to complete, and will then cancel the extra copies after the first task finishes.
Writing individual metadata files is atomic (at least in most storage backends), so my suggestion would be to achieve this by allowing for some way not to delete existing files in a storage directory. Maybe
mode='w+'
would be a good way to indicate "create (ignore other data in the store if it exists)"?Steps to reproduce
Example using multiple threads:
This typically fails with something like
FileNotFoundError: [Errno 2] No such file or directory: '.zarray'
:Additional output
No response
The text was updated successfully, but these errors were encountered: