diff --git a/util/env_posix.cc b/util/env_posix.cc index fcfea28ab4..ef7655e608 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 eb28293037..a442e3a5cd 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) {