Skip to content

Commit 6b0f480

Browse files
MDEV-39101 Make BACKUP SERVER mutually exclusive with itself and BACKUP STAGE
BACKUP SERVER blocks when another BACKUp SERVER is in progress or a BACKUP STAGE has been started. Takes advantage of existing mechanism using MDL which BACKUP STAGE uses to block concurrent backups.
1 parent fcf4ee1 commit 6b0f480

6 files changed

Lines changed: 174 additions & 30 deletions

File tree

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
connect con1,localhost,root,,test;
2+
connect con2,localhost,root,,test;
3+
connection con1;
4+
SET DEBUG_SYNC='after_backup_server_lock_acquired SIGNAL in_progress WAIT_FOR finish';
5+
BACKUP SERVER TO '<MYSQLTEST_VARDIR>/some_directory';
6+
connection default;
7+
SET DEBUG_SYNC='now WAIT_FOR in_progress';
8+
connection con2;
9+
BACKUP SERVER TO '<MYSQLTEST_VARDIR>/other_directory';
10+
connection default;
11+
SET @con2_id = <con2_id>;
12+
SELECT COMMAND, STATE, INFO FROM INFORMATION_SCHEMA.PROCESSLIST WHERE ID = @con2_id;
13+
COMMAND STATE INFO
14+
Query Waiting for backup lock BACKUP SERVER TO '<MYSQLTEST_VARDIR>/other_directory'
15+
SET DEBUG_SYNC='now SIGNAL finish';
16+
connection con1;
17+
connection default;
18+
connection con2;
19+
connection default;
20+
SET DEBUG_SYNC='RESET';
21+
BACKUP STAGE START;
22+
connection con2;
23+
BACKUP SERVER TO '<MYSQLTEST_VARDIR>/another_directory';
24+
connection default;
25+
SELECT COMMAND, STATE, INFO FROM INFORMATION_SCHEMA.PROCESSLIST WHERE ID = @con2_id;
26+
COMMAND STATE INFO
27+
Query Waiting for backup lock BACKUP SERVER TO '<MYSQLTEST_VARDIR>/another_directory'
28+
BACKUP STAGE END;
29+
connection con2;
30+
connection default;
31+
disconnect con1;
32+
disconnect con2;
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# Check that BACKUP SERVER blocks when another BACKUP SERVER is in progress
2+
3+
--source include/have_debug_sync.inc
4+
5+
--connect (con1,localhost,root,,test)
6+
--connect (con2,localhost,root,,test)
7+
8+
--connection con1
9+
# Simulate long-running backup by blocking it on a debug sync point
10+
SET DEBUG_SYNC='after_backup_server_lock_acquired SIGNAL in_progress WAIT_FOR finish';
11+
--replace_result $MYSQLTEST_VARDIR <MYSQLTEST_VARDIR>
12+
--send_eval BACKUP SERVER TO '$MYSQLTEST_VARDIR/some_directory'
13+
14+
--connection default
15+
# Wait for the "long-running" backup to lock the system
16+
SET DEBUG_SYNC='now WAIT_FOR in_progress';
17+
18+
--connection con2
19+
let $con2_id= `SELECT CONNECTION_ID()`;
20+
# Attempt a backup while the other backup is running
21+
--replace_result $MYSQLTEST_VARDIR <MYSQLTEST_VARDIR>
22+
--send_eval BACKUP SERVER TO '$MYSQLTEST_VARDIR/other_directory'
23+
24+
--connection default
25+
# Check that the backup on con2 is waiting for the backup on con1
26+
--replace_result $con2_id <con2_id>
27+
eval SET @con2_id = $con2_id;
28+
let $wait_condition=
29+
SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.PROCESSLIST
30+
WHERE ID = @con2_id AND State = 'Waiting for backup lock';
31+
--source include/wait_condition.inc
32+
--replace_result $MYSQLTEST_VARDIR <MYSQLTEST_VARDIR>
33+
SELECT COMMAND, STATE, INFO FROM INFORMATION_SCHEMA.PROCESSLIST WHERE ID = @con2_id;
34+
35+
SET DEBUG_SYNC='now SIGNAL finish';
36+
37+
--connection con1
38+
--reap
39+
40+
--connection default
41+
# When "long running" backup finishes, check that the backup on con2
42+
# is also allowed to finish
43+
let $wait_condition=
44+
SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.PROCESSLIST
45+
WHERE Info LIKE 'BACKUP SERVER TO %' AND State = 'Waiting for backup lock';
46+
--source include/wait_condition.inc
47+
48+
--connection con2
49+
--reap
50+
51+
--connection default
52+
SET DEBUG_SYNC='RESET';
53+
54+
# Test that BACKUP SERVER blocks when a BACKUP STAGE process is in progress
55+
56+
BACKUP STAGE START;
57+
58+
--connection con2
59+
--replace_result $MYSQLTEST_VARDIR <MYSQLTEST_VARDIR>
60+
--send_eval BACKUP SERVER TO '$MYSQLTEST_VARDIR/another_directory'
61+
62+
--connection default
63+
let $wait_condition=
64+
SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.PROCESSLIST
65+
WHERE ID = @con2_id AND State = 'Waiting for backup lock';
66+
--source include/wait_condition.inc
67+
--replace_result $MYSQLTEST_VARDIR <MYSQLTEST_VARDIR>
68+
SELECT COMMAND, STATE, INFO FROM INFORMATION_SCHEMA.PROCESSLIST WHERE ID = @con2_id;
69+
70+
BACKUP STAGE END;
71+
72+
# When "long running" backup finishes, check that the backup on con2
73+
# is also allowed to finish
74+
let $wait_condition=
75+
SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.PROCESSLIST
76+
WHERE Info LIKE 'BACKUP SERVER TO %' AND State = 'Waiting for backup lock';
77+
--source include/wait_condition.inc
78+
79+
--connection con2
80+
--reap
81+
82+
--connection default
83+
84+
--disconnect con1
85+
--disconnect con2
86+

sql/debug_sync.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
Declarations for the Debug Sync Facility. See debug_sync.cc for details.
2323
*/
2424

25+
#include <my_global.h>
26+
2527
class THD;
2628

2729
#if defined(ENABLED_DEBUG_SYNC)

sql/mdl.h

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -569,7 +569,7 @@ class MDL_request
569569
/** Set type of lock request. Can be only applied to pending locks. */
570570
inline void set_type(enum_mdl_type type_arg)
571571
{
572-
DBUG_ASSERT(ticket == NULL);
572+
DBUG_ASSERT(ticket == nullptr);
573573
type= type_arg;
574574
}
575575
void move_from(MDL_request &from)
@@ -580,7 +580,7 @@ class MDL_request
580580
next_in_list= from.next_in_list;
581581
prev_in_list= from.prev_in_list;
582582
key.mdl_key_init(&from.key);
583-
from.ticket= NULL; // that's what "move" means
583+
from.ticket= nullptr; // that's what "move" means
584584
}
585585

586586
/**
@@ -612,17 +612,17 @@ class MDL_request
612612
MDL_request& operator=(const MDL_request &)
613613
{
614614
type= MDL_NOT_INITIALIZED;
615-
ticket= NULL;
615+
ticket= nullptr;
616616
/* Do nothing, in particular, don't try to copy the key. */
617617
return *this;
618618
}
619619
/* Another piece of ugliness for TABLE_LIST constructor */
620-
MDL_request(): type(MDL_NOT_INITIALIZED), ticket(NULL) {}
620+
MDL_request(): type(MDL_NOT_INITIALIZED), ticket(nullptr) {}
621621

622622
MDL_request(const MDL_request *rhs)
623623
:type(rhs->type),
624624
duration(rhs->duration),
625-
ticket(NULL),
625+
ticket(nullptr),
626626
key(&rhs->key)
627627
{}
628628
};
@@ -1079,7 +1079,7 @@ class MDL_context
10791079
void done_waiting_for()
10801080
{
10811081
mysql_prlock_wrlock(&m_LOCK_waiting_for);
1082-
m_waiting_for= NULL;
1082+
m_waiting_for= nullptr;
10831083
mysql_prlock_unlock(&m_LOCK_waiting_for);
10841084
}
10851085
void lock_deadlock_victim()
@@ -1107,7 +1107,7 @@ class MDL_context
11071107
The coordinator thread holds the lock for the duration of worker's purge
11081108
job, or longer, possibly reusing shared MDL for different workers and jobs.
11091109
*/
1110-
MDL_context *lock_warrant= NULL;
1110+
MDL_context *lock_warrant= nullptr;
11111111

11121112
inline bool is_lock_warrantee(MDL_key::enum_mdl_namespace ns,
11131113
const char *db, const char *name,

sql/sql_backup.cc

Lines changed: 41 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
along with this program; if not, write to the Free Software
1414
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
1515

16+
#include "debug_sync.h"
17+
#include "mdl.h"
1618
#include "my_global.h"
1719
#include "mysys_err.h"
1820
#include "sql_class.h"
@@ -63,38 +65,58 @@ bool Sql_cmd_backup::execute(THD *thd)
6365
error_if_data_home_dir(target.str, "BACKUP SERVER TO"))
6466
return true;
6567

66-
if (my_mkdir(target.str, 0755, MYF(MY_WME)))
68+
/*
69+
Block concurrent BACKUP SERVER and BACKUP STAGE
70+
*/
71+
MDL_request mdl_request;
72+
MDL_REQUEST_INIT(&mdl_request, MDL_key::BACKUP, "", "", MDL_BACKUP_START,
73+
MDL_EXPLICIT);
74+
if (thd->mdl_context.acquire_lock(&mdl_request,
75+
thd->variables.lock_wait_timeout))
6776
return true;
6877

78+
DEBUG_SYNC(thd, "after_backup_server_lock_acquired");
79+
80+
bool fail = my_mkdir(target.str, 0755, MYF(MY_WME));
81+
6982
#ifndef _WIN32
70-
int dir= open(target.str, O_DIRECTORY);
71-
if (dir < 0)
83+
int dir;
84+
if(!fail)
7285
{
73-
my_error(EE_CANT_MKDIR, MYF(ME_BELL), target.str, errno);
74-
return true;
86+
dir = open(target.str, O_DIRECTORY);
87+
if (dir < 0)
88+
{
89+
my_error(EE_CANT_MKDIR, MYF(ME_BELL), target.str, errno);
90+
fail= true;
91+
}
7592
}
7693
#endif
7794

78-
bool fail= plugin_foreach_with_mask(thd, backup_start,
79-
MYSQL_STORAGE_ENGINE_PLUGIN,
80-
PLUGIN_IS_DELETED|PLUGIN_IS_READY,
81-
IF_WIN(const_cast<char*>(target.str),
82-
reinterpret_cast<void*>(dir)));
83-
if (!fail)
84-
fail= plugin_foreach_with_mask(thd, backup_step,
95+
if(!fail)
96+
{
97+
fail= plugin_foreach_with_mask(thd, backup_start,
8598
MYSQL_STORAGE_ENGINE_PLUGIN,
86-
PLUGIN_IS_DELETED|PLUGIN_IS_READY, nullptr);
99+
PLUGIN_IS_DELETED|PLUGIN_IS_READY,
100+
IF_WIN(const_cast<char*>(target.str),
101+
reinterpret_cast<void*>(dir)));
102+
103+
if (!fail)
104+
fail= plugin_foreach_with_mask(thd, backup_step,
105+
MYSQL_STORAGE_ENGINE_PLUGIN,
106+
PLUGIN_IS_DELETED|PLUGIN_IS_READY, nullptr);
87107

88-
plugin_foreach_with_mask(thd, backup_end, MYSQL_STORAGE_ENGINE_PLUGIN,
89-
PLUGIN_IS_DELETED|PLUGIN_IS_READY,
90-
reinterpret_cast<void*>(fail));
108+
plugin_foreach_with_mask(thd, backup_end, MYSQL_STORAGE_ENGINE_PLUGIN,
109+
PLUGIN_IS_DELETED|PLUGIN_IS_READY,
110+
reinterpret_cast<void*>(fail));
91111

92-
plugin_foreach_with_mask(thd, backup_finalize, MYSQL_STORAGE_ENGINE_PLUGIN,
93-
PLUGIN_IS_DELETED|PLUGIN_IS_READY, nullptr);
112+
plugin_foreach_with_mask(thd, backup_finalize, MYSQL_STORAGE_ENGINE_PLUGIN,
113+
PLUGIN_IS_DELETED|PLUGIN_IS_READY, nullptr);
94114
#ifndef _WIN32
95-
close(dir);
115+
close(dir);
96116
#endif
97117

118+
}
119+
thd->mdl_context.release_lock(mdl_request.ticket);
98120
if (!fail)
99121
my_ok(thd);
100122
return fail;

sql/sql_plist.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
1717

1818

19+
#include <my_global.h>
20+
1921
template <typename T, typename L>
2022
class I_P_List_iterator;
2123
class I_P_List_null_counter;
@@ -72,14 +74,14 @@ class I_P_List : public C, public I
7274
is a bad idea.
7375
*/
7476
public:
75-
I_P_List() : I(&m_first), m_first(NULL) {};
77+
I_P_List() : I(&m_first), m_first(nullptr) {};
7678
/*
7779
empty() is used in many places in the code instead of a constructor, to
7880
initialize a bzero-ed I_P_List instance.
7981
*/
8082

81-
inline void empty() { m_first= NULL; C::reset(); I::set_last(&m_first); }
82-
inline bool is_empty() const { return (m_first == NULL); }
83+
inline void empty() { m_first= nullptr; C::reset(); I::set_last(&m_first); }
84+
inline bool is_empty() const { return (m_first == nullptr); }
8385
inline void push_front(T* a)
8486
{
8587
*B::next_ptr(a)= m_first;
@@ -102,7 +104,7 @@ class I_P_List : public C, public I
102104
}
103105
inline void insert_after(T *pos, T *a)
104106
{
105-
if (pos == NULL)
107+
if (pos == nullptr)
106108
push_front(a);
107109
else
108110
{

0 commit comments

Comments
 (0)