1
0
mirror of https://github.com/MariaDB/server.git synced 2026-10-23 18:49:29 +03:00

Fix a race between IMPORT TABLESPACE and BACKUP SERVER

This commit is contained in:
Marko Mäkelä
2026-09-28 14:39:59 +03:00
parent 57925c1889
commit 92d9175305
2 changed files with 94 additions and 85 deletions

View File

@@ -448,11 +448,11 @@ public:
mysql_mutex_lock(&fil_system.mutex);
for (fil_space_t &space : fil_system.space_list)
{
UT_LIST_GET_FIRST(space.chain)->set_backup_name();
if (space.id < SRV_SPACE_ID_UPPER_BOUND &&
!space.is_being_imported() && !space.is_stopping() &&
space.create_lsn <= start) try
{
UT_LIST_GET_FIRST(space.chain)->set_backup_name();
/* FIXME: how to initialize create_lsn for old files, to
have efficient incremental backup?
fil_node_t::read_page0() cannot assign it from
@@ -766,95 +766,101 @@ private:
int res= -1;
uint32_t start{0};
fil_node_t *node= UT_LIST_GET_FIRST(space.chain);
if (UNIV_LIKELY(name != nullptr))
{
#ifdef _WIN32
if (sink.stream == sink.NO_STREAM)
{
for (;;)
if (sink.stream == sink.NO_STREAM)
{
if ((res= backup(target.path, node, name, start, limit)))
break;
fil_node_t *next= UT_LIST_GET_NEXT(chain, node);
if (!next)
break;
const uint32_t size{node->size};
start+= size;
if (limit >= size)
limit-= size;
else
limit= 0;
node= next;
for (;;)
{
if ((res= backup(target.path, node, name, start, limit)))
break;
fil_node_t *next= UT_LIST_GET_NEXT(chain, node);
if (!next)
break;
const uint32_t size{node->size};
start+= size;
if (limit >= size)
limit-= size;
else
limit= 0;
node= next;
}
}
}
else
{
for (;;)
{
if ((res= stream(sink.stream, node, name, start, limit)))
break;
fil_node_t *next= UT_LIST_GET_NEXT(chain, node);
if (!next)
break;
const uint32_t size{node->size};
start+= size;
if (limit >= size)
limit-= size;
else
limit= 0;
node= next;
}
}
#else
int fd;
int (*method)(int, fil_node_t *, const char *, uint32_t, uint32_t);
if (sink.stream == sink.NO_STREAM)
{
fd= target.fd;
method= backup;
}
else
{
fd= sink.stream;
method= stream;
}
for (;;)
{
# ifdef HAVE_POSIX_FALLOCATE
if (limit & 3 && !UT_LIST_GET_NEXT(chain, node))
{
const uint32_t page_size{space.physical_size()};
if ((limit * page_size) & 4095)
/*
os_file_set_size() extends ROW_FORMAT=COMPRESSED files to
multiples of 4096 bytes. There may be up to 3 pages
(of 1024 bytes) that have not been written out yet.
We must cap the limit to the actual file size.
*/
limit=
std::min(limit,
uint32_t(os_file_get_size(node->handle) / page_size));
}
# endif
res= (*method)(fd, node, name, start, limit);
#ifdef POSIX_FADV_DONTNEED
std::ignore= posix_fadvise(node->handle, 0, 0, POSIX_FADV_DONTNEED);
#endif
if (res)
break;
fil_node_t *next= UT_LIST_GET_NEXT(chain, node);
if (!next)
break;
const uint32_t size{node->size};
start+= size;
if (limit >= size)
limit-= size;
else
limit= 0;
node= next;
}
{
for (;;)
{
if ((res= stream(sink.stream, node, name, start, limit)))
break;
fil_node_t *next= UT_LIST_GET_NEXT(chain, node);
if (!next)
break;
const uint32_t size{node->size};
start+= size;
if (limit >= size)
limit-= size;
else
limit= 0;
node= next;
}
}
#else
int fd;
int (*method)(int, fil_node_t *, const char *, uint32_t, uint32_t);
if (sink.stream == sink.NO_STREAM)
{
fd= target.fd;
method= backup;
}
else
{
fd= sink.stream;
method= stream;
}
for (;;)
{
# ifdef HAVE_POSIX_FALLOCATE
if (limit & 3 && !UT_LIST_GET_NEXT(chain, node))
{
const uint32_t page_size{space.physical_size()};
if ((limit * page_size) & 4095)
/*
os_file_set_size() extends ROW_FORMAT=COMPRESSED files to
multiples of 4096 bytes. There may be up to 3 pages
(of 1024 bytes) that have not been written out yet.
We must cap the limit to the actual file size.
*/
limit=
std::min(limit,
uint32_t(os_file_get_size(node->handle) / page_size));
}
# endif
res= (*method)(fd, node, name, start, limit);
#ifdef POSIX_FADV_DONTNEED
std::ignore= posix_fadvise(node->handle, 0, 0, POSIX_FADV_DONTNEED);
#endif
mysql_mutex_lock(&fil_system.mutex);
node->clear_backup_name();
mysql_mutex_unlock(&fil_system.mutex);
if (res)
break;
fil_node_t *next= UT_LIST_GET_NEXT(chain, node);
if (!next)
break;
const uint32_t size{node->size};
start+= size;
if (limit >= size)
limit-= size;
else
limit= 0;
node= next;
}
#endif
mysql_mutex_lock(&fil_system.mutex);
node->clear_backup_name();
mysql_mutex_unlock(&fil_system.mutex);
}
else
/* This should be ALTER TABLE ... IMPORT TABLESPACE */
ut_ad(space.create_lsn >= ctx.checkpoint);
space.release();
ut_ad(res <= 0);
return res;

View File

@@ -5055,6 +5055,9 @@ import_error:
}
ib::info() << "Phase IV - Flush complete";
log_sys.latch.wr_lock();
table->space->create_lsn = log_sys.get_lsn();
log_sys.latch.wr_unlock();
table->space->set_imported();
err = lock_sys_tables(trx);