1 /*****************************************************************************
2
3 Copyright (c) 2011-2012 Percona Inc. All Rights Reserved.
4
5 This program is free software; you can redistribute it and/or modify it under
6 the terms of the GNU General Public License as published by the Free Software
7 Foundation; version 2 of the License.
8
9 This program is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License along with
14 this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
15 Street, Fifth Floor, Boston, MA 02110-1301, USA
16
17 *****************************************************************************/
18
19 /**************************************************//**
20 @file log/log0online.cc
21 Online database log parsing for changed page tracking
22
23 *******************************************************/
24
25 #include "log0online.h"
26
27 #include "my_dbug.h"
28
29 #include "log0recv.h"
30 #include "mach0data.h"
31 #include "mtr0log.h"
32 #include "srv0srv.h"
33 #include "srv0start.h"
34 #include "trx0sys.h"
35 #include "ut0rbt.h"
36
37 #ifdef __WIN__
38 /* error LNK2001: unresolved external symbol _debug_sync_C_callback_ptr */
39 # define DEBUG_SYNC_C(dummy) ((void) 0)
40 #else
41 # include "m_string.h" /* for my_sys.h */
42 # include "my_sys.h" /* DEBUG_SYNC_C */
43 #endif
44
45 enum { FOLLOW_SCAN_SIZE = 4 * (UNIV_PAGE_SIZE_MAX) };
46
47 #ifdef UNIV_PFS_MUTEX
48 /* Key to register log_bmp_sys->mutex with PFS */
49 UNIV_INTERN mysql_pfs_key_t log_bmp_sys_mutex_key;
50 #endif /* UNIV_PFS_MUTEX */
51
52 /** Log parsing and bitmap output data structure */
53 struct log_bitmap_struct {
54 byte* read_buf_ptr; /*!< Unaligned log read buffer */
55 byte* read_buf; /*!< log read buffer */
56 byte parse_buf[RECV_PARSING_BUF_SIZE];
57 /*!< log parse buffer */
58 byte* parse_buf_end; /*!< parse buffer position where the
59 next read log data should be copied to.
60 If the previous log records were fully
61 parsed, it points to the start,
62 otherwise points immediatelly past the
63 end of the incomplete log record. */
64 char bmp_file_home[FN_REFLEN];
65 /*!< directory for bitmap files */
66 log_online_bitmap_file_t out; /*!< The current bitmap file */
67 ulint out_seq_num; /*!< the bitmap file sequence number */
68 lsn_t start_lsn; /*!< the LSN of the next unparsed
69 record and the start of the next LSN
70 interval to be parsed. */
71 lsn_t end_lsn; /*!< the end of the LSN interval to be
72 parsed, equal to the next checkpoint
73 LSN at the time of parse */
74 lsn_t next_parse_lsn; /*!< the LSN of the next unparsed
75 record in the current parse */
76 ib_rbt_t* modified_pages; /*!< the current modified page set,
77 organized as the RB-tree with the keys
78 of (space, 4KB-block-start-page-id)
79 pairs */
80 ib_rbt_node_t* page_free_list; /*!< Singly-linked list of freed nodes
81 of modified_pages tree for later
82 reuse. Nodes are linked through
83 ib_rbt_node_t.left as this field has
84 both the correct type and the tree does
85 not mind its overwrite during
86 rbt_next() tree traversal. */
87 };
88
89 /* The log parsing and bitmap output struct instance */
90 static struct log_bitmap_struct* log_bmp_sys;
91
92 /* Mutex protecting log_bmp_sys */
93 static ib_mutex_t log_bmp_sys_mutex;
94
95 /** File name stem for bitmap files. */
96 static const char* bmp_file_name_stem = "ib_modified_log_";
97
98 /** File name template for bitmap files. The 1st format tag is a directory
99 name, the 2nd tag is the stem, the 3rd tag is a file sequence number, the 4th
100 tag is the start LSN for the file. */
101 static const char* bmp_file_name_template = "%s%s%lu_" LSN_PF ".xdb";
102
103 /* On server startup with empty database srv_start_lsn == 0, in
104 which case the first LSN of actual log records will be this. */
105 #define MIN_TRACKED_LSN ((LOG_START_LSN) + (LOG_BLOCK_HDR_SIZE))
106
107 /* Tests if num bit of bitmap is set */
108 #define IS_BIT_SET(bitmap, num) \
109 (*((bitmap) + ((num) >> 3)) & (1UL << ((num) & 7UL)))
110
111 /** The bitmap file block size in bytes. All writes will be multiples of this.
112 */
113 enum {
114 MODIFIED_PAGE_BLOCK_SIZE = 4096
115 };
116
117
118 /** Offsets in a file bitmap block */
119 enum {
120 MODIFIED_PAGE_IS_LAST_BLOCK = 0,/* 1 if last block in the current
121 write, 0 otherwise. */
122 MODIFIED_PAGE_START_LSN = 4, /* The starting tracked LSN of this and
123 other blocks in the same write */
124 MODIFIED_PAGE_END_LSN = 12, /* The ending tracked LSN of this and
125 other blocks in the same write */
126 MODIFIED_PAGE_SPACE_ID = 20, /* The space ID of tracked pages in
127 this block */
128 MODIFIED_PAGE_1ST_PAGE_ID = 24, /* The page ID of the first tracked
129 page in this block */
130 MODIFIED_PAGE_BLOCK_UNUSED_1 = 28,/* Unused in order to align the start
131 of bitmap at 8 byte boundary */
132 MODIFIED_PAGE_BLOCK_BITMAP = 32,/* Start of the bitmap itself */
133 MODIFIED_PAGE_BLOCK_UNUSED_2 = MODIFIED_PAGE_BLOCK_SIZE - 8,
134 /* Unused in order to align the end of
135 bitmap at 8 byte boundary */
136 MODIFIED_PAGE_BLOCK_CHECKSUM = MODIFIED_PAGE_BLOCK_SIZE - 4
137 /* The checksum of the current block */
138 };
139
140 /** Length of the bitmap data in a block in bytes */
141 enum { MODIFIED_PAGE_BLOCK_BITMAP_LEN
142 = MODIFIED_PAGE_BLOCK_UNUSED_2 - MODIFIED_PAGE_BLOCK_BITMAP };
143
144 /** Length of the bitmap data in a block in page ids */
145 enum { MODIFIED_PAGE_BLOCK_ID_COUNT = MODIFIED_PAGE_BLOCK_BITMAP_LEN * 8 };
146
147 /****************************************************************//**
148 Provide a comparisson function for the RB-tree tree (space,
149 block_start_page) pairs. Actual implementation does not matter as
150 long as the ordering is full.
151 @return -1 if p1 < p2, 0 if p1 == p2, 1 if p1 > p2
152 */
153 static
154 int
log_online_compare_bmp_keys(const void * p1,const void * p2)155 log_online_compare_bmp_keys(
156 /*========================*/
157 const void* p1, /*!<in: 1st key to compare */
158 const void* p2) /*!<in: 2nd key to compare */
159 {
160 const byte *k1 = (const byte *)p1;
161 const byte *k2 = (const byte *)p2;
162
163 ulint k1_space = mach_read_from_4(k1 + MODIFIED_PAGE_SPACE_ID);
164 ulint k2_space = mach_read_from_4(k2 + MODIFIED_PAGE_SPACE_ID);
165 if (k1_space == k2_space) {
166 ulint k1_start_page
167 = mach_read_from_4(k1 + MODIFIED_PAGE_1ST_PAGE_ID);
168 ulint k2_start_page
169 = mach_read_from_4(k2 + MODIFIED_PAGE_1ST_PAGE_ID);
170 return k1_start_page < k2_start_page
171 ? -1 : k1_start_page > k2_start_page ? 1 : 0;
172 }
173 return k1_space < k2_space ? -1 : 1;
174 }
175
176 /****************************************************************//**
177 Set a bit for tracked page in the bitmap. Expand the bitmap tree as
178 necessary. */
179 static
180 void
log_online_set_page_bit(ulint space,ulint page_no)181 log_online_set_page_bit(
182 /*====================*/
183 ulint space, /*!<in: log record space id */
184 ulint page_no)/*!<in: log record page id */
185 {
186 ut_ad(mutex_own(&log_bmp_sys_mutex));
187
188 ut_a(space != ULINT_UNDEFINED);
189 ut_a(page_no != ULINT_UNDEFINED);
190
191 ulint block_start_page = page_no / MODIFIED_PAGE_BLOCK_ID_COUNT
192 * MODIFIED_PAGE_BLOCK_ID_COUNT;
193 ulint block_pos = block_start_page ? (page_no % block_start_page / 8)
194 : (page_no / 8);
195 uint bit_pos = page_no % 8;
196
197 byte search_page[MODIFIED_PAGE_BLOCK_SIZE];
198 mach_write_to_4(search_page + MODIFIED_PAGE_SPACE_ID, space);
199 mach_write_to_4(search_page + MODIFIED_PAGE_1ST_PAGE_ID,
200 block_start_page);
201
202 byte *page_ptr;
203 ib_rbt_bound_t tree_search_pos;
204 if (!rbt_search(log_bmp_sys->modified_pages, &tree_search_pos,
205 search_page)) {
206 page_ptr = rbt_value(byte, tree_search_pos.last);
207 }
208 else {
209 ib_rbt_node_t *new_node;
210
211 if (log_bmp_sys->page_free_list) {
212 new_node = log_bmp_sys->page_free_list;
213 log_bmp_sys->page_free_list = new_node->left;
214 }
215 else {
216 new_node = static_cast<ib_rbt_node_t *>
217 (ut_malloc
218 (SIZEOF_NODE(log_bmp_sys->modified_pages)));
219 }
220 memset(new_node, 0, SIZEOF_NODE(log_bmp_sys->modified_pages));
221
222 page_ptr = rbt_value(byte, new_node);
223 mach_write_to_4(page_ptr + MODIFIED_PAGE_SPACE_ID, space);
224 mach_write_to_4(page_ptr + MODIFIED_PAGE_1ST_PAGE_ID,
225 block_start_page);
226
227 rbt_add_preallocated_node(log_bmp_sys->modified_pages,
228 &tree_search_pos, new_node);
229 }
230 page_ptr[MODIFIED_PAGE_BLOCK_BITMAP + block_pos] |= (1U << bit_pos);
231 }
232
233 /****************************************************************//**
234 Calculate a bitmap block checksum. Algorithm borrowed from
235 log_block_calc_checksum.
236 @return checksum */
237 UNIV_INLINE
238 ulint
log_online_calc_checksum(const byte * block)239 log_online_calc_checksum(
240 /*=====================*/
241 const byte* block) /*!<in: bitmap block */
242 {
243 ulint sum;
244 ulint sh;
245 ulint i;
246
247 sum = 1;
248 sh = 0;
249
250 for (i = 0; i < MODIFIED_PAGE_BLOCK_CHECKSUM; i++) {
251
252 ulint b = block[i];
253 sum &= 0x7FFFFFFFUL;
254 sum += b;
255 sum += b << sh;
256 sh++;
257 if (sh > 24) {
258 sh = 0;
259 }
260 }
261
262 return sum;
263 }
264
265 /****************************************************************//**
266 Read one bitmap data page and check it for corruption.
267
268 @return TRUE if page read OK, FALSE if I/O error */
269 static
270 ibool
log_online_read_bitmap_page(log_online_bitmap_file_t * bitmap_file,byte * page,ibool * checksum_ok)271 log_online_read_bitmap_page(
272 /*========================*/
273 log_online_bitmap_file_t *bitmap_file, /*!<in/out: bitmap
274 file */
275 byte *page, /*!<out: read page.
276 Must be at least
277 MODIFIED_PAGE_BLOCK_SIZE
278 bytes long */
279 ibool *checksum_ok) /*!<out: TRUE if page
280 checksum OK */
281 {
282 ulint checksum;
283 ulint actual_checksum;
284 ibool success;
285
286 ut_a(bitmap_file->size >= MODIFIED_PAGE_BLOCK_SIZE);
287 ut_a(bitmap_file->offset
288 <= bitmap_file->size - MODIFIED_PAGE_BLOCK_SIZE);
289 ut_a(bitmap_file->offset % MODIFIED_PAGE_BLOCK_SIZE == 0);
290
291 success = os_file_read(bitmap_file->file, page, bitmap_file->offset,
292 MODIFIED_PAGE_BLOCK_SIZE);
293
294 if (UNIV_UNLIKELY(!success)) {
295
296 /* The following call prints an error message */
297 os_file_get_last_error(TRUE);
298 ib_logf(IB_LOG_LEVEL_WARN,
299 "failed reading changed page bitmap file \'%s\'",
300 bitmap_file->name);
301 return FALSE;
302 }
303
304 bitmap_file->offset += MODIFIED_PAGE_BLOCK_SIZE;
305 ut_ad(bitmap_file->offset <= bitmap_file->size);
306
307 checksum = mach_read_from_4(page + MODIFIED_PAGE_BLOCK_CHECKSUM);
308 actual_checksum = log_online_calc_checksum(page);
309 *checksum_ok = (checksum == actual_checksum);
310
311 return TRUE;
312 }
313
314 /****************************************************************//**
315 Get the last tracked fully LSN from the bitmap file by reading
316 backwards untile a correct end page is found. Detects incomplete
317 writes and corrupted data. Sets the start output position for the
318 written bitmap data.
319
320 Multiple bitmap files are handled using the following assumptions:
321 1) Only the last file might be corrupted. In case where no good data was found
322 in the last file, assume that the next to last file is OK. This assumption
323 does not limit crash recovery capability in any way.
324 2) If the whole of the last file was corrupted, assume that the start LSN in
325 its name is correct and use it for (re-)tracking start.
326
327 @return the last fully tracked LSN */
328 static
329 lsn_t
log_online_read_last_tracked_lsn(void)330 log_online_read_last_tracked_lsn(void)
331 /*==================================*/
332 {
333 byte page[MODIFIED_PAGE_BLOCK_SIZE];
334 ibool is_last_page = FALSE;
335 ibool checksum_ok = FALSE;
336 lsn_t result;
337 os_offset_t read_offset = log_bmp_sys->out.offset;
338
339 while ((!checksum_ok || !is_last_page) && read_offset > 0)
340 {
341 read_offset -= MODIFIED_PAGE_BLOCK_SIZE;
342 log_bmp_sys->out.offset = read_offset;
343
344 if (!log_online_read_bitmap_page(&log_bmp_sys->out, page,
345 &checksum_ok)) {
346 checksum_ok = FALSE;
347 result = 0;
348 break;
349 }
350
351 if (checksum_ok) {
352 is_last_page
353 = mach_read_from_4
354 (page + MODIFIED_PAGE_IS_LAST_BLOCK);
355 } else {
356
357 ib_logf(IB_LOG_LEVEL_WARN,
358 "corruption detected in \'%s\' at offset "
359 UINT64PF,
360 log_bmp_sys->out.name, read_offset);
361 }
362 };
363
364 result = (checksum_ok && is_last_page)
365 ? mach_read_from_8(page + MODIFIED_PAGE_END_LSN) : 0;
366
367 /* Truncate the output file to discard the corrupted bitmap data, if
368 any */
369 if (!os_file_set_eof_at(log_bmp_sys->out.file,
370 log_bmp_sys->out.offset)) {
371 ib_logf(IB_LOG_LEVEL_WARN,
372 "failed truncating changed page bitmap file \'%s\' to "
373 UINT64PF " bytes",
374 log_bmp_sys->out.name, log_bmp_sys->out.offset);
375 result = 0;
376 }
377 return result;
378 }
379
380 /****************************************************************//**
381 Safely write the log_sys->tracked_lsn value. Uses atomic operations
382 if available, otherwise this field is protected with the log system
383 mutex. The reader counterpart function is log_get_tracked_lsn() in
384 log0log.c. */
385 UNIV_INLINE
386 void
log_set_tracked_lsn(lsn_t tracked_lsn)387 log_set_tracked_lsn(
388 /*================*/
389 lsn_t tracked_lsn) /*!<in: new value */
390 {
391 log_sys->tracked_lsn = tracked_lsn;
392 os_wmb;
393 }
394
395 /*********************************************************************//**
396 Check if missing, if any, LSN interval can be read and tracked using the
397 current LSN value, the LSN value where the tracking stopped, and the log group
398 capacity.
399
400 @return TRUE if the missing interval can be tracked or if there's no missing
401 data. */
402 static
403 ibool
log_online_can_track_missing(lsn_t last_tracked_lsn,lsn_t tracking_start_lsn)404 log_online_can_track_missing(
405 /*=========================*/
406 lsn_t last_tracked_lsn, /*!<in: last tracked LSN */
407 lsn_t tracking_start_lsn) /*!<in: current LSN */
408 {
409 /* last_tracked_lsn might be < MIN_TRACKED_LSN in the case of empty
410 bitmap file, handle this too. */
411 last_tracked_lsn = ut_max(last_tracked_lsn, MIN_TRACKED_LSN);
412
413 if (last_tracked_lsn > tracking_start_lsn) {
414 ib_logf(IB_LOG_LEVEL_ERROR,
415 "last tracked LSN " LSN_PF " is ahead of tracking "
416 "start LSN " LSN_PF ". This can be caused by "
417 "mismatched bitmap files.",
418 last_tracked_lsn, tracking_start_lsn);
419 exit(1);
420 }
421
422 return (last_tracked_lsn == tracking_start_lsn)
423 || (log_sys->lsn - last_tracked_lsn
424 <= log_sys->log_group_capacity);
425 }
426
427
428 /****************************************************************//**
429 Diagnose a gap in tracked LSN range on server startup due to crash or
430 very fast shutdown and try to close it by tracking the data
431 immediatelly, if possible. */
432 static
433 void
log_online_track_missing_on_startup(lsn_t last_tracked_lsn,lsn_t tracking_start_lsn)434 log_online_track_missing_on_startup(
435 /*================================*/
436 lsn_t last_tracked_lsn, /*!<in: last tracked LSN read from the
437 bitmap file */
438 lsn_t tracking_start_lsn) /*!<in: last checkpoint LSN of the
439 current server startup */
440 {
441 ut_ad(last_tracked_lsn != tracking_start_lsn);
442 ut_ad(srv_track_changed_pages);
443
444 ib_logf(IB_LOG_LEVEL_WARN, "last tracked LSN in \'%s\' is " LSN_PF
445 ", but the last checkpoint LSN is " LSN_PF ". This might be "
446 "due to a server crash or a very fast shutdown.",
447 log_bmp_sys->out.name, last_tracked_lsn, tracking_start_lsn);
448
449 /* See if we can fully recover the missing interval */
450 if (log_online_can_track_missing(last_tracked_lsn,
451 tracking_start_lsn)) {
452
453 ib_logf(IB_LOG_LEVEL_INFO,
454 "reading the log to advance the last tracked LSN.");
455
456 log_bmp_sys->start_lsn = ut_max(last_tracked_lsn,
457 MIN_TRACKED_LSN);
458 log_set_tracked_lsn(log_bmp_sys->start_lsn);
459 if (!log_online_follow_redo_log()) {
460 exit(1);
461 }
462 ut_ad(log_bmp_sys->end_lsn >= tracking_start_lsn);
463
464 ib_logf(IB_LOG_LEVEL_INFO,
465 "continuing tracking changed pages from LSN " LSN_PF,
466 log_bmp_sys->end_lsn);
467 }
468 else {
469 ib_logf(IB_LOG_LEVEL_WARN,
470 "the age of last tracked LSN exceeds log capacity, "
471 "tracking-based incremental backups will work only "
472 "from the higher LSN!");
473
474 log_bmp_sys->end_lsn = log_bmp_sys->start_lsn
475 = tracking_start_lsn;
476 log_set_tracked_lsn(log_bmp_sys->start_lsn);
477
478 ib_logf(IB_LOG_LEVEL_INFO,
479 "starting tracking changed pages from LSN " LSN_PF,
480 log_bmp_sys->end_lsn);
481 }
482 }
483
484 /*********************************************************************//**
485 Format a bitmap output file name to log_bmp_sys->out.name. */
486 static
487 void
log_online_make_bitmap_name(lsn_t start_lsn)488 log_online_make_bitmap_name(
489 /*=========================*/
490 lsn_t start_lsn) /*!< in: the start LSN name part */
491 {
492 ut_snprintf(log_bmp_sys->out.name, sizeof(log_bmp_sys->out.name), bmp_file_name_template,
493 log_bmp_sys->bmp_file_home, bmp_file_name_stem,
494 log_bmp_sys->out_seq_num, start_lsn);
495 }
496
497 /*********************************************************************//**
498 Check if an old file that has the name of a new bitmap file we are about to
499 create should be overwritten. */
500 static
501 ibool
log_online_should_overwrite(const char * path)502 log_online_should_overwrite(
503 /*========================*/
504 const char *path) /*!< in: path to file */
505 {
506 dberr_t err;
507 os_file_stat_t file_info;
508
509 /* Currently, it's OK to overwrite 0-sized files only */
510 err = os_file_get_status(path, &file_info, false);
511 return err == DB_SUCCESS && file_info.type == OS_FILE_TYPE_FILE
512 && file_info.size == 0LL;
513 }
514
515 /*********************************************************************//**
516 Create a new empty bitmap output file.
517
518 @return TRUE if operation succeeded, FALSE if I/O error */
519 static
520 ibool
log_online_start_bitmap_file(void)521 log_online_start_bitmap_file(void)
522 /*==============================*/
523 {
524 ibool success = TRUE;
525
526 /* Check for an old file that should be deleted first */
527 if (log_online_should_overwrite(log_bmp_sys->out.name)) {
528
529 success = static_cast<ibool>(
530 os_file_delete_if_exists(innodb_file_bmp_key,
531 log_bmp_sys->out.name));
532 }
533
534 if (UNIV_LIKELY(success)) {
535 log_bmp_sys->out.file
536 = os_file_create_simple_no_error_handling(
537 innodb_file_bmp_key,
538 log_bmp_sys->out.name,
539 OS_FILE_CREATE,
540 OS_FILE_READ_WRITE_CACHED,
541 &success);
542 }
543 if (UNIV_UNLIKELY(!success)) {
544
545 /* The following call prints an error message */
546 os_file_get_last_error(TRUE);
547 ib_logf(IB_LOG_LEVEL_ERROR,
548 "cannot create \'%s\'", log_bmp_sys->out.name);
549 return FALSE;
550 }
551
552 log_bmp_sys->out.offset = 0;
553 return TRUE;
554 }
555
556 /*********************************************************************//**
557 Close the current bitmap output file and create the next one.
558
559 @return TRUE if operation succeeded, FALSE if I/O error */
560 static
561 ibool
log_online_rotate_bitmap_file(lsn_t next_file_start_lsn)562 log_online_rotate_bitmap_file(
563 /*===========================*/
564 lsn_t next_file_start_lsn) /*!<in: the start LSN name
565 part */
566 {
567 if (!os_file_is_invalid(log_bmp_sys->out.file)) {
568 os_file_close(log_bmp_sys->out.file);
569 os_file_mark_invalid(&log_bmp_sys->out.file);
570 }
571 log_bmp_sys->out_seq_num++;
572 log_online_make_bitmap_name(next_file_start_lsn);
573 return log_online_start_bitmap_file();
574 }
575
576 /*********************************************************************//**
577 Check the name of a given file if it's a changed page bitmap file and
578 return file sequence and start LSN name components if it is. If is not,
579 the values of output parameters are undefined.
580
581 @return TRUE if a given file is a changed page bitmap file. */
582 static
583 ibool
log_online_is_bitmap_file(const os_file_stat_t * file_info,ulong * bitmap_file_seq_num,lsn_t * bitmap_file_start_lsn)584 log_online_is_bitmap_file(
585 /*======================*/
586 const os_file_stat_t* file_info, /*!<in: file to
587 check */
588 ulong* bitmap_file_seq_num, /*!<out: bitmap file
589 sequence number */
590 lsn_t* bitmap_file_start_lsn) /*!<out: bitmap file
591 start LSN */
592 {
593 char stem[FN_REFLEN];
594
595 ut_ad (strlen(file_info->name) < OS_FILE_MAX_PATH);
596
597 return ((file_info->type == OS_FILE_TYPE_FILE
598 || file_info->type == OS_FILE_TYPE_LINK)
599 && (sscanf(file_info->name, "%[a-z_]%lu_" LSN_PF ".xdb", stem,
600 bitmap_file_seq_num, bitmap_file_start_lsn) == 3)
601 && (!strcmp(stem, bmp_file_name_stem)));
602 }
603
604 /** Initialize the constant part of the log tracking subsystem */
605 UNIV_INTERN
606 void
log_online_init(void)607 log_online_init(void)
608 {
609 mutex_create(log_bmp_sys_mutex_key, &log_bmp_sys_mutex,
610 SYNC_LOG_ONLINE);
611 }
612
613 /** Initialize the dynamic part of the log tracking subsystem */
614 UNIV_INTERN
615 void
log_online_read_init(void)616 log_online_read_init(void)
617 {
618 ibool success;
619 lsn_t tracking_start_lsn
620 = ut_max(log_sys->last_checkpoint_lsn, MIN_TRACKED_LSN);
621 os_file_dir_t bitmap_dir;
622 os_file_stat_t bitmap_dir_file_info;
623 lsn_t last_file_start_lsn = MIN_TRACKED_LSN;
624 size_t srv_data_home_len;
625
626 /* Bitmap data start and end in a bitmap block must be 8-byte
627 aligned. */
628 compile_time_assert(MODIFIED_PAGE_BLOCK_BITMAP % 8 == 0);
629 compile_time_assert(MODIFIED_PAGE_BLOCK_BITMAP_LEN % 8 == 0);
630
631 ut_ad(srv_track_changed_pages);
632
633 log_bmp_sys = static_cast<log_bitmap_struct *>
634 (ut_malloc(sizeof(*log_bmp_sys)));
635 log_bmp_sys->read_buf_ptr = static_cast<byte *>
636 (ut_malloc(FOLLOW_SCAN_SIZE + OS_FILE_LOG_BLOCK_SIZE));
637 log_bmp_sys->read_buf = static_cast<byte *>
638 (ut_align(log_bmp_sys->read_buf_ptr, OS_FILE_LOG_BLOCK_SIZE));
639
640 /* Initialize bitmap file directory from srv_data_home and add a path
641 separator if needed. */
642 srv_data_home_len = strlen(srv_data_home);
643 ut_a (srv_data_home_len < FN_REFLEN);
644 strcpy(log_bmp_sys->bmp_file_home, srv_data_home);
645 if (srv_data_home_len
646 && log_bmp_sys->bmp_file_home[srv_data_home_len - 1]
647 != SRV_PATH_SEPARATOR) {
648
649 ut_a (srv_data_home_len < FN_REFLEN - 1);
650 log_bmp_sys->bmp_file_home[srv_data_home_len]
651 = SRV_PATH_SEPARATOR;
652 log_bmp_sys->bmp_file_home[srv_data_home_len + 1] = '\0';
653 }
654
655 /* Enumerate existing bitmap files to either open the last one to get
656 the last tracked LSN either to find that there are none and start
657 tracking from scratch. */
658 log_bmp_sys->out.name[0] = '\0';
659 log_bmp_sys->out_seq_num = 0;
660
661 bitmap_dir = os_file_opendir(log_bmp_sys->bmp_file_home, TRUE);
662 ut_a(bitmap_dir);
663 while (!os_file_readdir_next_file(log_bmp_sys->bmp_file_home,
664 bitmap_dir, &bitmap_dir_file_info)) {
665
666 ulong file_seq_num;
667 lsn_t file_start_lsn;
668
669 if (!log_online_is_bitmap_file(&bitmap_dir_file_info,
670 &file_seq_num,
671 &file_start_lsn)) {
672 continue;
673 }
674
675 if (file_seq_num > log_bmp_sys->out_seq_num
676 && bitmap_dir_file_info.size > 0) {
677 log_bmp_sys->out_seq_num = file_seq_num;
678 last_file_start_lsn = file_start_lsn;
679 /* No dir component (log_bmp_sys->bmp_file_home) here,
680 because that's the cwd */
681 strncpy(log_bmp_sys->out.name,
682 bitmap_dir_file_info.name, FN_REFLEN - 1);
683 log_bmp_sys->out.name[FN_REFLEN - 1] = '\0';
684 }
685 }
686
687 if (os_file_closedir(bitmap_dir)) {
688 os_file_get_last_error(TRUE);
689 ib_logf(IB_LOG_LEVEL_ERROR, "cannot close \'%s\'",
690 log_bmp_sys->bmp_file_home);
691 exit(1);
692 }
693
694 if (!log_bmp_sys->out_seq_num) {
695 log_bmp_sys->out_seq_num = 1;
696 log_online_make_bitmap_name(0);
697 }
698
699 log_bmp_sys->modified_pages = rbt_create(MODIFIED_PAGE_BLOCK_SIZE,
700 log_online_compare_bmp_keys);
701 log_bmp_sys->page_free_list = NULL;
702
703 log_bmp_sys->out.file
704 = os_file_create_simple_no_error_handling
705 (innodb_file_bmp_key, log_bmp_sys->out.name, OS_FILE_OPEN,
706 OS_FILE_READ_WRITE_CACHED, &success);
707
708 if (!success) {
709
710 /* New file, tracking from scratch */
711 if (!log_online_start_bitmap_file()) {
712 exit(1);
713 }
714 }
715 else {
716
717 /* Read the last tracked LSN from the last file */
718 lsn_t last_tracked_lsn;
719 lsn_t file_start_lsn;
720
721 log_bmp_sys->out.size
722 = os_file_get_size(log_bmp_sys->out.file);
723 log_bmp_sys->out.offset = log_bmp_sys->out.size;
724
725 if (log_bmp_sys->out.offset % MODIFIED_PAGE_BLOCK_SIZE != 0) {
726
727 ib_logf(IB_LOG_LEVEL_WARN,
728 "truncated block detected in \'%s\' at offset "
729 UINT64PF,
730 log_bmp_sys->out.name,
731 log_bmp_sys->out.offset);
732 log_bmp_sys->out.offset -=
733 log_bmp_sys->out.offset
734 % MODIFIED_PAGE_BLOCK_SIZE;
735 }
736
737 last_tracked_lsn = log_online_read_last_tracked_lsn();
738 /* Do not rotate if we truncated the file to zero length - we
739 can just start writing there */
740 const bool need_rotate = (last_tracked_lsn != 0);
741 if (!last_tracked_lsn) {
742
743 last_tracked_lsn = last_file_start_lsn;
744 }
745
746 /* Start a new file. Choose the LSN value in its name based on
747 if we can retrack any missing data. */
748 if (log_online_can_track_missing(last_tracked_lsn,
749 tracking_start_lsn)) {
750 file_start_lsn = last_tracked_lsn;
751 } else {
752 file_start_lsn = tracking_start_lsn;
753 }
754
755 if (need_rotate
756 && !log_online_rotate_bitmap_file(file_start_lsn)) {
757
758 exit(1);
759 }
760
761 if (last_tracked_lsn < tracking_start_lsn) {
762
763 log_online_track_missing_on_startup
764 (last_tracked_lsn, tracking_start_lsn);
765 return;
766 }
767
768 if (last_tracked_lsn > tracking_start_lsn) {
769
770 ib_logf(IB_LOG_LEVEL_WARN,
771 "last tracked LSN is " LSN_PF ", but the last "
772 "checkpoint LSN is " LSN_PF ". The "
773 "tracking-based incremental backups will work "
774 "only from the latter LSN!",
775 last_tracked_lsn, tracking_start_lsn);
776 }
777
778 }
779
780 ib_logf(IB_LOG_LEVEL_INFO, "starting tracking changed pages from LSN "
781 LSN_PF, tracking_start_lsn);
782 log_bmp_sys->start_lsn = tracking_start_lsn;
783 log_set_tracked_lsn(tracking_start_lsn);
784 }
785
786 /** Shut down the dynamic part of the log tracking subsystem */
787 UNIV_INTERN
788 void
log_online_read_shutdown(void)789 log_online_read_shutdown(void)
790 {
791 mutex_enter(&log_bmp_sys_mutex);
792
793 srv_track_changed_pages = FALSE;
794
795 ib_rbt_node_t *free_list_node = log_bmp_sys->page_free_list;
796
797 if (!os_file_is_invalid(log_bmp_sys->out.file)) {
798 os_file_close(log_bmp_sys->out.file);
799 os_file_mark_invalid(&log_bmp_sys->out.file);
800 }
801
802 rbt_free(log_bmp_sys->modified_pages);
803
804 while (free_list_node) {
805 ib_rbt_node_t *next = free_list_node->left;
806 ut_free(free_list_node);
807 free_list_node = next;
808 }
809
810 ut_free(log_bmp_sys->read_buf_ptr);
811 ut_free(log_bmp_sys);
812 log_bmp_sys = NULL;
813
814 srv_redo_log_thread_started = false;
815
816 mutex_exit(&log_bmp_sys_mutex);
817 }
818
819 /** Shut down the constant part of the log tracking subsystem */
820 UNIV_INTERN
821 void
log_online_shutdown(void)822 log_online_shutdown(void)
823 {
824 mutex_free(&log_bmp_sys_mutex);
825 }
826
827 /*********************************************************************//**
828 For the given minilog record type determine if the record has (space; page)
829 associated with it.
830 @return TRUE if the record has (space; page) in it */
831 static
832 ibool
log_online_rec_has_page(byte type)833 log_online_rec_has_page(
834 /*====================*/
835 byte type) /*!<in: the minilog record type */
836 {
837 return type != MLOG_MULTI_REC_END && type != MLOG_DUMMY_RECORD;
838 }
839
840 /*********************************************************************//**
841 Check if a page field for a given log record type actually contains a page
842 id. It does not for file operations and MLOG_LSN.
843 @return TRUE if page field contains actual page id, FALSE otherwise */
844 static
845 ibool
log_online_rec_page_means_page(byte type)846 log_online_rec_page_means_page(
847 /*===========================*/
848 byte type) /*!<in: log record type */
849 {
850 return log_online_rec_has_page(type)
851 #ifdef UNIV_LOG_LSN_DEBUG
852 && type != MLOG_LSN
853 #endif
854 && type != MLOG_FILE_CREATE
855 && type != MLOG_FILE_RENAME
856 && type != MLOG_FILE_DELETE
857 && type != MLOG_FILE_CREATE2;
858 }
859
860 /*********************************************************************//**
861 Parse the log data in the parse buffer for the (space, page) pairs and add
862 them to the modified page set as necessary. Removes the fully-parsed records
863 from the buffer. If an incomplete record is found, moves it to the end of the
864 buffer. */
865 static
866 void
log_online_parse_redo_log(void)867 log_online_parse_redo_log(void)
868 /*===========================*/
869 {
870 ut_ad(mutex_own(&log_bmp_sys_mutex));
871
872 byte *ptr = log_bmp_sys->parse_buf;
873 byte *end = log_bmp_sys->parse_buf_end;
874 ulint len = 0;
875
876 while (ptr != end
877 && log_bmp_sys->next_parse_lsn < log_bmp_sys->end_lsn) {
878
879 byte type;
880 ulint space;
881 ulint page_no;
882 byte* body;
883
884 /* recv_sys is not initialized, so on corrupt log we will
885 SIGSEGV. But the log of a live database should not be
886 corrupt. */
887 len = recv_parse_log_rec(ptr, end, &type, &space, &page_no,
888 &body);
889 if (len > 0) {
890
891 if (log_online_rec_page_means_page(type)) {
892
893 ut_a(len >= 3);
894 log_online_set_page_bit(space, page_no);
895 }
896
897 ptr += len;
898 ut_ad(ptr <= end);
899 log_bmp_sys->next_parse_lsn
900 = recv_calc_lsn_on_data_add
901 (log_bmp_sys->next_parse_lsn, len);
902 }
903 else {
904
905 /* Incomplete log record. Shift it to the
906 beginning of the parse buffer and leave it to be
907 completed on the next read. */
908 ut_memmove(log_bmp_sys->parse_buf, ptr, end - ptr);
909 log_bmp_sys->parse_buf_end
910 = log_bmp_sys->parse_buf + (end - ptr);
911 ptr = end;
912 }
913 }
914
915 if (len > 0) {
916
917 log_bmp_sys->parse_buf_end = log_bmp_sys->parse_buf;
918 }
919 }
920
921 /*********************************************************************//**
922 Check the log block checksum.
923 @return TRUE if the log block checksum is OK, FALSE otherwise. */
924 static
925 ibool
log_online_is_valid_log_seg(const byte * log_block)926 log_online_is_valid_log_seg(
927 /*========================*/
928 const byte* log_block) /*!< in: read log data */
929 {
930 ibool checksum_is_ok
931 = log_block_checksum_is_ok_or_old_format(log_block);
932
933 if (!checksum_is_ok) {
934
935 ib_logf(IB_LOG_LEVEL_ERROR,
936 "log block checksum mismatch: expected " ULINTPF ", "
937 "calculated checksum " ULINTPF,
938 log_block_get_checksum(log_block),
939 log_block_calc_checksum(log_block));
940 }
941
942 return checksum_is_ok;
943 }
944
945 /*********************************************************************//**
946 Copy new log data to the parse buffer while skipping log block header,
947 trailer and already parsed data. */
948 static
949 void
log_online_add_to_parse_buf(const byte * log_block,ulint data_len,ulint skip_len)950 log_online_add_to_parse_buf(
951 /*========================*/
952 const byte* log_block, /*!< in: read log data */
953 ulint data_len, /*!< in: length of read log data */
954 ulint skip_len) /*!< in: how much of log data to
955 skip */
956 {
957 ut_ad(mutex_own(&log_bmp_sys_mutex));
958
959 ulint start_offset = skip_len ? skip_len : LOG_BLOCK_HDR_SIZE;
960 ulint end_offset
961 = (data_len == OS_FILE_LOG_BLOCK_SIZE)
962 ? data_len - LOG_BLOCK_TRL_SIZE
963 : data_len;
964 ulint actual_data_len = (end_offset >= start_offset)
965 ? end_offset - start_offset : 0;
966
967 ut_memcpy(log_bmp_sys->parse_buf_end, log_block + start_offset,
968 actual_data_len);
969
970 log_bmp_sys->parse_buf_end += actual_data_len;
971
972 ut_a(log_bmp_sys->parse_buf_end - log_bmp_sys->parse_buf
973 <= RECV_PARSING_BUF_SIZE);
974 }
975
976 /*********************************************************************//**
977 Parse the log block: first copies the read log data to the parse buffer while
978 skipping log block header, trailer and already parsed data. Then it actually
979 parses the log to add to the modified page bitmap. */
980 static
981 void
log_online_parse_redo_log_block(const byte * log_block,ulint skip_already_parsed_len)982 log_online_parse_redo_log_block(
983 /*============================*/
984 const byte* log_block, /*!< in: read log data */
985 ulint skip_already_parsed_len) /*!< in: how many bytes of
986 log data should be skipped as
987 they were parsed before */
988 {
989 ut_ad(mutex_own(&log_bmp_sys_mutex));
990
991 ulint block_data_len = log_block_get_data_len(log_block);
992
993 ut_ad(block_data_len % OS_FILE_LOG_BLOCK_SIZE == 0
994 || block_data_len < OS_FILE_LOG_BLOCK_SIZE);
995
996 log_online_add_to_parse_buf(log_block, block_data_len,
997 skip_already_parsed_len);
998 log_online_parse_redo_log();
999 }
1000
1001 /*********************************************************************//**
1002 Read and parse one redo log chunk and updates the modified page bitmap. */
1003 static
1004 void
log_online_follow_log_seg(log_group_t * group,lsn_t block_start_lsn,lsn_t block_end_lsn)1005 log_online_follow_log_seg(
1006 /*======================*/
1007 log_group_t* group, /*!< in: the log group to use */
1008 lsn_t block_start_lsn, /*!< in: the LSN to read from */
1009 lsn_t block_end_lsn) /*!< in: the LSN to read to */
1010 {
1011 ut_ad(mutex_own(&log_bmp_sys_mutex));
1012
1013 /* Pointer to the current OS_FILE_LOG_BLOCK-sized chunk of the read log
1014 data to parse */
1015 byte* log_block = log_bmp_sys->read_buf;
1016 byte* log_block_end = log_bmp_sys->read_buf
1017 + (block_end_lsn - block_start_lsn);
1018
1019 mutex_enter(&log_sys->mutex);
1020 log_group_read_log_seg(LOG_RECOVER, log_bmp_sys->read_buf,
1021 group, block_start_lsn, block_end_lsn, TRUE);
1022 /* log_group_read_log_seg will release the log_sys->mutex for us */
1023
1024 while (log_block < log_block_end
1025 && log_bmp_sys->next_parse_lsn < log_bmp_sys->end_lsn) {
1026
1027 /* How many bytes of log data should we skip in the current log
1028 block. Skipping is necessary because we round down the next
1029 parse LSN thus it is possible to read the already-processed log
1030 data many times */
1031 ulint skip_already_parsed_len = 0;
1032
1033 if (!log_online_is_valid_log_seg(log_block)) {
1034 break;
1035 }
1036
1037 if ((block_start_lsn <= log_bmp_sys->next_parse_lsn)
1038 && (block_start_lsn + OS_FILE_LOG_BLOCK_SIZE
1039 > log_bmp_sys->next_parse_lsn)) {
1040
1041 /* The next parse LSN is inside the current block, skip
1042 data preceding it. */
1043 skip_already_parsed_len
1044 = (ulint)(log_bmp_sys->next_parse_lsn
1045 - block_start_lsn);
1046 }
1047 else {
1048
1049 /* If the next parse LSN is not inside the current
1050 block, then the only option is that we have processed
1051 ahead already. */
1052 ut_a(block_start_lsn > log_bmp_sys->next_parse_lsn);
1053 }
1054
1055 /* TODO: merge the copying to the parse buf code with
1056 skip_already_len calculations */
1057 log_online_parse_redo_log_block(log_block,
1058 skip_already_parsed_len);
1059
1060 log_block += OS_FILE_LOG_BLOCK_SIZE;
1061 block_start_lsn += OS_FILE_LOG_BLOCK_SIZE;
1062 }
1063
1064 return;
1065 }
1066
1067 /*********************************************************************//**
1068 Read and parse the redo log in a given group in FOLLOW_SCAN_SIZE-sized
1069 chunks and updates the modified page bitmap. */
1070 static
1071 void
log_online_follow_log_group(log_group_t * group,lsn_t contiguous_lsn)1072 log_online_follow_log_group(
1073 /*========================*/
1074 log_group_t* group, /*!< in: the log group to use */
1075 lsn_t contiguous_lsn) /*!< in: the LSN of log block start
1076 containing the log_parse_start_lsn */
1077 {
1078 ut_ad(mutex_own(&log_bmp_sys_mutex));
1079
1080 lsn_t block_start_lsn = contiguous_lsn;
1081 lsn_t block_end_lsn;
1082
1083 log_bmp_sys->next_parse_lsn = log_bmp_sys->start_lsn;
1084 log_bmp_sys->parse_buf_end = log_bmp_sys->parse_buf;
1085
1086 do {
1087 block_end_lsn = block_start_lsn + FOLLOW_SCAN_SIZE;
1088
1089 log_online_follow_log_seg(group, block_start_lsn,
1090 block_end_lsn);
1091
1092 /* Next parse LSN can become higher than the last read LSN
1093 only in the case when the read LSN falls right on the block
1094 boundary, in which case next parse lsn is bumped to the actual
1095 data LSN on the next (not yet read) block. This assert is
1096 slightly conservative. */
1097 ut_a(log_bmp_sys->next_parse_lsn
1098 <= block_end_lsn + LOG_BLOCK_HDR_SIZE
1099 + LOG_BLOCK_TRL_SIZE);
1100
1101 block_start_lsn = block_end_lsn;
1102 } while (block_end_lsn < log_bmp_sys->end_lsn);
1103
1104 /* Assert that the last read log record is a full one */
1105 ut_a(log_bmp_sys->parse_buf_end == log_bmp_sys->parse_buf);
1106 }
1107
1108 /*********************************************************************//**
1109 Write, flush one bitmap block to disk and advance the output position if
1110 successful.
1111
1112 @return TRUE if page written OK, FALSE if I/O error */
1113 static
1114 ibool
log_online_write_bitmap_page(const byte * block)1115 log_online_write_bitmap_page(
1116 /*=========================*/
1117 const byte *block) /*!< in: block to write */
1118 {
1119 ut_ad(mutex_own(&log_bmp_sys_mutex));
1120
1121 /* Simulate a write error */
1122 DBUG_EXECUTE_IF("bitmap_page_write_error",
1123 {
1124 ulint space_id
1125 = mach_read_from_4(block
1126 + MODIFIED_PAGE_SPACE_ID);
1127 if (space_id > 0) {
1128 ib_logf(IB_LOG_LEVEL_ERROR,
1129 "simulating bitmap write "
1130 "error in "
1131 "log_online_write_bitmap_page "
1132 "for space ID %lu",
1133 space_id);
1134 return FALSE;
1135 }
1136 });
1137
1138 /* A crash injection site that ensures last checkpoint LSN > last
1139 tracked LSN, so that LSN tracking for this interval is tested. */
1140 DBUG_EXECUTE_IF("crash_before_bitmap_write",
1141 {
1142 ulint space_id
1143 = mach_read_from_4(block
1144 + MODIFIED_PAGE_SPACE_ID);
1145 if (space_id > 0)
1146 DBUG_SUICIDE();
1147 });
1148
1149
1150 ibool success = os_file_write(log_bmp_sys->out.name,
1151 log_bmp_sys->out.file, block,
1152 log_bmp_sys->out.offset,
1153 MODIFIED_PAGE_BLOCK_SIZE);
1154 if (UNIV_UNLIKELY(!success)) {
1155
1156 /* The following call prints an error message */
1157 os_file_get_last_error(TRUE);
1158 ib_logf(IB_LOG_LEVEL_ERROR, "failed writing changed page "
1159 "bitmap file \'%s\'", log_bmp_sys->out.name);
1160 return FALSE;
1161 }
1162
1163 success = os_file_flush(log_bmp_sys->out.file);
1164 if (UNIV_UNLIKELY(!success)) {
1165
1166 /* The following call prints an error message */
1167 os_file_get_last_error(TRUE);
1168 ib_logf(IB_LOG_LEVEL_ERROR, "failed flushing changed page "
1169 "bitmap file \'%s\'", log_bmp_sys->out.name);
1170 return FALSE;
1171 }
1172
1173 os_file_advise(log_bmp_sys->out.file, log_bmp_sys->out.offset,
1174 MODIFIED_PAGE_BLOCK_SIZE, OS_FILE_ADVISE_DONTNEED);
1175
1176 log_bmp_sys->out.offset += MODIFIED_PAGE_BLOCK_SIZE;
1177 return TRUE;
1178 }
1179
1180 /*********************************************************************//**
1181 Append the current changed page bitmap to the bitmap file. Clears the
1182 bitmap tree and recycles its nodes to the free list.
1183
1184 @return TRUE if bitmap written OK, FALSE if I/O error*/
1185 static
1186 ibool
log_online_write_bitmap(void)1187 log_online_write_bitmap(void)
1188 /*=========================*/
1189 {
1190 ut_ad(mutex_own(&log_bmp_sys_mutex));
1191
1192 if (log_bmp_sys->out.offset >= srv_max_bitmap_file_size) {
1193 if (!log_online_rotate_bitmap_file(log_bmp_sys->start_lsn)) {
1194 return FALSE;
1195 }
1196 }
1197
1198 ib_rbt_node_t *bmp_tree_node
1199 = (ib_rbt_node_t *)rbt_first(log_bmp_sys->modified_pages);
1200 const ib_rbt_node_t * const last_bmp_tree_node
1201 = rbt_last(log_bmp_sys->modified_pages);
1202
1203 ibool success = TRUE;
1204
1205 while (bmp_tree_node) {
1206
1207 byte *page = rbt_value(byte, bmp_tree_node);
1208
1209 /* In case of a bitmap page write error keep on looping over
1210 the tree to reclaim its memory through the free list instead of
1211 returning immediatelly. */
1212 if (UNIV_LIKELY(success)) {
1213 if (bmp_tree_node == last_bmp_tree_node) {
1214 mach_write_to_4(page
1215 + MODIFIED_PAGE_IS_LAST_BLOCK,
1216 1);
1217 }
1218
1219 mach_write_to_8(page + MODIFIED_PAGE_START_LSN,
1220 log_bmp_sys->start_lsn);
1221 mach_write_to_8(page + MODIFIED_PAGE_END_LSN,
1222 log_bmp_sys->end_lsn);
1223 mach_write_to_4(page + MODIFIED_PAGE_BLOCK_CHECKSUM,
1224 log_online_calc_checksum(page));
1225
1226 success = log_online_write_bitmap_page(page);
1227 }
1228
1229 bmp_tree_node->left = log_bmp_sys->page_free_list;
1230 log_bmp_sys->page_free_list = bmp_tree_node;
1231
1232 bmp_tree_node = (ib_rbt_node_t*)
1233 rbt_next(log_bmp_sys->modified_pages, bmp_tree_node);
1234
1235 DBUG_EXECUTE_IF("bitmap_page_2_write_error",
1236 if (bmp_tree_node)
1237 {
1238 DBUG_SET("+d,bitmap_page_write_error");
1239 DBUG_SET("-d,bitmap_page_2_write_error");
1240 });
1241 }
1242
1243 rbt_reset(log_bmp_sys->modified_pages);
1244 return success;
1245 }
1246
1247 /*********************************************************************//**
1248 Read and parse the redo log up to last checkpoint LSN to build the changed
1249 page bitmap which is then written to disk.
1250
1251 @return TRUE if log tracking succeeded, FALSE if bitmap write I/O error */
1252 UNIV_INTERN
1253 ibool
log_online_follow_redo_log(void)1254 log_online_follow_redo_log(void)
1255 /*============================*/
1256 {
1257 lsn_t contiguous_start_lsn;
1258 log_group_t* group;
1259 ibool result;
1260
1261 ut_ad(!srv_read_only_mode);
1262
1263 if (!srv_track_changed_pages)
1264 return TRUE;
1265
1266 DEBUG_SYNC_C("log_online_follow_redo_log");
1267
1268 mutex_enter(&log_bmp_sys_mutex);
1269
1270 if (!srv_track_changed_pages) {
1271 mutex_exit(&log_bmp_sys_mutex);
1272 return TRUE;
1273 }
1274
1275 /* Grab the LSN of the last checkpoint, we will parse up to it */
1276 mutex_enter(&(log_sys->mutex));
1277 log_bmp_sys->end_lsn = log_sys->last_checkpoint_lsn;
1278 mutex_exit(&(log_sys->mutex));
1279
1280 if (log_bmp_sys->end_lsn == log_bmp_sys->start_lsn) {
1281 mutex_exit(&log_bmp_sys_mutex);
1282 return TRUE;
1283 }
1284
1285 group = UT_LIST_GET_FIRST(log_sys->log_groups);
1286 ut_a(group);
1287
1288 contiguous_start_lsn = ut_uint64_align_down(log_bmp_sys->start_lsn,
1289 OS_FILE_LOG_BLOCK_SIZE);
1290
1291 while (group) {
1292 log_online_follow_log_group(group, contiguous_start_lsn);
1293 group = UT_LIST_GET_NEXT(log_groups, group);
1294 }
1295
1296 result = log_online_write_bitmap();
1297 log_bmp_sys->start_lsn = log_bmp_sys->end_lsn;
1298 log_set_tracked_lsn(log_bmp_sys->start_lsn);
1299
1300 mutex_exit(&log_bmp_sys_mutex);
1301 return result;
1302 }
1303
1304 /*********************************************************************//**
1305 Diagnose a bitmap file range setup failure and free the partially-initialized
1306 bitmap file range. */
1307 UNIV_COLD
1308 static
1309 void
log_online_diagnose_inconsistent_dir(log_online_bitmap_file_range_t * bitmap_files)1310 log_online_diagnose_inconsistent_dir(
1311 /*=================================*/
1312 log_online_bitmap_file_range_t *bitmap_files) /*!<in/out: bitmap file
1313 range */
1314 {
1315 ib_logf(IB_LOG_LEVEL_WARN,
1316 "InnoDB: Warning: inconsistent bitmap file "
1317 "directory for a "
1318 "INFORMATION_SCHEMA.INNODB_CHANGED_PAGES query");
1319 free(bitmap_files->files);
1320 }
1321
1322 /*********************************************************************//**
1323 List the bitmap files in srv_data_home and setup their range that contains the
1324 specified LSN interval. This range, if non-empty, will start with a file that
1325 has the greatest LSN equal to or less than the start LSN and will include all
1326 the files up to the one with the greatest LSN less than the end LSN. Caller
1327 must free bitmap_files->files when done if bitmap_files set to non-NULL and
1328 this function returned TRUE. Field bitmap_files->count might be set to a
1329 larger value than the actual count of the files, and space for the unused array
1330 slots will be allocated but cleared to zeroes.
1331
1332 @return TRUE if succeeded
1333 */
1334 static
1335 ibool
log_online_setup_bitmap_file_range(log_online_bitmap_file_range_t * bitmap_files,lsn_t range_start,lsn_t range_end)1336 log_online_setup_bitmap_file_range(
1337 /*===============================*/
1338 log_online_bitmap_file_range_t *bitmap_files, /*!<in/out: bitmap file
1339 range */
1340 lsn_t range_start, /*!<in: start LSN */
1341 lsn_t range_end) /*!<in: end LSN */
1342 {
1343 os_file_dir_t bitmap_dir;
1344 os_file_stat_t bitmap_dir_file_info;
1345 ulong first_file_seq_num = ULONG_MAX;
1346 ulong last_file_seq_num = 0;
1347 lsn_t first_file_start_lsn = LSN_MAX;
1348
1349 ut_ad(range_end >= range_start);
1350
1351 bitmap_files->count = 0;
1352 bitmap_files->files = NULL;
1353
1354 /* 1st pass: size the info array */
1355
1356 bitmap_dir = os_file_opendir(srv_data_home, FALSE);
1357 if (UNIV_UNLIKELY(!bitmap_dir)) {
1358
1359 ib_logf(IB_LOG_LEVEL_ERROR,
1360 "failed to open bitmap directory \'%s\'",
1361 srv_data_home);
1362 return FALSE;
1363 }
1364
1365 while (!os_file_readdir_next_file(srv_data_home, bitmap_dir,
1366 &bitmap_dir_file_info)) {
1367
1368 ulong file_seq_num;
1369 lsn_t file_start_lsn;
1370
1371 if (!log_online_is_bitmap_file(&bitmap_dir_file_info,
1372 &file_seq_num,
1373 &file_start_lsn)
1374 || file_start_lsn >= range_end) {
1375
1376 continue;
1377 }
1378
1379 if (file_seq_num > last_file_seq_num) {
1380
1381 last_file_seq_num = file_seq_num;
1382 }
1383
1384 if (file_start_lsn >= range_start
1385 || file_start_lsn == first_file_start_lsn
1386 || first_file_start_lsn > range_start) {
1387
1388 /* A file that falls into the range */
1389
1390 if (file_start_lsn < first_file_start_lsn) {
1391
1392 first_file_start_lsn = file_start_lsn;
1393 }
1394 if (file_seq_num < first_file_seq_num) {
1395
1396 first_file_seq_num = file_seq_num;
1397 }
1398 } else if (file_start_lsn > first_file_start_lsn) {
1399
1400 /* A file that has LSN closer to the range start
1401 but smaller than it, replacing another such file */
1402 first_file_start_lsn = file_start_lsn;
1403 first_file_seq_num = file_seq_num;
1404 }
1405 }
1406
1407 if (UNIV_UNLIKELY(os_file_closedir(bitmap_dir))) {
1408
1409 os_file_get_last_error(TRUE);
1410 ib_logf(IB_LOG_LEVEL_ERROR, "cannot close \'%s\'",
1411 srv_data_home);
1412 return FALSE;
1413 }
1414
1415 if (first_file_seq_num == ULONG_MAX && last_file_seq_num == 0) {
1416
1417 bitmap_files->count = 0;
1418 return TRUE;
1419 }
1420
1421 bitmap_files->count = last_file_seq_num - first_file_seq_num + 1;
1422
1423 DEBUG_SYNC_C("setup_bitmap_range_middle");
1424
1425 /* 2nd pass: get the file names in the file_seq_num order */
1426
1427 bitmap_dir = os_file_opendir(srv_data_home, FALSE);
1428 if (UNIV_UNLIKELY(!bitmap_dir)) {
1429
1430 ib_logf(IB_LOG_LEVEL_ERROR,
1431 "failed to open bitmap directory \'%s\'",
1432 srv_data_home);
1433 return FALSE;
1434 }
1435
1436 bitmap_files->files
1437 = static_cast<log_online_bitmap_file_range_struct::files_t *>
1438 (ut_malloc(bitmap_files->count
1439 * sizeof(bitmap_files->files[0])));
1440 memset(bitmap_files->files, 0,
1441 bitmap_files->count * sizeof(bitmap_files->files[0]));
1442
1443 while (!os_file_readdir_next_file(srv_data_home, bitmap_dir,
1444 &bitmap_dir_file_info)) {
1445
1446 ulong file_seq_num;
1447 lsn_t file_start_lsn;
1448 size_t array_pos;
1449
1450 if (!log_online_is_bitmap_file(&bitmap_dir_file_info,
1451 &file_seq_num,
1452 &file_start_lsn)
1453 || file_start_lsn >= range_end
1454 || file_start_lsn < first_file_start_lsn) {
1455
1456 continue;
1457 }
1458
1459 array_pos = file_seq_num - first_file_seq_num;
1460 if (UNIV_UNLIKELY(array_pos >= bitmap_files->count)) {
1461
1462 log_online_diagnose_inconsistent_dir(bitmap_files);
1463 return FALSE;
1464 }
1465
1466
1467 if (file_seq_num > bitmap_files->files[array_pos].seq_num) {
1468
1469 bitmap_files->files[array_pos].seq_num = file_seq_num;
1470 memcpy(bitmap_files->files[array_pos].name,
1471 bitmap_dir_file_info.name, FN_REFLEN);
1472 bitmap_files->files[array_pos].name[FN_REFLEN - 1]
1473 = '\0';
1474 bitmap_files->files[array_pos].start_lsn
1475 = file_start_lsn;
1476 }
1477 }
1478
1479 if (UNIV_UNLIKELY(os_file_closedir(bitmap_dir))) {
1480
1481 os_file_get_last_error(TRUE);
1482 ib_logf(IB_LOG_LEVEL_ERROR, "cannot close \'%s\'",
1483 srv_data_home);
1484 free(bitmap_files->files);
1485 return FALSE;
1486 }
1487
1488 if (!bitmap_files->files[0].seq_num
1489 || bitmap_files->files[0].seq_num != first_file_seq_num) {
1490
1491 log_online_diagnose_inconsistent_dir(bitmap_files);
1492 return FALSE;
1493 }
1494
1495 {
1496 size_t i;
1497 for (i = 1; i < bitmap_files->count; i++) {
1498 if (!bitmap_files->files[i].seq_num) {
1499 break;
1500 }
1501 if ((bitmap_files->files[i].seq_num
1502 <= bitmap_files->files[i - 1].seq_num)
1503 || (bitmap_files->files[i].start_lsn
1504 < bitmap_files->files[i - 1].start_lsn)) {
1505
1506 log_online_diagnose_inconsistent_dir(
1507 bitmap_files);
1508 return FALSE;
1509 }
1510 }
1511 }
1512
1513 return TRUE;
1514 }
1515
1516 /****************************************************************//**
1517 Open a bitmap file for reading.
1518
1519 @return TRUE if opened successfully */
1520 static
1521 ibool
log_online_open_bitmap_file_read_only(const char * name,log_online_bitmap_file_t * bitmap_file)1522 log_online_open_bitmap_file_read_only(
1523 /*==================================*/
1524 const char* name, /*!<in: bitmap file
1525 name without directory,
1526 which is assumed to be
1527 srv_data_home */
1528 log_online_bitmap_file_t* bitmap_file) /*!<out: opened bitmap
1529 file */
1530 {
1531 ibool success = FALSE;
1532 size_t srv_data_home_len;
1533
1534 ut_ad(name[0] != '\0');
1535
1536 srv_data_home_len = strlen(srv_data_home);
1537 if (srv_data_home_len
1538 && srv_data_home[srv_data_home_len-1]
1539 != SRV_PATH_SEPARATOR) {
1540 ut_snprintf(bitmap_file->name, sizeof(bitmap_file->name), "%s%c%s",
1541 srv_data_home, SRV_PATH_SEPARATOR, name);
1542 } else {
1543 ut_snprintf(bitmap_file->name, sizeof(bitmap_file->name), "%s%s",
1544 srv_data_home, name);
1545 }
1546 bitmap_file->file
1547 = os_file_create_simple_no_error_handling(innodb_file_bmp_key,
1548 bitmap_file->name,
1549 OS_FILE_OPEN,
1550 OS_FILE_READ_ONLY,
1551 &success);
1552 if (UNIV_UNLIKELY(!success)) {
1553
1554 /* Here and below assume that bitmap file names do not
1555 contain apostrophes, thus no need for ut_print_filename(). */
1556 ib_logf(IB_LOG_LEVEL_WARN,
1557 "error opening the changed page bitmap \'%s\'",
1558 bitmap_file->name);
1559 return FALSE;
1560 }
1561
1562 bitmap_file->size = os_file_get_size(bitmap_file->file);
1563 bitmap_file->offset = 0;
1564
1565 os_file_advise(bitmap_file->file, 0, 0, OS_FILE_ADVISE_SEQUENTIAL);
1566 os_file_advise(bitmap_file->file, 0, 0, OS_FILE_ADVISE_NOREUSE);
1567
1568 return TRUE;
1569 }
1570
1571 /****************************************************************//**
1572 Diagnose one or both of the following situations if we read close to
1573 the end of bitmap file:
1574 1) Warn if the remainder of the file is less than one page.
1575 2) Error if we cannot read any more full pages but the last read page
1576 did not have the last-in-run flag set.
1577
1578 @return FALSE for the error */
1579 static
1580 ibool
log_online_diagnose_bitmap_eof(const log_online_bitmap_file_t * bitmap_file,ibool last_page_in_run)1581 log_online_diagnose_bitmap_eof(
1582 /*===========================*/
1583 const log_online_bitmap_file_t* bitmap_file, /*!< in: bitmap file */
1584 ibool last_page_in_run)/*!< in: "last page in
1585 run" flag value in the
1586 last read page */
1587 {
1588 /* Check if we are too close to EOF to read a full page */
1589 if ((bitmap_file->size < MODIFIED_PAGE_BLOCK_SIZE)
1590 || (bitmap_file->offset
1591 > bitmap_file->size - MODIFIED_PAGE_BLOCK_SIZE)) {
1592
1593 if (UNIV_UNLIKELY(bitmap_file->offset != bitmap_file->size)) {
1594
1595 /* If we are not at EOF and we have less than one page
1596 to read, it's junk. This error is not fatal in
1597 itself. */
1598
1599 ib_logf(IB_LOG_LEVEL_WARN,
1600 "junk at the end of changed page bitmap file "
1601 "\'%s\'.", bitmap_file->name);
1602 }
1603
1604 if (UNIV_UNLIKELY(!last_page_in_run)) {
1605
1606 /* We are at EOF but the last read page did not finish
1607 a run */
1608 /* It's a "Warning" here because it's not a fatal error
1609 for the whole server */
1610 ib_logf(IB_LOG_LEVEL_WARN,
1611 "changed page bitmap file \'%s\', size "
1612 UINT64PF " bytes, does not "
1613 "contain a complete run at the next read "
1614 "offset " UINT64PF,
1615 bitmap_file->name, bitmap_file->size,
1616 bitmap_file->offset);
1617 return FALSE;
1618 }
1619 }
1620 return TRUE;
1621 }
1622
1623 /*********************************************************************//**
1624 Initialize the log bitmap iterator for a given range. The records are
1625 processed at a bitmap block granularity, i.e. all the records in the same block
1626 share the same start and end LSN values, the exact LSN of each record is
1627 unavailable (nor is it defined for blocks that are touched more than once in
1628 the LSN interval contained in the block). Thus min_lsn and max_lsn should be
1629 set at block boundaries or bigger, otherwise the records at the 1st and the
1630 last blocks will not be returned. Also note that there might be returned
1631 records with LSN < min_lsn, as min_lsn is used to select the correct starting
1632 file but not block.
1633
1634 @return TRUE if the iterator is initialized OK, FALSE otherwise. */
1635 UNIV_INTERN
1636 ibool
log_online_bitmap_iterator_init(log_bitmap_iterator_t * i,lsn_t min_lsn,lsn_t max_lsn)1637 log_online_bitmap_iterator_init(
1638 /*============================*/
1639 log_bitmap_iterator_t *i, /*!<in/out: iterator */
1640 lsn_t min_lsn,/*!< in: start LSN */
1641 lsn_t max_lsn)/*!< in: end LSN */
1642 {
1643 ut_a(i);
1644
1645 i->max_lsn = max_lsn;
1646
1647 if (UNIV_UNLIKELY(min_lsn > max_lsn)) {
1648
1649 /* Empty range */
1650 i->in_files.count = 0;
1651 i->in_files.files = NULL;
1652 os_file_mark_invalid(&i->in.file);
1653 i->page = NULL;
1654 i->failed = FALSE;
1655 return TRUE;
1656 }
1657
1658 if (!log_online_setup_bitmap_file_range(&i->in_files, min_lsn,
1659 max_lsn)) {
1660
1661 i->failed = TRUE;
1662 return FALSE;
1663 }
1664
1665 i->in_i = 0;
1666
1667 if (i->in_files.count == 0) {
1668
1669 /* Empty range */
1670 os_file_mark_invalid(&i->in.file);
1671 i->page = NULL;
1672 i->failed = FALSE;
1673 return TRUE;
1674 }
1675
1676 /* Open the 1st bitmap file */
1677 if (UNIV_UNLIKELY(!log_online_open_bitmap_file_read_only(
1678 i->in_files.files[i->in_i].name,
1679 &i->in))) {
1680
1681 i->in_i = i->in_files.count;
1682 free(i->in_files.files);
1683 i->failed = TRUE;
1684 return FALSE;
1685 }
1686
1687 i->page = static_cast<byte *>(ut_malloc(MODIFIED_PAGE_BLOCK_SIZE));
1688 i->bit_offset = MODIFIED_PAGE_BLOCK_BITMAP_LEN;
1689 i->start_lsn = i->end_lsn = 0;
1690 i->space_id = 0;
1691 i->first_page_id = 0;
1692 i->last_page_in_run = TRUE;
1693 i->changed = FALSE;
1694 i->failed = FALSE;
1695
1696 return TRUE;
1697 }
1698
1699 /*********************************************************************//**
1700 Releases log bitmap iterator. */
1701 UNIV_INTERN
1702 void
log_online_bitmap_iterator_release(log_bitmap_iterator_t * i)1703 log_online_bitmap_iterator_release(
1704 /*===============================*/
1705 log_bitmap_iterator_t *i) /*!<in/out: iterator */
1706 {
1707 ut_a(i);
1708
1709 if (!os_file_is_invalid(i->in.file)) {
1710
1711 os_file_close(i->in.file);
1712 os_file_mark_invalid(&i->in.file);
1713 }
1714 if (i->in_files.files) {
1715
1716 ut_free(i->in_files.files);
1717 }
1718 if (i->page) {
1719
1720 ut_free(i->page);
1721 }
1722 i->failed = TRUE;
1723 }
1724
1725 /*********************************************************************//**
1726 Iterates through bits of saved bitmap blocks.
1727 Sequentially reads blocks from bitmap file(s) and interates through
1728 their bits. Ignores blocks with wrong checksum.
1729 @return TRUE if iteration is successful, FALSE if all bits are iterated. */
1730 UNIV_INTERN
1731 ibool
log_online_bitmap_iterator_next(log_bitmap_iterator_t * i)1732 log_online_bitmap_iterator_next(
1733 /*============================*/
1734 log_bitmap_iterator_t *i) /*!<in/out: iterator */
1735 {
1736 ibool checksum_ok = FALSE;
1737 ibool success;
1738
1739 ut_a(i);
1740
1741 if (UNIV_UNLIKELY(i->in_files.count == 0)) {
1742
1743 return FALSE;
1744 }
1745
1746 if (UNIV_LIKELY(i->bit_offset < MODIFIED_PAGE_BLOCK_BITMAP_LEN))
1747 {
1748 ++i->bit_offset;
1749 i->changed =
1750 IS_BIT_SET(i->page + MODIFIED_PAGE_BLOCK_BITMAP,
1751 i->bit_offset);
1752 return TRUE;
1753 }
1754
1755 if (i->end_lsn >= i->max_lsn && i->last_page_in_run)
1756 return FALSE;
1757
1758 while (!checksum_ok)
1759 {
1760 while (i->in.size < MODIFIED_PAGE_BLOCK_SIZE
1761 || (i->in.offset
1762 > i->in.size - MODIFIED_PAGE_BLOCK_SIZE)) {
1763
1764 /* Advance file */
1765 i->in_i++;
1766 success = os_file_close_no_error_handling(
1767 i->in.file);
1768 os_file_mark_invalid(&i->in.file);
1769 if (UNIV_UNLIKELY(!success)) {
1770
1771 os_file_get_last_error(TRUE);
1772 i->failed = TRUE;
1773 return FALSE;
1774 }
1775
1776 success = log_online_diagnose_bitmap_eof(
1777 &i->in, i->last_page_in_run);
1778 if (UNIV_UNLIKELY(!success)) {
1779
1780 i->failed = TRUE;
1781 return FALSE;
1782
1783 }
1784
1785 if (i->in_i == i->in_files.count) {
1786
1787 return FALSE;
1788 }
1789
1790 if (UNIV_UNLIKELY(i->in_files.files[i->in_i].seq_num
1791 == 0)) {
1792
1793 i->failed = TRUE;
1794 return FALSE;
1795 }
1796
1797 success = log_online_open_bitmap_file_read_only(
1798 i->in_files.files[i->in_i].name,
1799 &i->in);
1800 if (UNIV_UNLIKELY(!success)) {
1801
1802 i->failed = TRUE;
1803 return FALSE;
1804 }
1805 }
1806
1807 success = log_online_read_bitmap_page(&i->in, i->page,
1808 &checksum_ok);
1809 if (UNIV_UNLIKELY(!success)) {
1810
1811 os_file_get_last_error(TRUE);
1812 ib_logf(IB_LOG_LEVEL_WARN,
1813 "failed reading changed page bitmap file "
1814 "\'%s\'", i->in_files.files[i->in_i].name);
1815 i->failed = TRUE;
1816 return FALSE;
1817 }
1818 }
1819
1820 i->start_lsn = mach_read_from_8(i->page + MODIFIED_PAGE_START_LSN);
1821 i->end_lsn = mach_read_from_8(i->page + MODIFIED_PAGE_END_LSN);
1822 i->space_id = mach_read_from_4(i->page + MODIFIED_PAGE_SPACE_ID);
1823 i->first_page_id = mach_read_from_4(i->page
1824 + MODIFIED_PAGE_1ST_PAGE_ID);
1825 i->last_page_in_run = mach_read_from_4(i->page
1826 + MODIFIED_PAGE_IS_LAST_BLOCK);
1827 i->bit_offset = 0;
1828 i->changed = IS_BIT_SET(i->page + MODIFIED_PAGE_BLOCK_BITMAP,
1829 i->bit_offset);
1830
1831 return TRUE;
1832 }
1833
1834 /************************************************************//**
1835 Delete all the bitmap files for data less than the specified LSN.
1836 If called with lsn == 0 (i.e. set by RESET request) or LSN_MAX,
1837 restart the bitmap file sequence, otherwise continue it.
1838
1839 @return FALSE to indicate success, TRUE for failure. */
1840 UNIV_INTERN
1841 ibool
log_online_purge_changed_page_bitmaps(lsn_t lsn)1842 log_online_purge_changed_page_bitmaps(
1843 /*==================================*/
1844 lsn_t lsn) /*!< in: LSN to purge files up to */
1845 {
1846 log_online_bitmap_file_range_t bitmap_files;
1847 size_t i;
1848 ibool result = FALSE;
1849
1850 if (lsn == 0) {
1851 lsn = LSN_MAX;
1852 }
1853
1854 bool log_bmp_sys_inited = false;
1855 if (srv_redo_log_thread_started) {
1856 /* User requests might happen with both enabled and disabled
1857 tracking */
1858 log_bmp_sys_inited = true;
1859 mutex_enter(&log_bmp_sys_mutex);
1860 if (!srv_redo_log_thread_started) {
1861 log_bmp_sys_inited = false;
1862 mutex_exit(&log_bmp_sys_mutex);
1863 }
1864 }
1865
1866 if (!log_online_setup_bitmap_file_range(&bitmap_files, 0, LSN_MAX)) {
1867 if (log_bmp_sys_inited) {
1868 mutex_exit(&log_bmp_sys_mutex);
1869 }
1870 return TRUE;
1871 }
1872
1873 if (srv_redo_log_thread_started && lsn > log_bmp_sys->end_lsn) {
1874 /* If we have to delete the current output file, close it
1875 first. */
1876 os_file_close(log_bmp_sys->out.file);
1877 os_file_mark_invalid(&log_bmp_sys->out.file);
1878 }
1879
1880 for (i = 0; i < bitmap_files.count; i++) {
1881
1882 char full_bmp_file_name[2 * FN_REFLEN + 2];
1883
1884 /* We consider the end LSN of the current bitmap, derived from
1885 the start LSN of the subsequent bitmap file, to determine
1886 whether to remove the current bitmap. Note that bitmap_files
1887 does not contain an entry for the bitmap past the given LSN so
1888 we must check the boundary conditions as well. For example,
1889 consider 1_0.xdb and 2_10.xdb and querying LSN 5. bitmap_files
1890 will only contain 1_0.xdb and we must not delete it since it
1891 represents LSNs 0-9. */
1892 if ((i + 1 == bitmap_files.count
1893 || bitmap_files.files[i + 1].seq_num == 0
1894 || bitmap_files.files[i + 1].start_lsn > lsn)
1895 && (lsn != LSN_MAX)) {
1896
1897 break;
1898 }
1899
1900 /* In some non-trivial cases the sequence of .xdb files may
1901 have gaps. For instance:
1902 ib_modified_log_1_0.xdb
1903 ib_modified_log_2_<mmm>.xdb
1904 ib_modified_log_4_<nnn>.xdb
1905 Adding this check as a safety precaution. */
1906 if (bitmap_files.files[i].name[0] == '\0')
1907 continue;
1908
1909 /* If redo log tracking is enabled, reuse 'bmp_file_home'
1910 from 'log_bmp_sys'. Otherwise, compose the full '.xdb' file
1911 path from 'srv_data_home', adding a path separator if
1912 necessary. */
1913 if (log_bmp_sys != NULL) {
1914 ut_snprintf(full_bmp_file_name,
1915 sizeof(full_bmp_file_name),
1916 "%s%s", log_bmp_sys->bmp_file_home,
1917 bitmap_files.files[i].name);
1918 }
1919 else {
1920 char separator[2] = {0, 0};
1921 const size_t srv_data_home_len =
1922 strlen(srv_data_home);
1923
1924 ut_a(srv_data_home_len < FN_REFLEN);
1925 if (srv_data_home_len != 0 &&
1926 srv_data_home[srv_data_home_len - 1] !=
1927 SRV_PATH_SEPARATOR) {
1928 separator[0] = SRV_PATH_SEPARATOR;
1929 }
1930 ut_snprintf(full_bmp_file_name,
1931 sizeof(full_bmp_file_name), "%s%s%s",
1932 srv_data_home, separator,
1933 bitmap_files.files[i].name);
1934 }
1935
1936 if (!os_file_delete_if_exists(innodb_file_bmp_key,
1937 full_bmp_file_name)) {
1938
1939 os_file_get_last_error(TRUE);
1940 result = TRUE;
1941 break;
1942 }
1943 }
1944
1945 if (log_bmp_sys_inited) {
1946 if (lsn > log_bmp_sys->end_lsn) {
1947 lsn_t new_file_lsn;
1948 if (lsn == LSN_MAX) {
1949 /* RESET restarts the sequence */
1950 log_bmp_sys->out_seq_num = 0;
1951 new_file_lsn = 0;
1952 } else {
1953 new_file_lsn = log_bmp_sys->end_lsn;
1954 }
1955 if (!log_online_rotate_bitmap_file(new_file_lsn)) {
1956 /* If file create failed, stop log tracking */
1957 srv_track_changed_pages = FALSE;
1958 }
1959 }
1960
1961 mutex_exit(&log_bmp_sys_mutex);
1962 }
1963
1964 free(bitmap_files.files);
1965 return result;
1966 }
1967