2017-11-13 16:50:49 +08:00
|
|
|
#include "wal.hpp"
|
|
|
|
|
2017-11-15 16:28:41 +08:00
|
|
|
#include "communication/bolt/v1/decoder/decoded_value.hpp"
|
2017-11-20 18:58:05 +08:00
|
|
|
#include "durability/paths.hpp"
|
2017-11-13 16:50:49 +08:00
|
|
|
#include "utils/flag_validation.hpp"
|
|
|
|
|
2017-11-20 18:58:05 +08:00
|
|
|
DEFINE_HIDDEN_int32(
|
|
|
|
wal_flush_interval_millis, 2,
|
|
|
|
"Interval between two write-ahead log flushes, in milliseconds.");
|
2017-11-13 16:50:49 +08:00
|
|
|
|
2017-11-20 18:58:05 +08:00
|
|
|
DEFINE_HIDDEN_int32(
|
2017-12-07 20:09:34 +08:00
|
|
|
wal_rotate_deltas_count, 10000,
|
|
|
|
"How many write-ahead deltas should be stored in a single WAL file "
|
2017-11-20 18:58:05 +08:00
|
|
|
"before rotating it.");
|
2017-11-13 16:50:49 +08:00
|
|
|
|
2017-11-20 18:58:05 +08:00
|
|
|
DEFINE_VALIDATED_HIDDEN_int32(wal_buffer_size, 4096,
|
|
|
|
"Write-ahead log buffer size.",
|
|
|
|
FLAG_IN_RANGE(1, 1 << 30));
|
2017-11-15 16:28:41 +08:00
|
|
|
|
|
|
|
namespace durability {
|
|
|
|
|
2017-11-20 18:58:05 +08:00
|
|
|
WriteAheadLog::WriteAheadLog(
|
2018-01-18 17:22:54 +08:00
|
|
|
int worker_id, const std::experimental::filesystem::path &durability_dir,
|
2017-11-20 18:58:05 +08:00
|
|
|
bool durability_enabled)
|
2018-01-18 17:22:54 +08:00
|
|
|
: deltas_{FLAGS_wal_buffer_size}, wal_file_{worker_id, durability_dir} {
|
2017-11-20 18:58:05 +08:00
|
|
|
if (durability_enabled) {
|
|
|
|
CheckDurabilityDir(durability_dir);
|
2017-11-15 16:28:41 +08:00
|
|
|
wal_file_.Init();
|
|
|
|
scheduler_.Run(std::chrono::milliseconds(FLAGS_wal_flush_interval_millis),
|
2017-12-07 20:09:34 +08:00
|
|
|
[this]() { wal_file_.Flush(deltas_); });
|
2017-11-15 16:28:41 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
WriteAheadLog::~WriteAheadLog() {
|
2017-11-20 18:58:05 +08:00
|
|
|
// TODO review : scheduler.Stop() legal if it wasn't started?
|
|
|
|
scheduler_.Stop();
|
2017-12-07 20:09:34 +08:00
|
|
|
if (enabled_) wal_file_.Flush(deltas_);
|
2017-11-15 16:28:41 +08:00
|
|
|
}
|
|
|
|
|
2017-11-20 18:58:05 +08:00
|
|
|
WriteAheadLog::WalFile::WalFile(
|
2018-01-18 17:22:54 +08:00
|
|
|
int worker_id, const std::experimental::filesystem::path &durability_dir)
|
|
|
|
: worker_id_(worker_id), wal_dir_{durability_dir / kWalDir} {}
|
2017-11-20 18:58:05 +08:00
|
|
|
|
2017-11-15 16:28:41 +08:00
|
|
|
WriteAheadLog::WalFile::~WalFile() {
|
|
|
|
if (!current_wal_file_.empty()) writer_.Close();
|
|
|
|
}
|
|
|
|
|
|
|
|
void WriteAheadLog::WalFile::Init() {
|
2017-12-01 17:42:53 +08:00
|
|
|
if (!EnsureDir(wal_dir_)) {
|
2017-11-20 18:58:05 +08:00
|
|
|
LOG(ERROR) << "Can't write to WAL directory: " << wal_dir_;
|
2017-11-15 16:28:41 +08:00
|
|
|
current_wal_file_ = std::experimental::filesystem::path();
|
|
|
|
} else {
|
2018-01-18 17:22:54 +08:00
|
|
|
current_wal_file_ = WalFilenameForTransactionId(wal_dir_, worker_id_);
|
2017-11-15 16:28:41 +08:00
|
|
|
try {
|
|
|
|
writer_.Open(current_wal_file_);
|
|
|
|
} catch (std::ios_base::failure &) {
|
|
|
|
LOG(ERROR) << "Failed to open write-ahead log file: "
|
|
|
|
<< current_wal_file_;
|
|
|
|
current_wal_file_ = std::experimental::filesystem::path();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
latest_tx_ = 0;
|
2017-12-07 20:09:34 +08:00
|
|
|
current_wal_file_delta_count_ = 0;
|
2017-11-15 16:28:41 +08:00
|
|
|
}
|
|
|
|
|
2017-12-07 20:09:34 +08:00
|
|
|
void WriteAheadLog::WalFile::Flush(RingBuffer<database::StateDelta> &buffer) {
|
2017-11-15 16:28:41 +08:00
|
|
|
if (current_wal_file_.empty()) {
|
|
|
|
LOG(ERROR) << "Write-ahead log file uninitialized, discarding data.";
|
|
|
|
buffer.clear();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
while (true) {
|
2017-12-07 20:09:34 +08:00
|
|
|
auto delta = buffer.pop();
|
|
|
|
if (!delta) break;
|
|
|
|
latest_tx_ = std::max(latest_tx_, delta->transaction_id());
|
|
|
|
delta->Encode(writer_, encoder_);
|
|
|
|
if (++current_wal_file_delta_count_ >= FLAGS_wal_rotate_deltas_count)
|
2017-11-15 16:28:41 +08:00
|
|
|
RotateFile();
|
|
|
|
}
|
|
|
|
writer_.Flush();
|
|
|
|
} catch (std::ios_base::failure &) {
|
|
|
|
LOG(ERROR) << "Failed to write to write-ahead log, discarding data.";
|
|
|
|
buffer.clear();
|
|
|
|
return;
|
|
|
|
} catch (std::experimental::filesystem::filesystem_error &) {
|
|
|
|
LOG(ERROR) << "Failed to rotate write-ahead log.";
|
|
|
|
buffer.clear();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void WriteAheadLog::WalFile::RotateFile() {
|
|
|
|
writer_.Close();
|
|
|
|
std::experimental::filesystem::rename(
|
2018-01-18 17:22:54 +08:00
|
|
|
current_wal_file_,
|
|
|
|
WalFilenameForTransactionId(wal_dir_, worker_id_, latest_tx_));
|
2017-11-15 16:28:41 +08:00
|
|
|
Init();
|
|
|
|
}
|
|
|
|
|
2017-12-07 20:09:34 +08:00
|
|
|
void WriteAheadLog::Emplace(database::StateDelta &&delta) {
|
2017-11-15 16:28:41 +08:00
|
|
|
if (enabled_ && FLAGS_wal_flush_interval_millis >= 0)
|
2017-12-07 20:09:34 +08:00
|
|
|
deltas_.emplace(std::move(delta));
|
2017-11-15 16:28:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace durability
|