diff --git a/db/log_test.cc b/db/log_test.cc index 427a31a2be7ddb8d8b686bf6cb4eeb33185104e6..fe9b6308451d484a27c1f1208bf599148ebef2ad 100644 --- a/db/log_test.cc +++ b/db/log_test.cc @@ -174,6 +174,8 @@ class LogTest : public ::testing::TestWithParam { 3 * header_size; } + Slice* get_reader_contents() { return &reader_contents_; } + void Write(const std::string& msg) { writer_.AddRecord(Slice(msg)); } @@ -690,6 +692,29 @@ TEST_P(LogTest, ClearEofError2) { ASSERT_EQ("OK", MatchError("read error")); } +TEST_P(LogTest, Recycle) { + if (!GetParam()) { + return; // test is only valid for recycled logs + } + Write("foo"); + Write("bar"); + Write("baz"); + Write("bif"); + Write("blitz"); + while (get_reader_contents()->size() < log::kBlockSize * 2) { + Write("xxxxxxxxxxxxxxxx"); + } + unique_ptr dest_holder(test::GetWritableFileWriter( + new test::OverwritingStringSink(get_reader_contents()))); + Writer recycle_writer(std::move(dest_holder), 123, true); + recycle_writer.AddRecord(Slice("foooo")); + recycle_writer.AddRecord(Slice("bar")); + ASSERT_GE(get_reader_contents()->size(), log::kBlockSize * 2); + ASSERT_EQ("foooo", Read()); + ASSERT_EQ("bar", Read()); + ASSERT_EQ("EOF", Read()); +} + INSTANTIATE_TEST_CASE_P(bool, LogTest, ::testing::Values(0, 2)); } // namespace log diff --git a/util/testutil.h b/util/testutil.h index 80f6c55c104f12334a8dea8399d19d958e660045..d2c4b644995fec3671bedbfc73de6f3b83822daa 100644 --- a/util/testutil.h +++ b/util/testutil.h @@ -230,6 +230,49 @@ class StringSink: public WritableFile { size_t last_flush_; }; +// Like StringSink, this writes into a string. Unlink StringSink, it +// has some initial content and overwrites it, just like a recycled +// log file. +class OverwritingStringSink : public WritableFile { + public: + explicit OverwritingStringSink(Slice* reader_contents) + : WritableFile(), + contents_(""), + reader_contents_(reader_contents), + last_flush_(0) {} + + const std::string& contents() const { return contents_; } + + virtual Status Truncate(uint64_t size) override { + contents_.resize(static_cast(size)); + return Status::OK(); + } + virtual Status Close() override { return Status::OK(); } + virtual Status Flush() override { + if (last_flush_ < contents_.size()) { + assert(reader_contents_->size() >= contents_.size()); + memcpy((char*)reader_contents_->data() + last_flush_, + contents_.data() + last_flush_, contents_.size() - last_flush_); + last_flush_ = contents_.size(); + } + return Status::OK(); + } + virtual Status Sync() override { return Status::OK(); } + virtual Status Append(const Slice& slice) override { + contents_.append(slice.data(), slice.size()); + return Status::OK(); + } + void Drop(size_t bytes) { + contents_.resize(contents_.size() - bytes); + if (last_flush_ > contents_.size()) last_flush_ = contents_.size(); + } + + private: + std::string contents_; + Slice* reader_contents_; + size_t last_flush_; +}; + class StringSource: public RandomAccessFile { public: explicit StringSource(const Slice& contents, uint64_t uniq_id = 0,