Skip to content

avoid race condition during chunk write #327

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

Merged
merged 8 commits into from
Dec 4, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions docs/release.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,28 @@ Enhancements
* Support has been added for structured arrays with sub-array shape and/or nested fields. By
:user:`Tarik Onalan <onalant>`, :issue:`111`, :issue:`296`.

Maintenance
~~~~~~~~~~~
Bug fixes
~~~~~~~~~

* The implementation of the :class:`zarr.storage.DirectoryStore` class has been modified to
ensure that writes are atomic and there are no race conditions where a chunk might appear
transiently missing during a write operation. By :user:`sbalmer <sbalmer>`, :issue:`327`,
:issue:`263`.

* The required version of the `numcodecs <http://numcodecs.rtfd.io>`_ package has been upgraded
to 0.6.2, which has enabled some code simplification and fixes a failing test involving
msgpack encoding. By :user:`John Kirkham <jakirkham>`, :issue:`352`, :issue:`355`,
:issue:`324`.

* CI and test environments have been upgraded to include Python 3.7, drop Python 3.4, and
upgrade all pinned package requirements. :issue:`308`.

* Failing tests related to pickling/unpickling have been fixed. By :user:`Ryan Williams <ryan-williams>`,
:issue:`273`, :issue:`308`.

Acknowledgments
~~~~~~~~~~~~~~~
Maintenance
~~~~~~~~~~~

* CI and test environments have been upgraded to include Python 3.7, drop Python 3.4, and
upgrade all pinned package requirements. :issue:`308`.


.. _release_2.2.0:

Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ fasteners
numcodecs
numpy
pytest
pyosreplace; python_version < '3.3' and sys.platform == 'win32'
1 change: 1 addition & 0 deletions requirements_dev.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
asciitree==0.3.3
fasteners==0.14.1
numcodecs==0.6.2
pyosreplace==0.1; python_version < '3.3' and sys.platform == 'win32'
19 changes: 12 additions & 7 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, division
from setuptools import setup
import sys


DESCRIPTION = 'An implementation of chunked, compressed, ' \
Expand All @@ -9,6 +10,16 @@
with open('README.rst') as f:
LONG_DESCRIPTION = f.read()

dependencies = [
'asciitree',
'numpy>=1.7',
'fasteners',
'numcodecs>=0.6.2',
]

if sys.version_info < (3, 3) and sys.platform == "win32":
dependencies.append('pyosreplace')

setup(
name='zarr',
description=DESCRIPTION,
Expand All @@ -22,12 +33,7 @@
'setuptools>18.0',
'setuptools-scm>1.5.4'
],
install_requires=[
'asciitree',
'numpy>=1.7',
'fasteners',
'numcodecs>=0.6.2',
],
install_requires=dependencies,
package_dir={'': '.'},
packages=['zarr', 'zarr.tests'],
classifiers=[
Expand All @@ -42,7 +48,6 @@
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
Expand Down
13 changes: 10 additions & 3 deletions zarr/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,15 @@
from zarr.codecs import Zlib
default_compressor = Zlib()

# Find which function to use for atomic replace
if sys.version_info >= (3, 3):
from os import replace
elif sys.platform == "win32": # pragma: no cover
from osreplace import replace
else: # pragma: no cover
# POSIX rename() is always atomic
from os import rename as replace


def _path_to_prefix(path):
# assume path already normalized
Expand Down Expand Up @@ -752,9 +761,7 @@ def __setitem__(self, key, value):
f.write(value)

# move temporary file into place
if os.path.exists(file_path):
os.remove(file_path)
os.rename(temp_path, file_path)
replace(temp_path, file_path)

finally:
# clean up if temp file still exists for whatever reason
Expand Down