Skip to content

Commit 7f8121f

Browse files
committed
Make innodb_log_recovery_start settable (for incremental backup)
Also, implement a little more of backup_innodb.
1 parent 857edeb commit 7f8121f

File tree

7 files changed

+150
-24
lines changed

7 files changed

+150
-24
lines changed

mysql-test/suite/sys_vars/r/sysvars_innodb.result

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1027,7 +1027,7 @@ NUMERIC_MIN_VALUE 0
10271027
NUMERIC_MAX_VALUE 18446744073709551615
10281028
NUMERIC_BLOCK_SIZE 0
10291029
ENUM_VALUE_LIST NULL
1030-
READ_ONLY YES
1030+
READ_ONLY NO
10311031
COMMAND_LINE_ARGUMENT REQUIRED
10321032
VARIABLE_NAME INNODB_LOG_RECOVERY_TARGET
10331033
SESSION_VALUE NULL

storage/innobase/buf/buf0flu.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2006,7 +2006,10 @@ inline void log_t::write_checkpoint(lsn_t checkpoint, lsn_t end_lsn) noexcept
20062006
last_checkpoint_lsn= checkpoint;
20072007
this->end_lsn= end_lsn;
20082008
if (!archive)
2009+
{
2010+
archived_checkpoint= checkpoint;
20092011
archived_lsn= end_lsn;
2012+
}
20102013
else if (archive_header_was_reset)
20112014
{
20122015
ut_ad(resize_log.m_file != log.m_file);

storage/innobase/handler/backup_innodb.cc

Lines changed: 110 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,52 +17,153 @@
1717
#include "sql_class.h"
1818
#include "backup_innodb.h"
1919
#include "log0log.h"
20+
#include "srw_lock.h"
21+
#include "fil0fil.h"
22+
#include <vector>
2023

2124
namespace
2225
{
23-
struct InnoDB_backup
26+
class InnoDB_backup
2427
{
28+
/** mutex protecting the queue */
29+
srw_mutex_impl<false> mutex;
30+
2531
/** the original state of innodb_log_archive */
2632
bool was_archived;
2733

28-
void init(THD *thd) noexcept
34+
/** collection of files to be copied */
35+
std::vector<uint32_t> queue;
36+
37+
/** name of the target directory */
38+
const LEX_CSTRING *target;
39+
40+
/** the checkpoint from which the backup starts */
41+
lsn_t checkpoint;
42+
/** end_lsn of the checkpoint at the backup start */
43+
lsn_t checkpoint_end_lsn;
44+
public:
45+
/**
46+
Start of BACKUP SERVER: collect all files to be backed up
47+
@param thd current session
48+
@param target target directory
49+
@return error code
50+
@retval 0 on success
51+
*/
52+
int init(THD *thd, const LEX_CSTRING *target) noexcept
2953
{
3054
mysql_mutex_lock(&LOCK_global_system_variables);
55+
mutex.init();
56+
mutex.wr_lock();
3157
was_archived= log_sys.archive;
32-
log_sys.set_archive(true, thd);
58+
bool fail{log_sys.set_archive(true, thd)};
3359
mysql_mutex_unlock(&LOCK_global_system_variables);
60+
61+
if (!fail)
62+
{
63+
log_sys.latch.wr_lock();
64+
fail= !log_sys.archive;
65+
checkpoint= log_sys.archived_checkpoint;
66+
checkpoint_end_lsn= log_sys.archived_lsn;
67+
log_sys.latch.wr_unlock();
68+
}
69+
70+
if (!fail)
71+
{
72+
this->target= target;
73+
mysql_mutex_lock(&fil_system.mutex);
74+
for (fil_space_t &space : fil_system.space_list)
75+
queue.emplace_back(space.id);
76+
mysql_mutex_unlock(&fil_system.mutex);
77+
}
78+
mutex.wr_unlock();
79+
return fail;
80+
}
81+
82+
/**
83+
Process a file that was collected at init().
84+
This may be invoked from multiple concurrent threads.
85+
@param thd current session
86+
@return number of files remaining, or negative on error
87+
@retval 0 on completion
88+
*/
89+
int step(THD *thd) noexcept
90+
{
91+
uint32_t id= FIL_NULL;
92+
mutex.wr_lock();
93+
size_t size{queue.size()};
94+
if (size)
95+
{
96+
size--;
97+
id= queue.back();
98+
queue.pop_back();
99+
}
100+
mutex.wr_unlock();
101+
102+
if (fil_space_t *space= fil_space_t::get(id))
103+
{
104+
/* TODO: copy the file to target safely, even when there may be
105+
concurrent writes to it */
106+
space->release();
107+
}
108+
109+
size= std::min(size_t{std::numeric_limits<int>::max()}, size);
110+
111+
return int(size);
112+
}
113+
114+
/**
115+
Finish copying and determine the logical time of the backup snapshot.
116+
@param thd current sesssion
117+
@param abort whether BACKUP SERVER was aborted
118+
@return error code
119+
@retval 0 on success
120+
*/
121+
int end(THD *thd, bool abort) noexcept
122+
{
123+
mutex.wr_lock();
124+
if (abort)
125+
queue.clear();
126+
ut_ad(queue.empty());
127+
mutex.wr_unlock();
128+
return 0;
34129
}
35130

131+
/**
132+
After a successful end(), finalize the backup.
133+
@param thd current sesssion
134+
*/
36135
void fini(THD *thd) noexcept
37136
{
38137
mysql_mutex_lock(&LOCK_global_system_variables);
138+
ut_d(mutex.wr_lock());
139+
ut_ad(queue.empty());
140+
ut_d(mutex.wr_unlock());
141+
mutex.destroy();
39142
if (!was_archived)
40143
log_sys.set_archive(false, thd);
41144
mysql_mutex_unlock(&LOCK_global_system_variables);
42145
}
43146
};
44147

148+
/** The backup context */
45149
static InnoDB_backup backup;
46150
}
47151

48152
int innodb_backup_start(THD *thd, const LEX_CSTRING *target) noexcept
49153
{
50-
backup.init(thd);
51-
/* create a work queue of files that must be copied */
52-
return 0;
154+
return backup.init(thd, target);
53155
}
54156

55157
int innodb_backup_step(THD *thd) noexcept
56158
{
57-
return 0;
159+
return backup.step(thd);
58160
}
59161

60162
int innodb_backup_end(THD *thd, bool abort) noexcept
61163
{
62-
return 0;
164+
return backup.end(thd, abort);
63165
}
64166

65-
/** After a successful backup_end(), finalize the backup. */
66167
void innodb_backup_finalize(THD *thd) noexcept
67168
{
68169
backup.fini(thd);

storage/innobase/handler/ha_innodb.cc

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19619,10 +19619,20 @@ static MYSQL_SYSVAR_UINT64_T(log_archive_start, innodb_log_archive_start,
1961919619
"initial value of innodb_lsn_archived; 0=auto-detect",
1962019620
nullptr, nullptr, 0, 0, std::numeric_limits<uint64_t>::max(), 0);
1962119621

19622+
static void innodb_log_recovery_start_update(THD *, st_mysql_sys_var*,
19623+
void *, const void *save) noexcept
19624+
{
19625+
const lsn_t lsn{*static_cast<const uint64_t*>(save)};
19626+
recv_sys.recovery_start= lsn;
19627+
if (lsn && log_sys.archive)
19628+
log_sys.archived_checkpoint= lsn;
19629+
}
19630+
1962219631
static MYSQL_SYSVAR_UINT64_T(log_recovery_start, recv_sys.recovery_start,
19623-
PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
19632+
PLUGIN_VAR_RQCMDARG,
1962419633
"LSN to start recovery from (0=automatic)",
19625-
nullptr, nullptr, 0, 0, std::numeric_limits<uint64_t>::max(), 0);
19634+
nullptr, innodb_log_recovery_start_update,
19635+
0, 0, std::numeric_limits<uint64_t>::max(), 0);
1962619636

1962719637
static MYSQL_SYSVAR_UINT64_T(log_recovery_target, recv_sys.rpo,
1962819638
PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,

storage/innobase/include/log0log.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -286,8 +286,9 @@ struct log_t
286286
Atomic_relaxed<lsn_t> last_checkpoint_lsn;
287287
/** The log writer (protected by latch.wr_lock()) */
288288
lsn_t (*writer)() noexcept;
289-
/** end_lsn of the first available checkpoint, or 0;
290-
protected by latch.wr_lock() */
289+
/** the earliest available checkpoint; protected by latch.wr_lock() */
290+
lsn_t archived_checkpoint;
291+
/** end_lsn of archived_checkpoint; protected by latch.wr_lock() */
291292
lsn_t archived_lsn;
292293

293294
/** Log file */
@@ -397,8 +398,9 @@ struct log_t
397398

398399
/** SET GLOBAL innodb_log_archive
399400
@param archive the new value of innodb_log_archive
400-
@param thd SQL connection */
401-
void set_archive(my_bool archive, THD *thd) noexcept;
401+
@param thd SQL connection
402+
@return whether the operation failed */
403+
bool set_archive(my_bool archive, THD *thd) noexcept;
402404

403405
private:
404406
/** Replicate a write to the log.

storage/innobase/log/log0log.cc

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -761,9 +761,11 @@ void log_t::header_rewrite(my_bool archive) noexcept
761761

762762
/** SET GLOBAL innodb_log_archive
763763
@param archive the new value of innodb_log_archive
764-
@param thd SQL connection */
765-
void log_t::set_archive(my_bool archive, THD *thd) noexcept
764+
@param thd SQL connection
765+
@return whether the operation failed */
766+
bool log_t::set_archive(my_bool archive, THD *thd) noexcept
766767
{
768+
bool fail= false;
767769
thd_wait_begin(thd, THD_WAIT_DISKIO);
768770
tpool::tpool_wait_begin();
769771

@@ -775,17 +777,19 @@ void log_t::set_archive(my_bool archive, THD *thd) noexcept
775777
my_printf_error(ER_WRONG_USAGE,
776778
"SET GLOBAL innodb_log_file_size is in progress",
777779
MYF(0));
780+
fail:
781+
fail= true;
778782
break;
779783
}
780784
if (archive && file_size > ARCHIVE_FILE_SIZE_MAX)
781785
{
782786
my_printf_error(ER_WRONG_USAGE, "innodb_log_file_size>4G", MYF(0));
783-
break;
787+
goto fail;
784788
}
785789
if (archive == this->archive)
786790
break;
787791
if (thd_kill_level(thd))
788-
break;
792+
goto fail;
789793

790794
lsn_t wait_lsn;
791795

@@ -894,7 +898,7 @@ void log_t::set_archive(my_bool archive, THD *thd) noexcept
894898
if (!log.is_opened())
895899
{
896900
my_error(ER_ERROR_ON_READ, MYF(0), old_name, errno);
897-
break;
901+
goto fail;
898902
}
899903
}
900904
#endif
@@ -919,7 +923,7 @@ void log_t::set_archive(my_bool archive, THD *thd) noexcept
919923
{
920924
my_error(ER_ERROR_ON_RENAME, MYF(0), old_name, new_name, my_errno);
921925
first_lsn= old_first_lsn;
922-
break;
926+
goto fail;
923927
}
924928

925929
if (archive)
@@ -937,6 +941,7 @@ void log_t::set_archive(my_bool archive, THD *thd) noexcept
937941
IF_WIN(log_resize_release(), latch.wr_unlock());
938942
tpool::tpool_wait_end();
939943
thd_wait_end(thd);
944+
return fail;
940945
}
941946

942947
/** Start resizing the log and release the exclusive latch.

storage/innobase/log/log0recv.cc

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2074,6 +2074,7 @@ dberr_t recv_sys_t::find_checkpoint()
20742074
memset_aligned<4096>(const_cast<byte*>(field_ref_zero), 0, 4096);
20752075
/* Mark the redo log for upgrading. */
20762076
lsn= file_checkpoint= log_sys.last_checkpoint_lsn;
2077+
log_sys.archived_checkpoint= lsn;
20772078
log_sys.set_recovered_lsn(lsn);
20782079
if (rpo && rpo != lsn)
20792080
{
@@ -2152,12 +2153,14 @@ dberr_t recv_sys_t::find_checkpoint()
21522153
log_sys.set_recovered_checkpoint(checkpoint_lsn, lsn= end_lsn,
21532154
field == log_t::CHECKPOINT_1);
21542155
}
2155-
if (!log_sys.last_checkpoint_lsn)
2156+
log_sys.archived_checkpoint= log_sys.last_checkpoint_lsn;
2157+
if (!log_sys.archived_checkpoint)
21562158
goto got_no_checkpoint;
21572159
else if (!log_sys.archived_lsn)
21582160
log_sys.archived_lsn= lsn;
21592161
if (recv_sys_invalid_rpo(lsn))
21602162
return DB_READ_ONLY;
2163+
21612164
if (!memcmp(creator, "Backup ", 7) &&
21622165
!recv_sys.rpo && !high_level_read_only)
21632166
srv_start_after_restore= true;
@@ -5650,6 +5653,8 @@ dberr_t recv_sys_t::find_checkpoint_archived(lsn_t first_lsn, bool silent)
56505653
checkpoint= log_sys.last_checkpoint_lsn;
56515654
ut_ad(checkpoint);
56525655
ut_ad(checkpoint < lsn);
5656+
if (!log_sys.archived_checkpoint)
5657+
log_sys.archived_checkpoint= checkpoint;
56535658
if (!log_sys.archived_lsn)
56545659
log_sys.archived_lsn= parse_start;
56555660
end_lsn= parse_start;
@@ -5691,7 +5696,7 @@ dberr_t recv_sys_t::find_checkpoint_archived(lsn_t first_lsn, bool silent)
56915696
return DB_CORRUPTION;
56925697
}
56935698

5694-
if (recovery_start < log_sys.get_first_lsn());
5699+
if (!recovery_start);
56955700
else if (recovery_start_checkpoint)
56965701
checkpoint= recovery_start_checkpoint, end_lsn= recovery_start;
56975702
else

0 commit comments

Comments
 (0)