1 /*****************************************************************************
2 
3 
4 Copyright (c) 2018, 2020, Oracle and/or its affiliates. All Rights Reserved.
5 
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License, version 2.0,
8 as published by the Free Software Foundation.
9 
10 This program is also distributed with certain software (including
11 but not limited to OpenSSL) that is licensed under separate terms,
12 as designated in a particular file or component or in included license
13 documentation.  The authors of MySQL hereby grant you an additional
14 permission to link the program and your derivative works with the
15 separately licensed software that they have included with MySQL.
16 
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 GNU General Public License, version 2.0, for more details.
21 
22 You should have received a copy of the GNU General Public License
23 along with this program; if not, write to the Free Software
24 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
25 
26 *****************************************************************************/
27 
28 /**************************************************/ /**
29  @file include/arch0recv.h
30  Interface for crash recovery for page archiver system.
31 
32  *******************************************************/
33 
34 #include "arch0recv.h"
35 
recover()36 dberr_t Arch_Page_Sys::recover() {
37   DBUG_PRINT("page_archiver", ("Crash Recovery"));
38 
39   Recv arch_recv(ARCH_DIR);
40   dberr_t err;
41 
42   err = arch_recv.init();
43 
44   if (!arch_recv.scan_group()) {
45     DBUG_PRINT("page_archiver", ("No group information available"));
46     return (DB_SUCCESS);
47   }
48 
49   err = arch_recv.fill_info(this);
50 
51   if (err != DB_SUCCESS) {
52     ib::error(ER_IB_ERR_PAGE_ARCH_RECOVERY_FAILED);
53     return (DB_OUT_OF_MEMORY);
54   }
55 
56   return (err);
57 }
58 
init()59 dberr_t Arch_Page_Sys::Recv::init() {
60   dberr_t err;
61 
62   err = m_dblwr_ctx.init(
63       ARCH_DBLWR_DIR, ARCH_DBLWR_FILE, ARCH_DBLWR_NUM_FILES,
64       static_cast<uint64_t>(ARCH_PAGE_BLK_SIZE) * ARCH_DBLWR_FILE_CAPACITY);
65 
66   if (err != DB_SUCCESS) {
67     return (err);
68   }
69 
70   err = m_dblwr_ctx.read_blocks();
71 
72   return (err);
73 }
74 
init(const char * dblwr_path,const char * dblwr_base_file,uint dblwr_num_files,uint64_t dblwr_file_size)75 dberr_t Arch_Dblwr_Ctx::init(const char *dblwr_path,
76                              const char *dblwr_base_file, uint dblwr_num_files,
77                              uint64_t dblwr_file_size) {
78   m_file_size = dblwr_file_size;
79 
80   m_buf = static_cast<byte *>(UT_NEW_ARRAY_NOKEY(byte, m_file_size));
81 
82   if (m_buf == nullptr) {
83     return (DB_OUT_OF_MEMORY);
84   }
85 
86   memset(m_buf, 0, m_file_size);
87 
88   auto err = m_file_ctx.init(ARCH_DIR, dblwr_path, dblwr_base_file,
89                              dblwr_num_files, m_file_size);
90 
91   return (err);
92 }
93 
read_blocks()94 dberr_t Arch_Dblwr_Ctx::read_blocks() {
95   ut_ad(m_buf != nullptr);
96 
97   auto err = m_file_ctx.open(true, LSN_MAX, 0, 0);
98 
99   if (err != DB_SUCCESS) {
100     return (err);
101   }
102 
103   ut_ad(m_file_ctx.get_phy_size() == m_file_size);
104 
105   /* Read the entire file. */
106   err = m_file_ctx.read(m_buf, 0, static_cast<uint>(m_file_size));
107 
108   if (err != DB_SUCCESS) {
109     return (err);
110   }
111 
112   Arch_Dblwr_Block dblwr_block;
113 
114   for (uint block_num = 0; block_num < m_file_size / ARCH_PAGE_BLK_SIZE;
115        ++block_num) {
116     auto block = m_buf + (block_num * ARCH_PAGE_BLK_SIZE);
117 
118     if (!Arch_Block::validate(block)) {
119       continue;
120     }
121 
122     if (block_num == ARCH_PAGE_DBLWR_RESET_PAGE) {
123       dblwr_block.m_block_type = ARCH_RESET_BLOCK;
124       dblwr_block.m_flush_type = ARCH_FLUSH_NORMAL;
125     } else {
126       dblwr_block.m_block_type = ARCH_DATA_BLOCK;
127       dblwr_block.m_flush_type = (block_num == ARCH_PAGE_DBLWR_FULL_FLUSH_PAGE)
128                                      ? ARCH_FLUSH_NORMAL
129                                      : ARCH_FLUSH_PARTIAL;
130     }
131 
132     dblwr_block.m_block_num = Arch_Block::get_block_number(block);
133     dblwr_block.m_block = block;
134     m_blocks.push_back(dblwr_block);
135   }
136 
137   m_file_ctx.close();
138 
139   return (err);
140 }
141 
142 #ifdef UNIV_DEBUG
print()143 void Arch_Page_Sys::Recv::print() {
144   for (auto group : m_dir_group_info_map) {
145     DBUG_PRINT("page_archiver",
146                ("Group : %s\t%u", group.first.c_str(), group.second.m_active));
147   }
148 }
149 #endif
150 
read_group_dirs(const std::string file_path)151 void Arch_Page_Sys::Recv::read_group_dirs(const std::string file_path) {
152   if (file_path.find(ARCH_PAGE_DIR) == std::string::npos) {
153     return;
154   }
155 
156   Arch_Recv_Group_Info info;
157   m_dir_group_info_map.insert(
158       std::pair<std::string, Arch_Recv_Group_Info>(file_path, info));
159 }
160 
read_group_files(const std::string dir_path,const std::string file_path)161 void Arch_Page_Sys::Recv::read_group_files(const std::string dir_path,
162                                            const std::string file_path) {
163   if (file_path.find(ARCH_PAGE_FILE) == std::string::npos &&
164       file_path.find(ARCH_PAGE_GROUP_ACTIVE_FILE_NAME) == std::string::npos &&
165       file_path.find(ARCH_PAGE_GROUP_DURABLE_FILE_NAME) == std::string::npos) {
166     return;
167   }
168 
169   Arch_Recv_Group_Info &info = m_dir_group_info_map[dir_path];
170 
171   if (file_path.find(ARCH_PAGE_GROUP_ACTIVE_FILE_NAME) != std::string::npos) {
172     info.m_active = true;
173     return;
174   }
175 
176   if (file_path.find(ARCH_PAGE_GROUP_DURABLE_FILE_NAME) != std::string::npos) {
177     info.m_durable = true;
178     return;
179   }
180 
181   info.m_num_files += 1;
182 
183   auto found = file_path.find(ARCH_PAGE_FILE);
184   ut_ad(found != std::string::npos);
185   uint file_index = 0;
186 
187   /* Fetch start index. */
188   try {
189     file_index = static_cast<uint>(
190         std::stoi(file_path.substr(found + strlen(ARCH_PAGE_FILE))));
191   } catch (const std::exception &) {
192     ut_ad(0);
193     ib::error(ER_IB_ERR_PAGE_ARCH_INVALID_FORMAT) << ARCH_PAGE_FILE;
194     return;
195   }
196 
197   if (info.m_file_start_index > file_index) {
198     info.m_file_start_index = file_index;
199   }
200 }
201 
scan_group()202 bool Arch_Page_Sys::Recv::scan_group() {
203   os_file_type_t type;
204   bool exists;
205 
206   os_file_status(m_arch_dir_name.c_str(), &exists, &type);
207 
208   if (!exists || type != OS_FILE_TYPE_DIR) {
209     return (false);
210   }
211 
212   Dir_Walker::walk(m_arch_dir_name, false, [&](const std::string file_path) {
213     read_group_dirs(file_path);
214   });
215 
216   if (m_dir_group_info_map.size() == 0) {
217     return (false);
218   }
219 
220   for (auto it : m_dir_group_info_map) {
221     Dir_Walker::walk(it.first, true, [&](const std::string file_path) {
222       read_group_files(it.first, file_path);
223     });
224   }
225 
226   ut_d(print());
227 
228   return (true);
229 }
230 
recovery_replace_pages_from_dblwr(Arch_Dblwr_Ctx * dblwr_ctx)231 dberr_t Arch_Group::recovery_replace_pages_from_dblwr(
232     Arch_Dblwr_Ctx *dblwr_ctx) {
233   auto ARCH_UNKNOWN_BLOCK = std::numeric_limits<uint64_t>::max();
234   uint64_t full_flush_blk_num = ARCH_UNKNOWN_BLOCK;
235   auto dblwr_blocks = dblwr_ctx->get_blocks();
236   size_t num_files = get_file_count();
237 
238   ut_ad(num_files > 0);
239 
240   for (uint index = 0; index < dblwr_blocks.size(); ++index) {
241     auto dblwr_block = dblwr_blocks[index];
242 
243     switch (dblwr_block.m_block_type) {
244       case ARCH_RESET_BLOCK:
245 
246         ut_ad(dblwr_block.m_block_num < num_files);
247         /* If the block does not belong to the last file then ignore. */
248         if (dblwr_block.m_block_num != num_files - 1) {
249           continue;
250         } else {
251           break;
252         }
253 
254       case ARCH_DATA_BLOCK: {
255         uint file_index = Arch_Block::get_file_index(dblwr_block.m_block_num);
256         ut_ad(file_index < num_files);
257 
258         /* If the block does not belong to the last file then ignore. */
259         if (file_index < num_files - 1) {
260           continue;
261         }
262 
263         if (dblwr_block.m_flush_type == ARCH_FLUSH_NORMAL) {
264           full_flush_blk_num = dblwr_block.m_block_num;
265         } else {
266           /* It's possible that the partial flush block might have been fully
267           flushed, in which case we need to skip this block. */
268           if (full_flush_blk_num != ARCH_UNKNOWN_BLOCK &&
269               full_flush_blk_num >= dblwr_block.m_block_num) {
270             continue;
271           }
272         }
273       } break;
274 
275       default:
276         ut_ad(false);
277     }
278 
279     uint64_t offset = Arch_Block::get_file_offset(dblwr_block.m_block_num,
280                                                   dblwr_block.m_block_type);
281 
282     ut_ad(m_file_ctx.is_closed());
283 
284     dberr_t err;
285 
286     err = m_file_ctx.open(false, m_begin_lsn, static_cast<uint>(num_files) - 1,
287                           0);
288 
289     if (err != DB_SUCCESS) {
290       return (err);
291     }
292 
293     err = m_file_ctx.write(nullptr, dblwr_block.m_block,
294                            static_cast<uint>(offset), ARCH_PAGE_BLK_SIZE);
295 
296     if (err != DB_SUCCESS) {
297       return (err);
298     }
299 
300     m_file_ctx.close();
301   }
302 
303   return (DB_SUCCESS);
304 }
305 
recovery_cleanup_if_required(uint & num_files,uint start_index,bool durable,bool & empty_file)306 dberr_t Arch_Group::recovery_cleanup_if_required(uint &num_files,
307                                                  uint start_index, bool durable,
308                                                  bool &empty_file) {
309   dberr_t err;
310 
311   ut_ad(!durable || num_files > 0);
312   ut_ad(m_file_ctx.is_closed());
313 
314   uint index = start_index + num_files - 1;
315 
316   /* Open the last file in the group. */
317   err = m_file_ctx.open(true, m_begin_lsn, index, 0);
318 
319   if (err != DB_SUCCESS) {
320     return (err);
321   }
322 
323   if (m_file_ctx.get_phy_size() != 0 && durable) {
324     m_file_ctx.close();
325     return (DB_SUCCESS);
326   }
327 
328   empty_file = true;
329 
330   /* No blocks have been flushed into the file so delete the file. */
331 
332   char file_path[MAX_ARCH_PAGE_FILE_NAME_LEN];
333   char dir_path[MAX_ARCH_DIR_NAME_LEN];
334 
335   m_file_ctx.build_name(index, m_begin_lsn, file_path,
336                         MAX_ARCH_PAGE_FILE_NAME_LEN);
337 
338   auto found = std::string(file_path).find(ARCH_PAGE_FILE);
339   ut_ad(found != std::string::npos);
340   auto file_name = std::string(file_path).substr(found);
341 
342   m_file_ctx.build_dir_name(m_begin_lsn, dir_path, MAX_ARCH_DIR_NAME_LEN);
343 
344   m_file_ctx.close();
345 
346   arch_remove_file(dir_path, file_name.c_str());
347 
348   --num_files;
349 
350   /* If there are no archive files in the group we might as well
351   purge it. */
352   if (num_files == 0 || !durable) {
353     m_is_active = false;
354 
355     found = std::string(dir_path).find(ARCH_PAGE_DIR);
356     ut_ad(found != std::string::npos);
357 
358     auto path = std::string(dir_path).substr(0, found - 1);
359     auto dir_name = std::string(dir_path).substr(found);
360 
361     num_files = 0;
362     arch_remove_dir(path.c_str(), dir_name.c_str());
363   }
364 
365   /* Need to reinitialize the file context as num_files has changed. */
366   err = m_file_ctx.init(
367       ARCH_DIR, ARCH_PAGE_DIR, ARCH_PAGE_FILE, num_files,
368       static_cast<uint64_t>(ARCH_PAGE_BLK_SIZE) * ARCH_PAGE_FILE_CAPACITY);
369 
370   return (err);
371 }
372 
fill_info(Arch_Page_Sys * page_sys)373 dberr_t Arch_Page_Sys::Recv::fill_info(Arch_Page_Sys *page_sys) {
374   uint num_active = 0;
375   dberr_t err = DB_SUCCESS;
376   bool new_empty_file = false;
377 
378   for (auto info = m_dir_group_info_map.begin();
379        info != m_dir_group_info_map.end(); ++info) {
380     auto group_info = info->second;
381     auto dir_name = info->first;
382 
383     auto pos = dir_name.find(ARCH_PAGE_DIR);
384     lsn_t start_lsn = static_cast<lsn_t>(
385         std::stoull(dir_name.substr(pos + strlen(ARCH_PAGE_DIR))));
386 
387     Arch_Group *group = UT_NEW(
388         Arch_Group(start_lsn, ARCH_PAGE_FILE_HDR_SIZE, page_sys->get_mutex()),
389         mem_key_archive);
390 
391     Arch_Page_Pos write_pos;
392     Arch_Page_Pos reset_pos;
393 
394     err = group->recover(&group_info, new_empty_file, &m_dblwr_ctx, write_pos,
395                          reset_pos);
396 
397     if (err != DB_SUCCESS) {
398       UT_DELETE(group);
399       return (err);
400     }
401 
402     if (group_info.m_num_files == 0) {
403       UT_DELETE(group);
404       continue;
405     }
406 
407     page_sys->m_group_list.push_back(group);
408 
409     if (group_info.m_active) {
410       /* Group was active at the time of shutdown/crash, so
411       we need to start page archiving */
412 
413       page_sys->m_write_pos = write_pos;
414       page_sys->m_reset_pos = reset_pos;
415 
416       ++num_active;
417       int error = page_sys->start_during_recovery(group, new_empty_file);
418 
419       if (error != 0) {
420         return (DB_ERROR);
421       }
422     }
423   }
424 
425   /* There can be only one active group at a time. */
426   ut_ad(num_active <= 1);
427 
428   return (DB_SUCCESS);
429 }
430 
recover(Arch_Recv_Group_Info * group_info,bool & new_empty_file,Arch_Dblwr_Ctx * dblwr_ctx,Arch_Page_Pos & write_pos,Arch_Page_Pos & reset_pos)431 dberr_t Arch_Group::recover(Arch_Recv_Group_Info *group_info,
432                             bool &new_empty_file, Arch_Dblwr_Ctx *dblwr_ctx,
433                             Arch_Page_Pos &write_pos,
434                             Arch_Page_Pos &reset_pos) {
435   dberr_t err;
436 
437   err = init_file_ctx(
438       ARCH_DIR, ARCH_PAGE_DIR, ARCH_PAGE_FILE, group_info->m_num_files,
439       static_cast<uint64_t>(ARCH_PAGE_BLK_SIZE) * ARCH_PAGE_FILE_CAPACITY);
440 
441   if (err != DB_SUCCESS) {
442     return (err);
443   }
444 
445   if (group_info->m_active) {
446     /* Since the group was active at the time of crash it's possible that the
447     doublewrite buffer might have the latest data in case of a crash. */
448 
449     err = recovery_replace_pages_from_dblwr(dblwr_ctx);
450 
451     if (err != DB_SUCCESS) {
452       return (err);
453     }
454   }
455 
456   err = recovery_cleanup_if_required(group_info->m_num_files,
457                                      group_info->m_file_start_index,
458                                      group_info->m_durable, new_empty_file);
459 
460   if (err != DB_SUCCESS) {
461     return (err);
462   }
463 
464   if (group_info->m_num_files == 0) {
465     return (err);
466   }
467 
468   err = recovery_parse(write_pos, reset_pos, group_info->m_file_start_index);
469 
470   if (err != DB_SUCCESS) {
471     return (err);
472   }
473 
474   if (!group_info->m_active) {
475     /* Group was inactive at the time of shutdown/crash, so
476     we just add the group to the group list that the
477     archiver maintains. */
478 
479     attach_during_recovery();
480     m_stop_pos = write_pos;
481 
482     auto end_lsn = m_file_ctx.get_last_stop_point();
483     ut_ad(end_lsn != LSN_MAX);
484 
485     disable(end_lsn);
486   } else {
487     err = open_file_during_recovery(write_pos, new_empty_file);
488   }
489 
490   ut_d(m_file_ctx.recovery_reset_print(group_info->m_file_start_index));
491 
492   return (err);
493 }
494 
495 #ifdef UNIV_DEBUG
recovery_reset_print(uint file_start_index)496 void Arch_File_Ctx::recovery_reset_print(uint file_start_index) {
497   Arch_Reset reset;
498   Arch_Reset_File reset_file;
499   Arch_Point start_point;
500 
501   DBUG_PRINT("page_archiver", ("No. of files : %u", m_count));
502 
503   if (m_reset.size() == 0) {
504     DBUG_PRINT("page_archiver", ("No reset info available for this group."));
505   }
506 
507   for (auto reset_file : m_reset) {
508     DBUG_PRINT("page_archiver", ("File %u\tFile LSN : %" PRIu64 "",
509                                  reset_file.m_file_index, reset_file.m_lsn));
510 
511     if (reset_file.m_start_point.size() == 0) {
512       DBUG_PRINT("page_archiver", ("No reset info available for this file."));
513     }
514 
515     for (uint i = 0; i < reset_file.m_start_point.size(); i++) {
516       start_point = reset_file.m_start_point[i];
517       DBUG_PRINT("page_archiver",
518                  ("\tReset lsn : %" PRIu64 ", reset_pos : %" PRIu64 "\t %u",
519                   start_point.lsn, start_point.pos.m_block_num,
520                   start_point.pos.m_offset));
521     }
522   }
523 
524   DBUG_PRINT("page_archiver",
525              ("Starting index of the file : %u", file_start_index));
526 
527   DBUG_PRINT("page_archiver", ("Latest stop points"));
528   uint file_index = 0;
529   for (auto stop_point : m_stop_points) {
530     DBUG_PRINT("page_archiver",
531                ("\tFile %u : %" PRIu64 "", file_index, stop_point));
532     ++file_index;
533   }
534 }
535 #endif
536 
recovery_parse(Arch_Page_Pos & write_pos,Arch_Page_Pos & reset_pos,size_t start_index)537 dberr_t Arch_Group::recovery_parse(Arch_Page_Pos &write_pos,
538                                    Arch_Page_Pos &reset_pos,
539                                    size_t start_index) {
540   size_t num_files = get_file_count();
541   dberr_t err = DB_SUCCESS;
542 
543   if (num_files == 0) {
544     DBUG_PRINT("page_archiver", ("No group information available"));
545     return (DB_SUCCESS);
546   }
547 
548   ut_ad(m_file_ctx.is_closed());
549 
550   size_t file_count = start_index + num_files;
551 
552   for (auto file_index = start_index; file_index < file_count; ++file_index) {
553     if (file_index == start_index) {
554       err =
555           m_file_ctx.open(true, m_begin_lsn, static_cast<uint>(start_index), 0);
556     } else {
557       err = m_file_ctx.open_next(m_begin_lsn, 0);
558     }
559 
560     if (err != DB_SUCCESS) {
561       break;
562     }
563 
564     err =
565         m_file_ctx.fetch_reset_points(static_cast<uint>(file_index), reset_pos);
566 
567     if (err != DB_SUCCESS) {
568       break;
569     }
570 
571     bool last_file = (file_index + 1 == file_count);
572     err = m_file_ctx.fetch_stop_points(last_file, write_pos);
573 
574     if (err != DB_SUCCESS) {
575       break;
576     }
577 
578     m_file_ctx.close();
579   }
580 
581   m_file_ctx.close();
582 
583   return (err);
584 }
585 
fetch_stop_points(bool last_file,Arch_Page_Pos & write_pos)586 dberr_t Arch_File_Ctx::fetch_stop_points(bool last_file,
587                                          Arch_Page_Pos &write_pos) {
588   ut_ad(!is_closed());
589 
590   uint64_t offset;
591   byte buf[ARCH_PAGE_BLK_SIZE];
592 
593   if (last_file) {
594     offset = get_phy_size() - ARCH_PAGE_BLK_SIZE;
595   } else {
596     offset = ARCH_PAGE_FILE_DATA_CAPACITY * ARCH_PAGE_BLK_SIZE;
597   }
598 
599   auto err = read(buf, offset, ARCH_PAGE_BLK_SIZE);
600 
601   if (err != DB_SUCCESS) {
602     return (err);
603   }
604 
605   auto stop_lsn = Arch_Block::get_stop_lsn(buf);
606   m_stop_points.push_back(stop_lsn);
607 
608   write_pos.init();
609   write_pos.m_block_num = Arch_Block::get_block_number(buf);
610   write_pos.m_offset =
611       Arch_Block::get_data_len(buf) + ARCH_PAGE_BLK_HEADER_LENGTH;
612 
613   return (err);
614 }
615 
fetch_reset_points(uint file_index,Arch_Page_Pos & reset_pos)616 dberr_t Arch_File_Ctx::fetch_reset_points(uint file_index,
617                                           Arch_Page_Pos &reset_pos) {
618   ut_ad(!is_closed());
619   ut_ad(m_index == file_index);
620 
621   byte buf[ARCH_PAGE_BLK_SIZE];
622 
623   /* Read reset block to fetch reset points. */
624   auto err = read(buf, 0, ARCH_PAGE_BLK_SIZE);
625 
626   if (err != DB_SUCCESS) {
627     return (err);
628   }
629 
630   auto block_num = Arch_Block::get_block_number(buf);
631   auto data_len = Arch_Block::get_data_len(buf);
632 
633   if (file_index != block_num) {
634     /* This means there was no reset for this file and hence the
635     reset block was not flushed. */
636 
637     ut_ad(Arch_Block::is_zeroes(buf));
638     reset_pos.init();
639     reset_pos.m_block_num = file_index;
640     return (err);
641   }
642 
643   /* Normal case. */
644   reset_pos.m_block_num = block_num;
645   reset_pos.m_offset = data_len + ARCH_PAGE_BLK_HEADER_LENGTH;
646 
647   Arch_Reset_File reset_file;
648   reset_file.init();
649   reset_file.m_file_index = file_index;
650 
651   if (data_len != 0) {
652     uint length = 0;
653     byte *buf1 = buf + ARCH_PAGE_BLK_HEADER_LENGTH;
654 
655     ut_ad(data_len >= ARCH_PAGE_FILE_HEADER_RESET_LSN_SIZE +
656                           ARCH_PAGE_FILE_HEADER_RESET_POS_SIZE);
657 
658     reset_file.m_lsn = mach_read_from_8(buf1);
659     length += ARCH_PAGE_FILE_HEADER_RESET_LSN_SIZE;
660 
661     Arch_Point start_point;
662     Arch_Page_Pos pos;
663 
664     while (length != data_len) {
665       ut_ad((data_len - length) % ARCH_PAGE_FILE_HEADER_RESET_POS_SIZE == 0);
666 
667       pos.m_block_num = mach_read_from_2(buf1 + length);
668       length += ARCH_PAGE_FILE_HEADER_RESET_BLOCK_NUM_SIZE;
669 
670       pos.m_offset = mach_read_from_2(buf1 + length);
671       length += ARCH_PAGE_FILE_HEADER_RESET_BLOCK_OFFSET_SIZE;
672 
673       start_point.lsn = fetch_reset_lsn(pos.m_block_num);
674       start_point.pos = pos;
675 
676       reset_file.m_start_point.push_back(start_point);
677     }
678 
679     m_reset.push_back(reset_file);
680   }
681 
682   return (err);
683 }
684 
fetch_reset_lsn(uint64_t block_num)685 lsn_t Arch_File_Ctx::fetch_reset_lsn(uint64_t block_num) {
686   ut_ad(!is_closed());
687   ut_ad(Arch_Block::get_file_index(block_num) == m_index);
688 
689   byte buf[ARCH_PAGE_BLK_SIZE];
690 
691   auto offset = Arch_Block::get_file_offset(block_num, ARCH_DATA_BLOCK);
692 
693   ut_ad(offset + ARCH_PAGE_BLK_SIZE <= get_phy_size());
694 
695   auto err = read(buf, offset, ARCH_PAGE_BLK_HEADER_LENGTH);
696 
697   if (err != DB_SUCCESS) {
698     return (LSN_MAX);
699   }
700 
701   auto lsn = Arch_Block::get_reset_lsn(buf);
702 
703   ut_ad(lsn != LSN_MAX);
704 
705   return (lsn);
706 }
707 
recovery_read_latest_blocks(byte * buf,uint64_t offset,Arch_Blk_Type type)708 dberr_t Arch_Group::recovery_read_latest_blocks(byte *buf, uint64_t offset,
709                                                 Arch_Blk_Type type) {
710   dberr_t err = DB_SUCCESS;
711 
712   ut_ad(!m_file_ctx.is_closed());
713 
714   switch (type) {
715     case ARCH_RESET_BLOCK: {
716       ut_d(uint64_t file_size = m_file_ctx.get_phy_size());
717       ut_ad((file_size > ARCH_PAGE_FILE_NUM_RESET_PAGE * ARCH_PAGE_BLK_SIZE) &&
718             (file_size % ARCH_PAGE_BLK_SIZE == 0));
719 
720       err = m_file_ctx.read(buf, 0, ARCH_PAGE_BLK_SIZE);
721     } break;
722 
723     case ARCH_DATA_BLOCK:
724       err = m_file_ctx.read(buf, offset, ARCH_PAGE_BLK_SIZE);
725       break;
726   }
727 
728   return (err);
729 }
730