Skip to content

Commit b0f350f

Browse files
MDEV-39092 Copy Aria data and logs as part of backup
This is an initial simple implementation which copies all the Aria files in the "end" phase of the backup. Nothing protects the copy from concurrent DDL or DML. Copying only works on MacOS (intended for refactoring to use common file copy method across engines and SQL layer).
1 parent bcbda03 commit b0f350f

5 files changed

Lines changed: 337 additions & 1 deletion

File tree

sql/sql_backup.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ static my_bool backup_step(THD *thd, plugin_ref plugin, void *) noexcept
5252
static my_bool backup_finalize(THD *thd, plugin_ref plugin, void *dst) noexcept
5353
{
5454
handlerton *hton= plugin_hton(plugin);
55-
if (hton->backup_step)
55+
if (hton->backup_finalize)
5656
return hton->backup_finalize
5757
(thd, IF_WIN(static_cast<const char*>(dst),
5858
int(reinterpret_cast<uintptr_t>(dst))));

storage/maria/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ SET(ARIA_SOURCES ma_init.c ma_open.c ma_extra.c ma_info.c ma_rkey.c
4545
ha_maria.h maria_def.h ma_recovery_util.c ma_servicethread.c
4646
ma_norec.c
4747
ma_crypt.c ma_backup.c
48+
ma_backup.cc ma_backup.h
4849
)
4950

5051
IF(APPLE)

storage/maria/ha_maria.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <myisampack.h>
2424
#include <my_bit.h>
2525
#include "ha_maria.h"
26+
#include "ma_backup.h"
2627
#include "trnman_public.h"
2728
#include "trnman.h"
2829

@@ -3941,6 +3942,9 @@ static int ha_maria_init(void *p)
39413942
maria_hton->prepare_for_backup= maria_prepare_for_backup;
39423943
maria_hton->end_backup= maria_end_backup;
39433944
maria_hton->update_optimizer_costs= aria_update_optimizer_costs;
3945+
maria_hton->backup_start= aria_backup_start;
3946+
maria_hton->backup_step= aria_backup_step;
3947+
maria_hton->backup_end= aria_backup_end;
39443948

39453949
/* TODO: decide if we support Maria being used for log tables */
39463950
maria_hton->flags= (HTON_CAN_RECREATE | HTON_SUPPORT_LOG_TABLES |

storage/maria/ma_backup.cc

Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
1+
/* Copyright (c) 2026, MariaDB plc
2+
3+
This program is free software; you can redistribute it and/or modify
4+
it under the terms of the GNU General Public License as published by
5+
the Free Software Foundation; version 2 of the License.
6+
7+
This program is distributed in the hope that it will be useful,
8+
but WITHOUT ANY WARRANTY; without even the implied warranty of
9+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10+
GNU General Public License for more details.
11+
12+
You should have received a copy of the GNU General Public License
13+
along with this program; if not, write to the Free Software
14+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
15+
16+
#include "maria_def.h"
17+
#include "ma_backup.h"
18+
#include <mysqld_error.h>
19+
#include <string>
20+
#include <utility>
21+
#include <vector>
22+
23+
#ifdef __APPLE__
24+
#include <sys/attr.h>
25+
#include <sys/clonefile.h>
26+
#include <copyfile.h>
27+
#endif
28+
29+
/*
30+
Implementation of functions declatred in ma_backup.h:
31+
BACKUP SERVER support for Aria engine
32+
*/
33+
34+
using namespace std::string_literals;
35+
36+
namespace
37+
{
38+
class Source_dir
39+
{
40+
public:
41+
Source_dir(const char* path, myf flags) noexcept
42+
{
43+
dir_info= my_dir(path, flags);
44+
if (!dir_info)
45+
{
46+
my_error(ER_CANT_READ_DIR, MYF(0), path, my_errno);
47+
}
48+
}
49+
~Source_dir() noexcept
50+
{
51+
my_dirend(dir_info);
52+
}
53+
bool is_error() const noexcept
54+
{
55+
return !dir_info;
56+
}
57+
template<typename Fn>
58+
int for_each(Fn fn) const noexcept
59+
{
60+
for (size_t i= 0; i < dir_info->number_of_files; i++)
61+
{
62+
if (fn(dir_info->dir_entry[i]) != 0)
63+
return 1;
64+
}
65+
return 0;
66+
}
67+
68+
private:
69+
MY_DIR *dir_info {nullptr};
70+
};
71+
72+
73+
/** Backup state; protected by log_sys.latch */
74+
class Aria_backup
75+
{
76+
public:
77+
explicit Aria_backup(THD *thd, IF_WIN(const char*,int) target) noexcept
78+
: target_dir(target)
79+
{
80+
translog_disable_purge();
81+
}
82+
83+
int end(THD *thd, bool abort) noexcept
84+
{
85+
int ret_val = 0;
86+
if (!abort) {
87+
if (int err= perform_backup() != 0)
88+
{
89+
ret_val= err;
90+
};
91+
}
92+
translog_enable_purge();
93+
return ret_val;
94+
}
95+
private:
96+
const IF_WIN(const char*,int) target_dir;
97+
static const std::vector<std::string> data_exts;;
98+
const std::string log_file_prefix {"aria_log."};
99+
using dir_name = std::string;
100+
using dir_contents = std::vector<std::string>;
101+
using database_dir = std::pair<dir_name, dir_contents>;
102+
std::vector<database_dir> database_dirs;
103+
std::vector<std::string> log_files;
104+
bool have_control_file = false;
105+
106+
int perform_backup() noexcept
107+
{
108+
if (scan_datadir())
109+
return 1;
110+
if (copy_databases())
111+
return 1;
112+
if (copy_control_file())
113+
return 1;
114+
if (copy_logs())
115+
return 1;
116+
return 0;
117+
}
118+
119+
int scan_datadir() noexcept
120+
{
121+
const char* base_dir = maria_data_root;
122+
Source_dir datadir(base_dir, MYF(MY_WANT_STAT));
123+
if (datadir.is_error())
124+
return 1;
125+
datadir.for_each([this](const fileinfo &fi)
126+
{
127+
if (fi.mystat->st_mode & S_IFDIR)
128+
{
129+
if (scan_database_dir(fi.name) != 0)
130+
return 1;
131+
} else if (begins_with(fi.name, log_file_prefix))
132+
log_files.emplace_back(fi.name);
133+
else if (strcmp(fi.name, "aria_log_control") == 0)
134+
have_control_file = true;
135+
return 0;
136+
});
137+
return 0;
138+
}
139+
140+
int scan_database_dir(const char* dir_name) noexcept
141+
{
142+
const char* base_dir = maria_data_root;
143+
std::string dir_path = std::string(base_dir) + "/" + dir_name;
144+
Source_dir db_dir(dir_path.c_str(), MYF(0));
145+
if (db_dir.is_error())
146+
return 1;
147+
std::vector<std::string> files_to_backup;
148+
db_dir.for_each([&files_to_backup](const fileinfo &fi)
149+
{
150+
if (is_db_file(fi.name))
151+
files_to_backup.emplace_back(fi.name);
152+
return 0;
153+
});
154+
if (!files_to_backup.empty())
155+
database_dirs.emplace_back(dir_name, std::move(files_to_backup));
156+
return 0;
157+
}
158+
159+
int copy_databases() noexcept
160+
{
161+
for (const database_dir& dir : database_dirs)
162+
{
163+
const char* dir_name = dir.first.c_str();
164+
if (mkdirat(target_dir, dir_name, 0777) != 0)
165+
{
166+
if (errno != EEXIST)
167+
{
168+
my_error(ER_CANT_CREATE_FILE, MYF(0), dir_name, errno);
169+
return 1;
170+
}
171+
}
172+
if (copy_database(dir) != 0)
173+
return 1;
174+
}
175+
return 0;
176+
}
177+
178+
int copy_database(const database_dir& dir) noexcept
179+
{
180+
for (const std::string& file : dir.second)
181+
{
182+
std::string file_path= dir.first + "/" + file;
183+
if (copy_file(file_path) != 0)
184+
return 1;
185+
}
186+
return 0;
187+
}
188+
189+
int copy_control_file() noexcept
190+
{
191+
if (!have_control_file)
192+
return 0;
193+
return copy_file("aria_log_control");
194+
}
195+
196+
int copy_logs() noexcept
197+
{
198+
for (const std::string& file : log_files)
199+
{
200+
if (copy_file(file) != 0)
201+
return 1;
202+
}
203+
return 0;
204+
}
205+
206+
int copy_file(const std::string &path) const noexcept
207+
{
208+
std::string src_path= std::string(maria_data_root) + "/" + path;
209+
#ifdef __APPLE__
210+
int ret_val = 0;
211+
int src_fd = open(src_path.c_str(), O_RDONLY);
212+
if (src_fd < 0)
213+
{
214+
my_error(ER_CANT_OPEN_FILE, MYF(0), src_path.c_str(), errno);
215+
return 1;
216+
}
217+
int tgt_fd = openat(target_dir, path.c_str(),
218+
O_CREAT | O_EXCL | O_WRONLY, 0777);
219+
if (tgt_fd < 0)
220+
{
221+
my_error(ER_CANT_CREATE_FILE, MYF(0), path.c_str(), errno);
222+
ret_val = 1;
223+
goto finish;
224+
}
225+
if (fcopyfile(src_fd, tgt_fd, nullptr, COPYFILE_DATA) != 0)
226+
{
227+
my_error(ER_ERROR_ON_WRITE, MYF(0), path.c_str(), errno);
228+
ret_val = 1;
229+
}
230+
close(tgt_fd);
231+
finish:
232+
close(src_fd);
233+
return ret_val;
234+
#else
235+
return 1;
236+
#endif
237+
}
238+
239+
240+
static bool is_db_file(const char* file_name) noexcept
241+
{
242+
for (const std::string& ext : data_exts)
243+
{
244+
if (ends_with(file_name, ext))
245+
return true;
246+
}
247+
return false;
248+
}
249+
250+
static bool ends_with(const char* str, const std::string& suffix) noexcept
251+
{
252+
size_t str_len = strlen(str);
253+
size_t suffix_len = suffix.size();
254+
if (str_len < suffix_len)
255+
return false;
256+
return memcmp(str + str_len - suffix_len,
257+
suffix.data(),
258+
suffix_len) == 0;
259+
}
260+
261+
static bool begins_with(const char* str, const std::string& prefix) noexcept
262+
{
263+
return strncmp(str, prefix.data(), prefix.size()) == 0;
264+
}
265+
};
266+
267+
const std::vector<std::string> Aria_backup::data_exts {".MAD"s, ".MAI"s};
268+
269+
std::unique_ptr<Aria_backup> aria_backup;
270+
}
271+
272+
int aria_backup_start(THD *thd, IF_WIN(const char*,int) target) noexcept
273+
{
274+
aria_backup= std::make_unique<Aria_backup>(thd, target);
275+
return 0;
276+
}
277+
278+
int aria_backup_step(THD *thd) noexcept
279+
{
280+
return 0;
281+
}
282+
283+
int aria_backup_end(THD *thd, bool abort) noexcept
284+
{
285+
int ret_val= aria_backup->end(thd, abort);
286+
aria_backup.reset();
287+
return ret_val;
288+
}
289+

storage/maria/ma_backup.h

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/* Copyright (c) 2026, MariaDB plc
2+
3+
This program is free software; you can redistribute it and/or modify
4+
it under the terms of the GNU General Public License as published by
5+
the Free Software Foundation; version 2 of the License.
6+
7+
This program is distributed in the hope that it will be useful,
8+
but WITHOUT ANY WARRANTY; without even the implied warranty of
9+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10+
GNU General Public License for more details.
11+
12+
You should have received a copy of the GNU General Public License
13+
along with this program; if not, write to the Free Software
14+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
15+
16+
/* BACKUP SERVER support for Aria engine. */
17+
18+
/**
19+
Start of BACKUP SERVER: collect all files to be backed up
20+
@param thd current session
21+
@param target target directory
22+
@return error code
23+
@retval 0 on success
24+
*/
25+
int aria_backup_start(THD *thd, IF_WIN(const char*,int) target) noexcept;
26+
27+
/**
28+
Process a file that was collected in backup_start().
29+
@param thd current session
30+
@return number of files remaining, or negative on error
31+
@retval 0 on completion
32+
*/
33+
int aria_backup_step(THD *thd) noexcept;
34+
35+
/**
36+
Finish copying and determine the logical time of the backup snapshot.
37+
@param thd current session
38+
@param abort whether BACKUP SERVER was aborted
39+
@return error code
40+
@retval 0 on success
41+
*/
42+
int aria_backup_end(THD *thd, bool abort) noexcept;

0 commit comments

Comments
 (0)