2013-10-16 21:59:46 +00:00
|
|
|
// Copyright (c) 2013, Facebook, Inc. All rights reserved.
|
|
|
|
// This source code is licensed under the BSD-style license found in the
|
|
|
|
// LICENSE file in the root directory of this source tree. An additional grant
|
|
|
|
// of patent rights can be found in the PATENTS file in the same directory.
|
2014-04-15 20:39:26 +00:00
|
|
|
|
|
|
|
#ifndef ROCKSDB_LITE
|
2014-10-30 00:43:37 +00:00
|
|
|
#ifndef __STDC_FORMAT_MACROS
|
|
|
|
#define __STDC_FORMAT_MACROS
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <inttypes.h>
|
2013-08-06 19:54:37 +00:00
|
|
|
#include "db/transaction_log_impl.h"
|
2012-11-30 01:28:37 +00:00
|
|
|
#include "db/write_batch_internal.h"
|
2013-03-21 22:12:35 +00:00
|
|
|
|
2013-10-04 04:49:15 +00:00
|
|
|
namespace rocksdb {
|
2012-11-30 01:28:37 +00:00
|
|
|
|
|
|
|
TransactionLogIteratorImpl::TransactionLogIteratorImpl(
|
2014-02-05 21:12:23 +00:00
|
|
|
const std::string& dir, const DBOptions* options,
|
2014-02-28 19:50:36 +00:00
|
|
|
const TransactionLogIterator::ReadOptions& read_options,
|
2014-02-05 21:12:23 +00:00
|
|
|
const EnvOptions& soptions, const SequenceNumber seq,
|
2014-10-30 00:43:37 +00:00
|
|
|
std::unique_ptr<VectorLogPtr> files, VersionSet const* const versions)
|
2014-02-05 21:12:23 +00:00
|
|
|
: dir_(dir),
|
|
|
|
options_(options),
|
2014-02-28 19:50:36 +00:00
|
|
|
read_options_(read_options),
|
2014-02-05 21:12:23 +00:00
|
|
|
soptions_(soptions),
|
|
|
|
startingSequenceNumber_(seq),
|
|
|
|
files_(std::move(files)),
|
|
|
|
started_(false),
|
|
|
|
isValid_(false),
|
|
|
|
currentFileIndex_(0),
|
|
|
|
currentBatchSeq_(0),
|
|
|
|
currentLastSeq_(0),
|
2014-10-30 00:43:37 +00:00
|
|
|
versions_(versions) {
|
2013-10-13 22:28:24 +00:00
|
|
|
assert(files_ != nullptr);
|
2014-10-30 00:43:37 +00:00
|
|
|
assert(versions_ != nullptr);
|
2012-11-30 01:28:37 +00:00
|
|
|
|
2013-04-29 20:19:24 +00:00
|
|
|
reporter_.env = options_->env;
|
|
|
|
reporter_.info_log = options_->info_log.get();
|
2013-10-13 22:28:24 +00:00
|
|
|
SeekToStartSequence(); // Seek till starting sequence
|
2012-11-30 01:28:37 +00:00
|
|
|
}
|
|
|
|
|
2013-01-20 10:07:13 +00:00
|
|
|
Status TransactionLogIteratorImpl::OpenLogFile(
|
2013-10-21 02:06:19 +00:00
|
|
|
const LogFile* logFile,
|
|
|
|
unique_ptr<SequentialFile>* file) {
|
2012-11-30 01:28:37 +00:00
|
|
|
Env* env = options_->env;
|
2013-08-06 19:54:37 +00:00
|
|
|
if (logFile->Type() == kArchivedLogFile) {
|
2013-10-01 21:46:52 +00:00
|
|
|
std::string fname = ArchivedLogFileName(dir_, logFile->LogNumber());
|
2013-03-15 00:00:04 +00:00
|
|
|
return env->NewSequentialFile(fname, file, soptions_);
|
2012-11-30 01:28:37 +00:00
|
|
|
} else {
|
2013-10-01 21:46:52 +00:00
|
|
|
std::string fname = LogFileName(dir_, logFile->LogNumber());
|
2014-11-06 19:14:28 +00:00
|
|
|
Status s = env->NewSequentialFile(fname, file, soptions_);
|
|
|
|
if (!s.ok()) {
|
2012-11-30 01:28:37 +00:00
|
|
|
// If cannot open file in DB directory.
|
|
|
|
// Try the archive dir, as it could have moved in the meanwhile.
|
2013-10-01 21:46:52 +00:00
|
|
|
fname = ArchivedLogFileName(dir_, logFile->LogNumber());
|
2014-11-06 19:14:28 +00:00
|
|
|
s = env->NewSequentialFile(fname, file, soptions_);
|
2012-11-30 01:28:37 +00:00
|
|
|
}
|
2014-11-06 19:14:28 +00:00
|
|
|
return s;
|
2012-11-30 01:28:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-04 18:44:04 +00:00
|
|
|
BatchResult TransactionLogIteratorImpl::GetBatch() {
|
2012-11-30 01:28:37 +00:00
|
|
|
assert(isValid_); // cannot call in a non valid state.
|
2013-03-04 18:44:04 +00:00
|
|
|
BatchResult result;
|
2013-10-13 22:28:24 +00:00
|
|
|
result.sequence = currentBatchSeq_;
|
2013-03-04 18:44:04 +00:00
|
|
|
result.writeBatchPtr = std::move(currentBatch_);
|
|
|
|
return result;
|
2012-11-30 01:28:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Status TransactionLogIteratorImpl::status() {
|
|
|
|
return currentStatus_;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TransactionLogIteratorImpl::Valid() {
|
|
|
|
return started_ && isValid_;
|
|
|
|
}
|
|
|
|
|
2013-10-21 02:06:19 +00:00
|
|
|
bool TransactionLogIteratorImpl::RestrictedRead(
|
|
|
|
Slice* record,
|
|
|
|
std::string* scratch) {
|
|
|
|
// Don't read if no more complete entries to read from logs
|
2014-10-30 00:43:37 +00:00
|
|
|
if (currentLastSeq_ >= versions_->LastSequence()) {
|
2013-10-21 02:06:19 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return currentLogReader_->ReadRecord(record, scratch);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TransactionLogIteratorImpl::SeekToStartSequence(
|
|
|
|
uint64_t startFileIndex,
|
|
|
|
bool strict) {
|
|
|
|
std::string scratch;
|
|
|
|
Slice record;
|
|
|
|
started_ = false;
|
|
|
|
isValid_ = false;
|
|
|
|
if (files_->size() <= startFileIndex) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
Status s = OpenLogReader(files_->at(startFileIndex).get());
|
|
|
|
if (!s.ok()) {
|
|
|
|
currentStatus_ = s;
|
2014-05-13 00:50:21 +00:00
|
|
|
reporter_.Info(currentStatus_.ToString().c_str());
|
2013-10-21 02:06:19 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
while (RestrictedRead(&record, &scratch)) {
|
|
|
|
if (record.size() < 12) {
|
|
|
|
reporter_.Corruption(
|
|
|
|
record.size(), Status::Corruption("very small log record"));
|
|
|
|
continue;
|
2012-11-30 01:28:37 +00:00
|
|
|
}
|
2013-10-21 02:06:19 +00:00
|
|
|
UpdateCurrentWriteBatch(record);
|
2013-10-25 02:09:02 +00:00
|
|
|
if (currentLastSeq_ >= startingSequenceNumber_) {
|
2013-10-21 02:06:19 +00:00
|
|
|
if (strict && currentBatchSeq_ != startingSequenceNumber_) {
|
|
|
|
currentStatus_ = Status::Corruption("Gap in sequence number. Could not "
|
|
|
|
"seek to required sequence number");
|
|
|
|
reporter_.Info(currentStatus_.ToString().c_str());
|
2013-10-13 22:28:24 +00:00
|
|
|
return;
|
2013-10-21 02:06:19 +00:00
|
|
|
} else if (strict) {
|
|
|
|
reporter_.Info("Could seek required sequence number. Iterator will "
|
|
|
|
"continue.");
|
2012-11-30 01:28:37 +00:00
|
|
|
}
|
2013-10-21 02:06:19 +00:00
|
|
|
isValid_ = true;
|
|
|
|
started_ = true; // set started_ as we could seek till starting sequence
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
isValid_ = false;
|
2012-11-30 01:28:37 +00:00
|
|
|
}
|
2013-10-21 02:06:19 +00:00
|
|
|
}
|
2013-10-25 02:09:02 +00:00
|
|
|
|
2013-10-21 02:06:19 +00:00
|
|
|
// Could not find start sequence in first file. Normally this must be the
|
|
|
|
// only file. Otherwise log the error and let the iterator return next entry
|
2013-10-25 02:09:02 +00:00
|
|
|
// If strict is set, we want to seek exactly till the start sequence and it
|
|
|
|
// should have been present in the file we scanned above
|
|
|
|
if (strict) {
|
|
|
|
currentStatus_ = Status::Corruption("Gap in sequence number. Could not "
|
|
|
|
"seek to required sequence number");
|
|
|
|
reporter_.Info(currentStatus_.ToString().c_str());
|
|
|
|
} else if (files_->size() != 1) {
|
2013-10-21 02:06:19 +00:00
|
|
|
currentStatus_ = Status::Corruption("Start sequence was not found, "
|
|
|
|
"skipping to the next available");
|
2013-10-25 02:09:02 +00:00
|
|
|
reporter_.Info(currentStatus_.ToString().c_str());
|
|
|
|
// Let NextImpl find the next available entry. started_ remains false
|
|
|
|
// because we don't want to check for gaps while moving to start sequence
|
|
|
|
NextImpl(true);
|
2013-10-21 02:06:19 +00:00
|
|
|
}
|
2013-10-13 22:28:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TransactionLogIteratorImpl::Next() {
|
2013-10-25 02:09:02 +00:00
|
|
|
return NextImpl(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TransactionLogIteratorImpl::NextImpl(bool internal) {
|
2013-10-13 22:28:24 +00:00
|
|
|
std::string scratch;
|
|
|
|
Slice record;
|
|
|
|
isValid_ = false;
|
2013-10-25 02:09:02 +00:00
|
|
|
if (!internal && !started_) {
|
|
|
|
// Runs every time until we can seek to the start sequence
|
2013-10-13 22:28:24 +00:00
|
|
|
return SeekToStartSequence();
|
2013-04-08 23:28:09 +00:00
|
|
|
}
|
2013-10-13 22:28:24 +00:00
|
|
|
while(true) {
|
2013-01-20 10:07:13 +00:00
|
|
|
assert(currentLogReader_);
|
2013-10-25 02:09:02 +00:00
|
|
|
if (currentLogReader_->IsEOF()) {
|
|
|
|
currentLogReader_->UnmarkEOF();
|
|
|
|
}
|
|
|
|
while (RestrictedRead(&record, &scratch)) {
|
|
|
|
if (record.size() < 12) {
|
|
|
|
reporter_.Corruption(
|
|
|
|
record.size(), Status::Corruption("very small log record"));
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
// started_ should be true if called by application
|
|
|
|
assert(internal || started_);
|
|
|
|
// started_ should be false if called internally
|
|
|
|
assert(!internal || !started_);
|
|
|
|
UpdateCurrentWriteBatch(record);
|
|
|
|
if (internal && !started_) {
|
|
|
|
started_ = true;
|
2013-03-21 22:12:35 +00:00
|
|
|
}
|
2013-10-25 02:09:02 +00:00
|
|
|
return;
|
2012-11-30 01:28:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-13 22:28:24 +00:00
|
|
|
// Open the next file
|
|
|
|
if (currentFileIndex_ < files_->size() - 1) {
|
|
|
|
++currentFileIndex_;
|
2014-11-06 19:14:28 +00:00
|
|
|
Status s = OpenLogReader(files_->at(currentFileIndex_).get());
|
|
|
|
if (!s.ok()) {
|
2012-11-30 01:28:37 +00:00
|
|
|
isValid_ = false;
|
2014-11-06 19:14:28 +00:00
|
|
|
currentStatus_ = s;
|
2013-10-13 22:28:24 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
isValid_ = false;
|
2014-10-30 00:43:37 +00:00
|
|
|
if (currentLastSeq_ == versions_->LastSequence()) {
|
2013-10-13 22:28:24 +00:00
|
|
|
currentStatus_ = Status::OK();
|
|
|
|
} else {
|
2014-02-12 19:42:54 +00:00
|
|
|
currentStatus_ = Status::Corruption("NO MORE DATA LEFT");
|
2012-11-30 01:28:37 +00:00
|
|
|
}
|
2013-10-13 22:28:24 +00:00
|
|
|
return;
|
2012-11-30 01:28:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-25 02:09:02 +00:00
|
|
|
bool TransactionLogIteratorImpl::IsBatchExpected(
|
2013-10-21 02:06:19 +00:00
|
|
|
const WriteBatch* batch,
|
|
|
|
const SequenceNumber expectedSeq) {
|
|
|
|
assert(batch);
|
|
|
|
SequenceNumber batchSeq = WriteBatchInternal::Sequence(batch);
|
2013-10-25 02:09:02 +00:00
|
|
|
if (batchSeq != expectedSeq) {
|
2013-10-21 02:06:19 +00:00
|
|
|
char buf[200];
|
|
|
|
snprintf(buf, sizeof(buf),
|
2014-10-30 00:43:37 +00:00
|
|
|
"Discontinuity in log records. Got seq=%" PRIu64
|
|
|
|
", Expected seq=%" PRIu64 ", Last flushed seq=%" PRIu64
|
|
|
|
".Log iterator will reseek the correct batch.",
|
|
|
|
batchSeq, expectedSeq, versions_->LastSequence());
|
2013-10-21 02:06:19 +00:00
|
|
|
reporter_.Info(buf);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-03-04 18:44:04 +00:00
|
|
|
void TransactionLogIteratorImpl::UpdateCurrentWriteBatch(const Slice& record) {
|
2013-11-07 23:46:48 +00:00
|
|
|
std::unique_ptr<WriteBatch> batch(new WriteBatch());
|
|
|
|
WriteBatchInternal::SetContents(batch.get(), record);
|
2013-10-21 02:06:19 +00:00
|
|
|
|
2013-10-25 02:09:02 +00:00
|
|
|
SequenceNumber expectedSeq = currentLastSeq_ + 1;
|
|
|
|
// If the iterator has started, then confirm that we get continuous batches
|
2013-11-07 23:46:48 +00:00
|
|
|
if (started_ && !IsBatchExpected(batch.get(), expectedSeq)) {
|
2013-10-21 02:06:19 +00:00
|
|
|
// Seek to the batch having expected sequence number
|
|
|
|
if (expectedSeq < files_->at(currentFileIndex_)->StartSequence()) {
|
|
|
|
// Expected batch must lie in the previous log file
|
2013-11-13 04:05:28 +00:00
|
|
|
// Avoid underflow.
|
|
|
|
if (currentFileIndex_ != 0) {
|
|
|
|
currentFileIndex_--;
|
|
|
|
}
|
2013-10-21 02:06:19 +00:00
|
|
|
}
|
|
|
|
startingSequenceNumber_ = expectedSeq;
|
2013-10-25 02:09:02 +00:00
|
|
|
// currentStatus_ will be set to Ok if reseek succeeds
|
|
|
|
currentStatus_ = Status::NotFound("Gap in sequence numbers");
|
2013-10-21 02:06:19 +00:00
|
|
|
return SeekToStartSequence(currentFileIndex_, true);
|
|
|
|
}
|
|
|
|
|
2013-11-07 23:46:48 +00:00
|
|
|
currentBatchSeq_ = WriteBatchInternal::Sequence(batch.get());
|
|
|
|
currentLastSeq_ = currentBatchSeq_ +
|
|
|
|
WriteBatchInternal::Count(batch.get()) - 1;
|
2013-10-21 02:06:19 +00:00
|
|
|
// currentBatchSeq_ can only change here
|
2014-10-30 00:43:37 +00:00
|
|
|
assert(currentLastSeq_ <= versions_->LastSequence());
|
2013-10-21 02:06:19 +00:00
|
|
|
|
2013-11-07 23:46:48 +00:00
|
|
|
currentBatch_ = move(batch);
|
2013-03-21 22:12:35 +00:00
|
|
|
isValid_ = true;
|
2013-03-21 22:49:20 +00:00
|
|
|
currentStatus_ = Status::OK();
|
2013-03-04 18:44:04 +00:00
|
|
|
}
|
|
|
|
|
2013-08-06 19:54:37 +00:00
|
|
|
Status TransactionLogIteratorImpl::OpenLogReader(const LogFile* logFile) {
|
2013-04-08 23:28:09 +00:00
|
|
|
unique_ptr<SequentialFile> file;
|
2014-11-06 19:14:28 +00:00
|
|
|
Status s = OpenLogFile(logFile, &file);
|
|
|
|
if (!s.ok()) {
|
|
|
|
return s;
|
2013-04-08 23:28:09 +00:00
|
|
|
}
|
|
|
|
assert(file);
|
2014-02-28 19:50:36 +00:00
|
|
|
currentLogReader_.reset(new log::Reader(std::move(file), &reporter_,
|
|
|
|
read_options_.verify_checksums_, 0));
|
2013-04-08 23:28:09 +00:00
|
|
|
return Status::OK();
|
|
|
|
}
|
2013-10-04 04:49:15 +00:00
|
|
|
} // namespace rocksdb
|
2014-04-15 20:39:26 +00:00
|
|
|
#endif // ROCKSDB_LITE
|