591 lines
20 KiB
C++
591 lines
20 KiB
C++
/*
|
|
Copyright (c) 2011, 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
|
|
*/
|
|
|
|
#include "storage/ndb/plugin/ndb_global_schema_lock.h"
|
|
|
|
#include <mutex>
|
|
|
|
#include "my_dbug.h"
|
|
#include "mysql/plugin.h"
|
|
#include "sql/debug_sync.h"
|
|
#include "sql/sql_class.h"
|
|
#include "sql/sql_thd_internal_api.h" // thd_query_unsafe
|
|
#include "storage/ndb/include/ndbapi/NdbApi.hpp"
|
|
#include "storage/ndb/plugin/ndb_schema_dist.h"
|
|
#include "storage/ndb/plugin/ndb_sleep.h"
|
|
#include "storage/ndb/plugin/ndb_table_guard.h"
|
|
|
|
/**
|
|
* There is a potential for deadlocks between MDL and GSL locks:
|
|
*
|
|
* A client thread might have acquired an MDL_INTENTIONAL_EXCLUSIVE (IX)
|
|
* lock, and attempt to upgrade this to a MDL_EXCLUSIVE (X) locks, which
|
|
* requires the GSL lock to be taken.
|
|
*
|
|
* However, the GSL lock may already be held by the binlog schema-change
|
|
* coordinator on another mysqld. All participants has to complete
|
|
* the schema change op before the coordinator will release the GSL.
|
|
* As part of that, the participants will request a MDL-X-lock which blocks
|
|
* due to the other client thread holding an MDL-IX-lock. Thus, we
|
|
* have effectively a deadlock between the client thread and the
|
|
* schema change participant.
|
|
*
|
|
* We detect, and break, such deadlock by recording whether we
|
|
* have an active 'IS_SCHEMA_DIST_PARTICIPANT' on this mysqld.
|
|
* Iff another GSL request times-out while there are active
|
|
* schema dist participants, we *assume* we were involved in
|
|
* a deadlock.
|
|
*
|
|
* The MDL code is able to handle such deadlocks by releasing the
|
|
* locks and retry later
|
|
*/
|
|
|
|
static class Ndb_thd_gsl_participant {
|
|
std::mutex m_mutex;
|
|
const THD *m_thd{nullptr};
|
|
|
|
public:
|
|
Ndb_thd_gsl_participant &operator=(const THD *thd) {
|
|
std::lock_guard<std::mutex> lock_thd(m_mutex);
|
|
m_thd = thd;
|
|
return *this;
|
|
}
|
|
bool operator!=(const THD *thd) {
|
|
std::lock_guard<std::mutex> lock_thd(m_mutex);
|
|
return m_thd != thd;
|
|
}
|
|
} thd_gsl_participant;
|
|
|
|
static void ndb_set_gsl_participant(THD *thd) { thd_gsl_participant = thd; }
|
|
|
|
static bool ndb_is_gsl_participant_active() {
|
|
return (thd_gsl_participant != nullptr);
|
|
}
|
|
|
|
/**
|
|
* Another potential scenario for a deadlock between MDL and GSL locks is as
|
|
* follows:
|
|
*
|
|
* A disk data table DDL will try and acquire the following -
|
|
* - Global read lock of type INTENTION EXCLUSIVE (IX)
|
|
* - IX lock on the schema
|
|
* - Shared lock on the table
|
|
* - Backup lock of type IX
|
|
* - IX lock on the tablespace
|
|
* - Upgrade the previously acquired shared lock on the table to an EXCLUSIVE
|
|
* (X) lock
|
|
* - The X lock is granted only after the GSL has been acquired
|
|
*
|
|
* A tablespace DDL will try and acquire the following -
|
|
* - Global read lock of type IX
|
|
* - X lock on the 'ts1' tablespace
|
|
* - The X lock is granted only after the GSL has been acquired
|
|
* - Backup lock of type IX
|
|
*
|
|
* Assume that the table DDL has acquired an IX lock on the tablespace and is
|
|
* waiting for the GSL in order to acquire an X lock on the table. At the same
|
|
* time the tablespace DDL has acquired the GSL and is waiting to acquire an X
|
|
* lock on the tablespace - Deadlock!
|
|
*
|
|
* We detect such a deadlock by tracking when the GSL is acquired (and released)
|
|
* during an attempt to obtain an X lock on a tablespace. When this condition
|
|
* holds true (along with the other 2 conditions specified in gsl_lock_ext()
|
|
* below), we assume that a deadlock has occurred
|
|
*/
|
|
|
|
static class Ndb_tablespace_gsl_guard {
|
|
std::mutex m_tablespace_gsl_acquired_mutex; // for m_tablespace_gsl_acquired
|
|
bool m_tablespace_gsl_acquired{false};
|
|
|
|
public:
|
|
void tablespace_gsl_acquired() {
|
|
std::lock_guard<std::mutex> lock_gsl_acquired(
|
|
m_tablespace_gsl_acquired_mutex);
|
|
m_tablespace_gsl_acquired = true;
|
|
}
|
|
|
|
void tablespace_gsl_released() {
|
|
std::lock_guard<std::mutex> lock_gsl_acquired(
|
|
m_tablespace_gsl_acquired_mutex);
|
|
m_tablespace_gsl_acquired = false;
|
|
}
|
|
|
|
bool is_tablespace_gsl_acquired() {
|
|
std::lock_guard<std::mutex> lock_gsl_acquired(
|
|
m_tablespace_gsl_acquired_mutex);
|
|
return m_tablespace_gsl_acquired;
|
|
}
|
|
} tablespace_gsl_guard;
|
|
|
|
/*
|
|
The lock/unlock functions use the BACKUP_SEQUENCE row in SYSTAB_0
|
|
|
|
In case no_retry = false, the function will retry infinitely or until the THD
|
|
is killed or a GSL / MDL deadlock is detected/assumed. In the latter case a
|
|
timeout error (266) is returned. If no_retry is set, then the function
|
|
attempts to acquire GSL only once and returns.
|
|
|
|
Returns a NdbTransaction owning the gsl-lock if it was taken. NULL is returned
|
|
if failed to take lock. Returned NdbError will then contain the error code if
|
|
lock failed due to some NdbError. If there is no error code set, lock was
|
|
rejected by lock manager, likely due to deadlock.
|
|
*/
|
|
static NdbTransaction *gsl_lock_ext(THD *thd, Ndb *ndb, NdbError &ndb_error,
|
|
bool no_retry = false,
|
|
bool no_wait = false) {
|
|
ndb->setDatabaseName("sys");
|
|
ndb->setDatabaseSchemaName("def");
|
|
NdbDictionary::Dictionary *dict = ndb->getDictionary();
|
|
Ndb_table_guard ndbtab_g(dict, "SYSTAB_0");
|
|
const NdbDictionary::Table *ndbtab = nullptr;
|
|
NdbOperation *op = nullptr;
|
|
NdbTransaction *trans = nullptr;
|
|
|
|
while (1) {
|
|
if (!ndbtab) {
|
|
if (!(ndbtab = ndbtab_g.get_table())) {
|
|
if (dict->getNdbError().status == NdbError::TemporaryError) goto retry;
|
|
ndb_error = dict->getNdbError();
|
|
goto error_handler;
|
|
}
|
|
}
|
|
|
|
trans = ndb->startTransaction();
|
|
if (trans == nullptr) {
|
|
ndb_error = ndb->getNdbError();
|
|
goto error_handler;
|
|
}
|
|
|
|
op = trans->getNdbOperation(ndbtab);
|
|
if (op == nullptr) {
|
|
if (dict->getNdbError().status == NdbError::TemporaryError) goto retry;
|
|
ndb_error = dict->getNdbError();
|
|
goto error_handler;
|
|
}
|
|
if (op->readTuple(NdbOperation::LM_Exclusive)) goto error_handler;
|
|
if (no_wait) {
|
|
if (op->setNoWait()) goto error_handler;
|
|
}
|
|
if (op->equal("SYSKEY_0", NDB_BACKUP_SEQUENCE)) goto error_handler;
|
|
if (trans->execute(NdbTransaction::NoCommit) == 0) {
|
|
// The transaction is successful but still check if the operation has
|
|
// failed since the abort mode is set to AO_IgnoreError. Error 635
|
|
// is the expected error when no_wait has been set and the row could not
|
|
// be locked immediately
|
|
if (trans->getNdbError().code == 635) goto error_handler;
|
|
break;
|
|
}
|
|
|
|
if (trans->getNdbError().status != NdbError::TemporaryError)
|
|
goto error_handler;
|
|
if (thd_killed(thd)) goto error_handler;
|
|
|
|
/**
|
|
* Check for MDL / GSL deadlock. A deadlock is assumed if:
|
|
* 1) ::execute failed with a timeout error.
|
|
* 2a) There already is another THD being an participant in a schema distr.
|
|
* operation (which implies that the coordinator already held the GSL
|
|
* OR
|
|
* 2b) The GSL has already been acquired for a pending exclusive MDL on a
|
|
* tablespace. It's highly likely that there are two DDL statements
|
|
* competing for a lock on the same tablespace
|
|
* 3) This THD holds a lock being waited for by another THD
|
|
*
|
|
* Note: If we incorrectly assume a deadlock above, the caller
|
|
* will still either retry indefinitely as today, (notify_alter),
|
|
* or now be able to release locks gotten so far and retry later.
|
|
*/
|
|
if (trans->getNdbError().code == 266 && // 1)
|
|
(ndb_is_gsl_participant_active() || // 2a)
|
|
tablespace_gsl_guard.is_tablespace_gsl_acquired()) && // 2b)
|
|
thd->mdl_context.has_locks_waited_for()) // 3)
|
|
goto error_handler;
|
|
|
|
retry:
|
|
if (trans) {
|
|
ndb->closeTransaction(trans);
|
|
trans = nullptr;
|
|
}
|
|
|
|
if (no_retry) {
|
|
break;
|
|
}
|
|
|
|
ndb_trans_retry_sleep();
|
|
}
|
|
return trans;
|
|
|
|
error_handler:
|
|
if (trans) {
|
|
ndb_error = trans->getNdbError();
|
|
ndb->closeTransaction(trans);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
static bool gsl_unlock_ext(Ndb *ndb, NdbTransaction *trans,
|
|
NdbError &ndb_error) {
|
|
if (trans->execute(NdbTransaction::Commit)) {
|
|
ndb_error = trans->getNdbError();
|
|
ndb->closeTransaction(trans);
|
|
return false;
|
|
}
|
|
ndb->closeTransaction(trans);
|
|
return true;
|
|
}
|
|
|
|
class Thd_proc_info_guard {
|
|
public:
|
|
Thd_proc_info_guard(THD *thd) : m_thd(thd), m_proc_info(NULL) {}
|
|
void set(const char *message) {
|
|
const char *old = thd_proc_info(m_thd, message);
|
|
if (!m_proc_info) {
|
|
// Save the original on first change
|
|
m_proc_info = old;
|
|
}
|
|
}
|
|
~Thd_proc_info_guard() {
|
|
if (m_proc_info) thd_proc_info(m_thd, m_proc_info);
|
|
}
|
|
|
|
private:
|
|
THD *const m_thd;
|
|
const char *m_proc_info;
|
|
};
|
|
|
|
#include "storage/ndb/plugin/ndb_log.h"
|
|
#include "storage/ndb/plugin/ndb_thd.h"
|
|
#include "storage/ndb/plugin/ndb_thd_ndb.h"
|
|
|
|
/*
|
|
lock/unlock calls are reference counted, so calls to lock
|
|
must be matched to a call to unlock if the lock call succeeded
|
|
*/
|
|
static int ndbcluster_global_schema_lock(THD *thd,
|
|
bool report_cluster_disconnected,
|
|
bool is_tablespace, bool *victimized) {
|
|
Ndb *ndb = check_ndb_in_thd(thd);
|
|
Thd_ndb *thd_ndb = get_thd_ndb(thd);
|
|
NdbError ndb_error;
|
|
*victimized = false;
|
|
|
|
if (thd_ndb->check_option(Thd_ndb::IS_SCHEMA_DIST_PARTICIPANT)) {
|
|
ndb_set_gsl_participant(thd);
|
|
return 0;
|
|
}
|
|
DBUG_TRACE;
|
|
|
|
if (thd_ndb->global_schema_lock_count) {
|
|
// Remember that GSL was locked for tablespace
|
|
if (is_tablespace) tablespace_gsl_guard.tablespace_gsl_acquired();
|
|
|
|
if (thd_ndb->global_schema_lock_trans)
|
|
thd_ndb->global_schema_lock_trans->refresh();
|
|
else
|
|
DBUG_ASSERT(thd_ndb->global_schema_lock_error != 0);
|
|
thd_ndb->global_schema_lock_count++;
|
|
DBUG_PRINT("exit", ("global_schema_lock_count: %d",
|
|
thd_ndb->global_schema_lock_count));
|
|
return 0;
|
|
}
|
|
DBUG_ASSERT(thd_ndb->global_schema_lock_count == 0);
|
|
thd_ndb->global_schema_lock_count = 1;
|
|
thd_ndb->global_schema_lock_error = 0;
|
|
DBUG_PRINT("exit", ("global_schema_lock_count: %d",
|
|
thd_ndb->global_schema_lock_count));
|
|
|
|
/*
|
|
Take the lock
|
|
*/
|
|
Thd_proc_info_guard proc_info(thd);
|
|
proc_info.set("Waiting for ndbcluster global schema lock");
|
|
thd_ndb->global_schema_lock_trans = gsl_lock_ext(thd, ndb, ndb_error);
|
|
|
|
if (DBUG_EVALUATE_IF("sleep_after_global_schema_lock", true, false)) {
|
|
ndb_milli_sleep(6000);
|
|
}
|
|
|
|
if (thd_ndb->global_schema_lock_trans) {
|
|
ndb_log_verbose(19, "Global schema lock acquired");
|
|
|
|
// Count number of global schema locks taken by this thread
|
|
thd_ndb->schema_locks_count++;
|
|
thd_ndb->global_schema_lock_count = 1;
|
|
DBUG_PRINT("info", ("schema_locks_count: %d", thd_ndb->schema_locks_count));
|
|
|
|
// Remember that GSL was locked for tablespace
|
|
if (is_tablespace) tablespace_gsl_guard.tablespace_gsl_acquired();
|
|
|
|
// Sync point used when testing global schema lock concurrency
|
|
DEBUG_SYNC(thd, "ndb_global_schema_lock_acquired");
|
|
|
|
return 0;
|
|
}
|
|
// Else, didn't get GSL: Deadlock or failure from NDB
|
|
|
|
/**
|
|
* If GSL request failed due to no cluster connection (4009),
|
|
* we consider the lock granted, else GSL request failed.
|
|
*/
|
|
if (ndb_error.code != 4009) // No cluster connection
|
|
{
|
|
DBUG_ASSERT(thd_ndb->global_schema_lock_count == 1);
|
|
// This reset triggers the special case in ndbcluster_global_schema_unlock()
|
|
thd_ndb->global_schema_lock_count = 0;
|
|
}
|
|
|
|
if (ndb_error.code == 266) // Deadlock resolution
|
|
{
|
|
ndb_log_info(
|
|
"Failed to acquire global schema lock due to deadlock resolution");
|
|
*victimized = true;
|
|
} else if (ndb_error.code != 4009 || report_cluster_disconnected) {
|
|
if (ndb_thd_is_background_thread(thd)) {
|
|
// Don't push any warning when background thread fail to acquire GSL
|
|
} else {
|
|
thd_ndb->push_ndb_error_warning(ndb_error);
|
|
thd_ndb->push_warning("Could not acquire global schema lock");
|
|
}
|
|
}
|
|
thd_ndb->global_schema_lock_error = ndb_error.code ? ndb_error.code : -1;
|
|
return -1;
|
|
}
|
|
|
|
static int ndbcluster_global_schema_unlock(THD *thd, bool is_tablespace) {
|
|
Thd_ndb *thd_ndb = get_thd_ndb(thd);
|
|
if (unlikely(thd_ndb == NULL)) {
|
|
return 0;
|
|
}
|
|
|
|
if (thd_ndb->check_option(Thd_ndb::IS_SCHEMA_DIST_PARTICIPANT)) {
|
|
ndb_set_gsl_participant(NULL);
|
|
return 0;
|
|
}
|
|
|
|
if (thd_ndb->global_schema_lock_error != 4009 &&
|
|
thd_ndb->global_schema_lock_count == 0) {
|
|
// Special case to handle unlock after failure to acquire GSL due to
|
|
// any error other than 4009.
|
|
// - when error 4009 occurs the lock is granted anyway and the lock count is
|
|
// not reset, thus unlock() should be called.
|
|
// - for other errors the lock is not granted, lock count is reset and
|
|
// the exact same error code is returned. Thus it's impossible to know
|
|
// that there is actually no need to call unlock. Fix by allowing unlock
|
|
// without doing anything since the trans is already closed.
|
|
DBUG_ASSERT(thd_ndb->global_schema_lock_trans == NULL);
|
|
thd_ndb->global_schema_lock_count++;
|
|
}
|
|
|
|
Ndb *ndb = thd_ndb->ndb;
|
|
DBUG_TRACE;
|
|
NdbTransaction *trans = thd_ndb->global_schema_lock_trans;
|
|
// Don't allow decrementing from zero
|
|
DBUG_ASSERT(thd_ndb->global_schema_lock_count > 0);
|
|
thd_ndb->global_schema_lock_count--;
|
|
DBUG_PRINT("exit", ("global_schema_lock_count: %d",
|
|
thd_ndb->global_schema_lock_count));
|
|
DBUG_ASSERT(ndb != NULL);
|
|
if (ndb == NULL) {
|
|
return 0;
|
|
}
|
|
DBUG_ASSERT(trans != NULL || thd_ndb->global_schema_lock_error != 0);
|
|
if (thd_ndb->global_schema_lock_count != 0) {
|
|
return 0;
|
|
}
|
|
thd_ndb->global_schema_lock_error = 0;
|
|
|
|
if (trans) {
|
|
thd_ndb->global_schema_lock_trans = NULL;
|
|
|
|
// Remember GSL for tablespace released
|
|
if (is_tablespace) tablespace_gsl_guard.tablespace_gsl_released();
|
|
|
|
NdbError ndb_error;
|
|
if (!gsl_unlock_ext(ndb, trans, ndb_error)) {
|
|
ndb_log_warning("Failed to release global schema lock, error: (%d)%s",
|
|
ndb_error.code, ndb_error.message);
|
|
thd_ndb->push_ndb_error_warning(ndb_error);
|
|
thd_ndb->push_warning("Failed to release global schema lock");
|
|
return -1;
|
|
}
|
|
|
|
ndb_log_verbose(19, "Global schema lock release");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool ndb_gsl_lock(THD *thd, bool lock, bool is_tablespace, bool *victimized) {
|
|
DBUG_TRACE;
|
|
|
|
if (lock) {
|
|
if (ndbcluster_global_schema_lock(thd, true, is_tablespace, victimized) !=
|
|
0) {
|
|
DBUG_PRINT("error", ("Failed to lock global schema lock"));
|
|
return true; // Error
|
|
}
|
|
|
|
return false; // OK
|
|
}
|
|
|
|
*victimized = false;
|
|
if (ndbcluster_global_schema_unlock(thd, is_tablespace) != 0) {
|
|
DBUG_PRINT("error", ("Failed to unlock global schema lock"));
|
|
return true; // Error
|
|
}
|
|
|
|
return false; // OK
|
|
}
|
|
|
|
bool Thd_ndb::has_required_global_schema_lock(const char *func) const {
|
|
if (global_schema_lock_error) {
|
|
// An error occurred while locking, either because
|
|
// no connection to cluster or another user has locked
|
|
// the lock -> ok, but caller should not allow to continue
|
|
return false;
|
|
}
|
|
|
|
if (global_schema_lock_trans) {
|
|
global_schema_lock_trans->refresh();
|
|
return true; // All OK
|
|
}
|
|
|
|
// No attempt at taking global schema lock has been done, neither
|
|
// error or trans set -> programming error
|
|
LEX_CSTRING query = thd_query_unsafe(m_thd);
|
|
ndb_log_error(
|
|
"programming error, no lock taken while running "
|
|
"query '%*s' in function '%s'",
|
|
(int)query.length, query.str, func);
|
|
abort();
|
|
return false;
|
|
}
|
|
|
|
#include "storage/ndb/plugin/ndb_global_schema_lock_guard.h"
|
|
|
|
Ndb_global_schema_lock_guard::Ndb_global_schema_lock_guard(THD *thd)
|
|
: m_thd(thd), m_locked(false), m_try_locked(false) {}
|
|
|
|
Ndb_global_schema_lock_guard::~Ndb_global_schema_lock_guard() {
|
|
if (m_try_locked)
|
|
unlock();
|
|
else if (m_locked)
|
|
ndbcluster_global_schema_unlock(m_thd, false /* is_tablespace */);
|
|
}
|
|
|
|
/**
|
|
* Set a Global Schema Lock.
|
|
* May fail due to either Ndb Cluster failure, or due to being
|
|
* 'victimized' as part of deadlock resolution. In the later case we
|
|
* retry the GSL locking.
|
|
*/
|
|
int Ndb_global_schema_lock_guard::lock(void) {
|
|
/* only one lock call allowed */
|
|
assert(!m_locked);
|
|
|
|
/*
|
|
Always set m_locked, even if lock fails. Since the
|
|
lock/unlock calls are reference counted, the number
|
|
of calls to lock and unlock need to match up.
|
|
*/
|
|
m_locked = true;
|
|
bool victimized = false;
|
|
bool ret;
|
|
do {
|
|
ret = ndbcluster_global_schema_lock(m_thd, false, false /* is_tablespace */,
|
|
&victimized);
|
|
if (ret && thd_killed(m_thd)) {
|
|
// Failed to acuire GSL and THD is killed -> give up!
|
|
break; // Terminate loop
|
|
}
|
|
} while (victimized);
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool Ndb_global_schema_lock_guard::try_lock(void) {
|
|
/*
|
|
Always set m_locked, even if lock fails. Since the lock/unlock calls are
|
|
reference counted, the number of calls to lock and unlock need to match up.
|
|
*/
|
|
m_locked = true;
|
|
m_try_locked = true;
|
|
Thd_ndb *thd_ndb = get_thd_ndb(m_thd);
|
|
// Check if this thd has acquired GSL already
|
|
if (thd_ndb->global_schema_lock_count) return false;
|
|
|
|
thd_ndb->global_schema_lock_error = 0;
|
|
|
|
Ndb *ndb = check_ndb_in_thd(m_thd);
|
|
NdbError ndb_error;
|
|
// Attempt to take the GSL with no_retry and no_wait both set
|
|
thd_ndb->global_schema_lock_trans =
|
|
gsl_lock_ext(m_thd, ndb, ndb_error, true, /* no_retry */
|
|
true /* no_wait */);
|
|
|
|
if (thd_ndb->global_schema_lock_trans != nullptr) {
|
|
ndb_log_verbose(19, "Global schema lock acquired");
|
|
|
|
// Count number of global schema locks taken by this thread
|
|
thd_ndb->schema_locks_count++;
|
|
thd_ndb->global_schema_lock_count = 1;
|
|
DBUG_PRINT("info", ("schema_locks_count: %d", thd_ndb->schema_locks_count));
|
|
|
|
return true;
|
|
}
|
|
thd_ndb->global_schema_lock_error = ndb_error.code ? ndb_error.code : -1;
|
|
return false;
|
|
}
|
|
|
|
bool Ndb_global_schema_lock_guard::unlock() {
|
|
// This function should only be called in conjunction with try_lock()
|
|
DBUG_ASSERT(m_try_locked);
|
|
|
|
Thd_ndb *thd_ndb = get_thd_ndb(m_thd);
|
|
if (unlikely(thd_ndb == nullptr)) {
|
|
return true;
|
|
}
|
|
|
|
Ndb *ndb = thd_ndb->ndb;
|
|
if (ndb == nullptr) {
|
|
return true;
|
|
}
|
|
NdbTransaction *trans = thd_ndb->global_schema_lock_trans;
|
|
thd_ndb->global_schema_lock_error = 0;
|
|
if (trans != nullptr) {
|
|
thd_ndb->global_schema_lock_trans = nullptr;
|
|
thd_ndb->global_schema_lock_count = 0;
|
|
|
|
NdbError ndb_error;
|
|
if (!gsl_unlock_ext(ndb, trans, ndb_error)) {
|
|
ndb_log_warning("Failed to release global schema lock, error: (%d)%s",
|
|
ndb_error.code, ndb_error.message);
|
|
thd_ndb->push_ndb_error_warning(ndb_error);
|
|
thd_ndb->push_warning("Failed to release global schema lock");
|
|
return false;
|
|
}
|
|
ndb_log_verbose(19, "Global schema lock release");
|
|
}
|
|
return true;
|
|
}
|