diff --git a/mysql-test/suite/galera/r/galera_defaults.result b/mysql-test/suite/galera/r/galera_defaults.result index 5d07ac0c44f61..e65cf9a8facca 100644 --- a/mysql-test/suite/galera/r/galera_defaults.result +++ b/mysql-test/suite/galera/r/galera_defaults.result @@ -1,9 +1,9 @@ connection node_2; connection node_1; # Correct Galera library found -SELECT COUNT(*) `expect 50` FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME LIKE 'wsrep_%'; -expect 50 -50 +SELECT COUNT(*) `expect 51` FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME LIKE 'wsrep_%'; +expect 51 +51 SELECT VARIABLE_NAME, VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME LIKE 'wsrep_%' @@ -57,6 +57,7 @@ WSREP_SST_AUTH ******** WSREP_SST_DONOR WSREP_SST_DONOR_REJECTS_QUERIES OFF WSREP_SST_METHOD rsync +WSREP_SST_TMP_DIR WSREP_STATUS_FILE WSREP_SYNC_WAIT 15 WSREP_TRX_FRAGMENT_SIZE 0 diff --git a/mysql-test/suite/galera/r/galera_sst_tmp_directory.result b/mysql-test/suite/galera/r/galera_sst_tmp_directory.result new file mode 100644 index 0000000000000..175eba75d7cac --- /dev/null +++ b/mysql-test/suite/galera/r/galera_sst_tmp_directory.result @@ -0,0 +1,22 @@ +connection node_2; +connection node_1; +connection node_1; +connection node_2; +create table t1(id int not null primary key, b varchar(32)) engine=InnoDB; +insert into t1 SELECT seq, md5(rand()) from seq_1_to_50000; +connection node_2; +Shutting down server ... +connection node_1; +connection node_2; +Starting server ... +connection node_1; +SELECT COUNT(*) AS EXPECT_5000 FROM t1; +EXPECT_5000 +50000 +connection node_2; +SELECT COUNT(*) AS EXPECT_5000 FROM t1; +EXPECT_5000 +50000 +DROP TABLE t1; +disconnect node_2; +disconnect node_1; diff --git a/mysql-test/suite/galera/t/galera_defaults.test b/mysql-test/suite/galera/t/galera_defaults.test index c69a32db67a1e..673e4ac8c2546 100644 --- a/mysql-test/suite/galera/t/galera_defaults.test +++ b/mysql-test/suite/galera/t/galera_defaults.test @@ -13,12 +13,12 @@ --source include/force_restart.inc # Make sure that the test is operating on the right version of galera library. ---let $galera_version=26.4.23 +--let $galera_version=26.4.26 source ../wsrep/include/check_galera_version.inc; # Global Variables -SELECT COUNT(*) `expect 50` FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME LIKE 'wsrep_%'; +SELECT COUNT(*) `expect 51` FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME LIKE 'wsrep_%'; SELECT VARIABLE_NAME, VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES diff --git a/mysql-test/suite/galera/t/galera_sst_tmp_directory.cnf b/mysql-test/suite/galera/t/galera_sst_tmp_directory.cnf new file mode 100644 index 0000000000000..259b1e49a7d2d --- /dev/null +++ b/mysql-test/suite/galera/t/galera_sst_tmp_directory.cnf @@ -0,0 +1,19 @@ +!include ../galera_2nodes.cnf + +[mysqld] +wsrep_sst_method=mariabackup +wsrep_sst_auth="root:" +wsrep_debug=1 +innodb_fast_shutdown=0 +innodb_undo_tablespaces=0 + +[mysqld.1] +wsrep_provider_options='pc.ignore_sb=true;base_port=@mysqld.1.#galera_port' + +[mysqld.2] +wsrep_provider_options='pc.ignore_sb=true;base_port=@mysqld.2.#galera_port' +wsrep_sst_tmp_dir=@ENV.MYSQLTEST_VARDIR/tmp + +[sst] +transferfmt=@ENV.MTR_GALERA_TFMT +streamfmt=mbstream diff --git a/mysql-test/suite/galera/t/galera_sst_tmp_directory.combinations b/mysql-test/suite/galera/t/galera_sst_tmp_directory.combinations new file mode 100644 index 0000000000000..cef98e75213f7 --- /dev/null +++ b/mysql-test/suite/galera/t/galera_sst_tmp_directory.combinations @@ -0,0 +1,5 @@ +[binlogon] +log-bin +log-slave-updates + +[binlogoff] diff --git a/mysql-test/suite/galera/t/galera_sst_tmp_directory.test b/mysql-test/suite/galera/t/galera_sst_tmp_directory.test new file mode 100644 index 0000000000000..e86d8880568eb --- /dev/null +++ b/mysql-test/suite/galera/t/galera_sst_tmp_directory.test @@ -0,0 +1,59 @@ +--source include/galera_cluster.inc +--source include/have_sequence.inc + +# Save original auto_increment_offset values. +--let $node_1=node_1 +--let $node_2=node_2 +--source include/auto_increment_offset_save.inc + +# +# TODO : This would be correct way to test but as CREATE TABLE is TOI +# exactly the same string is executed on node_2 also leading to problem +# where t1.ibd file would be in same location on both node_1 and node_2 +# and this leads to fact that CREATE TABLE fails on node_2 as it +# file already exists. +# +# EVAL create table t1(id int not null primary key, b varchar(32)) engine=innodb DATA DIRECTORY='$MYSQL_TMP_DIR'; +# insert into t1 SELECT seq, md5(rand()) from seq_1_to_50000; +# --list_files $MYSQL_TMP_DIR/test +# --list_files $MYSQLD_DATADIR/test + +create table t1(id int not null primary key, b varchar(32)) engine=InnoDB; +insert into t1 SELECT seq, md5(rand()) from seq_1_to_50000; + +--connection node_2 +--let $wait_condition = SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.INNODB_SYS_TABLES WHERE NAME LIKE 'test/t1' +--source include/wait_condition.inc +--let $wait_condition = SELECT COUNT(*) = 50000 FROM t1 +--source include/wait_condition.inc + +--echo Shutting down server ... +--source include/shutdown_mysqld.inc + +--connection node_1 +--let $wait_condition = SELECT VARIABLE_VALUE = 1 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size' +--source include/wait_condition.inc + +--connection node_2 +# enforce SST +--exec rm -rf $MYSQLTEST_VARDIR/mysqld.2/data/grastate.dat + +--echo Starting server ... +--source include/start_mysqld.inc + +--connection node_1 +--let $wait_condition = SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size' +--source include/wait_condition.inc +SELECT COUNT(*) AS EXPECT_5000 FROM t1; + +--connection node_2 +--let $wait_condition = SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size' +--source include/wait_condition.inc +SELECT COUNT(*) AS EXPECT_5000 FROM t1; + +# Restore original auto_increment_offset values. +--source include/auto_increment_offset_restore.inc + +DROP TABLE t1; + +--source include/galera_end.inc diff --git a/mysql-test/suite/sys_vars/r/sysvars_wsrep.result b/mysql-test/suite/sys_vars/r/sysvars_wsrep.result index 7d9a0338a4dd7..95d605985f682 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_wsrep.result +++ b/mysql-test/suite/sys_vars/r/sysvars_wsrep.result @@ -783,6 +783,23 @@ COMMAND_LINE_ARGUMENT REQUIRED GLOBAL_VALUE_PATH NULL IS_DEPRECATED NO DEPRECATED_REPLACEMENT NULL +VARIABLE_NAME WSREP_SST_TMP_DIR +SESSION_VALUE NULL +GLOBAL_VALUE +GLOBAL_VALUE_ORIGIN COMPILE-TIME +DEFAULT_VALUE +VARIABLE_SCOPE GLOBAL +VARIABLE_TYPE VARCHAR +VARIABLE_COMMENT Path to SST temp root directory +NUMERIC_MIN_VALUE NULL +NUMERIC_MAX_VALUE NULL +NUMERIC_BLOCK_SIZE NULL +ENUM_VALUE_LIST NULL +READ_ONLY YES +COMMAND_LINE_ARGUMENT REQUIRED +GLOBAL_VALUE_PATH NULL +IS_DEPRECATED NO +DEPRECATED_REPLACEMENT NULL VARIABLE_NAME WSREP_START_POSITION SESSION_VALUE NULL GLOBAL_VALUE 00000000-0000-0000-0000-000000000000:-1 diff --git a/scripts/wsrep_sst_common.sh b/scripts/wsrep_sst_common.sh index e8bedfcce2e06..f99ce81c92982 100644 --- a/scripts/wsrep_sst_common.sh +++ b/scripts/wsrep_sst_common.sh @@ -1,4 +1,4 @@ -# Copyright (C) 2017-2024 MariaDB +# Copyright (C) 2017-2025 MariaDB # Copyright (C) 2012-2015 Codership Oy # # This program is free software; you can redistribute it and/or modify @@ -198,6 +198,7 @@ WSREP_SST_OPT_BINLOG="" WSREP_SST_OPT_BINLOG_INDEX="" WSREP_SST_OPT_LOG_BASENAME="" WSREP_SST_OPT_DATA="" +WSREP_SST_OPT_TMP_DIR="" WSREP_SST_OPT_AUTH="${WSREP_SST_OPT_AUTH:-}" WSREP_SST_OPT_USER="${WSREP_SST_OPT_USER:-}" WSREP_SST_OPT_PSWD="${WSREP_SST_OPT_PSWD:-}" @@ -315,6 +316,10 @@ case "$1" in readonly WSREP_SST_OPT_DATA=$(trim_dir "$2") shift ;; + '--sst-tmp-dir') + readonly WSREP_SST_OPT_TMP_DIR=$(trim_dir "$2") + shift + ;; '--aria-log-dir-path') # Let's remove the trailing slash: readonly ARIA_LOG_DIR=$(trim_dir "$2") @@ -657,6 +662,12 @@ case "$1" in fi skip_mysqld_arg=1 ;; + '--sst-tmp-dir') + if [ -z "$WSREP_SST_OPT_TMP_DIR" ]; then + MYSQLD_OPT_SST_TMP_DIR=$(trim_dir "$value") + fi + skip_mysqld_arg=1 + ;; esac if [ $skip_mysqld_arg -eq 0 ]; then original_cmd="$original_cmd '$1'" @@ -736,6 +747,10 @@ if [ -n "${MYSQLD_OPT_LOG_BASENAME:-}" -a \ -z "$WSREP_SST_OPT_LOG_BASENAME" ]; then readonly WSREP_SST_OPT_LOG_BASENAME="$MYSQLD_OPT_LOG_BASENAME" fi +if [ -n "${MYSQLD_OPT_SST_TMP_DIR:-}" -a \ + -z "$WSREP_SST_OPT_TMP_DIR" ]; then + readonly WSREP_SST_OPT_TMP_DIR="$MYSQLD_OPT_SST_TMP_DIR" +fi # If the --log-bin option is present without a value, then # set WSREP_SST_OPT_BINLOG value using other arguments: diff --git a/scripts/wsrep_sst_mariabackup.sh b/scripts/wsrep_sst_mariabackup.sh index d56715ef89533..fcf74db8292c1 100644 --- a/scripts/wsrep_sst_mariabackup.sh +++ b/scripts/wsrep_sst_mariabackup.sh @@ -1,8 +1,10 @@ #!/usr/bin/env bash set -ue - -# Copyright (C) 2017-2024 MariaDB +# For script debugging +# set -uex +# +# Copyright (C) 2017-2025 MariaDB # Copyright (C) 2013 Percona Inc # # This program is free software; you can redistribute it and/or modify @@ -84,6 +86,7 @@ STATDIR="" tmpopts="" itmpdir="" xtmpdir="" +sstdir="" scomp="" sdecomp="" @@ -1315,14 +1318,23 @@ else # joiner fi fi - if [ -d "$DATA/.sst" ]; then + # If user has configured temporary sst directory use it + if [ -n "$WSREP_SST_OPT_TMP_DIR" ]; then + sstdir="$WSREP_SST_OPT_TMP_DIR/.sst" + wsrep_log_info "Using user configured SST directory $sstdir" + else + sstdir="$DATA/.sst" + wsrep_log_info "Using default SST directory $sstdir" + fi + + if [ -d "$sstdir" ]; then wsrep_log_info \ "WARNING: Stale temporary SST directory:" \ - "'$DATA/.sst' from previous state transfer, removing..." - rm -rf "$DATA/.sst" + "'$sstdir' from previous state transfer, removing..." + rm -rf "$sstdir" fi - mkdir -p "$DATA/.sst" - (recv_joiner "$DATA/.sst" "$stagemsg-SST" 0 0 0) & + mkdir -p "$sstdir" + (recv_joiner "$sstdir" "$stagemsg-SST" 0 0 0) & BACKUP_PID=$! wsrep_log_info "Proceeding with SST" @@ -1383,7 +1395,7 @@ else # joiner [ -f "$DATA/mariadb_backup_binlog_pos_innodb" ] && rm -f "$DATA/mariadb_backup_binlog_pos_innodb" TDATA="$DATA" - DATA="$DATA/.sst" + DATA="$sstdir" MAGIC_FILE="$DATA/$INFO_FILE" wsrep_log_info "Waiting for SST streaming to complete!" @@ -1543,6 +1555,7 @@ else # joiner cd "$OLD_PWD" fi + cd "$TDATA" MAGIC_FILE="$TDATA/$INFO_FILE" DONOR_MAGIC_FILE="$TDATA/$DONOR_INFO_FILE" diff --git a/sql/mysqld.cc b/sql/mysqld.cc index de79500825457..cdbfcc974f507 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -1,5 +1,5 @@ /* Copyright (c) 2000, 2015, Oracle and/or its affiliates. - Copyright (c) 2008, 2023, MariaDB + Copyright (c) 2008, 2025, MariaDB plc 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 diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 0936776cf0a00..e6e0579dba7b0 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -1,5 +1,5 @@ /* Copyright (c) 2002, 2015, Oracle and/or its affiliates. - Copyright (c) 2012, 2022, MariaDB Corporation. + Copyright (c) 2012, 2025, MariaDB plc. 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 @@ -6813,6 +6813,11 @@ static Sys_var_uint Sys_wsrep_applier_retry_count ( VALID_RANGE(0, UINT_MAX), DEFAULT(0), BLOCK_SIZE(1), NO_MUTEX_GUARD, NOT_IN_BINLOG); +static Sys_var_charptr_fscs Sys_wsrep_sst_tmp_dir( + "wsrep_sst_tmp_dir", "Path to SST temp root directory", + READ_ONLY GLOBAL_VAR(wsrep_sst_tmp_dir), CMD_LINE(REQUIRED_ARG), + DEFAULT(0), NO_MUTEX_GUARD, NOT_IN_BINLOG); + #endif /* WITH_WSREP */ static bool fix_host_cache_size(sys_var *, THD *, enum_var_type) diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc index 2187a5468c439..a8a3f54af7031 100644 --- a/sql/wsrep_mysqld.cc +++ b/sql/wsrep_mysqld.cc @@ -86,6 +86,8 @@ const char *wsrep_dbug_option; const char *wsrep_notify_cmd; const char *wsrep_status_file; const char *wsrep_allowlist; +const char *wsrep_sst_tmp_dir; +char *wsrep_sst_tmp_dir_real=nullptr; ulong wsrep_debug; // Debug level logging my_bool wsrep_convert_LOCK_to_trx; // Convert locking sessions to trx @@ -911,6 +913,9 @@ int wsrep_init() if (!wsrep_data_home_dir || strlen(wsrep_data_home_dir) == 0) wsrep_data_home_dir= mysql_real_data_home; + /* In case of errors, SST tmp dir is not set */ + wsrep_sst_tmp_dir_check(); + Wsrep_server_state::init_provider_services(); if (Wsrep_server_state::instance().load_provider( wsrep_provider, @@ -947,6 +952,7 @@ int wsrep_init() WSREP_DEBUG("SR storage init for: %s", (wsrep_SR_store_type == WSREP_SR_STORE_TABLE) ? "table" : "void"); + return 0; } @@ -1067,6 +1073,12 @@ void wsrep_deinit(bool free_options) wsrep_inited= 0; + if (wsrep_sst_tmp_dir_real) + { + my_free(wsrep_sst_tmp_dir_real); + wsrep_sst_tmp_dir_real= nullptr; + } + if (wsrep_provider_capabilities != NULL) { char* p= wsrep_provider_capabilities; diff --git a/sql/wsrep_mysqld.h b/sql/wsrep_mysqld.h index 1b381cca4147f..dca2c7b038faf 100644 --- a/sql/wsrep_mysqld.h +++ b/sql/wsrep_mysqld.h @@ -92,6 +92,8 @@ extern uint32 wsrep_gtid_domain_id; extern std::atomic wsrep_thread_create_failed; extern ulonglong wsrep_mode; extern uint wsrep_applier_retry_count; +extern char *wsrep_sst_tmp_dir_real; +extern const char *wsrep_sst_tmp_dir; enum enum_wsrep_reject_types { WSREP_REJECT_NONE, /* nothing rejected */ diff --git a/sql/wsrep_sst.cc b/sql/wsrep_sst.cc index e7de810abd351..665e728cf92cb 100644 --- a/sql/wsrep_sst.cc +++ b/sql/wsrep_sst.cc @@ -37,6 +37,7 @@ #include #include "debug_sync.h" #include "my_rnd.h" +#include #include @@ -609,8 +610,37 @@ static int generate_binlog_index_opt_val(char** ret) return 0; } +/* + Generate sst_opt_tmp_dir string for sst_donate_other() + + Returns zero on success, negative error code otherwise. + + String containing wsrep_sst_tmp_dir is stored in param ret if configured, + otherwise empty string. Returned string should be + freed with my_free(). + */ +static int generate_sst_opt_tmp_dir(char** ret) +{ + DBUG_ASSERT(ret); + *ret= NULL; + if (wsrep_sst_tmp_dir_real && strlen(wsrep_sst_tmp_dir_real)) + { + *ret= generate_name_value(WSREP_SST_OPT_TMP_DIR, + wsrep_sst_tmp_dir_real); + WSREP_INFO("Generate sst_tmp_dir = %s", *ret); + } + else + { + *ret= my_strdup(PSI_INSTRUMENT_ME, "", MYF(0)); + } + if (!*ret) return -ENOMEM; + return 0; +} + + // report progress event -static void sst_report_progress(int const from, +static bool sst_report_progress(const bool joiner, + int const from, long long const total_prev, long long const total, long long const complete) @@ -620,12 +650,42 @@ static void sst_report_progress(int const from, snprintf(buf, buf_len, "{ \"from\": %d, \"to\": %d, \"total\": %lld, \"done\": %lld, " "\"indefinite\": -1 }", - from, WSREP_MEMBER_JOINED, total_prev + total, total_prev +complete); - WSREP_DEBUG("REPORTING SST PROGRESS: '%s'", buf); + from, WSREP_MEMBER_JOINED, total_prev + total, total_prev + complete); + + if (joiner) + { + const char *path = (wsrep_sst_tmp_dir_real && strlen(wsrep_sst_tmp_dir_real) != 0) ? + wsrep_sst_tmp_dir_real : mysql_real_data_home_ptr; + + const std::filesystem::path p(path); + std::error_code ec; + std::filesystem::space_info si = std::filesystem::space(p, ec); + constexpr std::uintmax_t failed(-1); + if (si.available == failed) + si.available= 0; + + const unsigned long long requested= total + total_prev; + + WSREP_INFO("Requested total size %llu space available %llu in path %s", + total, (unsigned long long)si.available, path); + + if (requested >= si.available) + { + WSREP_ERROR("Target path %s does not have enough available space for" + " SST data available: %llu bytes < requested total %llu bytes ", + path, (unsigned long long)si.available, requested); + return true; + } + } + + WSREP_INFO("SST in progress '%s'", buf); + + return false; } // process "complete" event from SST script feedback -static void sst_handle_complete(const char* const input, +static bool sst_handle_complete(const bool joiner, + const char* const input, long long const total_prev, long long* total, long long* complete, @@ -637,12 +697,14 @@ static void sst_handle_complete(const char* const input, { *complete= x; if (*complete > *total) *total= *complete; - sst_report_progress(from, total_prev, *total, *complete); + return sst_report_progress(joiner, from, total_prev, *total, *complete); } + return false; } // process "total" event from SST script feedback -static void sst_handle_total(const char* const input, +static bool sst_handle_total(const bool joiner, + const char* const input, long long* total_prev, long long* total, long long* complete, @@ -656,8 +718,9 @@ static void sst_handle_total(const char* const input, *total_prev+= *total; *total= x; *complete= 0; - sst_report_progress(from, *total_prev, *total, *complete); + return sst_report_progress(joiner, from, *total_prev, *total, *complete); } + return false; } struct sst_thread_init @@ -758,13 +821,21 @@ static void* sst_joiner_thread (void* a) if (!strncasecmp (tmp, magic_complete, complete_len)) { - sst_handle_complete(tmp + complete_len, total_prev, &total, &complete, - from); + if (sst_handle_complete(true, tmp + complete_len, total_prev, + &total, &complete, from)) + { + err = 1; + goto err; + } goto wait_signal; } else if (!strncasecmp (tmp, magic_total, total_len)) { - sst_handle_total(tmp + total_len, &total_prev, &total, &complete, from); + if (sst_handle_total(true, tmp + total_len, &total_prev, &total, &complete, from)) + { + err = 1; + goto err; + } goto wait_signal; } } @@ -1206,6 +1277,7 @@ static ssize_t sst_prepare_other (const char* method, char* binlog_opt_val= NULL; char* binlog_index_opt_val= NULL; + char* sst_tmp_dir=NULL; int ret; if ((ret= generate_binlog_opt_val(&binlog_opt_val))) @@ -1223,6 +1295,14 @@ static ssize_t sst_prepare_other (const char* method, return ret; } + if ((ret= generate_sst_opt_tmp_dir(&sst_tmp_dir))) + { + WSREP_ERROR("sst_prepare_other(): generate_sst_opt_tmp_dir() failed %d", + ret); + if (sst_tmp_dir) my_free(sst_tmp_dir); + return ret; + } + make_wsrep_defaults_file(); ret= snprintf (cmd_str(), cmd_len, @@ -1234,15 +1314,18 @@ static ssize_t sst_prepare_other (const char* method, WSREP_SST_OPT_PARENT " %d " WSREP_SST_OPT_PROGRESS " %d" "%s" + "%s" "%s", method, addr_in, mysql_real_data_home, wsrep_defaults_file, (int)getpid(), wsrep_debug ? 1 : 0, - binlog_opt_val, binlog_index_opt_val); + binlog_opt_val, binlog_index_opt_val, + sst_tmp_dir); my_free(binlog_opt_val); my_free(binlog_index_opt_val); + my_free(sst_tmp_dir); if (ret < 0 || size_t(ret) >= cmd_len) { @@ -2138,13 +2221,13 @@ static void* sst_donor_thread (void* a) if (!strncasecmp (out, magic_complete, complete_len)) { - sst_handle_complete(out + complete_len, total_prev, &total, &complete, + sst_handle_complete(false, out + complete_len, total_prev, &total, &complete, from); goto wait_signal; } else if (!strncasecmp (out, magic_total, total_len)) { - sst_handle_total(out + total_len, &total_prev, &total, &complete, from); + sst_handle_total(false, out + total_len, &total_prev, &total, &complete, from); goto wait_signal; } else if (!strcasecmp (out, magic_flush)) diff --git a/sql/wsrep_sst.h b/sql/wsrep_sst.h index 929a6daa8cc89..d37d6585e319b 100644 --- a/sql/wsrep_sst.h +++ b/sql/wsrep_sst.h @@ -1,4 +1,5 @@ -/* Copyright (C) 2013-2018 Codership Oy +/* Copyright (C) 2013-2025 Codership Oy + Copyright (C) 2025 MariaDB plc 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 @@ -47,6 +48,7 @@ #define WSREP_SST_OPT_GTID "--gtid" #define WSREP_SST_OPT_BYPASS "--bypass" #define WSREP_SST_OPT_GTID_DOMAIN_ID "--gtid-domain-id" +#define WSREP_SST_OPT_TMP_DIR "--sst-tmp-dir" #define WSREP_SST_MYSQLDUMP "mysqldump" #define WSREP_SST_RSYNC "rsync" diff --git a/sql/wsrep_var.cc b/sql/wsrep_var.cc index f20eda78e9985..5db95a183df84 100644 --- a/sql/wsrep_var.cc +++ b/sql/wsrep_var.cc @@ -30,6 +30,10 @@ #include "wsrep_server_state.h" #include "wsrep_plugin.h" /* wsrep_provider_plugin_enabled() */ #include "wsrep_schema.h" +#include "wsrep_mysqld.h" + +#include // statinfo +#include // stat ulong wsrep_reject_queries; @@ -1206,3 +1210,59 @@ bool wsrep_max_ws_rows_check(sys_var *self, THD* thd, set_var* var) } return false; } + +/** Function is used to check if user given sst temporary directory +is valid. Function allows nullptr or empty string i.e. no directory +given. If something real is given it must be an path that exists, +short enough, path must be a directory and it must not be +same as datadir. If proper path was not given actual used +parameter value remains as nullptr. If proper path was given +it is copied to wsrep_sst_tmp_dir_real and used on SST. */ +void wsrep_sst_tmp_dir_check(void) +{ + if (wsrep_sst_tmp_dir == nullptr || strlen(wsrep_sst_tmp_dir) == 0) + return; // DEFAULT is ok + + if (strlen(wsrep_sst_tmp_dir) >= FN_REFLEN) + { + WSREP_ERROR("Option --wsrep-sst-tmp-dir value %s is too long", wsrep_sst_tmp_dir); + return; + } + + struct stat statinfo; + int ret = stat(wsrep_sst_tmp_dir, &statinfo); + + if (!ret) + { + /* path exists, ok */ + } else { + /* path does not exist */ + WSREP_WARN("SST temporary path %s does not exists, path not used.", + wsrep_sst_tmp_dir); + return; + } + + if (!S_ISDIR(statinfo.st_mode)) + { + WSREP_WARN("SST temporary path %s is not a directory, path not used.", + wsrep_sst_tmp_dir); + return; + } + + /* If path is almost same, do not allow it. */ + struct stat datadir_stat; + if (stat(mysql_real_data_home_ptr, &datadir_stat) == 0) + { + if (statinfo.st_dev == datadir_stat.st_dev && + statinfo.st_ino == datadir_stat.st_ino) + { + WSREP_WARN("SST temporary path %s is same prefix as datadir %s, path not used.", + wsrep_sst_tmp_dir, mysql_real_data_home_ptr); + return; + } + } + + wsrep_sst_tmp_dir_real= my_strdup(PSI_INSTRUMENT_ME, wsrep_sst_tmp_dir, MYF(0)); + if (wsrep_sst_tmp_dir_real) + WSREP_INFO("SST temporary path set to %s", wsrep_sst_tmp_dir_real); +} diff --git a/sql/wsrep_var.h b/sql/wsrep_var.h index 00cd0baa2a441..2a4ef3ea5a47d 100644 --- a/sql/wsrep_var.h +++ b/sql/wsrep_var.h @@ -113,6 +113,8 @@ extern bool wsrep_gtid_domain_id_update UPDATE_ARGS; extern bool wsrep_mode_check CHECK_ARGS; extern bool wsrep_forced_binlog_format_check CHECK_ARGS; + +extern void wsrep_sst_tmp_dir_check(void); #else /* WITH_WSREP */ #define wsrep_provider_init(X)