Merge branch '2.3' into 2.4
This commit is contained in:
106
BUILD/mdbci/create_full_repo.sh
Executable file
106
BUILD/mdbci/create_full_repo.sh
Executable file
@ -0,0 +1,106 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# $box - Vagrant box to be used for build
|
||||||
|
|
||||||
|
# $target - name of repository to put results
|
||||||
|
|
||||||
|
# $cmake_flags - cmake flags to be used in the build
|
||||||
|
|
||||||
|
# $MDBCI_VM_PATH - path to the MDBCI virtual machies directory
|
||||||
|
|
||||||
|
# $source - reference to the point in the source code repository
|
||||||
|
|
||||||
|
# $do_not_destroy_vm - if "yes" VM stays alive after the build
|
||||||
|
|
||||||
|
# $try_already_running - if "yes" already running VM will be used for build
|
||||||
|
|
||||||
|
# $gpg_keys_path - path to the directory containing GPG keys for repo signing
|
||||||
|
# directory have to contain only one file *.public and only one *.private
|
||||||
|
|
||||||
|
set -x
|
||||||
|
|
||||||
|
# read the name of build scripts directory
|
||||||
|
export script_dir="$(dirname $(readlink -f $0))"
|
||||||
|
|
||||||
|
# load all needed variables
|
||||||
|
. ${script_dir}/set_build_variables.sh
|
||||||
|
|
||||||
|
export platform=`${mdbci_dir}/mdbci show boxinfo --box-name=$box --field='platform' --silent`
|
||||||
|
export platform_version=`${mdbci_dir}/mdbci show boxinfo --box-name=$box --field='platform_version' --silent`
|
||||||
|
export dist_sfx="$platform"."$platform_version"
|
||||||
|
|
||||||
|
# prerare VM
|
||||||
|
export provider=`${mdbci_dir}/mdbci show provider $box --silent 2> /dev/null`
|
||||||
|
export name="$box-${JOB_NAME}-${BUILD_NUMBER}"
|
||||||
|
export name=`echo $name | sed "s|/|-|g"`
|
||||||
|
|
||||||
|
|
||||||
|
# destroying existing box
|
||||||
|
if [ -d "$MDBCI_VM_PATH/${name}" ]; then
|
||||||
|
${mdbci_dir}/mdbci destroy $name
|
||||||
|
fi
|
||||||
|
|
||||||
|
eval "cat <<EOF
|
||||||
|
$(<${script_dir}/templates/build.json.template)
|
||||||
|
" 2> /dev/null > $MDBCI_VM_PATH/${name}.json
|
||||||
|
|
||||||
|
# starting VM for build
|
||||||
|
echo "Generating build VM template"
|
||||||
|
${mdbci_dir}/mdbci --override --template $MDBCI_VM_PATH/$name.json generate $name
|
||||||
|
echo "starting VM for build"
|
||||||
|
${mdbci_dir}/mdbci up --attempts=1 $name
|
||||||
|
if [ $? != 0 ] ; then
|
||||||
|
echo "Error starting VM"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "copying public keys to VM"
|
||||||
|
cp ~/build-scripts/team_keys .
|
||||||
|
${mdbci_dir}/mdbci public_keys --key team_keys --silent $name
|
||||||
|
|
||||||
|
|
||||||
|
echo "Get VM info"
|
||||||
|
export sshuser=`${mdbci_dir}/mdbci ssh --command 'whoami' --silent $name/build 2> /dev/null | tr -d '\r'`
|
||||||
|
export IP=`${mdbci_dir}/mdbci show network $name/build --silent 2> /dev/null`
|
||||||
|
export sshkey=`${mdbci_dir}/mdbci show keyfile $name/build --silent 2> /dev/null | sed 's/"//g'`
|
||||||
|
export scpopt="-i $sshkey -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o ConnectTimeout=120 "
|
||||||
|
export sshopt="$scpopt $sshuser@$IP"
|
||||||
|
|
||||||
|
rm -rf $pre_repo_dir/$target/$box
|
||||||
|
mkdir -p $pre_repo_dir/$target/SRC
|
||||||
|
mkdir -p $pre_repo_dir/$target/$box
|
||||||
|
|
||||||
|
export work_dir="MaxScale"
|
||||||
|
export orig_image=$box
|
||||||
|
|
||||||
|
ssh $sshopt "sudo rm -rf $work_dir"
|
||||||
|
echo "copying stuff to $image machine"
|
||||||
|
ssh $sshopt "mkdir -p $work_dir"
|
||||||
|
|
||||||
|
rsync -avz --delete -e "ssh $scpopt" ${script_dir}/../../ $sshuser@$IP:./$work_dir/
|
||||||
|
if [ $? -ne 0 ] ; then
|
||||||
|
echo "Error copying stuff to $box machine"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
ssh $sshopt ./MaxScale/BUILD/install_build_deps.sh
|
||||||
|
${script_dir}/create_remote_repo.sh full_repo
|
||||||
|
export build_result=$?
|
||||||
|
|
||||||
|
if [ ${build_result} -eq 0 ]; then
|
||||||
|
${script_dir}/copy_repos.sh
|
||||||
|
export build_result=$?
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Removing locks and destroying VM"
|
||||||
|
|
||||||
|
if [[ "$do_not_destroy_vm" != "yes" && "$try_already_running" != "yes" ]] ; then
|
||||||
|
echo "Destroying VM"
|
||||||
|
${mdbci_dir}/mdbci destroy $name
|
||||||
|
fi
|
||||||
|
cd $dir
|
||||||
|
|
||||||
|
if [ $build_result -ne 0 ] ; then
|
||||||
|
echo "Build FAILED!"
|
||||||
|
exit $build_result
|
||||||
|
fi
|
||||||
|
|
||||||
@ -16,7 +16,12 @@ echo " creating dirs on VM"
|
|||||||
ssh $sshopt "mkdir -p dest ; mkdir -p src; mkdir gpg_keys"
|
ssh $sshopt "mkdir -p dest ; mkdir -p src; mkdir gpg_keys"
|
||||||
|
|
||||||
echo "copying stuff to VM"
|
echo "copying stuff to VM"
|
||||||
scp $scpopt $pre_repo_dir/$target/$box/* $sshuser@$IP:src/
|
if [ $1 == "full_repo" ] ; then
|
||||||
|
find ${repo_path}/maxscale-${major_ver}.*-release/mariadb-maxscale/${platform}/${platform_version}/* -name "*.rpm" -exec scp $scpopt {} $sshuser@$IP:src/ \;
|
||||||
|
find ${repo_path}/maxscale-${major_ver}.*-release/mariadb-maxscale/${platform}/dists/${platform_version}/* -name "*.deb" -exec scp $scpopt {} $sshuser@$IP:src/ \;
|
||||||
|
else
|
||||||
|
scp $scpopt $pre_repo_dir/$target/$box/* $sshuser@$IP:src/
|
||||||
|
fi
|
||||||
|
|
||||||
scp $scpopt -r ${gpg_keys_path}/* $sshuser@$IP:./gpg_keys/
|
scp $scpopt -r ${gpg_keys_path}/* $sshuser@$IP:./gpg_keys/
|
||||||
ssh $sshopt "key=\`ls ~/gpg_keys/*.public -1\` ; gpg --import \$key"
|
ssh $sshopt "key=\`ls ~/gpg_keys/*.public -1\` ; gpg --import \$key"
|
||||||
|
|||||||
@ -3,7 +3,11 @@
|
|||||||
{
|
{
|
||||||
"hostname" : "default",
|
"hostname" : "default",
|
||||||
"memory_size" : "4096",
|
"memory_size" : "4096",
|
||||||
"box" : "$box"
|
"box" : "$box",
|
||||||
|
"product" : {
|
||||||
|
"name": "docker"
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -303,6 +303,16 @@ public:
|
|||||||
return m_state & FATAL_FAILURE;
|
return m_state & FATAL_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the backend replaying session command history
|
||||||
|
*
|
||||||
|
* @return If a list of session commands was provided at connect time, the function returns true as long
|
||||||
|
* as the backend has not completed those session commands.
|
||||||
|
*/
|
||||||
|
inline bool is_replaying_history() const
|
||||||
|
{
|
||||||
|
return m_history_size > 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the object name of this server
|
* @brief Get the object name of this server
|
||||||
@ -389,7 +399,8 @@ private:
|
|||||||
|
|
||||||
maxbase::StopWatch m_session_timer;
|
maxbase::StopWatch m_session_timer;
|
||||||
maxbase::IntervalTimer m_select_timer;
|
maxbase::IntervalTimer m_select_timer;
|
||||||
int64_t m_num_selects = 0;
|
int64_t m_num_selects {0};
|
||||||
|
int64_t m_history_size {0};
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::shared_ptr<Backend> SBackend;
|
typedef std::shared_ptr<Backend> SBackend;
|
||||||
|
|||||||
@ -1001,6 +1001,9 @@ add_test_executable(sr_basics.cpp sr_basics sr_basics LABELS REPL_BACKEND)
|
|||||||
# MXS-2490: Direct execution doesn't work with MaxScale
|
# MXS-2490: Direct execution doesn't work with MaxScale
|
||||||
add_test_executable(mxs2490_ps_execute_direct.cpp mxs2490_ps_execute_direct replication LABELS REPL_BACKEND readwritesplit)
|
add_test_executable(mxs2490_ps_execute_direct.cpp mxs2490_ps_execute_direct replication LABELS REPL_BACKEND readwritesplit)
|
||||||
|
|
||||||
|
# MXS-2609: Maxscale crash in RWSplitSession::retry_master_query()
|
||||||
|
add_test_executable(mxs2609_history_replay.cpp mxs2609_history_replay mxs2609_history_replay LABELS readwritesplit REPL_BACKEND)
|
||||||
|
|
||||||
# MXS-2621: Incorrect SQL if lower_case_table_names is used.
|
# MXS-2621: Incorrect SQL if lower_case_table_names is used.
|
||||||
add_test_executable(mxs2621_lower_case_tables.cpp mxs2621_lower_case_tables mxs2621_lower_case_tables LABELS REPL_BACKEND)
|
add_test_executable(mxs2621_lower_case_tables.cpp mxs2621_lower_case_tables mxs2621_lower_case_tables LABELS REPL_BACKEND)
|
||||||
|
|
||||||
|
|||||||
27
maxscale-system-test/cnf/maxscale.cnf.template.mxs2609_history_replay
Executable file
27
maxscale-system-test/cnf/maxscale.cnf.template.mxs2609_history_replay
Executable file
@ -0,0 +1,27 @@
|
|||||||
|
[maxscale]
|
||||||
|
threads=###threads###
|
||||||
|
|
||||||
|
###server###
|
||||||
|
|
||||||
|
[MySQL-Monitor]
|
||||||
|
type=monitor
|
||||||
|
module=mariadbmon
|
||||||
|
servers=###server_line###
|
||||||
|
user=maxskysql
|
||||||
|
password=skysql
|
||||||
|
monitor_interval=1000
|
||||||
|
|
||||||
|
[RW-Split-Router]
|
||||||
|
type=service
|
||||||
|
router=readwritesplit
|
||||||
|
servers=###server_line###
|
||||||
|
user=maxskysql
|
||||||
|
password=skysql
|
||||||
|
transaction_replay=true
|
||||||
|
master_accept_reads=true
|
||||||
|
|
||||||
|
[RW-Split-Listener]
|
||||||
|
type=listener
|
||||||
|
service=RW-Split-Router
|
||||||
|
protocol=MySQLClient
|
||||||
|
port=4006
|
||||||
69
maxscale-system-test/mxs2609_history_replay.cpp
Normal file
69
maxscale-system-test/mxs2609_history_replay.cpp
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/**
|
||||||
|
* MXS-2609: Maxscale crash in RWSplitSession::retry_master_query()
|
||||||
|
*
|
||||||
|
* https://jira.mariadb.org/browse/MXS-2609
|
||||||
|
*
|
||||||
|
* This test attempts to reproduce the crash described in MXS-2609 which
|
||||||
|
* occurred during a retrying attempt of a session command that failed on
|
||||||
|
* the master.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "testconnections.h"
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
TestConnections test(argc, argv);
|
||||||
|
|
||||||
|
auto block = [&test](int n){
|
||||||
|
test.repl->block_node(n);
|
||||||
|
test.maxscales->wait_for_monitor();
|
||||||
|
test.repl->unblock_node(n);
|
||||||
|
test.maxscales->wait_for_monitor();
|
||||||
|
};
|
||||||
|
auto conn = test.maxscales->rwsplit();
|
||||||
|
|
||||||
|
//
|
||||||
|
// Test 1: Master failure mid-reconnect should trigger query replay
|
||||||
|
//
|
||||||
|
|
||||||
|
test.expect(conn.connect(), "First connect should work: %s", conn.error());
|
||||||
|
|
||||||
|
// Queue up session commands so that the history replay takes some time
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
conn.query("SET @a = (SLEEP 1)");
|
||||||
|
}
|
||||||
|
|
||||||
|
block(0);
|
||||||
|
|
||||||
|
test.set_timeout(90);
|
||||||
|
|
||||||
|
std::thread([&](){
|
||||||
|
sleep(5);
|
||||||
|
block(0);
|
||||||
|
}).detach();
|
||||||
|
|
||||||
|
test.expect(conn.query("SELECT @@last_insert_id"), "Query should work: %s", conn.error());
|
||||||
|
|
||||||
|
test.stop_timeout();
|
||||||
|
conn.disconnect();
|
||||||
|
|
||||||
|
//
|
||||||
|
// Test 2: Exceed history limit and trigger a master reconnection
|
||||||
|
//
|
||||||
|
|
||||||
|
test.maxctrl("alter service RW-Split-Router max_sescmd_history 2");
|
||||||
|
test.expect(conn.connect(), "Second should work: %s", conn.error());
|
||||||
|
|
||||||
|
for (int i = 0; i < 5; i++)
|
||||||
|
{
|
||||||
|
conn.query("SET @a = (SLEEP 1)");
|
||||||
|
}
|
||||||
|
|
||||||
|
block(0);
|
||||||
|
|
||||||
|
test.expect(!conn.query("SELECT @@last_insert_id"), "Query should fail");
|
||||||
|
|
||||||
|
return test.global_result;
|
||||||
|
}
|
||||||
@ -54,6 +54,7 @@ void Backend::close(close_type type)
|
|||||||
m_closed = true;
|
m_closed = true;
|
||||||
m_closed_at = time(NULL);
|
m_closed_at = time(NULL);
|
||||||
m_session_commands.clear();
|
m_session_commands.clear();
|
||||||
|
m_history_size = 0;
|
||||||
|
|
||||||
if (in_use())
|
if (in_use())
|
||||||
{
|
{
|
||||||
@ -139,6 +140,12 @@ uint64_t Backend::complete_session_command()
|
|||||||
{
|
{
|
||||||
uint64_t rval = m_session_commands.front()->get_position();
|
uint64_t rval = m_session_commands.front()->get_position();
|
||||||
m_session_commands.pop_front();
|
m_session_commands.pop_front();
|
||||||
|
|
||||||
|
if (m_history_size > 0)
|
||||||
|
{
|
||||||
|
--m_history_size;
|
||||||
|
}
|
||||||
|
|
||||||
return rval;
|
return rval;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,10 +199,12 @@ bool Backend::connect(MXS_SESSION* session, SessionCommandList* sescmd)
|
|||||||
m_state = IN_USE;
|
m_state = IN_USE;
|
||||||
mxb::atomic::add(&m_backend->connections, 1, mxb::atomic::RELAXED);
|
mxb::atomic::add(&m_backend->connections, 1, mxb::atomic::RELAXED);
|
||||||
rval = true;
|
rval = true;
|
||||||
|
m_history_size = 0;
|
||||||
|
|
||||||
if (sescmd && sescmd->size())
|
if (sescmd && !sescmd->empty())
|
||||||
{
|
{
|
||||||
append_session_command(*sescmd);
|
append_session_command(*sescmd);
|
||||||
|
m_history_size = sescmd->size();
|
||||||
|
|
||||||
if (!execute_session_command())
|
if (!execute_session_command())
|
||||||
{
|
{
|
||||||
|
|||||||
@ -58,7 +58,7 @@ static std::string do_query(MonitorServer* srv, const char* query)
|
|||||||
// Returns a numeric version similar to mysql_get_server_version
|
// Returns a numeric version similar to mysql_get_server_version
|
||||||
int get_cs_version(MonitorServer* srv)
|
int get_cs_version(MonitorServer* srv)
|
||||||
{
|
{
|
||||||
int rval = 0;
|
int rval = -1;
|
||||||
std::string prefix = "Columnstore ";
|
std::string prefix = "Columnstore ";
|
||||||
std::string result = do_query(srv, "SELECT @@version_comment");
|
std::string result = do_query(srv, "SELECT @@version_comment");
|
||||||
auto pos = result.find(prefix);
|
auto pos = result.find(prefix);
|
||||||
@ -107,16 +107,21 @@ void CsMonitor::update_server_status(MonitorServer* srv)
|
|||||||
|
|
||||||
if (do_query(srv, alive_query) == "1")
|
if (do_query(srv, alive_query) == "1")
|
||||||
{
|
{
|
||||||
status |= SERVER_RUNNING;
|
auto version = get_cs_version(srv);
|
||||||
|
|
||||||
if (get_cs_version(srv) >= 10200)
|
if (version >= 0)
|
||||||
{
|
{
|
||||||
// 1.2 supports the mcsSystemPrimary function
|
status |= SERVER_RUNNING;
|
||||||
status |= do_query(srv, role_query) == "1" ? SERVER_MASTER : SERVER_SLAVE;
|
|
||||||
}
|
if (version >= 10200)
|
||||||
else
|
{
|
||||||
{
|
// 1.2 supports the mcsSystemPrimary function
|
||||||
status |= srv->server == m_primary ? SERVER_MASTER : SERVER_SLAVE;
|
status |= do_query(srv, role_query) == "1" ? SERVER_MASTER : SERVER_SLAVE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
status |= srv->server == m_primary ? SERVER_MASTER : SERVER_SLAVE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -355,7 +355,7 @@ bool RWSplitSession::route_single_stmt(GWBUF* querybuf)
|
|||||||
else if (target->has_session_commands())
|
else if (target->has_session_commands())
|
||||||
{
|
{
|
||||||
// We need to wait until the session commands are executed
|
// We need to wait until the session commands are executed
|
||||||
m_query_queue.emplace_back(gwbuf_clone(querybuf));
|
m_query_queue.emplace_front(gwbuf_clone(querybuf));
|
||||||
MXS_INFO("Queuing query until '%s' completes session command", target->name());
|
MXS_INFO("Queuing query until '%s' completes session command", target->name());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -652,7 +652,8 @@ RWBackend* RWSplitSession::get_master_backend()
|
|||||||
|
|
||||||
if (master)
|
if (master)
|
||||||
{
|
{
|
||||||
if (master->in_use() || (m_config.master_reconnection && master->can_connect()))
|
if (master->in_use()
|
||||||
|
|| (m_config.master_reconnection && master->can_connect() && can_recover_servers()))
|
||||||
{
|
{
|
||||||
if (can_continue_using_master(master))
|
if (can_continue_using_master(master))
|
||||||
{
|
{
|
||||||
@ -667,7 +668,8 @@ RWBackend* RWSplitSession::get_master_backend()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
MXS_ERROR("Server '%s' is not in use and can't be chosen as the master.",
|
MXS_ERROR("Cannot choose server '%s' as the master because it is not "
|
||||||
|
"in use and a new connection to it cannot be created.",
|
||||||
master->name());
|
master->name());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -957,17 +957,40 @@ bool RWSplitSession::retry_master_query(RWBackend* backend)
|
|||||||
{
|
{
|
||||||
bool can_continue = false;
|
bool can_continue = false;
|
||||||
|
|
||||||
if (backend->has_session_commands())
|
if (backend->is_replaying_history())
|
||||||
{
|
{
|
||||||
// Try to route the session command again. If the master is not available, the response will be
|
// Master failed while it was replaying the session command history
|
||||||
// returned from one of the slaves.
|
mxb_assert(m_config.master_reconnection);
|
||||||
|
mxb_assert(!m_query_queue.empty());
|
||||||
|
|
||||||
|
retry_query(m_query_queue.front().release());
|
||||||
|
m_query_queue.pop_front();
|
||||||
|
can_continue = true;
|
||||||
|
}
|
||||||
|
else if (backend->has_session_commands())
|
||||||
|
{
|
||||||
|
// We were routing a session command to all servers but the master server from which the response
|
||||||
|
// was expected failed: try to route the session command again. If the master is not available,
|
||||||
|
// the response will be returned from one of the slaves if the configuration allows it.
|
||||||
|
|
||||||
|
mxb_assert(backend->next_session_command()->get_position() == m_recv_sescmd + 1);
|
||||||
|
mxb_assert(m_qc.current_route_info().target() == TARGET_ALL);
|
||||||
mxb_assert(!m_current_query.get());
|
mxb_assert(!m_current_query.get());
|
||||||
mxb_assert(!m_sescmd_list.empty());
|
mxb_assert(!m_sescmd_list.empty());
|
||||||
mxb_assert(m_sescmd_count >= 2);
|
mxb_assert(m_sescmd_count >= 2);
|
||||||
MXS_INFO("Retrying session command due to master failure: %s",
|
MXS_INFO("Retrying session command due to master failure: %s",
|
||||||
backend->next_session_command()->to_string().c_str());
|
backend->next_session_command()->to_string().c_str());
|
||||||
|
|
||||||
|
// MXS-2609: Maxscale crash in RWSplitSession::retry_master_query()
|
||||||
|
// To prevent a crash from happening, we make sure the session command list is not empty before
|
||||||
|
// we touch it. This should be converted into a debug assertion once the true root cause of the
|
||||||
|
// problem is found.
|
||||||
|
if (m_sescmd_count < 2 || m_sescmd_list.empty())
|
||||||
|
{
|
||||||
|
MXS_WARNING("Session command list was empty when it should not be");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Before routing it, pop the failed session command off the list and decrement the number of
|
// Before routing it, pop the failed session command off the list and decrement the number of
|
||||||
// executed session commands. This "overwrites" the existing command and prevents history duplication.
|
// executed session commands. This "overwrites" the existing command and prevents history duplication.
|
||||||
m_sescmd_list.pop_back();
|
m_sescmd_list.pop_back();
|
||||||
@ -978,6 +1001,8 @@ bool RWSplitSession::retry_master_query(RWBackend* backend)
|
|||||||
}
|
}
|
||||||
else if (m_current_query.get())
|
else if (m_current_query.get())
|
||||||
{
|
{
|
||||||
|
// A query was in progress, try to route it again
|
||||||
|
mxb_assert(m_prev_target == backend);
|
||||||
retry_query(m_current_query.release());
|
retry_query(m_current_query.release());
|
||||||
can_continue = true;
|
can_continue = true;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user