From 26ac5603f479d930b954fbe7083bdc5bfc2fe4dc Mon Sep 17 00:00:00 2001 From: Igor Canadi Date: Thu, 6 Mar 2014 15:59:27 -0800 Subject: [PATCH] Truncate unused space on PosixWritableFile::Close() Summary: Blocks allocated with fallocate will take extra space on disk even if they are unused and the file is close. Now we remove the extra blocks at the end of the file by calling `ftruncate`. Test Plan: added a test to env_test Reviewers: dhruba Reviewed By: dhruba CC: leveldb Differential Revision: https://reviews.facebook.net/D16647 --- util/env_posix.cc | 9 +++++++++ util/env_test.cc | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/util/env_posix.cc b/util/env_posix.cc index fcfea28ab..ef7655e60 100644 --- a/util/env_posix.cc +++ b/util/env_posix.cc @@ -678,10 +678,19 @@ class PosixWritableFile : public WritableFile { Status s; s = Flush(); // flush cache to OS if (!s.ok()) { + return s; } TEST_KILL_RANDOM(rocksdb_kill_odds); + size_t block_size; + size_t last_allocated_block; + GetPreallocationStatus(&block_size, &last_allocated_block); + if (last_allocated_block > 0) { + // trim the extra space preallocated at the end of the file + ftruncate(fd_, filesize_); // ignore errors + } + if (close(fd_) < 0) { if (s.ok()) { s = IOError(filename_, errno); diff --git a/util/env_test.cc b/util/env_test.cc index eb2829303..a442e3a5c 100644 --- a/util/env_test.cc +++ b/util/env_test.cc @@ -12,6 +12,11 @@ #include #include +#ifdef OS_LINUX +#include +#include +#endif + #include "rocksdb/env.h" #include "port/port.h" #include "util/coding.h" @@ -258,6 +263,41 @@ TEST(EnvPosixTest, RandomAccessUniqueID) { env_->DeleteFile(fname); } +// only works in linux platforms +#ifdef ROCKSDB_FALLOCATE_PRESENT +TEST(EnvPosixTest, AllocateTest) { + std::string fname = GetOnDiskTestDir() + "/preallocate_testfile"; + EnvOptions soptions; + soptions.use_mmap_writes = false; + unique_ptr wfile; + ASSERT_OK(env_->NewWritableFile(fname, &wfile, soptions)); + + // allocate 100 MB + size_t kPreallocateSize = 100 * 1024 * 1024; + size_t kBlockSize = 512; + size_t kPageSize = 4096; + std::string data = "test"; + wfile->SetPreallocationBlockSize(kPreallocateSize); + ASSERT_OK(wfile->Append(Slice(data))); + ASSERT_OK(wfile->Flush()); + + struct stat f_stat; + stat(fname.c_str(), &f_stat); + ASSERT_EQ(data.size(), f_stat.st_size); + // verify that blocks are preallocated + ASSERT_EQ(kPreallocateSize / kBlockSize, f_stat.st_blocks); + + // close the file, should deallocate the blocks + wfile.reset(); + + stat(fname.c_str(), &f_stat); + ASSERT_EQ(data.size(), f_stat.st_size); + // verify that preallocated blocks were deallocated on file close + size_t data_blocks_pages = ((data.size() + kPageSize - 1) / kPageSize); + ASSERT_EQ(data_blocks_pages * kPageSize / kBlockSize, f_stat.st_blocks); +} +#endif + // Returns true if any of the strings in ss are the prefix of another string. bool HasPrefix(const std::unordered_set& ss) { for (const std::string& s: ss) {