#At file:///export/home/tmp/oysteing/mysql/mysql-6.0-backup/
2674 Oystein Grovlen 2008-08-01
Bug#36795 Concurrency issues when starting backups in parallel.
Raise condition on Backup_restore_ctx::mem_alloc since it is static.
The failing backup will set mem_alloc to null when terminating. The
next time the running backup wants to allocate memory, an assert will
fail.
Makes mem_alloc non-static. That way, concurrent backups will not
interfere. This requires that it is possible to bstream_alloc to find
the right Backup_restore_ctx to use. Fixes that by changing the
static is_running flag to a static pointer to the Backup_restore_ctx
of the currently running backup. If the pointer is null, it means
that no backup is currently running.
added:
mysql-test/r/backup_concurrent.result
mysql-test/t/backup_concurrent.test
modified:
mysql-test/lib/mtr_report.pl
sql/backup/backup_kernel.h
sql/backup/kernel.cc
per-file comments:
mysql-test/lib/mtr_report.pl
Filter out expected backup error in backup_concurrent test.
mysql-test/r/backup_concurrent.result
New result file.
mysql-test/t/backup_concurrent.test
Test starting backups in parallel.
sql/backup/backup_kernel.h
Change is_running flag into a pointer (current) to the
Backup_restore_ctx of the running operation. Make mem_alloc an
instance variable to avoid concurrency issues.
sql/backup/kernel.cc
Change is_running flag into a pointer (current) to the
Backup_restore_ctx of the running operation. Make mem_alloc an
instance variable to avoid concurrency issues.
=== modified file 'mysql-test/lib/mtr_report.pl'
=== modified file 'mysql-test/lib/mtr_report.pl'
--- a/mysql-test/lib/mtr_report.pl 2008-07-09 07:12:43 +0000
+++ b/mysql-test/lib/mtr_report.pl 2008-08-01 13:29:53 +0000
@@ -334,6 +334,12 @@
/Backup:/ or /Restore:/ or /Can't open the online backup progress tables/
) or
+ # backup_concurrent performs a backup that should fail
+ ($testname eq 'main.backup_concurrent') and
+ (
+ /Backup: Can't execute this command because another BACKUP\/RESTORE operation is in progress/
+ ) or
+
# The tablespace test triggers error below on purpose
($testname eq 'main.backup_tablespace') and
(
=== added file 'mysql-test/r/backup_concurrent.result'
--- a/mysql-test/r/backup_concurrent.result 1970-01-01 00:00:00 +0000
+++ b/mysql-test/r/backup_concurrent.result 2008-08-01 13:29:53 +0000
@@ -0,0 +1,29 @@
+DROP DATABASE IF EXISTS backup_concurrent;
+CREATE DATABASE backup_concurrent;
+USE backup_concurrent;
+Creating Table
+CREATE TABLE t (
+t1 INTEGER NOT NULL,
+t2 CHAR(36),
+PRIMARY KEY (t1)
+);
+First Backup
+USE backup_concurrent;
+BACKUP DATABASE backup_concurrent TO 'backup1';
+Starting second backup in another connection.
+(Should fail because another backup is running.)
+BACKUP DATABASE backup_concurrent TO 'backup2';
+ERROR HY000: Can't execute this command because another BACKUP/RESTORE operation is in progress
+Insert Data
+INSERT INTO t VALUES (1, 'test');
+Wait for first backup to complete
+backup_id
+#
+Starting a third backup when first backup has completed
+BACKUP DATABASE backup_concurrent TO 'backup3';
+backup_id
+#
+
+Test completed. Cleaning up.
+
+DROP DATABASE backup_concurrent;
=== added file 'mysql-test/t/backup_concurrent.test'
--- a/mysql-test/t/backup_concurrent.test 1970-01-01 00:00:00 +0000
+++ b/mysql-test/t/backup_concurrent.test 2008-08-01 13:29:53 +0000
@@ -0,0 +1,59 @@
+###########################################################################
+# Author: Oystein Grovlen
+# Date: 2008-07-31
+# Purpose: To test starting backups in parallel
+###############################################################################
+--source include/not_embedded.inc
+
+#Create Database and object view for this test.
+
+--disable_warnings
+DROP DATABASE IF EXISTS backup_concurrent;
+--enable_warnings
+
+CREATE DATABASE backup_concurrent;
+USE backup_concurrent;
+
+#Create table
+
+--echo Creating Table
+CREATE TABLE t (
+t1 INTEGER NOT NULL,
+t2 CHAR(36),
+PRIMARY KEY (t1)
+);
+
+--echo First Backup
+connect (backup,localhost,root,,);
+USE backup_concurrent;
+send BACKUP DATABASE backup_concurrent TO 'backup1';
+
+--echo Starting second backup in another connection.
+--echo (Should fail because another backup is running.)
+connection default;
+--error ER_BACKUP_RUNNING
+BACKUP DATABASE backup_concurrent TO 'backup2';
+
+--echo Insert Data
+INSERT INTO t VALUES (1, 'test');
+
+--echo Wait for first backup to complete
+connection backup;
+replace_column 1 #;
+reap;
+
+--echo Starting a third backup when first backup has completed
+replace_column 1 #;
+BACKUP DATABASE backup_concurrent TO 'backup3';
+
+# Test cleanup section
+
+--echo
+--echo Test completed. Cleaning up.
+--echo
+
+DROP DATABASE backup_concurrent;
+
+--remove_file $MYSQLTEST_VARDIR/master-data/backup1
+--remove_file $MYSQLTEST_VARDIR/master-data/backup3
+
=== modified file 'sql/backup/backup_kernel.h'
--- a/sql/backup/backup_kernel.h 2008-07-07 12:51:56 +0000
+++ b/sql/backup/backup_kernel.h 2008-08-01 13:29:53 +0000
@@ -79,9 +79,9 @@
private:
- /** Indicates if a backup/restore operation is in progress. */
- static bool is_running;
- static pthread_mutex_t run_lock; ///< To guard @c is_running flag.
+ /** Indicates if and which backup/restore operation is in progress. */
+ static Backup_restore_ctx *current;
+ static pthread_mutex_t run_lock; ///< To guard @c current pointer.
/**
@brief State of a context object.
@@ -109,7 +109,7 @@
backup::Image_info *m_catalog; ///< Pointer to the image catalogue object.
/** Memory allocator for backup stream library. */
- static backup::Mem_allocator *mem_alloc;
+ backup::Mem_allocator *mem_alloc;
int prepare(LEX_STRING location);
void disable_fkey_constraints();
=== modified file 'sql/backup/kernel.cc'
--- a/sql/backup/kernel.cc 2008-07-31 10:45:02 +0000
+++ b/sql/backup/kernel.cc 2008-08-01 13:29:53 +0000
@@ -344,15 +344,14 @@
// static members
-bool Backup_restore_ctx::is_running= FALSE;
+Backup_restore_ctx *Backup_restore_ctx::current= NULL;
pthread_mutex_t Backup_restore_ctx::run_lock;
-backup::Mem_allocator *Backup_restore_ctx::mem_alloc= NULL;
Backup_restore_ctx::Backup_restore_ctx(THD *thd)
:Logger(thd), m_state(CREATED), m_thd_options(thd->options),
m_error(0), m_path(NULL), m_remove_loc(FALSE), m_stream(NULL),
- m_catalog(NULL), m_tables_locked(FALSE)
+ m_catalog(NULL), mem_alloc(NULL), m_tables_locked(FALSE)
{
/*
Check for progress tables.
@@ -365,6 +364,7 @@
{
close();
+ delete mem_alloc;
delete m_catalog;
delete m_stream;
}
@@ -412,8 +412,8 @@
pthread_mutex_lock(&run_lock);
- if (!is_running)
- is_running= TRUE;
+ if (!current)
+ current= this;
else
fatal_error(ER_BACKUP_RUNNING);
@@ -802,10 +802,11 @@
delete mem_alloc;
mem_alloc= NULL;
- // deregister this operation
-
+ // deregister this operation if it was running
pthread_mutex_lock(&run_lock);
- is_running= FALSE;
+ if (current == this) {
+ current= NULL;
+ }
pthread_mutex_unlock(&run_lock);
/*
@@ -1194,9 +1195,10 @@
{
using namespace backup;
- DBUG_ASSERT(Backup_restore_ctx::mem_alloc);
+ DBUG_ASSERT(Backup_restore_ctx::current
+ && Backup_restore_ctx::current->mem_alloc);
- return (bstream_byte*)Backup_restore_ctx::mem_alloc->alloc(size);
+ return (bstream_byte*)Backup_restore_ctx::current->mem_alloc->alloc(size);
}
/**
@@ -1206,8 +1208,8 @@
void bstream_free(bstream_byte *ptr)
{
using namespace backup;
- if (Backup_restore_ctx::mem_alloc)
- Backup_restore_ctx::mem_alloc->free(ptr);
+ if (Backup_restore_ctx::current && Backup_restore_ctx::current->mem_alloc)
+ Backup_restore_ctx::current->mem_alloc->free(ptr);
}
/**