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
This commit is contained in:
Igor Canadi 2014-03-06 15:59:27 -08:00
parent 9c8ad62691
commit 26ac5603f4
2 changed files with 49 additions and 0 deletions

View file

@ -678,10 +678,19 @@ class PosixWritableFile : public WritableFile {
Status s; Status s;
s = Flush(); // flush cache to OS s = Flush(); // flush cache to OS
if (!s.ok()) { if (!s.ok()) {
return s;
} }
TEST_KILL_RANDOM(rocksdb_kill_odds); 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 (close(fd_) < 0) {
if (s.ok()) { if (s.ok()) {
s = IOError(filename_, errno); s = IOError(filename_, errno);

View file

@ -12,6 +12,11 @@
#include <iostream> #include <iostream>
#include <unordered_set> #include <unordered_set>
#ifdef OS_LINUX
#include <sys/stat.h>
#include <unistd.h>
#endif
#include "rocksdb/env.h" #include "rocksdb/env.h"
#include "port/port.h" #include "port/port.h"
#include "util/coding.h" #include "util/coding.h"
@ -258,6 +263,41 @@ TEST(EnvPosixTest, RandomAccessUniqueID) {
env_->DeleteFile(fname); 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<WritableFile> 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. // Returns true if any of the strings in ss are the prefix of another string.
bool HasPrefix(const std::unordered_set<std::string>& ss) { bool HasPrefix(const std::unordered_set<std::string>& ss) {
for (const std::string& s: ss) { for (const std::string& s: ss) {