Skip to content

Commit 3c8be10

Browse files
committed
fixed issues 66 (leaking files on disk error) and 68 (no sync of CURRENT file)
1 parent c8c5866 commit 3c8be10

File tree

5 files changed

+95
-34
lines changed

5 files changed

+95
-34
lines changed

db/db_impl.cc

+6-14
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,8 @@ void DBImpl::BackgroundCompaction() {
655655
CompactionState* compact = new CompactionState(c);
656656
status = DoCompactionWork(compact);
657657
CleanupCompaction(compact);
658+
c->ReleaseInputs();
659+
DeleteObsoleteFiles();
658660
}
659661
delete c;
660662

@@ -672,6 +674,9 @@ void DBImpl::BackgroundCompaction() {
672674

673675
if (is_manual) {
674676
ManualCompaction* m = manual_compaction_;
677+
if (!status.ok()) {
678+
m->done = true;
679+
}
675680
if (!m->done) {
676681
// We only compacted part of the requested range. Update *m
677682
// to the range that is left to be compacted.
@@ -793,21 +798,8 @@ Status DBImpl::InstallCompactionResults(CompactionState* compact) {
793798
compact->compaction->edit()->AddFile(
794799
level + 1,
795800
out.number, out.file_size, out.smallest, out.largest);
796-
pending_outputs_.erase(out.number);
797801
}
798-
compact->outputs.clear();
799-
800-
Status s = versions_->LogAndApply(compact->compaction->edit(), &mutex_);
801-
if (s.ok()) {
802-
compact->compaction->ReleaseInputs();
803-
DeleteObsoleteFiles();
804-
} else {
805-
// Discard any files we may have created during this failed compaction
806-
for (size_t i = 0; i < compact->outputs.size(); i++) {
807-
env_->DeleteFile(TableFileName(dbname_, compact->outputs[i].number));
808-
}
809-
}
810-
return s;
802+
return versions_->LogAndApply(compact->compaction->edit(), &mutex_);
811803
}
812804

813805
Status DBImpl::DoCompactionWork(CompactionState* compact) {

db/db_test.cc

+56-7
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,12 @@ class SpecialEnv : public EnvWrapper {
2828
// sstable Sync() calls are blocked while this pointer is non-NULL.
2929
port::AtomicPointer delay_sstable_sync_;
3030

31+
// Simulate no-space errors while this pointer is non-NULL.
32+
port::AtomicPointer no_space_;
33+
3134
explicit SpecialEnv(Env* base) : EnvWrapper(base) {
3235
delay_sstable_sync_.Release_Store(NULL);
36+
no_space_.Release_Store(NULL);
3337
}
3438

3539
Status NewWritableFile(const std::string& f, WritableFile** r) {
@@ -44,7 +48,14 @@ class SpecialEnv : public EnvWrapper {
4448
base_(base) {
4549
}
4650
~SSTableFile() { delete base_; }
47-
Status Append(const Slice& data) { return base_->Append(data); }
51+
Status Append(const Slice& data) {
52+
if (env_->no_space_.Acquire_Load() != NULL) {
53+
// Drop writes on the floor
54+
return Status::OK();
55+
} else {
56+
return base_->Append(data);
57+
}
58+
}
4859
Status Close() { return base_->Close(); }
4960
Status Flush() { return base_->Flush(); }
5061
Status Sync() {
@@ -239,6 +250,12 @@ class DBTest {
239250
return result;
240251
}
241252

253+
int CountFiles() {
254+
std::vector<std::string> files;
255+
env_->GetChildren(dbname_, &files);
256+
return static_cast<int>(files.size());
257+
}
258+
242259
uint64_t Size(const Slice& start, const Slice& limit) {
243260
Range r(start, limit);
244261
uint64_t size;
@@ -1266,6 +1283,37 @@ TEST(DBTest, DBOpen_Options) {
12661283
db = NULL;
12671284
}
12681285

1286+
// Check that number of files does not grow when we are out of space
1287+
TEST(DBTest, NoSpace) {
1288+
Options options;
1289+
options.env = env_;
1290+
Reopen(&options);
1291+
1292+
ASSERT_OK(Put("foo", "v1"));
1293+
ASSERT_EQ("v1", Get("foo"));
1294+
Compact("a", "z");
1295+
const int num_files = CountFiles();
1296+
env_->no_space_.Release_Store(env_); // Force out-of-space errors
1297+
for (int i = 0; i < 10; i++) {
1298+
for (int level = 0; level < config::kNumLevels-1; level++) {
1299+
dbfull()->TEST_CompactRange(level, NULL, NULL);
1300+
}
1301+
}
1302+
env_->no_space_.Release_Store(NULL);
1303+
ASSERT_LT(CountFiles(), num_files + 5);
1304+
}
1305+
1306+
TEST(DBTest, FilesDeletedAfterCompaction) {
1307+
ASSERT_OK(Put("foo", "v2"));
1308+
Compact("a", "z");
1309+
const int num_files = CountFiles();
1310+
for (int i = 0; i < 10; i++) {
1311+
ASSERT_OK(Put("foo", "v2"));
1312+
Compact("a", "z");
1313+
}
1314+
ASSERT_EQ(CountFiles(), num_files);
1315+
}
1316+
12691317
// Multi-threaded test:
12701318
namespace {
12711319

@@ -1287,14 +1335,15 @@ struct MTThread {
12871335

12881336
static void MTThreadBody(void* arg) {
12891337
MTThread* t = reinterpret_cast<MTThread*>(arg);
1338+
int id = t->id;
12901339
DB* db = t->state->test->db_;
12911340
uintptr_t counter = 0;
1292-
fprintf(stderr, "... starting thread %d\n", t->id);
1293-
Random rnd(1000 + t->id);
1341+
fprintf(stderr, "... starting thread %d\n", id);
1342+
Random rnd(1000 + id);
12941343
std::string value;
12951344
char valbuf[1500];
12961345
while (t->state->stop.Acquire_Load() == NULL) {
1297-
t->state->counter[t->id].Release_Store(reinterpret_cast<void*>(counter));
1346+
t->state->counter[id].Release_Store(reinterpret_cast<void*>(counter));
12981347

12991348
int key = rnd.Uniform(kNumKeys);
13001349
char keybuf[20];
@@ -1304,7 +1353,7 @@ static void MTThreadBody(void* arg) {
13041353
// Write values of the form <key, my id, counter>.
13051354
// We add some padding for force compactions.
13061355
snprintf(valbuf, sizeof(valbuf), "%d.%d.%-1000d",
1307-
key, t->id, static_cast<int>(counter));
1356+
key, id, static_cast<int>(counter));
13081357
ASSERT_OK(db->Put(WriteOptions(), Slice(keybuf), Slice(valbuf)));
13091358
} else {
13101359
// Read a value and verify that it matches the pattern written above.
@@ -1325,8 +1374,8 @@ static void MTThreadBody(void* arg) {
13251374
}
13261375
counter++;
13271376
}
1328-
t->state->thread_done[t->id].Release_Store(t);
1329-
fprintf(stderr, "... stopping thread %d after %d ops\n", t->id, int(counter));
1377+
t->state->thread_done[id].Release_Store(t);
1378+
fprintf(stderr, "... stopping thread %d after %d ops\n", id, int(counter));
13301379
}
13311380

13321381
} // namespace

db/filename.cc

+5-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111

1212
namespace leveldb {
1313

14+
// A utility routine: write "data" to the named file and Sync() it.
15+
extern Status WriteStringToFileSync(Env* env, const Slice& data,
16+
const std::string& fname);
17+
1418
static std::string MakeFileName(const std::string& name, uint64_t number,
1519
const char* suffix) {
1620
char buf[100];
@@ -122,7 +126,7 @@ Status SetCurrentFile(Env* env, const std::string& dbname,
122126
assert(contents.starts_with(dbname + "/"));
123127
contents.remove_prefix(dbname.size() + 1);
124128
std::string tmp = TempFileName(dbname, descriptor_number);
125-
Status s = WriteStringToFile(env, contents.ToString() + "\n", tmp);
129+
Status s = WriteStringToFileSync(env, contents.ToString() + "\n", tmp);
126130
if (s.ok()) {
127131
s = env->RenameFile(tmp, CurrentFileName(dbname));
128132
}

util/env.cc

+16-2
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,18 @@ void Log(Logger* info_log, const char* format, ...) {
3333
}
3434
}
3535

36-
Status WriteStringToFile(Env* env, const Slice& data,
37-
const std::string& fname) {
36+
static Status DoWriteStringToFile(Env* env, const Slice& data,
37+
const std::string& fname,
38+
bool should_sync) {
3839
WritableFile* file;
3940
Status s = env->NewWritableFile(fname, &file);
4041
if (!s.ok()) {
4142
return s;
4243
}
4344
s = file->Append(data);
45+
if (s.ok() && should_sync) {
46+
s = file->Sync();
47+
}
4448
if (s.ok()) {
4549
s = file->Close();
4650
}
@@ -51,6 +55,16 @@ Status WriteStringToFile(Env* env, const Slice& data,
5155
return s;
5256
}
5357

58+
Status WriteStringToFile(Env* env, const Slice& data,
59+
const std::string& fname) {
60+
return DoWriteStringToFile(env, data, fname, false);
61+
}
62+
63+
Status WriteStringToFileSync(Env* env, const Slice& data,
64+
const std::string& fname) {
65+
return DoWriteStringToFile(env, data, fname, true);
66+
}
67+
5468
Status ReadFileToString(Env* env, const std::string& fname, std::string* data) {
5569
data->clear();
5670
SequentialFile* file;

util/env_test.cc

+12-10
Original file line numberDiff line numberDiff line change
@@ -22,29 +22,30 @@ class EnvPosixTest {
2222
};
2323

2424
static void SetBool(void* ptr) {
25-
*(reinterpret_cast<bool*>(ptr)) = true;
25+
reinterpret_cast<port::AtomicPointer*>(ptr)->NoBarrier_Store(ptr);
2626
}
2727

2828
TEST(EnvPosixTest, RunImmediately) {
29-
bool called = false;
29+
port::AtomicPointer called (NULL);
3030
env_->Schedule(&SetBool, &called);
3131
Env::Default()->SleepForMicroseconds(kDelayMicros);
32-
ASSERT_TRUE(called);
32+
ASSERT_TRUE(called.NoBarrier_Load() != NULL);
3333
}
3434

3535
TEST(EnvPosixTest, RunMany) {
36-
int last_id = 0;
36+
port::AtomicPointer last_id (NULL);
3737

3838
struct CB {
39-
int* last_id_ptr; // Pointer to shared slot
40-
int id; // Order# for the execution of this callback
39+
port::AtomicPointer* last_id_ptr; // Pointer to shared slot
40+
uintptr_t id; // Order# for the execution of this callback
4141

42-
CB(int* p, int i) : last_id_ptr(p), id(i) { }
42+
CB(port::AtomicPointer* p, int i) : last_id_ptr(p), id(i) { }
4343

4444
static void Run(void* v) {
4545
CB* cb = reinterpret_cast<CB*>(v);
46-
ASSERT_EQ(cb->id-1, *cb->last_id_ptr);
47-
*cb->last_id_ptr = cb->id;
46+
void* cur = cb->last_id_ptr->NoBarrier_Load();
47+
ASSERT_EQ(cb->id-1, reinterpret_cast<uintptr_t>(cur));
48+
cb->last_id_ptr->Release_Store(reinterpret_cast<void*>(cb->id));
4849
}
4950
};
5051

@@ -59,7 +60,8 @@ TEST(EnvPosixTest, RunMany) {
5960
env_->Schedule(&CB::Run, &cb4);
6061

6162
Env::Default()->SleepForMicroseconds(kDelayMicros);
62-
ASSERT_EQ(4, last_id);
63+
void* cur = last_id.Acquire_Load();
64+
ASSERT_EQ(4, reinterpret_cast<uintptr_t>(cur));
6365
}
6466

6567
struct State {

0 commit comments

Comments
 (0)