From 9e65dd9cebc382f160fd3a5af98b76f9b49c4995 Mon Sep 17 00:00:00 2001 From: Prashanth Pai Date: Wed, 3 Jun 2015 12:31:40 +0530 Subject: [PATCH] Introduce fallocate support fallocate() allows for reserving disk space for a particular inode and fd. Hence, a client can be sure that he won't see a ENOSPC (eventually a 507 HTTP response) later during writes. Swift's object server has had fallocate() support from a long time. P.S: Older versions of glusterfs (<3.6) did not support fallocate because FUSE did not support it earlier. http://review.gluster.org/4969 http://fuse.996288.n3.nabble.com/fallocate-support-in-FUSE-td10668.html Change-Id: Ida4b16357901707d624f92bf1b2dc8f07da4f1ad Signed-off-by: Prashanth Pai --- etc/object-server.conf-swiftonfile | 9 +++++++-- swiftonfile/swift/obj/diskfile.py | 20 +++++++++++++------- test/unit/obj/test_diskfile.py | 7 +++++++ 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/etc/object-server.conf-swiftonfile b/etc/object-server.conf-swiftonfile index 23f67c5..51eff46 100644 --- a/etc/object-server.conf-swiftonfile +++ b/etc/object-server.conf-swiftonfile @@ -20,8 +20,13 @@ max_clients = 1024 # If not doing the above, setting this value initially to match the number of # CPUs is a good starting point for determining the right value. workers = 1 -# Override swift's default behaviour for fallocate. -disable_fallocate = true +# You can set disable_fallocate to true to turn off usage of fallocate() +# sys call to reserve space during a PUT operation. +# disable_fallocate = false +# +# You can set fallocate_reserve to the number of bytes you'd like fallocate to +# reserve, whether there is space for the given file size or not. +# fallocate_reserve = 0 [pipeline:main] pipeline = object-server diff --git a/swiftonfile/swift/obj/diskfile.py b/swiftonfile/swift/obj/diskfile.py index 4d00ef9..b6c1b23 100644 --- a/swiftonfile/swift/obj/diskfile.py +++ b/swiftonfile/swift/obj/diskfile.py @@ -28,7 +28,8 @@ from eventlet import sleep from contextlib import contextmanager from swiftonfile.swift.common.exceptions import AlreadyExistsAsFile, \ AlreadyExistsAsDir -from swift.common.utils import ThreadPool, hash_path, normalize_timestamp +from swift.common.utils import ThreadPool, hash_path, \ + normalize_timestamp, fallocate from swift.common.exceptions import DiskFileNotExist, DiskFileError, \ DiskFileNoSpace, DiskFileDeviceUnavailable, DiskFileNotOpen, \ DiskFileExpired @@ -931,17 +932,22 @@ class DiskFile(object): break dw = None try: + if size is not None and size > 0: + try: + fallocate(fd, size) + except OSError as err: + if err.errno in (errno.ENOSPC, errno.EDQUOT): + raise DiskFileNoSpace() + raise # Ensure it is properly owned before we make it available. do_fchown(fd, self._uid, self._gid) - # NOTE: we do not perform the fallocate() call at all. We ignore - # it completely since at the time of this writing FUSE does not - # support it. dw = DiskFileWriter(fd, tmppath, self) yield dw finally: - dw.close() - if dw._tmppath: - do_unlink(dw._tmppath) + if dw: + dw.close() + if dw._tmppath: + do_unlink(dw._tmppath) def write_metadata(self, metadata): """ diff --git a/test/unit/obj/test_diskfile.py b/test/unit/obj/test_diskfile.py index 25d1de3..93ad505 100644 --- a/test/unit/obj/test_diskfile.py +++ b/test/unit/obj/test_diskfile.py @@ -89,6 +89,10 @@ def _mock_do_fsync(fd): return +def _mock_fallocate(fd, size): + return + + class MockRenamerCalled(Exception): pass @@ -134,6 +138,8 @@ class TestDiskFile(unittest.TestCase): swiftonfile.swift.common.utils.read_metadata = _mock_read_metadata self._saved_do_fsync = swiftonfile.swift.obj.diskfile.do_fsync swiftonfile.swift.obj.diskfile.do_fsync = _mock_do_fsync + self._saved_fallocate = swiftonfile.swift.obj.diskfile.fallocate + swiftonfile.swift.obj.diskfile.fallocate = _mock_fallocate self.td = tempfile.mkdtemp() self.conf = dict(devices=self.td, mb_per_sync=2, keep_cache_size=(1024 * 1024), mount_check=False) @@ -148,6 +154,7 @@ class TestDiskFile(unittest.TestCase): swiftonfile.swift.common.utils.write_metadata = self._saved_ut_wm swiftonfile.swift.common.utils.read_metadata = self._saved_ut_rm swiftonfile.swift.obj.diskfile.do_fsync = self._saved_do_fsync + swiftonfile.swift.obj.diskfile.fallocate = self._saved_fallocate shutil.rmtree(self.td) def _get_diskfile(self, d, p, a, c, o, **kwargs):