135 lines
5.1 KiB
C++
135 lines
5.1 KiB
C++
/*
|
|
Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License, version 2.0,
|
|
as published by the Free Software Foundation.
|
|
|
|
This program is also distributed with certain software (including
|
|
but not limited to OpenSSL) that is licensed under separate terms,
|
|
as designated in a particular file or component or in included license
|
|
documentation. The authors of MySQL hereby grant you an additional
|
|
permission to link the program and your derivative works with the
|
|
separately licensed software that they have included with MySQL.
|
|
|
|
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, version 2.0, 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 St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#ifndef NDB_RETRY_H
|
|
#define NDB_RETRY_H
|
|
|
|
#include <functional> // std::function
|
|
|
|
#include "ndbapi/NdbApi.hpp" // NdbError
|
|
#include "sql/sql_class.h" // THD
|
|
#include "storage/ndb/plugin/ndb_sleep.h" // ndb_retry_sleep
|
|
|
|
/**
|
|
A wrapper to execute the given std::function instance that uses NdbTransaction
|
|
to perform transactions on the data nodes. The transaction has a possibility
|
|
of failing due to a temporary error. In such cases, this wrapper will execute
|
|
the function again after sleeping for some time.
|
|
|
|
@note
|
|
A generic std::function instance is used here rather than a generic
|
|
function pointer. This is because the std::function handles the member
|
|
functions better than the function pointers.
|
|
|
|
@note
|
|
Execution will be retried only if the THD has not been killed. To disable
|
|
this check, pass nullptr to thd param instead of passing a valid pointer.
|
|
|
|
@param ndb* The Ndb object.
|
|
@param thd* THD object pointer.
|
|
@param retry_sleep The amount of time(in ms) to sleep before retrying
|
|
in case of a temporary error.
|
|
@param last_ndb_err[out] The last NdbError in case of failure
|
|
@param ndb_func The std::function instance of the function that
|
|
needs to be executed by this wrapper. The function
|
|
should take a NdbTransaction* parameter followed by
|
|
any number of other parameters. It should return
|
|
a const NdbError* on failure and nullptr on success.
|
|
@param args The arguments that need to be passed to the
|
|
function during execution.
|
|
|
|
@retval true On success
|
|
@retval false On failure
|
|
*/
|
|
template <typename... FunctionArgTypes, typename... FunctionArgs>
|
|
bool ndb_execute_and_retry(
|
|
Ndb *ndb, const THD *thd, unsigned int retry_sleep, NdbError &last_ndb_err,
|
|
std::function<const NdbError *(NdbTransaction *, FunctionArgTypes...)>
|
|
ndb_func,
|
|
FunctionArgs... args) {
|
|
int retries = 100;
|
|
const NdbError *ndbError;
|
|
DBUG_ASSERT(ndb != nullptr);
|
|
|
|
do {
|
|
/* Start transaction */
|
|
NdbTransaction *ndb_transaction = ndb->startTransaction();
|
|
if (ndb_transaction != nullptr) {
|
|
/* Starting the transaction succeeded.
|
|
Execute the function and store the error */
|
|
ndbError = ndb_func(ndb_transaction, args...);
|
|
} else {
|
|
/* Failed to create transaction */
|
|
ndbError = &(ndb->getNdbError());
|
|
}
|
|
|
|
/* Handle the error */
|
|
if (ndbError == nullptr) {
|
|
/* No error, the function execution is a success */
|
|
ndb_transaction->close();
|
|
return true;
|
|
}
|
|
|
|
// Copy the last NdbError
|
|
last_ndb_err = *ndbError;
|
|
|
|
if (ndbError->status == NdbError::TemporaryError &&
|
|
(thd == nullptr || !thd->killed)) {
|
|
/* Temporary error. Close transaction, sleep and retry. */
|
|
if (ndb_transaction != nullptr) {
|
|
ndb_transaction->close();
|
|
}
|
|
ndb_retry_sleep(retry_sleep);
|
|
} else {
|
|
/* Permanent error or the thread has been killed.
|
|
Skip retrying and return. */
|
|
if (ndb_transaction != nullptr) {
|
|
ndb_transaction->close();
|
|
}
|
|
return false;
|
|
}
|
|
} while (retries--); /* Retry on temporary error */
|
|
|
|
/* All retries failed.*/
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
@brief Wrapper of function ndb_execute_and_retry with a fixed sleep of 30ms
|
|
in between retries. To be used to execute functions that build
|
|
and execute NDB Transactions.
|
|
*/
|
|
template <typename... FunctionArgTypes, typename... FunctionArgs>
|
|
bool ndb_trans_retry(
|
|
Ndb *ndb, const THD *thd, NdbError &last_ndb_err,
|
|
std::function<const NdbError *(NdbTransaction *, FunctionArgTypes...)>
|
|
ndb_func,
|
|
FunctionArgs... args) {
|
|
return ndb_execute_and_retry<FunctionArgTypes...>(
|
|
ndb, thd, 30, last_ndb_err, ndb_func,
|
|
std::forward<FunctionArgs>(args)...);
|
|
}
|
|
|
|
#endif /* NDB_RETRY_H */
|