From 337fdb80f3edf3c6fdb47fbd06d044abbafc7041 Mon Sep 17 00:00:00 2001 From: Seppo Jaakola Date: Sun, 6 Oct 2013 23:59:20 +0300 Subject: [PATCH] Merged revisions 3409..3411 from mariadb-galera-5.5 --- .bzrignore | 3 + cmake/install_macros.cmake | 24 ++ cmake/os/FreeBSD.cmake | 6 + scripts/mysqld_safe.sh | 5 +- scripts/wsrep_sst_rsync.sh.moved | 261 ++++++++++++++ scripts/wsrep_sst_xtrabackup.sh.moved | 481 ++++++++++++++++++++++++++ sql/sql_parse.cc | 12 +- sql/wsrep_utils.cc.moved | 468 +++++++++++++++++++++++++ 8 files changed, 1251 insertions(+), 9 deletions(-) create mode 100755 scripts/wsrep_sst_rsync.sh.moved create mode 100644 scripts/wsrep_sst_xtrabackup.sh.moved create mode 100644 sql/wsrep_utils.cc.moved diff --git a/.bzrignore b/.bzrignore index 65624749a5a..2111942ed69 100644 --- a/.bzrignore +++ b/.bzrignore @@ -72,6 +72,9 @@ RelWithDebInfo .vimrc ac_available_languages_fragment BitKeeper/ +build_debug +build_install +build_release client/*.ds? client/completion_hash.cpp client/decimal.c diff --git a/cmake/install_macros.cmake b/cmake/install_macros.cmake index 14e43ee5e95..f9ff1f5cf31 100644 --- a/cmake/install_macros.cmake +++ b/cmake/install_macros.cmake @@ -13,8 +13,30 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +if(APPLE) + LIST(APPEND CMAKE_CXX_LINK_EXECUTABLE "dsymutil ") + LIST(APPEND CMAKE_C_LINK_EXECUTABLE "dsymutil ") + LIST(APPEND CMAKE_CXX_CREATE_SHARED_LIBRARY "dsymutil ") + LIST(APPEND CMAKE_C_CREATE_SHARED_LIBRARY "dsymutil ") + LIST(APPEND CMAKE_CXX_CREATE_SHARED_MODULE "dsymutil ") + LIST(APPEND CMAKE_C_CREATE_SHARED_MODULE "dsymutil ") +ENDIF() + GET_FILENAME_COMPONENT(MYSQL_CMAKE_SCRIPT_DIR ${CMAKE_CURRENT_LIST_FILE} PATH) INCLUDE(${MYSQL_CMAKE_SCRIPT_DIR}/cmake_parse_arguments.cmake) +MACRO (INSTALL_DSYM_DIRECTORIES targets) + IF(APPLE) + FOREACH(target ${targets}) + GET_TARGET_PROPERTY(location ${target} LOCATION) + GET_TARGET_PROPERTY(type ${target} TYPE) + # It's a dirty hack, but cmake too stupid and mysql cmake files too buggy */ + STRING(REPLACE "liblibmysql.dylib" "libmysqlclient.${SHARED_LIB_MAJOR_VERSION}.dylib" location ${location}) + IF(type MATCHES "EXECUTABLE" OR type MATCHES "MODULE" OR type MATCHES "SHARED_LIBRARY") + INSTALL(DIRECTORY "${location}.dSYM" DESTINATION ${INSTALL_LOCATION} COMPONENT Debuginfo) + ENDIF() + ENDFOREACH() + ENDIF() +ENDMACRO() FUNCTION (INSTALL_DEBUG_SYMBOLS) IF(MSVC) @@ -31,6 +53,7 @@ FUNCTION (INSTALL_DEBUG_SYMBOLS) MESSAGE(FATAL_ERROR "No INSTALL_LOCATION passed to INSTALL_DEBUG_SYMBOLS") ENDIF() SET(targets ${ARG_DEFAULT_ARGS}) + FOREACH(target ${targets}) GET_TARGET_PROPERTY(type ${target} TYPE) GET_TARGET_PROPERTY(location ${target} LOCATION) @@ -252,6 +275,7 @@ FUNCTION(MYSQL_INSTALL_TARGETS) INSTALL(TARGETS ${TARGETS} DESTINATION ${ARG_DESTINATION} ${COMP}) INSTALL_DEBUG_SYMBOLS(${TARGETS} ${COMP} INSTALL_LOCATION ${ARG_DESTINATION}) + INSTALL_DSYM_DIRECTORIES("${TARGETS}") ENDFUNCTION() diff --git a/cmake/os/FreeBSD.cmake b/cmake/os/FreeBSD.cmake index 3a5a29f6de6..bc38fa3e5ba 100644 --- a/cmake/os/FreeBSD.cmake +++ b/cmake/os/FreeBSD.cmake @@ -32,3 +32,9 @@ IF(EXECINFO) SET(LIBEXECINFO ${EXECINFO}) ENDIF() +<<<<<<< TREE +======= + +SET(HAVE_SYS_TIMEB_H CACHE INTERNAL "") + +>>>>>>> MERGE-SOURCE diff --git a/scripts/mysqld_safe.sh b/scripts/mysqld_safe.sh index 113c3236fab..1d41d7142f1 100644 --- a/scripts/mysqld_safe.sh +++ b/scripts/mysqld_safe.sh @@ -239,7 +239,8 @@ wsrep_recover_position() { if [ -z "$rp" ]; then local skipped="$(grep WSREP $wr_logfile | grep 'skipping position recovery')" if [ -z "$skipped" ]; then - log_error "WSREP: Failed to recover position: " `cat $wr_logfile`; + log_error "WSREP: Failed to recover position: +'`cat $wr_logfile`'" ret=1 else log_notice "WSREP: Position recovery skipped" @@ -251,7 +252,7 @@ wsrep_recover_position() { wsrep_start_position_opt="--wsrep_start_position=$start_pos" fi - rm $wr_logfile + [ $ret -eq 0 ] && rm $wr_logfile return $ret } diff --git a/scripts/wsrep_sst_rsync.sh.moved b/scripts/wsrep_sst_rsync.sh.moved new file mode 100755 index 00000000000..7cd232e0887 --- /dev/null +++ b/scripts/wsrep_sst_rsync.sh.moved @@ -0,0 +1,261 @@ +#!/bin/bash -ue + +# Copyright (C) 2010 Codership Oy +# +# 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; see the file COPYING. If not, write to the +# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston +# MA 02110-1301 USA. + +# This is a reference script for rsync-based state snapshot tansfer + +RSYNC_PID= +RSYNC_CONF= +OS=$(uname) +[ "$OS" == "Darwin" ] && export -n LD_LIBRARY_PATH + +. $(dirname $0)/wsrep_sst_common + +cleanup_joiner() +{ + wsrep_log_info "Joiner cleanup." + local PID=$(cat "$RSYNC_PID" 2>/dev/null || echo 0) + [ "0" != "$PID" ] && kill $PID && sleep 0.5 && kill -9 $PID >/dev/null 2>&1 \ + || : + rm -rf "$RSYNC_CONF" + rm -rf "$MAGIC_FILE" + rm -rf "$RSYNC_PID" + wsrep_log_info "Joiner cleanup done." + if [ "${WSREP_SST_OPT_ROLE}" = "joiner" ];then + wsrep_cleanup_progress_file + fi +} + +check_pid() +{ + local pid_file=$1 + [ -r "$pid_file" ] && ps -p $(cat $pid_file) >/dev/null 2>&1 +} + +check_pid_and_port() +{ + local pid_file=$1 + local rsync_pid=$(cat $pid_file) + local rsync_port=$2 + + if [ "$OS" == "Darwin" -o "$OS" == "FreeBSD" ]; then + # no netstat --program(-p) option in Darwin and FreeBSD + check_pid $pid_file && \ + lsof -i -Pn 2>/dev/null | \ + grep "(LISTEN)" | grep ":$rsync_port" | grep -w '^rsync[[:space:]]\+'"$rsync_pid" >/dev/null + else + check_pid $pid_file && \ + netstat -lnpt 2>/dev/null | \ + grep LISTEN | grep \:$rsync_port | grep $rsync_pid/rsync >/dev/null + fi +} + +MAGIC_FILE="$WSREP_SST_OPT_DATA/rsync_sst_complete" +rm -rf "$MAGIC_FILE" + +# Old filter - include everything except selected +# FILTER=(--exclude '*.err' --exclude '*.pid' --exclude '*.sock' \ +# --exclude '*.conf' --exclude core --exclude 'galera.*' \ +# --exclude grastate.txt --exclude '*.pem' \ +# --exclude '*.[0-9][0-9][0-9][0-9][0-9][0-9]' --exclude '*.index') + +# New filter - exclude everything except dirs (schemas) and innodb files +FILTER=(-f '- lost+found' -f '+ /ib_lru_dump' -f '+ /ibdata*' -f '+ /ib_logfile*' -f '+ */' -f '-! */*') +# Old versions of rsync have a bug transferring filter rules to daemon, so specify filter rules directly to daemon +FILTER_DAEMON="- lost+found + /ib_lru_dump + /ibdata* + ib_logfile* + */ -! */*" + +if [ "$WSREP_SST_OPT_ROLE" = "donor" ] +then + + if [ $WSREP_SST_OPT_BYPASS -eq 0 ] + then + + FLUSHED="$WSREP_SST_OPT_DATA/tables_flushed" + rm -rf "$FLUSHED" + + # Use deltaxfer only for WAN + inv=$(basename $0) + [ "$inv" = "wsrep_sst_rsync_wan" ] && WHOLE_FILE_OPT="" \ + || WHOLE_FILE_OPT="--whole-file" + + echo "flush tables" + + # wait for tables flushed and state ID written to the file + while [ ! -r "$FLUSHED" ] && ! grep -q ':' "$FLUSHED" >/dev/null 2>&1 + do + sleep 0.2 + done + + STATE="$(cat $FLUSHED)" + rm -rf "$FLUSHED" + + sync + + # first, the normal directories, so that we can detect incompatible protocol + RC=0 + rsync --archive --no-times --ignore-times --inplace --delete --quiet \ + --no-recursive --dirs \ + $WHOLE_FILE_OPT "${FILTER[@]}" "$WSREP_SST_OPT_DATA/" \ + rsync://$WSREP_SST_OPT_ADDR-with_filter || RC=$? + + [ $RC -ne 0 ] && wsrep_log_error "rsync returned code $RC:" + + case $RC in + 0) RC=0 # Success + ;; + 12) RC=71 # EPROTO + wsrep_log_error \ + "rsync server on the other end has incompatible protocol. " \ + "Make sure you have the same version of rsync on all nodes." + ;; + 22) RC=12 # ENOMEM + ;; + *) RC=255 # unknown error + ;; + esac + + [ $RC -ne 0 ] && exit $RC + + # then, we parallelize the transfer of database directories, use . so that pathconcatenation works + pushd "$WSREP_SST_OPT_DATA" 1>/dev/null + + count=1 + [ "$OS" == "Linux" ] && count=$(grep -c processor /proc/cpuinfo) + [ "$OS" == "Darwin" -o "$OS" == "FreeBSD" ] && count=$(sysctl -n hw.ncpu) + + find . -maxdepth 1 -mindepth 1 -type d -print0 | xargs -I{} -0 -P $count \ + rsync --archive --no-times --ignore-times --inplace --delete --quiet \ + $WHOLE_FILE_OPT "$WSREP_SST_OPT_DATA"/{}/ \ + rsync://$WSREP_SST_OPT_ADDR/{} || RC=$? + + popd 1>/dev/null + + [ $RC -ne 0 ] && wsrep_log_error "find/rsync returned code $RC:" + + case $RC in + 0) RC=0 # Success + ;; + *) RC=255 # unknown error + ;; + esac + + [ $RC -ne 0 ] && exit $RC + + + else # BYPASS + wsrep_log_info "Bypassing state dump." + STATE="$WSREP_SST_OPT_GTID" + fi + + echo "continue" # now server can resume updating data + + echo "$STATE" > "$MAGIC_FILE" + rsync --archive --quiet --checksum "$MAGIC_FILE" rsync://$WSREP_SST_OPT_ADDR + + echo "done $STATE" + +elif [ "$WSREP_SST_OPT_ROLE" = "joiner" ] +then + touch $SST_PROGRESS_FILE + MYSQLD_PID=$WSREP_SST_OPT_PARENT + + MODULE="rsync_sst" + + RSYNC_PID="$WSREP_SST_OPT_DATA/$MODULE.pid" + + if check_pid $RSYNC_PID + then + wsrep_log_error "rsync daemon already running." + exit 114 # EALREADY + fi + rm -rf "$RSYNC_PID" + + ADDR=$WSREP_SST_OPT_ADDR + RSYNC_PORT=$(echo $ADDR | awk -F ':' '{ print $2 }') + if [ -z "$RSYNC_PORT" ] + then + RSYNC_PORT=4444 + ADDR="$(echo $ADDR | awk -F ':' '{ print $1 }'):$RSYNC_PORT" + fi + + trap "exit 32" HUP PIPE + trap "exit 3" INT TERM ABRT + trap cleanup_joiner EXIT + + MYUID=$(id -u) + MYGID=$(id -g) + RSYNC_CONF="$WSREP_SST_OPT_DATA/$MODULE.conf" + +cat << EOF > "$RSYNC_CONF" +pid file = $RSYNC_PID +use chroot = no +[$MODULE-with_filter] + path = $WSREP_SST_OPT_DATA + read only = no + timeout = 300 + uid = $MYUID + gid = $MYGID + filter = $FILTER_DAEMON +[$MODULE] + path = $WSREP_SST_OPT_DATA + read only = no + timeout = 300 + uid = $MYUID + gid = $MYGID +EOF + +# rm -rf "$DATA"/ib_logfile* # we don't want old logs around + + # listen at all interfaces (for firewalled setups) + rsync --daemon --port $RSYNC_PORT --config "$RSYNC_CONF" + + until check_pid_and_port $RSYNC_PID $RSYNC_PORT + do + sleep 0.2 + done + + echo "ready $ADDR/$MODULE" + + # wait for SST to complete by monitoring magic file + while [ ! -r "$MAGIC_FILE" ] && check_pid "$RSYNC_PID" && \ + ps -p $MYSQLD_PID >/dev/null + do + sleep 1 + done + + if ! ps -p $MYSQLD_PID >/dev/null + then + wsrep_log_error \ + "Parent mysqld process (PID:$MYSQLD_PID) terminated unexpectedly." + exit 32 + fi + + if [ -r "$MAGIC_FILE" ] + then + cat "$MAGIC_FILE" # output UUID:seqno + else + # this message should cause joiner to abort + echo "rsync process ended without creating '$MAGIC_FILE'" + fi + wsrep_cleanup_progress_file +# cleanup_joiner +else + wsrep_log_error "Unrecognized role: '$WSREP_SST_OPT_ROLE'" + exit 22 # EINVAL +fi + +exit 0 diff --git a/scripts/wsrep_sst_xtrabackup.sh.moved b/scripts/wsrep_sst_xtrabackup.sh.moved new file mode 100644 index 00000000000..3eb747f50c3 --- /dev/null +++ b/scripts/wsrep_sst_xtrabackup.sh.moved @@ -0,0 +1,481 @@ +#!/bin/bash -ue +# Copyright (C) 2013 Percona Inc +# +# 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; see the file COPYING. If not, write to the +# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston +# MA 02110-1301 USA. + +############################################################################################################# +# This is a reference script for Percona XtraBackup-based state snapshot transfer # +# Dependencies: (depending on configuration) # +# xbcrypt for encryption/decryption. # +# qpress for decompression. Download from http://www.quicklz.com/qpress-11-linux-x64.tar till # +# https://blueprints.launchpad.net/percona-xtrabackup/+spec/package-qpress is fixed. # +# my_print_defaults to extract values from my.cnf. # +# netcat for transfer. # +# xbstream/tar for streaming. (and xtrabackup ofc) # +# # +# Currently only option in cnf is read specifically for SST # +# [sst] # +# streamfmt=tar|xbstream # +# # +# Default is tar till lp:1193240 is fixed # +# You need to use xbstream for encryption, compression etc., however, # +# lp:1193240 requires you to manually cleanup the directory prior to SST # +# # +############################################################################################################# + +. $(dirname $0)/wsrep_sst_common + +ealgo="" +ekey="" +ekeyfile="" +encrypt=0 +nproc=1 +ecode=0 +XTRABACKUP_PID="" + +sfmt="tar" +strmcmd="" +declare -a RC + +get_keys() +{ + # There is no metadata in the stream to indicate that it is encrypted + # So, if the cnf file on joiner contains 'encrypt' under [xtrabackup] section then + # it means encryption is being used + if ! my_print_defaults -c $WSREP_SST_OPT_CONF xtrabackup | grep -q encrypt; then + return + fi + if [[ $sfmt == 'tar' ]];then + wsrep_log_info "NOTE: Encryption cannot be enabled with tar format" + return + fi + + wsrep_log_info "Encryption enabled in my.cnf - not supported at the moment - Bug in Xtrabackup - lp:1190343" + ealgo=$(my_print_defaults -c $WSREP_SST_OPT_CONF xtrabackup | grep -- '--encrypt=' | cut -d= -f2) + ekey=$(my_print_defaults -c $WSREP_SST_OPT_CONF xtrabackup | grep -- '--encrypt-key=' | cut -d= -f2) + ekeyfile=$(my_print_defaults -c $WSREP_SST_OPT_CONF xtrabackup | grep -- '--encrypt-key-file=' | cut -d= -f2) + + if [[ -z $ealgo ]];then + wsrep_log_error "FATAL: Encryption algorithm empty from my.cnf, bailing out" + exit 3 + fi + + if [[ -z $ekey && ! -r $ekeyfile ]];then + wsrep_log_error "FATAL: Either key or keyfile must be readable" + exit 3 + fi + encrypt=1 +} + +read_cnf() +{ + sfmt=$(my_print_defaults -c $WSREP_SST_OPT_CONF sst | grep -- '--streamfmt' | cut -d= -f2) + if [[ $sfmt == 'xbstream' ]];then + wsrep_log_info "Streaming with xbstream" + if [[ "$WSREP_SST_OPT_ROLE" == "joiner" ]];then + wsrep_log_info "xbstream requires manual cleanup of data directory before SST - lp:1193240" + strmcmd="xbstream -x -C ${DATA}" + elif [[ "$WSREP_SST_OPT_ROLE" == "donor" ]];then + strmcmd="xbstream -c ${INFO_FILE} ${IST_FILE}" + else + wsrep_log_error "Invalid role: $WSREP_SST_OPT_ROLE" + exit 22 + fi + else + sfmt="tar" + wsrep_log_info "Streaming with tar" + wsrep_log_info "Note: Advanced xtrabackup features - encryption,compression etc. not available with tar." + if [[ "$WSREP_SST_OPT_ROLE" == "joiner" ]];then + wsrep_log_info "However, xbstream requires manual cleanup of data directory before SST - lp:1193240." + strmcmd="tar xfi - -C ${DATA}" + elif [[ "$WSREP_SST_OPT_ROLE" == "donor" ]];then + strmcmd="tar cf - ${INFO_FILE} ${IST_FILE}" + else + wsrep_log_error "Invalid role: $WSREP_SST_OPT_ROLE" + exit 22 + fi + + fi +} + +get_proc() +{ + set +e + nproc=$(grep -c processor /proc/cpuinfo) + [[ -z $nproc || $nproc -eq 0 ]] && nproc=1 + set -e +} + +cleanup_joiner() +{ + # Since this is invoked just after exit NNN + local estatus=$? + if [[ $estatus -ne 0 ]];then + wsrep_log_error "Cleanup after exit with status:$estatus" + fi + local PID=$(ps -aef |grep nc| grep $NC_PORT | awk '{ print $2 }') + if [[ $estatus -ne 0 ]];then + wsrep_log_error "Killing nc pid $PID" + else + wsrep_log_info "Killing nc pid $PID" + fi + [ -n "$PID" -a "0" != "$PID" ] && kill $PID && (kill $PID && kill -9 $PID) || : + rm -f "$MAGIC_FILE" + if [ "${WSREP_SST_OPT_ROLE}" = "joiner" ];then + wsrep_log_info "Removing the sst_in_progress file" + wsrep_cleanup_progress_file + fi +} + +check_pid() +{ + local pid_file="$1" + [ -r "$pid_file" ] && ps -p $(cat "$pid_file") >/dev/null 2>&1 +} + +cleanup_donor() +{ + # Since this is invoked just after exit NNN + local estatus=$? + if [[ $estatus -ne 0 ]];then + wsrep_log_error "Cleanup after exit with status:$estatus" + fi + local pid=$XTRABACKUP_PID + if check_pid "$pid" + then + wsrep_log_error "xtrabackup process is still running. Killing... " + kill_xtrabackup + fi + + rm -f "$pid" + rm -f ${DATA}/${IST_FILE} +} + +kill_xtrabackup() +{ +#set -x + local PID=$(cat $XTRABACKUP_PID) + [ -n "$PID" -a "0" != "$PID" ] && kill $PID && (kill $PID && kill -9 $PID) || : + rm -f "$XTRABACKUP_PID" +#set +x +} + +# waits ~10 seconds for nc to open the port and then reports ready +# (regardless of timeout) +wait_for_nc() +{ + local PORT=$1 + local ADDR=$2 + local MODULE=$3 + for i in $(seq 1 50) + do + netstat -nptl 2>/dev/null | grep '/nc\s*$' | awk '{ print $4 }' | \ + sed 's/.*://' | grep \^${PORT}\$ >/dev/null && break + sleep 0.2 + done + echo "ready ${ADDR}/${MODULE}" +} + +INNOBACKUPEX_BIN=innobackupex +INNOBACKUPEX_ARGS="" +NC_BIN=nc + +for TOOL_BIN in INNOBACKUPEX_BIN NC_BIN ; do + if ! which ${!TOOL_BIN} > /dev/null 2>&1 + then + echo "Can't find ${!TOOL_BIN} in the path" + exit 22 # EINVAL + fi +done + +#ROLE=$1 +#ADDR=$2 +readonly AUTH=(${WSREP_SST_OPT_AUTH//:/ }) +readonly DATA="${WSREP_SST_OPT_DATA}" +#CONF=$5 + +INFO_FILE="xtrabackup_galera_info" +IST_FILE="xtrabackup_ist" + +MAGIC_FILE="${DATA}/${INFO_FILE}" +rm -f "${MAGIC_FILE}" + +read_cnf + +if [ "$WSREP_SST_OPT_ROLE" = "donor" ] +then + trap cleanup_donor EXIT + + NC_PORT=$(echo $WSREP_SST_OPT_ADDR | awk -F '[:/]' '{ print $2 }') + REMOTEIP=$(echo $WSREP_SST_OPT_ADDR | awk -F ':' '{ print $1 }') + + if [ $WSREP_SST_OPT_BYPASS -eq 0 ] + then + TMPDIR="/tmp" + + INNOBACKUPEX_ARGS="--galera-info --stream=$sfmt + --defaults-file=${WSREP_SST_OPT_CONF} + --socket=${WSREP_SST_OPT_SOCKET}" + + if [ "${AUTH[0]}" != "(null)" ]; then + INNOBACKUPEX_ARGS="${INNOBACKUPEX_ARGS} --user=${AUTH[0]}" + fi + + if [ ${#AUTH[*]} -eq 2 ]; then + INNOBACKUPEX_ARGS="${INNOBACKUPEX_ARGS} --password=${AUTH[1]}" + else + # Empty password, used for testing, debugging etc. + INNOBACKUPEX_ARGS="${INNOBACKUPEX_ARGS} --password=" + fi + + get_keys + if [[ $encrypt -eq 1 ]];then + if [[ -n $ekey ]];then + INNOBACKUPEX_ARGS="${INNOBACKUPEX_ARGS} --encrypt=$ealgo --encrypt-key=$ekey" + else + INNOBACKUPEX_ARGS="${INNOBACKUPEX_ARGS} --encrypt=$ealgo --encrypt-key-file=$ekeyfile" + fi + fi + + wsrep_log_info "Streaming the backup to joiner at ${REMOTEIP} ${NC_PORT}" + + set +e + ${INNOBACKUPEX_BIN} ${INNOBACKUPEX_ARGS} ${TMPDIR} \ + 2> ${DATA}/innobackup.backup.log | \ + ${NC_BIN} ${REMOTEIP} ${NC_PORT} + + RC=( "${PIPESTATUS[@]}" ) + set -e + + if [ ${RC[0]} -ne 0 ]; then + wsrep_log_error "${INNOBACKUPEX_BIN} finished with error: ${RC[0]}. " \ + "Check ${DATA}/innobackup.backup.log" + exit 22 + elif [ ${RC[1]} -ne 0 ]; then + wsrep_log_error "${NC_BIN} finished with error: ${RC[1]}" + exit 22 + fi + + # innobackupex implicitly writes PID to fixed location in ${TMPDIR} + XTRABACKUP_PID="${TMPDIR}/xtrabackup_pid" + + + else # BYPASS + wsrep_log_info "Bypassing the SST for IST" + STATE="${WSREP_SST_OPT_GTID}" + echo "continue" # now server can resume updating data + echo "${STATE}" > "${MAGIC_FILE}" + echo "1" > "${DATA}/${IST_FILE}" + get_keys + pushd ${DATA} 1>/dev/null + set +e + if [[ $encrypt -eq 1 ]];then + if [[ -n $ekey ]];then + xbstream -c ${INFO_FILE} ${IST_FILE} | xbcrypt --encrypt-algo=$ealgo --encrypt-key=$ekey | ${NC_BIN} ${REMOTEIP} ${NC_PORT} + else + xbstream -c ${INFO_FILE} ${IST_FILE} | xbcrypt --encrypt-algo=$ealgo --encrypt-key-file=$ekeyfile | ${NC_BIN} ${REMOTEIP} ${NC_PORT} + fi + else + $strmcmd | ${NC_BIN} ${REMOTEIP} ${NC_PORT} + fi + RC=( "${PIPESTATUS[@]}" ) + set -e + popd 1>/dev/null + + for ecode in "${RC[@]}";do + if [[ $ecode -ne 0 ]];then + wsrep_log_error "Error while streaming data to joiner node: " \ + "exit codes: ${RC[@]}" + exit 1 + fi + done + #rm -f ${DATA}/${IST_FILE} + fi + + echo "done ${WSREP_SST_OPT_GTID}" + +elif [ "${WSREP_SST_OPT_ROLE}" = "joiner" ] +then + [[ -e $SST_PROGRESS_FILE ]] && wsrep_log_info "Stale sst_in_progress file: $SST_PROGRESS_FILE" + touch $SST_PROGRESS_FILE + + sencrypted=1 + nthreads=1 + + MODULE="xtrabackup_sst" + + rm -f ${DATA}/xtrabackup_* + + ADDR=${WSREP_SST_OPT_ADDR} + NC_PORT=$(echo ${ADDR} | awk -F ':' '{ print $2 }') + if [ -z "${NC_PORT}" ] + then + NC_PORT=4444 + ADDR="$(echo ${ADDR} | awk -F ':' '{ print $1 }'):${NC_PORT}" + fi + + wait_for_nc ${NC_PORT} ${ADDR} ${MODULE} & + + trap "exit 32" HUP PIPE + trap "exit 3" INT TERM + trap cleanup_joiner EXIT + + get_keys + set +e + if [[ $encrypt -eq 1 && $sencrypted -eq 1 ]];then + if [[ -n $ekey ]];then + ${NC_BIN} -dl ${NC_PORT} | xbcrypt -d --encrypt-algo=$ealgo --encrypt-key=$ekey | xbstream -x -C ${DATA} + else + ${NC_BIN} -dl ${NC_PORT} | xbcrypt -d --encrypt-algo=$ealgo --encrypt-key-file=$ekeyfile | xbstream -x -C ${DATA} + fi + else + ${NC_BIN} -dl ${NC_PORT} | $strmcmd + fi + RC=( "${PIPESTATUS[@]}" ) + set -e + + if [[ $sfmt == 'xbstream' ]];then + # Special handling till lp:1193240 is fixed" + if [[ ${RC[$(( ${#RC[@]}-1 ))]} -eq 1 ]];then + wsrep_log_error "Xbstream failed" + wsrep_log_error "Data directory ${DATA} needs to be empty for SST: lp:1193240" \ + "Manual intervention required in that case" + exit 32 + fi + fi + + wait %% # join wait_for_nc thread + + for ecode in "${RC[@]}";do + if [[ $ecode -ne 0 ]];then + wsrep_log_error "Error while getting data from donor node: " \ + "exit codes: ${RC[@]}" + wsrep_log_error "Data directory ${DATA} needs to be empty for SST:" \ + "Manual intervention required in that case" + exit 32 + fi + done + + if [ ! -r "${MAGIC_FILE}" ] + then + # this message should cause joiner to abort + wsrep_log_error "xtrabackup process ended without creating '${MAGIC_FILE}'" + exit 32 + fi + + if ! ps -p ${WSREP_SST_OPT_PARENT} >/dev/null + then + wsrep_log_error "Parent mysqld process (PID:${WSREP_SST_OPT_PARENT}) terminated unexpectedly." + exit 32 + fi + + if [ ! -r "${IST_FILE}" ] + then + wsrep_log_info "Proceeding with SST" + rebuild="" + wsrep_log_info "Removing existing ib_logfile files" + rm -f ${DATA}/ib_logfile* + + # Decrypt only if not encrypted in stream. + # NOT USED NOW. + # Till https://blueprints.launchpad.net/percona-xtrabackup/+spec/add-support-for-rsync-url + # is implemented + #get_keys + if [[ $encrypt -eq 1 && $sencrypted -eq 0 ]];then + # Decrypt the files if any + find ${DATA} -type f -name '*.xbcrypt' -printf '%p\n' | while read line;do + input=$line + output=${input%.xbcrypt} + + if [[ -n $ekey ]];then + xbcrypt -d --encrypt-algo=$ealgo --encrypt-key=$ekey -i $input > $output + else + xbcrypt -d --encrypt-algo=$ealgo --encrypt-key-file=$ekeyfile -i $input > $output + fi + done + + if [[ $? = 0 ]];then + find ${DATA} -type f -name '*.xbcrypt' -delete + fi + fi + + get_proc + + # Rebuild indexes for compact backups + if grep -q 'compact = 1' ${DATA}/xtrabackup_checkpoints;then + wsrep_log_info "Index compaction detected" + nthreads=$(my_print_defaults -c $WSREP_SST_OPT_CONF xtrabackup | grep -- '--rebuild-threads' | cut -d= -f2) + [[ -z $nthreads ]] && nthreads=$nproc + wsrep_log_info "Rebuilding with $nthreads threads" + rebuild="--rebuild-indexes --rebuild-threads=$nthreads" + fi + + if test -n "$(find ${DATA} -maxdepth 1 -name '*.qp' -print -quit)";then + + wsrep_log_info "Compressed qpress files found" + + if [[ ! -x `which qpress` ]];then + wsrep_log_error "qpress not found in PATH" + exit 22 + fi + + set +e + + wsrep_log_info "Removing existing ibdata1 file" + rm -f ${DATA}/ibdata1 + + # Decompress the qpress files + wsrep_log_info "Decompression with $nproc threads" + find ${DATA} -type f -name '*.qp' -printf '%p\n%h\n' | xargs -P $nproc -n 2 qpress -d + extcode=$? + + set -e + + if [[ $extcode -eq 0 ]];then + wsrep_log_info "Removing qpress files after decompression" + find ${DATA} -type f -name '*.qp' -delete + if [[ $? -ne 0 ]];then + wsrep_log_error "Something went wrong with deletion of qpress files. Investigate" + fi + else + wsrep_log_error "Decompression failed. Exit code: $extcode" + exit 22 + fi + fi + + wsrep_log_info "Preparing the backup at ${DATA}" + + ${INNOBACKUPEX_BIN} --defaults-file=${WSREP_SST_OPT_CONF} --apply-log $rebuild \ + ${DATA} 1>&2 2> ${DATA}/innobackup.prepare.log + if [ $? -ne 0 ]; + then + wsrep_log_error "${INNOBACKUPEX_BIN} finished with errors. Check ${DATA}/innobackup.prepare.log" + exit 22 + fi + else + wsrep_log_info "${IST_FILE} received from donor: Running IST" + fi + + cat "${MAGIC_FILE}" # output UUID:seqno + + #Cleanup not required here since EXIT trap should be called + #wsrep_cleanup_progress_file + +else + wsrep_log_error "Unrecognized role: ${WSREP_SST_OPT_ROLE}" + exit 22 # EINVAL +fi + +exit 0 diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index e2e96dd543a..84fc036431d 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -9019,13 +9019,11 @@ void wsrep_replication_process(THD *thd) break; } - if (thd->killed != KILL_CONNECTION) - { - mysql_mutex_lock(&LOCK_thread_count); - wsrep_close_applier(thd); - mysql_cond_broadcast(&COND_thread_count); - mysql_mutex_unlock(&LOCK_thread_count); - } + mysql_mutex_lock(&LOCK_thread_count); + wsrep_close_applier(thd); + mysql_cond_broadcast(&COND_thread_count); + mysql_mutex_unlock(&LOCK_thread_count); + wsrep_return_from_bf_mode(thd, &shadow); DBUG_VOID_RETURN; } diff --git a/sql/wsrep_utils.cc.moved b/sql/wsrep_utils.cc.moved new file mode 100644 index 00000000000..19ffb101841 --- /dev/null +++ b/sql/wsrep_utils.cc.moved @@ -0,0 +1,468 @@ +/* Copyright 2010 Codership Oy + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//! @file declares symbols private to wsrep integration layer + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE // POSIX_SPAWN_USEVFORK flag +#endif + +#include // posix_spawn() +#include // pipe() +#include // errno +#include // strerror() +#include // waitpid() + +#include +#include "wsrep_priv.h" + +extern char** environ; // environment variables + +static wsp::string wsrep_PATH; + +void +wsrep_prepend_PATH (const char* path) +{ + int count = 0; + + while (environ[count]) + { + if (strncmp (environ[count], "PATH=", 5)) + { + count++; + continue; + } + + char* const old_path (environ[count]); + + if (strstr (old_path, path)) return; // path already there + + size_t const new_path_len(strlen(old_path) + strlen(":") + + strlen(path) + 1); + + char* const new_path (reinterpret_cast(malloc(new_path_len))); + + if (new_path) + { + snprintf (new_path, new_path_len, "PATH=%s:%s", path, + old_path + strlen("PATH=")); + + wsrep_PATH.set (new_path); + environ[count] = new_path; + } + else + { + WSREP_ERROR ("Failed to allocate 'PATH' environment variable " + "buffer of size %zu.", new_path_len); + } + + return; + } + + WSREP_ERROR ("Failed to find 'PATH' environment variable. " + "State snapshot transfer may not be working."); +} + +namespace wsp +{ + +#define PIPE_READ 0 +#define PIPE_WRITE 1 +#define STDIN_FD 0 +#define STDOUT_FD 1 + +#ifndef POSIX_SPAWN_USEVFORK +# define POSIX_SPAWN_USEVFORK 0 +#endif + +process::process (const char* cmd, const char* type) + : str_(cmd ? strdup(cmd) : strdup("")), io_(NULL), err_(EINVAL), pid_(0) +{ + if (0 == str_) + { + WSREP_ERROR ("Can't allocate command line of size: %zu", strlen(cmd)); + err_ = ENOMEM; + return; + } + + if (0 == strlen(str_)) + { + WSREP_ERROR ("Can't start a process: null or empty command line."); + return; + } + + if (NULL == type || (strcmp (type, "w") && strcmp(type, "r"))) + { + WSREP_ERROR ("type argument should be either \"r\" or \"w\"."); + return; + } + + int pipe_fds[2] = { -1, }; + if (::pipe(pipe_fds)) + { + err_ = errno; + WSREP_ERROR ("pipe() failed: %d (%s)", err_, strerror(err_)); + return; + } + + // which end of pipe will be returned to parent + int const parent_end (strcmp(type,"w") ? PIPE_READ : PIPE_WRITE); + int const child_end (parent_end == PIPE_READ ? PIPE_WRITE : PIPE_READ); + int const close_fd (parent_end == PIPE_READ ? STDOUT_FD : STDIN_FD); + + char* const pargv[4] = { strdup("sh"), strdup("-c"), strdup(str_), NULL }; + if (!(pargv[0] && pargv[1] && pargv[2])) + { + err_ = ENOMEM; + WSREP_ERROR ("Failed to allocate pargv[] array."); + goto cleanup_pipe; + } + + posix_spawnattr_t attr; + err_ = posix_spawnattr_init (&attr); + if (err_) + { + WSREP_ERROR ("posix_spawnattr_init() failed: %d (%s)", + err_, strerror(err_)); + goto cleanup_pipe; + } + + err_ = posix_spawnattr_setflags (&attr, POSIX_SPAWN_SETSIGDEF | + POSIX_SPAWN_USEVFORK); + if (err_) + { + WSREP_ERROR ("posix_spawnattr_setflags() failed: %d (%s)", + err_, strerror(err_)); + goto cleanup_attr; + } + + posix_spawn_file_actions_t fact; + err_ = posix_spawn_file_actions_init (&fact); + if (err_) + { + WSREP_ERROR ("posix_spawn_file_actions_init() failed: %d (%s)", + err_, strerror(err_)); + goto cleanup_attr; + } + + // close child's stdout|stdin depending on what we returning + err_ = posix_spawn_file_actions_addclose (&fact, close_fd); + if (err_) + { + WSREP_ERROR ("posix_spawn_file_actions_addclose() failed: %d (%s)", + err_, strerror(err_)); + goto cleanup_fact; + } + + // substitute our pipe descriptor in place of the closed one + err_ = posix_spawn_file_actions_adddup2 (&fact, + pipe_fds[child_end], close_fd); + if (err_) + { + WSREP_ERROR ("posix_spawn_file_actions_addup2() failed: %d (%s)", + err_, strerror(err_)); + goto cleanup_fact; + } + + err_ = posix_spawnp (&pid_, pargv[0], &fact, &attr, pargv, environ); + if (err_) + { + WSREP_ERROR ("posix_spawnp(%s) failed: %d (%s)", + pargv[2], err_, strerror(err_)); + pid_ = 0; // just to make sure it was not messed up in the call + goto cleanup_fact; + } + + io_ = fdopen (pipe_fds[parent_end], type); + + if (io_) + { + pipe_fds[parent_end] = -1; // skip close on cleanup + } + else + { + err_ = errno; + WSREP_ERROR ("fdopen() failed: %d (%s)", err_, strerror(err_)); + } + +cleanup_fact: + int err; // to preserve err_ code + err = posix_spawn_file_actions_destroy (&fact); + if (err) + { + WSREP_ERROR ("posix_spawn_file_actions_destroy() failed: %d (%s)\n", + err, strerror(err)); + } + +cleanup_attr: + err = posix_spawnattr_destroy (&attr); + if (err) + { + WSREP_ERROR ("posix_spawnattr_destroy() failed: %d (%s)", + err, strerror(err)); + } + +cleanup_pipe: + if (pipe_fds[0] >= 0) close (pipe_fds[0]); + if (pipe_fds[1] >= 0) close (pipe_fds[1]); + + free (pargv[0]); + free (pargv[1]); + free (pargv[2]); +} + +process::~process () +{ + if (io_) + { + assert (pid_); + assert (str_); + + WSREP_WARN("Closing pipe to child process: %s, PID(%ld) " + "which might still be running.", str_, (long)pid_); + + if (fclose (io_) == -1) + { + err_ = errno; + WSREP_ERROR("fclose() failed: %d (%s)", err_, strerror(err_)); + } + } + + if (str_) free (const_cast(str_)); +} + +int +process::wait () +{ + if (pid_) + { + int status; + if (-1 == waitpid(pid_, &status, 0)) + { + err_ = errno; assert (err_); + WSREP_ERROR("Waiting for process failed: %s, PID(%ld): %d (%s)", + str_, (long)pid_, err_, strerror (err_)); + } + else + { // command completed, check exit status + if (WIFEXITED (status)) { + err_ = WEXITSTATUS (status); + } + else { // command didn't complete with exit() + WSREP_ERROR("Process was aborted."); + err_ = errno ? errno : ECHILD; + } + + if (err_) { + switch (err_) /* Translate error codes to more meaningful */ + { + case 126: err_ = EACCES; break; /* Permission denied */ + case 127: err_ = ENOENT; break; /* No such file or directory */ + } + WSREP_ERROR("Process completed with error: %s: %d (%s)", + str_, err_, strerror(err_)); + } + + pid_ = 0; + if (io_) fclose (io_); + io_ = NULL; + } + } + else { + assert (NULL == io_); + WSREP_ERROR("Command did not run: %s", str_); + } + + return err_; +} + +thd::thd (my_bool won) : init(), ptr(new THD) +{ + if (ptr) + { + ptr->thread_stack= (char*) &ptr; + ptr->store_globals(); + ptr->variables.option_bits&= ~OPTION_BIN_LOG; // disable binlog + ptr->variables.wsrep_on = won; + ptr->security_ctx->master_access= ~(ulong)0; + lex_start(ptr); + } +} + +thd::~thd () +{ + if (ptr) + { + delete ptr; + my_pthread_setspecific_ptr (THR_THD, 0); + } +} + +} // namespace wsp + +extern ulong my_bind_addr; +extern uint mysqld_port; + +size_t guess_ip (char* buf, size_t buf_len) +{ + size_t ip_len = 0; + + if (htonl(INADDR_NONE) == my_bind_addr) { + WSREP_ERROR("Networking not configured, cannot receive state transfer."); + return 0; + } + + if (htonl(INADDR_ANY) != my_bind_addr) { + uint8_t* b = (uint8_t*)&my_bind_addr; + ip_len = snprintf (buf, buf_len, + "%hhu.%hhu.%hhu.%hhu", b[0],b[1],b[2],b[3]); + return ip_len; + } + + // mysqld binds to all interfaces - try IP from wsrep_node_address + if (wsrep_node_address && wsrep_node_address[0] != '\0') { + const char* const colon_ptr = strchr(wsrep_node_address, ':'); + + if (colon_ptr) + ip_len = colon_ptr - wsrep_node_address; + else + ip_len = strlen(wsrep_node_address); + + if (ip_len >= buf_len) { + WSREP_WARN("default_ip(): buffer too short: %zu <= %zd", buf_len, ip_len); + return 0; + } + + memcpy (buf, wsrep_node_address, ip_len); + buf[ip_len] = '\0'; + return ip_len; + } + + // try to find the address of the first one +#if (TARGET_OS_LINUX == 1) + const char cmd[] = "ip addr show | grep -E '^\\s*inet' | grep -m1 global |" + " awk '{ print $2 }' | sed 's/\\/.*//'"; +#elif defined(__sun__) + const char cmd[] = "/sbin/ifconfig -a | " + "/usr/gnu/bin/grep -m1 -1 -E 'net[0-9]:' | tail -n 1 | awk '{ print $2 }'"; +#elif defined(__APPLE__) || defined(__FreeBSD__) + const char cmd[] = "route -nv get 8.8.8.8 | tail -n1 | awk '{print $5}'"; +#else + char *cmd; +#error "OS not supported" +#endif + wsp::process proc (cmd, "r"); + + if (NULL != proc.pipe()) { + char* ret; + + ret = fgets (buf, buf_len, proc.pipe()); + + if (proc.wait()) return 0; + + if (NULL == ret) { + WSREP_ERROR("Failed to read output of: '%s'", cmd); + return 0; + } + } + else { + WSREP_ERROR("Failed to execute: '%s'", cmd); + return 0; + } + + // clear possible \n at the end of ip string left by fgets() + ip_len = strlen (buf); + if (ip_len > 0 && '\n' == buf[ip_len - 1]) { + ip_len--; + buf[ip_len] = '\0'; + } + + if (INADDR_NONE == inet_addr(buf)) { + if (strlen(buf) != 0) { + WSREP_WARN("Shell command returned invalid address: '%s'", buf); + } + return 0; + } + + return ip_len; +} + +size_t guess_address(char* buf, size_t buf_len) +{ + size_t addr_len = guess_ip (buf, buf_len); + + if (addr_len && addr_len < buf_len) { + addr_len += snprintf (buf + addr_len, buf_len - addr_len, + ":%u", mysqld_port); + } + + return addr_len; +} + +/* + * WSREPXid + */ + +#define WSREP_XID_PREFIX "WSREPXid" +#define WSREP_XID_PREFIX_LEN MYSQL_XID_PREFIX_LEN +#define WSREP_XID_UUID_OFFSET 8 +#define WSREP_XID_SEQNO_OFFSET (WSREP_XID_UUID_OFFSET + sizeof(wsrep_uuid_t)) +#define WSREP_XID_GTRID_LEN (WSREP_XID_SEQNO_OFFSET + sizeof(wsrep_seqno_t)) + +void wsrep_xid_init(XID* xid, const wsrep_uuid_t* uuid, wsrep_seqno_t seqno) +{ + xid->formatID= 1; + xid->gtrid_length= WSREP_XID_GTRID_LEN; + xid->bqual_length= 0; + memset(xid->data, 0, sizeof(xid->data)); + memcpy(xid->data, WSREP_XID_PREFIX, WSREP_XID_PREFIX_LEN); + memcpy(xid->data + WSREP_XID_UUID_OFFSET, uuid, sizeof(wsrep_uuid_t)); + memcpy(xid->data + WSREP_XID_SEQNO_OFFSET, &seqno, sizeof(wsrep_seqno_t)); +} + +const wsrep_uuid_t* wsrep_xid_uuid(const XID* xid) +{ + if (wsrep_is_wsrep_xid(xid)) + return reinterpret_cast(xid->data + + WSREP_XID_UUID_OFFSET); + else + return &WSREP_UUID_UNDEFINED; +} + +wsrep_seqno_t wsrep_xid_seqno(const XID* xid) +{ + + if (wsrep_is_wsrep_xid(xid)) + { + wsrep_seqno_t seqno; + memcpy(&seqno, xid->data + WSREP_XID_SEQNO_OFFSET, sizeof(wsrep_seqno_t)); + return seqno; + } + else + { + return WSREP_SEQNO_UNDEFINED; + } +} + +extern "C" +int wsrep_is_wsrep_xid(const void* xid_ptr) +{ + const XID* xid= reinterpret_cast(xid_ptr); + return (xid->formatID == 1 && + xid->gtrid_length == WSREP_XID_GTRID_LEN && + xid->bqual_length == 0 && + !memcmp(xid->data, WSREP_XID_PREFIX, WSREP_XID_PREFIX_LEN)); +}