mirror of
				https://github.com/MariaDB/server.git
				synced 2025-11-04 04:46:15 +01:00 
			
		
		
		
	Added support to BACKUP STAGE to maria-backup
This is a port of the code from ES 10.6
See MDEV-5336 for backup stages description.
The following old options are not supported by the new code:
--rsync             ; This is because rsync will not work on tables
                      that are in used.
--no-backup-locks   ; This is disabled as mariadb-backup will always
                      use backup locks for better performance.
		
	
			
		
			
				
	
	
		
			234 lines
		
	
	
	
		
			6.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			234 lines
		
	
	
	
		
			6.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/******************************************************
 | 
						|
Copyright (c) 2012 Percona LLC and/or its affiliates.
 | 
						|
 | 
						|
tmpfile datasink for XtraBackup.
 | 
						|
 | 
						|
This program is free software; you can redistribute it and/or modify
 | 
						|
it under the terms of the GNU General Public License as published by
 | 
						|
the Free Software Foundation; version 2 of the License.
 | 
						|
 | 
						|
This program is distributed in the hope that it will be useful,
 | 
						|
but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
GNU General Public License for more details.
 | 
						|
 | 
						|
You should have received a copy of the GNU General Public License
 | 
						|
along with this program; if not, write to the Free Software
 | 
						|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1335  USA
 | 
						|
 | 
						|
*******************************************************/
 | 
						|
 | 
						|
/* Do all writes to temporary files first, then pipe them to the specified
 | 
						|
datasink in a serialized way in deinit(). */
 | 
						|
 | 
						|
#include <my_global.h>
 | 
						|
#include <my_base.h>
 | 
						|
#include "common.h"
 | 
						|
#include "datasink.h"
 | 
						|
 | 
						|
typedef struct {
 | 
						|
	pthread_mutex_t	 mutex;
 | 
						|
	LIST		*file_list;
 | 
						|
} ds_tmpfile_ctxt_t;
 | 
						|
 | 
						|
typedef struct {
 | 
						|
	LIST		 list;
 | 
						|
	File		 fd;
 | 
						|
	char		*orig_path;
 | 
						|
	MY_STAT		 mystat;
 | 
						|
	ds_file_t	*file;
 | 
						|
} ds_tmp_file_t;
 | 
						|
 | 
						|
static ds_ctxt_t *tmpfile_init(const char *root);
 | 
						|
static ds_file_t *tmpfile_open(ds_ctxt_t *ctxt, const char *path,
 | 
						|
			const MY_STAT *mystat, bool rewrite);
 | 
						|
static int tmpfile_write(ds_file_t *file, const uchar *buf, size_t len);
 | 
						|
static int tmpfile_close(ds_file_t *file);
 | 
						|
static void tmpfile_deinit(ds_ctxt_t *ctxt);
 | 
						|
 | 
						|
datasink_t datasink_tmpfile = {
 | 
						|
	&tmpfile_init,
 | 
						|
	&tmpfile_open,
 | 
						|
	&tmpfile_write,
 | 
						|
	nullptr,
 | 
						|
	&tmpfile_close,
 | 
						|
	&dummy_remove,
 | 
						|
	nullptr,
 | 
						|
	nullptr,
 | 
						|
	&tmpfile_deinit
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
static ds_ctxt_t *
 | 
						|
tmpfile_init(const char *root)
 | 
						|
{
 | 
						|
	ds_ctxt_t		*ctxt;
 | 
						|
	ds_tmpfile_ctxt_t	*tmpfile_ctxt;
 | 
						|
 | 
						|
	ctxt = (ds_ctxt_t *)my_malloc(PSI_NOT_INSTRUMENTED,
 | 
						|
                   sizeof(ds_ctxt_t) + sizeof(ds_tmpfile_ctxt_t), MYF(MY_FAE));
 | 
						|
	tmpfile_ctxt = (ds_tmpfile_ctxt_t *) (ctxt + 1);
 | 
						|
	tmpfile_ctxt->file_list = NULL;
 | 
						|
	if (pthread_mutex_init(&tmpfile_ctxt->mutex, NULL)) {
 | 
						|
 | 
						|
		my_free(ctxt);
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	ctxt->ptr = tmpfile_ctxt;
 | 
						|
	ctxt->root = my_strdup(PSI_NOT_INSTRUMENTED, root, MYF(MY_FAE));
 | 
						|
 | 
						|
	return ctxt;
 | 
						|
}
 | 
						|
 | 
						|
static ds_file_t *
 | 
						|
tmpfile_open(ds_ctxt_t *ctxt, const char *path,
 | 
						|
			const MY_STAT *mystat, bool rewrite)
 | 
						|
{
 | 
						|
  DBUG_ASSERT(rewrite == false);
 | 
						|
	ds_tmpfile_ctxt_t	*tmpfile_ctxt;
 | 
						|
	char			 tmp_path[FN_REFLEN];
 | 
						|
	ds_tmp_file_t		*tmp_file;
 | 
						|
	ds_file_t		*file;
 | 
						|
	size_t			 path_len;
 | 
						|
	File			 fd;
 | 
						|
 | 
						|
	/* Create a temporary file in tmpdir. The file will be automatically
 | 
						|
	removed on close. Code copied from mysql_tmpfile(). */
 | 
						|
	fd = create_temp_file(tmp_path,xtrabackup_tmpdir,
 | 
						|
			      "xbtemp", O_BINARY | O_SEQUENTIAL,
 | 
						|
			      MYF(MY_WME | MY_TEMPORARY));
 | 
						|
 | 
						|
	if (fd < 0) {
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	path_len = strlen(path) + 1; /* terminating '\0' */
 | 
						|
 | 
						|
	file = (ds_file_t *) my_malloc(PSI_NOT_INSTRUMENTED,
 | 
						|
                        sizeof(ds_file_t) + sizeof(ds_tmp_file_t) + path_len, MYF(MY_FAE));
 | 
						|
 | 
						|
	tmp_file = (ds_tmp_file_t *) (file + 1);
 | 
						|
	tmp_file->file = file;
 | 
						|
	memcpy(&tmp_file->mystat, mystat, sizeof(MY_STAT));
 | 
						|
	/* Save a copy of 'path', since it may not be accessible later */
 | 
						|
	tmp_file->orig_path = (char *) tmp_file + sizeof(ds_tmp_file_t);
 | 
						|
 | 
						|
	tmp_file->fd = fd;
 | 
						|
	memcpy(tmp_file->orig_path, path, path_len);
 | 
						|
 | 
						|
	/* Store the real temporary file name in file->path */
 | 
						|
	file->path = my_strdup(PSI_NOT_INSTRUMENTED, tmp_path, MYF(MY_FAE));
 | 
						|
	file->ptr = tmp_file;
 | 
						|
 | 
						|
	/* Store the file object in the list to be piped later */
 | 
						|
	tmpfile_ctxt = (ds_tmpfile_ctxt_t *) ctxt->ptr;
 | 
						|
	tmp_file->list.data = tmp_file;
 | 
						|
 | 
						|
	pthread_mutex_lock(&tmpfile_ctxt->mutex);
 | 
						|
	tmpfile_ctxt->file_list = list_add(tmpfile_ctxt->file_list,
 | 
						|
					   &tmp_file->list);
 | 
						|
	pthread_mutex_unlock(&tmpfile_ctxt->mutex);
 | 
						|
 | 
						|
	return file;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
tmpfile_write(ds_file_t *file, const uchar *buf, size_t len)
 | 
						|
{
 | 
						|
	File fd = ((ds_tmp_file_t *) file->ptr)->fd;
 | 
						|
 | 
						|
	if (!my_write(fd, buf, len, MYF(MY_WME | MY_NABP))) {
 | 
						|
		posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED);
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	return 1;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
tmpfile_close(ds_file_t *file)
 | 
						|
{
 | 
						|
	/* Do nothing -- we will close (and thus remove) the file after piping
 | 
						|
	it to the destination datasink in tmpfile_deinit(). */
 | 
						|
 | 
						|
	my_free(file->path);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
tmpfile_deinit(ds_ctxt_t *ctxt)
 | 
						|
{
 | 
						|
	LIST			*list;
 | 
						|
	ds_tmpfile_ctxt_t	*tmpfile_ctxt;
 | 
						|
	MY_STAT			 mystat;
 | 
						|
	ds_tmp_file_t 		*tmp_file;
 | 
						|
	ds_file_t		*dst_file;
 | 
						|
	ds_ctxt_t		*pipe_ctxt;
 | 
						|
	void			*buf = NULL;
 | 
						|
	const size_t		 buf_size = 10 * 1024 * 1024;
 | 
						|
	size_t			 bytes;
 | 
						|
	size_t			 offset;
 | 
						|
 | 
						|
	pipe_ctxt = ctxt->pipe_ctxt;
 | 
						|
	xb_a(pipe_ctxt != NULL);
 | 
						|
 | 
						|
	buf = my_malloc(PSI_NOT_INSTRUMENTED, buf_size, MYF(MY_FAE));
 | 
						|
 | 
						|
	tmpfile_ctxt = (ds_tmpfile_ctxt_t *) ctxt->ptr;
 | 
						|
	list = tmpfile_ctxt->file_list;
 | 
						|
 | 
						|
	/* Walk the files in the order they have been added */
 | 
						|
	list = list_reverse(list);
 | 
						|
	while (list != NULL) {
 | 
						|
		tmp_file = (ds_tmp_file_t *)list->data;
 | 
						|
		/* Stat the file to replace size and mtime on the original
 | 
						|
		* mystat struct */
 | 
						|
		if (my_fstat(tmp_file->fd, &mystat, MYF(0))) {
 | 
						|
			die("my_fstat() failed.");
 | 
						|
		}
 | 
						|
		tmp_file->mystat.st_size = mystat.st_size;
 | 
						|
		tmp_file->mystat.st_mtime = mystat.st_mtime;
 | 
						|
 | 
						|
		dst_file = ds_open(pipe_ctxt, tmp_file->orig_path,
 | 
						|
				   &tmp_file->mystat);
 | 
						|
		if (dst_file == NULL) {
 | 
						|
			die("could not stream a temporary file to "
 | 
						|
			    "'%s'", tmp_file->orig_path);
 | 
						|
		}
 | 
						|
 | 
						|
		/* copy to the destination datasink */
 | 
						|
		posix_fadvise(tmp_file->fd, 0, 0, POSIX_FADV_SEQUENTIAL);
 | 
						|
		if (my_seek(tmp_file->fd, 0, SEEK_SET, MYF(0)) ==
 | 
						|
		    MY_FILEPOS_ERROR) {
 | 
						|
			die("my_seek() failed for '%s', errno = %d.",
 | 
						|
			    tmp_file->file->path, my_errno);
 | 
						|
		}
 | 
						|
		offset = 0;
 | 
						|
		while ((bytes = my_read(tmp_file->fd, (unsigned char *)buf, buf_size,
 | 
						|
					MYF(MY_WME))) > 0) {
 | 
						|
			posix_fadvise(tmp_file->fd, offset, buf_size, POSIX_FADV_DONTNEED);
 | 
						|
			offset += buf_size;
 | 
						|
			if (ds_write(dst_file, buf, bytes)) {
 | 
						|
				die("cannot write to stream for '%s'.",
 | 
						|
				    tmp_file->orig_path);
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if (bytes == (size_t) -1) {
 | 
						|
			die("my_read failed for %s", tmp_file->orig_path);
 | 
						|
		}
 | 
						|
 | 
						|
		my_close(tmp_file->fd, MYF(MY_WME));
 | 
						|
		ds_close(dst_file);
 | 
						|
 | 
						|
		list = list_rest(list);
 | 
						|
		my_free(tmp_file->file);
 | 
						|
	}
 | 
						|
 | 
						|
	pthread_mutex_destroy(&tmpfile_ctxt->mutex);
 | 
						|
 | 
						|
	my_free(buf);
 | 
						|
	my_free(ctxt->root);
 | 
						|
	my_free(ctxt);
 | 
						|
}
 |