Loading...
Searching...
No Matches
recovery.cpp
Go to the documentation of this file.
1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
21
22#include <fcntl.h>
23#include <glog/logging.h>
24#include <sys/stat.h>
25#include <sys/types.h>
26#include <unistd.h>
27
28#include <filesystem>
29
30#include "common/utils/utils.h"
31
32namespace resdb {
33
34Recovery::Recovery(const ResDBConfig& config, CheckPoint* checkpoint,
35 SystemInfo* system_info, Storage* storage)
36 : config_(config),
37 checkpoint_(checkpoint),
38 system_info_(system_info),
39 storage_(storage) {
40 recovery_enabled_ = config_.GetConfigData().recovery_enabled();
41 file_path_ = config_.GetConfigData().recovery_path();
42 if (file_path_.empty()) {
43 file_path_ = "./wal_log/log";
44 }
46
48 .public_key()
49 .public_key_info()
50 .type() == CertificateKeyInfo::CLIENT) {
51 recovery_enabled_ = false;
52 }
53
54 if (recovery_enabled_ == false) {
55 return;
56 }
57
58 buffer_size_ = config_.GetConfigData().recovery_buffer_size();
59 if (buffer_size_ == 0) {
60 buffer_size_ = 1024;
61 }
62
63 LOG(INFO) << "file path:" << file_path_
64 << " dir:" << std::filesystem::path(file_path_).parent_path();
65
66 recovery_ckpt_time_s_ = config_.GetConfigData().recovery_ckpt_time_s();
67 if (recovery_ckpt_time_s_ == 0) {
69 }
70
71 int ret =
72 mkdir(std::filesystem::path(file_path_).parent_path().c_str(), 0777);
73 if (ret) {
74 LOG(INFO) << "mkdir fail:" << ret << " error:" << strerror(errno);
75 }
76
77 fd_ = -1;
78 stop_ = false;
79 Init();
80}
81
83 GetLastFile();
84 SwitchFile(file_path_);
85
86 ckpt_thread_ = std::thread(&Recovery::UpdateStableCheckPoint, this);
87}
88
90 if (recovery_enabled_ == false) {
91 return;
92 }
93 Flush();
94 close(fd_);
95 stop_ = true;
96 if (ckpt_thread_.joinable()) {
97 ckpt_thread_.join();
98 }
99}
100
101int64_t Recovery::GetMaxSeq() { return max_seq_; }
102
103int64_t Recovery::GetMinSeq() { return min_seq_; }
104
105void Recovery::UpdateStableCheckPoint() {
106 if (checkpoint_ == nullptr) {
107 return;
108 }
109 while (!stop_) {
110 int64_t latest_ckpt = checkpoint_->GetStableCheckpoint();
111 LOG(ERROR) << "get stable ckpt:" << latest_ckpt;
112 if (last_ckpt_ == latest_ckpt) {
114 continue;
115 }
116 last_ckpt_ = latest_ckpt;
117 FinishFile(latest_ckpt);
118 }
119}
120
121void Recovery::GetLastFile() {
122 std::string dir = std::filesystem::path(file_path_).parent_path();
123 last_ckpt_ = -1;
124 int m_time_s = 0;
125 for (const auto& entry : std::filesystem::directory_iterator(dir)) {
126 std::string dir = std::filesystem::path(entry.path()).parent_path();
127 std::string file_name = std::filesystem::path(entry.path()).stem();
128 std::string ext = std::filesystem::path(entry.path()).extension();
129 if (ext != ".log") continue;
130 int pos = file_name.rfind("_");
131 int64_t ckpt = std::stoll(file_name.substr(pos + 1));
132 int max_seq_pos = file_name.rfind("_", pos - 1);
133 int min_seq_pos = file_name.rfind("_", max_seq_pos - 1);
134 int time_pos = file_name.rfind("_", min_seq_pos - 1);
135
136 int64_t min_seq = std::stoll(
137 file_name.substr(min_seq_pos + 1, max_seq_pos - min_seq_pos - 1));
138
139 int64_t time_s =
140 std::stoll(file_name.substr(time_pos + 1, min_seq_pos - time_pos - 1));
141 if (min_seq == -1) {
142 if (last_ckpt_ == -1 || m_time_s < time_s) {
143 file_path_ = entry.path();
144 last_ckpt_ = ckpt;
145 m_time_s = time_s;
146 LOG(ERROR) << "get last path:" << file_name << " min:" << min_seq;
147 }
148 }
149 }
150 if (last_ckpt_ == -1) {
151 last_ckpt_ = 0;
152 file_path_ = GenerateFile(last_ckpt_, -1, -1);
153 }
154}
155
156std::string Recovery::GenerateFile(int64_t seq, int64_t min_seq,
157 int64_t max_seq) {
158 std::string dir = std::filesystem::path(file_path_).parent_path();
159 std::string file_name = std::filesystem::path(base_file_path_).stem();
160 int64_t time = GetCurrentTime();
161 file_name = file_name + "_" + std::to_string(time) + "_" +
162 std::to_string(min_seq) + "_" + std::to_string(max_seq) + "_" +
163 std::to_string(seq);
164 std::string ext = std::filesystem::path(base_file_path_).extension();
165 if (ext == "") ext = "log";
166 return dir + "/" + file_name + "." + ext;
167}
168
169void Recovery::FinishFile(int64_t seq) {
170 std::unique_lock<std::mutex> lk(mutex_);
171 Flush();
172 if (storage_) {
173 if (!storage_->Flush()) {
174 return;
175 }
176 }
177 std::string new_file_path = GenerateFile(seq, min_seq_, max_seq_);
178 close(fd_);
179
180 min_seq_ = -1;
181 max_seq_ = -1;
182
183 std::rename(file_path_.c_str(), new_file_path.c_str());
184
185 LOG(INFO) << "rename:" << file_path_ << " to:" << new_file_path;
186 std::string next_file_path = GenerateFile(seq, -1, -1);
187 file_path_ = next_file_path;
188
189 OpenFile(file_path_);
190}
191
192void Recovery::SwitchFile(const std::string& file_path) {
193 std::unique_lock<std::mutex> lk(mutex_);
194
195 min_seq_ = -1;
196 max_seq_ = -1;
197
198 ReadLogsFromFiles(
199 file_path, 0, 0, [&](const SystemInfoData& data) {},
200 [&](std::unique_ptr<Context> context, std::unique_ptr<Request> request) {
201 min_seq_ == -1
202 ? min_seq_ = request->seq()
203 : std::min(min_seq_, static_cast<int64_t>(request->seq()));
204 max_seq_ = std::max(max_seq_, static_cast<int64_t>(request->seq()));
205 });
206
207 OpenFile(file_path);
208 LOG(INFO) << "switch to file:" << file_path << " seq:"
209 << "[" << min_seq_ << "," << max_seq_ << "]";
210}
211
212void Recovery::OpenFile(const std::string& path) {
213 if (fd_ >= 0) {
214 close(fd_);
215 }
216 fd_ = open(path.c_str(), O_CREAT | O_WRONLY, 0666);
217 if (fd_ < 0) {
218 LOG(ERROR) << "open file fail:" << path << " error:" << strerror(errno);
219 }
220
221 int pos = lseek(fd_, 0, SEEK_END);
222 LOG(INFO) << "file path:" << path << " len:" << pos << " fd:" << fd_;
223
224 if (pos == 0) {
225 WriteSystemInfo();
226 }
227
228 lseek(fd_, 0, SEEK_END);
229 LOG(INFO) << "open file:" << path << " pos:" << lseek(fd_, 0, SEEK_CUR)
230 << " fd:" << fd_;
231 assert(fd_ >= 0);
232}
233
234void Recovery::WriteSystemInfo() {
235 int view = system_info_->GetCurrentView();
236 int primary_id = system_info_->GetPrimaryId();
237 SystemInfoData data;
238 data.set_view(view);
239 data.set_primary_id(primary_id);
240
241 std::string data_str;
242 data.SerializeToString(&data_str);
243
244 AppendData(data_str);
245 Flush();
246}
247
248void Recovery::AddRequest(const Context* context, const Request* request) {
249 if (recovery_enabled_ == false) {
250 return;
251 }
252 switch (request->type()) {
253 case Request::TYPE_PRE_PREPARE:
254 case Request::TYPE_PREPARE:
255 case Request::TYPE_COMMIT:
256 case Request::TYPE_CHECKPOINT:
257 case Request::TYPE_NEWVIEW:
258 return WriteLog(context, request);
259 default:
260 break;
261 }
262}
263
264void Recovery::WriteLog(const Context* context, const Request* request) {
265 std::string data;
266 if (request) {
267 request->SerializeToString(&data);
268 }
269
270 std::string sig;
271 if (context) {
272 context->signature.SerializeToString(&sig);
273 }
274
275 std::unique_lock<std::mutex> lk(mutex_);
276 min_seq_ = min_seq_ == -1
277 ? request->seq()
278 : std::min(min_seq_, static_cast<int64_t>(request->seq()));
279 max_seq_ = std::max(max_seq_, static_cast<int64_t>(request->seq()));
280 AppendData(data);
281 AppendData(sig);
282
283 Flush();
284}
285
286void Recovery::AppendData(const std::string& data) {
287 size_t len = data.size();
288 buffer_.append(reinterpret_cast<const char*>(&len), sizeof(len));
289 buffer_.append(data);
290}
291
292std::vector<std::unique_ptr<Recovery::RecoveryData>> Recovery::ParseData(
293 const std::string& data) {
294 std::vector<std::unique_ptr<RecoveryData>> request_list;
295
296 std::vector<std::string> data_list;
297 int pos = 0;
298 while (pos < data.size()) {
299 size_t len;
300 memcpy(&len, data.c_str() + pos, sizeof(len));
301 pos += sizeof(len);
302
303 std::string item = data.substr(pos, len);
304 pos += len;
305 data_list.push_back(item);
306 }
307
308 for (size_t i = 0; i < data_list.size(); i += 2) {
309 std::unique_ptr<RecoveryData> recovery_data =
310 std::make_unique<RecoveryData>();
311 recovery_data->request = std::make_unique<Request>();
312 recovery_data->context = std::make_unique<Context>();
313
314 if (!recovery_data->request->ParseFromString(data_list[i])) {
315 LOG(ERROR) << "Parse from data fail";
316 break;
317 }
318
319 if (!recovery_data->context->signature.ParseFromString(data_list[i + 1])) {
320 LOG(ERROR) << "Parse from data fail";
321 break;
322 }
323
324 request_list.push_back(std::move(recovery_data));
325 }
326 return request_list;
327}
328
329std::vector<std::string> Recovery::ParseRawData(const std::string& data) {
330 std::vector<std::string> data_list;
331 int pos = 0;
332 while (pos < data.size()) {
333 size_t len;
334 memcpy(&len, data.c_str() + pos, sizeof(len));
335 pos += sizeof(len);
336
337 std::string item = data.substr(pos, len);
338 pos += len;
339 data_list.push_back(item);
340 }
341 return data_list;
342}
343
344void Recovery::MayFlush() {
345 if (buffer_.size() > buffer_size_) {
346 Flush();
347 }
348}
349
350void Recovery::Flush() {
351 size_t len = buffer_.size();
352 if (len == 0) {
353 return;
354 }
355
356 Write(reinterpret_cast<const char*>(&len), sizeof(len));
357 Write(reinterpret_cast<const char*>(buffer_.c_str()), len);
358 buffer_.clear();
359 fsync(fd_);
360}
361
362void Recovery::Write(const char* data, size_t len) {
363 int pos = 0;
364 while (len > 0) {
365 int write_len = write(fd_, data + pos, len);
366 len -= write_len;
367 pos += write_len;
368 }
369}
370
371bool Recovery::Read(int fd, size_t len, char* data) {
372 int pos = 0;
373 while (len > 0) {
374 int read_len = read(fd, data + pos, len);
375 if (read_len <= 0) {
376 return false;
377 }
378 len -= read_len;
379 pos += read_len;
380 }
381 return true;
382}
383
384std::pair<std::vector<std::pair<int64_t, std::string>>, int64_t>
385Recovery::GetRecoveryFiles() {
386 std::string dir = std::filesystem::path(file_path_).parent_path();
387 int64_t last_ckpt = 0;
388 for (const auto& entry : std::filesystem::directory_iterator(dir)) {
389 std::string dir = std::filesystem::path(entry.path()).parent_path();
390 std::string file_name = std::filesystem::path(entry.path()).stem();
391 std::string ext = std::filesystem::path(entry.path()).extension();
392 if (ext != ".log") continue;
393 int pos = file_name.rfind("_");
394 int64_t ckpt = std::stoll(file_name.substr(pos + 1));
395 if (ckpt > last_ckpt) {
396 last_ckpt = ckpt;
397 }
398 }
399 std::vector<std::pair<int64_t, std::string>> list;
400
401 std::vector<std::pair<int64_t, std::string>> e_list;
402
403 for (const auto& entry : std::filesystem::directory_iterator(dir)) {
404 std::string dir = std::filesystem::path(entry.path()).parent_path();
405 std::string file_name = std::filesystem::path(entry.path()).stem();
406 std::string ext = std::filesystem::path(entry.path()).extension();
407 if (ext != ".log") continue;
408 int pos = file_name.rfind("_");
409
410 int max_seq_pos = file_name.rfind("_", pos - 1);
411 int64_t max_seq =
412 std::stoll(file_name.substr(max_seq_pos + 1, pos - max_seq_pos - 1));
413
414 int min_seq_pos = file_name.rfind("_", max_seq_pos - 1);
415 int64_t min_seq = std::stoll(
416 file_name.substr(min_seq_pos + 1, max_seq_pos - min_seq_pos - 1));
417
418 int time_pos = file_name.rfind("_", min_seq_pos - 1);
419 int64_t time =
420 std::stoll(file_name.substr(time_pos + 1, min_seq_pos - time_pos - 1));
421
422 if (min_seq == -1) {
423 e_list.push_back(std::make_pair(time, entry.path()));
424 } else if ((min_seq <= last_ckpt && max_seq >= last_ckpt)) {
425 list.push_back(std::make_pair(time, entry.path()));
426 }
427 }
428
429 sort(e_list.begin(), e_list.end());
430 list.push_back(e_list.back());
431
432 sort(list.begin(), list.end());
433 return std::make_pair(list, last_ckpt);
434}
435
437 std::function<void(const SystemInfoData& data)> system_callback,
438 std::function<void(std::unique_ptr<Context> context,
439 std::unique_ptr<Request> request)>
440 call_back) {
441 if (recovery_enabled_ == false) {
442 return;
443 }
444 std::unique_lock<std::mutex> lk(mutex_);
445 auto recovery_files_pair = GetRecoveryFiles();
446 int64_t ckpt = recovery_files_pair.second;
447 int idx = 0;
448 for (auto path : recovery_files_pair.first) {
449 ReadLogsFromFiles(path.second, ckpt, idx++, system_callback, call_back);
450 }
451}
452
453void Recovery::ReadLogsFromFiles(
454 const std::string& path, int64_t ckpt, int file_idx,
455 std::function<void(const SystemInfoData& data)> system_callback,
456 std::function<void(std::unique_ptr<Context> context,
457 std::unique_ptr<Request> request)>
458 call_back) {
459 int fd = open(path.c_str(), O_CREAT | O_RDONLY, 0666);
460 if (fd < 0) {
461 LOG(ERROR) << " open file fail:" << path;
462 }
463 LOG(INFO) << "read logs:" << path << " pos:" << lseek(fd, 0, SEEK_CUR);
464 assert(fd >= 0);
465
466 size_t data_len = 0;
467 Read(fd, sizeof(data_len), reinterpret_cast<char*>(&data_len));
468 {
469 std::string data;
470 char* buf = new char[data_len];
471 if (!Read(fd, data_len, buf)) {
472 LOG(ERROR) << "Read system info fail";
473 return;
474 }
475 data = std::string(buf, data_len);
476 delete buf;
477 std::vector<std::string> data_list = ParseRawData(data);
478
479 SystemInfoData info;
480 if (data_list.empty() || !info.ParseFromString(data_list[0])) {
481 LOG(ERROR) << "parse info fail:" << data.size();
482 return;
483 }
484 LOG(INFO) << "read system info:" << info.DebugString();
485 if (file_idx == 0) {
486 system_callback(info);
487 }
488 }
489
490 std::vector<std::unique_ptr<RecoveryData>> request_list;
491
492 while (Read(fd, sizeof(data_len), reinterpret_cast<char*>(&data_len))) {
493 std::string data;
494 char* buf = new char[data_len];
495 if (!Read(fd, data_len, buf)) {
496 LOG(ERROR) << "Read data log fail";
497 break;
498 }
499 data = std::string(buf, data_len);
500 delete buf;
501
502 std::vector<std::unique_ptr<RecoveryData>> list = ParseData(data);
503 if (list.size() == 0) {
504 request_list.clear();
505 break;
506 }
507 for (auto& l : list) {
508 request_list.push_back(std::move(l));
509 }
510 }
511 if (request_list.size() == 0) {
512 ftruncate(fd, 0);
513 }
514 for (std::unique_ptr<RecoveryData>& recovery_data : request_list) {
515 if (ckpt < recovery_data->request->seq()) {
516 recovery_data->request->set_is_recovery(true);
517 call_back(std::move(recovery_data->context),
518 std::move(recovery_data->request));
519 }
520 }
521
522 LOG(INFO) << "read log from files:" << path << " done";
523
524 close(fd);
525}
526
527} // namespace resdb
std::string data
virtual uint64_t GetStableCheckpoint()=0
std::atomic< bool > stop_
Definition recovery.h:100
std::thread ckpt_thread_
Definition recovery.h:89
SystemInfo * system_info_
Definition recovery.h:102
std::string buffer_
Definition recovery.h:91
ResDBConfig config_
Definition recovery.h:87
int64_t last_ckpt_
Definition recovery.h:97
int64_t GetMinSeq()
Definition recovery.cpp:103
std::string base_file_path_
Definition recovery.h:92
void ReadLogs(std::function< void(const SystemInfoData &data)> system_callback, std::function< void(std::unique_ptr< Context > context, std::unique_ptr< Request > request)> call_back)
Definition recovery.cpp:436
std::mutex mutex_
Definition recovery.h:95
virtual void AddRequest(const Context *context, const Request *request)
Definition recovery.cpp:248
int recovery_ckpt_time_s_
Definition recovery.h:101
virtual ~Recovery()
Definition recovery.cpp:89
int64_t max_seq_
Definition recovery.h:98
Recovery(const ResDBConfig &config, CheckPoint *checkpoint, SystemInfo *system_info, Storage *storage)
Definition recovery.cpp:34
size_t buffer_size_
Definition recovery.h:93
int64_t min_seq_
Definition recovery.h:98
int64_t GetMaxSeq()
Definition recovery.cpp:101
std::string file_path_
Definition recovery.h:92
CheckPoint * checkpoint_
Definition recovery.h:88
Storage * storage_
Definition recovery.h:103
bool recovery_enabled_
Definition recovery.h:90
ResConfigData GetConfigData() const
CertificateInfo GetPublicKeyCertificateInfo() const
virtual bool Flush()
Definition storage.h:57
uint32_t GetPrimaryId() const
uint64_t GetCurrentView() const
SystemInfo system_info_
std::unique_ptr< Storage > storage
uint64_t GetCurrentTime()
Definition utils.cpp:36
MockCheckPoint checkpoint_
std::unique_ptr< ResDBConfig > config_
SignatureInfo signature
Definition server_comm.h:29