/* 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 // 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 bool ndb_execute_and_retry( Ndb *ndb, const THD *thd, unsigned int retry_sleep, NdbError &last_ndb_err, std::function 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 bool ndb_trans_retry( Ndb *ndb, const THD *thd, NdbError &last_ndb_err, std::function ndb_func, FunctionArgs... args) { return ndb_execute_and_retry( ndb, thd, 30, last_ndb_err, ndb_func, std::forward(args)...); } #endif /* NDB_RETRY_H */