MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
ndb_global_schema_lock.cc
1 /*
2  Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
3 
4  This program is free software; you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation; version 2 of the License.
7 
8  This program is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  GNU General Public License for more details.
12 
13  You should have received a copy of the GNU General Public License
14  along with this program; if not, write to the Free Software
15  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
16 */
17 
18 
19 #include <mysql/plugin.h>
20 #include <ndbapi/NdbApi.hpp>
21 #include <portlib/NdbTick.h>
22 #include <my_sys.h> // my_sleep.h
23 
24 /* perform random sleep in the range milli_sleep to 2*milli_sleep */
25 static inline
26 void do_retry_sleep(unsigned milli_sleep)
27 {
28  my_sleep(1000*(milli_sleep + 5*(rand()%(milli_sleep/5))));
29 }
30 
31 
32 #include "ndb_table_guard.h"
33 
34 /*
35  The lock/unlock functions use the BACKUP_SEQUENCE row in SYSTAB_0
36 
37  retry_time == 0 means no retry
38  retry_time < 0 means infinite retries
39  retry_time > 0 means retries for max 'retry_time' seconds
40 */
41 static NdbTransaction *
42 gsl_lock_ext(THD *thd, Ndb *ndb, NdbError &ndb_error,
43  int retry_time= 10)
44 {
45  ndb->setDatabaseName("sys");
46  ndb->setDatabaseSchemaName("def");
48  Ndb_table_guard ndbtab_g(dict, "SYSTAB_0");
49  const NdbDictionary::Table *ndbtab= NULL;
50  NdbOperation *op;
51  NdbTransaction *trans= NULL;
52  int retry_sleep= 50; /* 50 milliseconds, transaction */
53  NDB_TICKS time_end;
54 
55  if (retry_time > 0)
56  {
57  time_end= NdbTick_CurrentMillisecond();
58  time_end+= retry_time * 1000;
59  }
60  while (1)
61  {
62  if (!ndbtab)
63  {
64  if (!(ndbtab= ndbtab_g.get_table()))
65  {
67  goto retry;
68  ndb_error= dict->getNdbError();
69  goto error_handler;
70  }
71  }
72 
73  trans= ndb->startTransaction();
74  if (trans == NULL)
75  {
76  ndb_error= ndb->getNdbError();
77  goto error_handler;
78  }
79 
80  op= trans->getNdbOperation(ndbtab);
82  op->equal("SYSKEY_0", NDB_BACKUP_SEQUENCE);
83 
84  if (trans->execute(NdbTransaction::NoCommit) == 0)
85  break;
86 
88  goto error_handler;
89  else if (thd_killed(thd))
90  goto error_handler;
91  retry:
92  if (retry_time == 0)
93  goto error_handler;
94  if (retry_time > 0 &&
95  time_end < NdbTick_CurrentMillisecond())
96  goto error_handler;
97  if (trans)
98  {
99  ndb->closeTransaction(trans);
100  trans= NULL;
101  }
102  do_retry_sleep(retry_sleep);
103  }
104  return trans;
105 
106  error_handler:
107  if (trans)
108  {
109  ndb_error= trans->getNdbError();
110  ndb->closeTransaction(trans);
111  }
112  return NULL;
113 }
114 
115 
116 static bool
117 gsl_unlock_ext(Ndb *ndb, NdbTransaction *trans,
118  NdbError &ndb_error)
119 {
120  if (trans->execute(NdbTransaction::Commit))
121  {
122  ndb_error= trans->getNdbError();
123  ndb->closeTransaction(trans);
124  return false;
125  }
126  ndb->closeTransaction(trans);
127  return true;
128 }
129 
130 /*
131  lock/unlock calls are reference counted, so calls to lock
132  must be matched to a call to unlock even if the lock call fails
133 */
134 static int gsl_is_locked_or_queued= 0;
135 static int gsl_no_locking_allowed= 0;
136 static pthread_mutex_t gsl_mutex;
137 
138 /*
139  Indicates if ndb_global_schema_lock module is active/initialized, normally
140  turned on/off in ndbcluster_init/deinit with LOCK_plugin held.
141 */
142 static bool gsl_initialized= false;
143 
144 // NOTE! 'thd_proc_info' is defined in myql/plugin.h but not implemented, only
145 // a #define available in sql_class.h -> include sql_class.h until
146 // bug#11844974 has been fixed.
147 #include <sql_class.h>
148 
150 {
151 public:
152  Thd_proc_info_guard(THD *thd)
153  : m_thd(thd), m_proc_info(NULL) {}
154  void set(const char* message)
155  {
156  const char* old= thd_proc_info(m_thd, message);
157  if (!m_proc_info)
158  {
159  // Save the original on first change
160  m_proc_info = old;
161  }
162  }
164  {
165  if (m_proc_info)
166  thd_proc_info(m_thd, m_proc_info);
167  }
168 private:
169  THD *m_thd;
170  const char *m_proc_info;
171 };
172 
173 
174 #include "ndb_thd.h"
175 #include "ndb_thd_ndb.h"
176 
177 
178 extern ulong opt_ndb_extra_logging;
179 
180 static
181 int
182 ndbcluster_global_schema_lock(THD *thd, bool no_lock_queue,
183  bool report_cluster_disconnected)
184 {
185  if (!gsl_initialized)
186  return 0;
187 
188  Ndb *ndb= check_ndb_in_thd(thd);
189  Thd_ndb *thd_ndb= get_thd_ndb(thd);
190  NdbError ndb_error;
191  if (thd_ndb->options & TNO_NO_LOCK_SCHEMA_OP)
192  return 0;
193  DBUG_ENTER("ndbcluster_global_schema_lock");
194  DBUG_PRINT("enter", ("query: '%-.4096s', no_lock_queue: %d",
195  *thd_query(thd), no_lock_queue));
196  if (thd_ndb->global_schema_lock_count)
197  {
198  if (thd_ndb->global_schema_lock_trans)
199  thd_ndb->global_schema_lock_trans->refresh();
200  else
201  DBUG_ASSERT(thd_ndb->global_schema_lock_error != 0);
202  thd_ndb->global_schema_lock_count++;
203  DBUG_PRINT("exit", ("global_schema_lock_count: %d",
204  thd_ndb->global_schema_lock_count));
205  DBUG_RETURN(0);
206  }
207  DBUG_ASSERT(thd_ndb->global_schema_lock_count == 0);
208  thd_ndb->global_schema_lock_count= 1;
209  thd_ndb->global_schema_lock_error= 0;
210  DBUG_PRINT("exit", ("global_schema_lock_count: %d",
211  thd_ndb->global_schema_lock_count));
212 
213  /*
214  Check that taking the lock is allowed
215  - if not allowed to enter lock queue, return if lock exists
216  - wait until allowed
217  - increase global lock count
218  */
219  Thd_proc_info_guard proc_info(thd);
220  pthread_mutex_lock(&gsl_mutex);
221  /* increase global lock count */
222  gsl_is_locked_or_queued++;
223  if (no_lock_queue)
224  {
225  if (gsl_is_locked_or_queued != 1)
226  {
227  /* Other thread has lock and this thread may not enter lock queue */
228  pthread_mutex_unlock(&gsl_mutex);
229  thd_ndb->global_schema_lock_error= -1;
230  DBUG_PRINT("exit", ("aborting as lock exists"));
231  DBUG_RETURN(-1);
232  }
233  /* Mark that no other thread may be take lock */
234  gsl_no_locking_allowed= 1;
235  }
236  else
237  {
238  while (gsl_no_locking_allowed)
239  {
240  proc_info.set("Waiting for allowed to take ndbcluster global schema lock");
241  /* Wait until locking is allowed */
242  pthread_mutex_unlock(&gsl_mutex);
243  do_retry_sleep(50);
244  if (thd_killed(thd))
245  {
246  thd_ndb->global_schema_lock_error= -1;
247  DBUG_RETURN(-1);
248  }
249  pthread_mutex_lock(&gsl_mutex);
250  }
251  }
252  pthread_mutex_unlock(&gsl_mutex);
253 
254  /*
255  Take the lock
256  */
257  proc_info.set("Waiting for ndbcluster global schema lock");
258  thd_ndb->global_schema_lock_trans= gsl_lock_ext(thd, ndb, ndb_error, -1);
259 
260  DBUG_EXECUTE_IF("sleep_after_global_schema_lock", my_sleep(6000000););
261 
262  if (no_lock_queue)
263  {
264  pthread_mutex_lock(&gsl_mutex);
265  /* Mark that other thread may be take lock */
266  gsl_no_locking_allowed= 0;
267  pthread_mutex_unlock(&gsl_mutex);
268  }
269 
270  if (thd_ndb->global_schema_lock_trans)
271  {
272  if (opt_ndb_extra_logging > 19)
273  {
274  sql_print_information("NDB: Global schema lock acquired");
275  }
276 
277  // Count number of global schema locks taken by this thread
278  thd_ndb->schema_locks_count++;
279  DBUG_PRINT("info", ("schema_locks_count: %d",
280  thd_ndb->schema_locks_count));
281 
282  DBUG_RETURN(0);
283  }
284 
285  if (ndb_error.code != 4009 || report_cluster_disconnected)
286  {
287  sql_print_warning("NDB: Could not acquire global schema lock (%d)%s",
288  ndb_error.code, ndb_error.message);
289  push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
290  ER_GET_ERRMSG, ER_DEFAULT(ER_GET_ERRMSG),
291  ndb_error.code, ndb_error.message,
292  "NDB. Could not acquire global schema lock");
293  }
294  thd_ndb->global_schema_lock_error= ndb_error.code ? ndb_error.code : -1;
295  DBUG_RETURN(-1);
296 }
297 
298 
299 static
300 int
301 ndbcluster_global_schema_unlock(THD *thd)
302 {
303  if (!gsl_initialized)
304  return 0;
305 
306  Thd_ndb *thd_ndb= get_thd_ndb(thd);
307  DBUG_ASSERT(thd_ndb != 0);
308  if (thd_ndb == 0 || (thd_ndb->options & TNO_NO_LOCK_SCHEMA_OP))
309  return 0;
310  Ndb *ndb= thd_ndb->ndb;
311  DBUG_ENTER("ndbcluster_global_schema_unlock");
312  NdbTransaction *trans= thd_ndb->global_schema_lock_trans;
313  thd_ndb->global_schema_lock_count--;
314  DBUG_PRINT("exit", ("global_schema_lock_count: %d",
315  thd_ndb->global_schema_lock_count));
316  DBUG_ASSERT(ndb != NULL);
317  if (ndb == NULL)
318  return 0;
319  DBUG_ASSERT(trans != NULL || thd_ndb->global_schema_lock_error != 0);
320  if (thd_ndb->global_schema_lock_count != 0)
321  {
322  DBUG_RETURN(0);
323  }
324  thd_ndb->global_schema_lock_error= 0;
325 
326  /*
327  Decrease global lock count
328  */
329  pthread_mutex_lock(&gsl_mutex);
330  gsl_is_locked_or_queued--;
331  pthread_mutex_unlock(&gsl_mutex);
332 
333  if (trans)
334  {
335  thd_ndb->global_schema_lock_trans= NULL;
336  NdbError ndb_error;
337  if (!gsl_unlock_ext(ndb, trans, ndb_error))
338  {
339  sql_print_warning("NDB: Releasing global schema lock (%d)%s",
340  ndb_error.code, ndb_error.message);
341  push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
342  ER_GET_ERRMSG, ER_DEFAULT(ER_GET_ERRMSG),
343  ndb_error.code,
344  ndb_error.message,
345  "ndb. Releasing global schema lock");
346  DBUG_RETURN(-1);
347  }
348  if (opt_ndb_extra_logging > 19)
349  {
350  sql_print_information("NDB: Global schema lock release");
351  }
352  }
353  DBUG_RETURN(0);
354 }
355 
356 
357 #ifndef NDB_WITHOUT_GLOBAL_SCHEMA_LOCK
358 static
359 int
360 ndbcluster_global_schema_func(THD *thd, bool lock, void* args)
361 {
362  if (lock)
363  {
364  bool no_lock_queue = (bool)args;
365  return ndbcluster_global_schema_lock(thd, no_lock_queue, true);
366  }
367 
368  return ndbcluster_global_schema_unlock(thd);
369 }
370 #endif
371 
372 
373 #include "ndb_global_schema_lock.h"
374 
375 void ndbcluster_global_schema_lock_init(handlerton *hton)
376 {
377  assert(gsl_initialized == false);
378  assert(gsl_is_locked_or_queued == 0);
379  assert(gsl_no_locking_allowed == 0);
380  gsl_initialized= true;
381  pthread_mutex_init(&gsl_mutex, MY_MUTEX_INIT_FAST);
382 
383 #ifndef NDB_WITHOUT_GLOBAL_SCHEMA_LOCK
384  hton->global_schema_func= ndbcluster_global_schema_func;
385 #endif
386 }
387 
388 
389 void ndbcluster_global_schema_lock_deinit(void)
390 {
391  assert(gsl_initialized == true);
392  assert(gsl_is_locked_or_queued == 0);
393  assert(gsl_no_locking_allowed == 0);
394  gsl_initialized= false;
395  pthread_mutex_destroy(&gsl_mutex);
396 }
397 
398 
399 bool
400 Thd_ndb::has_required_global_schema_lock(const char* func)
401 {
402 #ifdef NDB_WITHOUT_GLOBAL_SCHEMA_LOCK
403  // The global schema lock hook is not installed ->
404  // no thd has gsl
405  return true;
406 #else
407  if (global_schema_lock_error)
408  {
409  // An error occured while locking, either because
410  // no connection to cluster or another user has locked
411  // the lock -> ok, but caller should not allow to continue
412  return false;
413  }
414 
415  if (global_schema_lock_trans)
416  {
417  global_schema_lock_trans->refresh();
418  return true; // All OK
419  }
420 
421  // No attempt at taking global schema lock has been done, neither
422  // error or trans set -> programming error
423  LEX_STRING* query= thd_query_string(m_thd);
424  sql_print_error("NDB: programming error, no lock taken while running "
425  "query '%*s' in function '%s'",
426  (int)query->length, query->str, func);
427  abort();
428  return false;
429 #endif
430 }
431 
432 
433 #include "ndb_global_schema_lock_guard.h"
434 
435 Ndb_global_schema_lock_guard::Ndb_global_schema_lock_guard(THD *thd)
436  : m_thd(thd), m_locked(false)
437 {
438 }
439 
440 
441 Ndb_global_schema_lock_guard::~Ndb_global_schema_lock_guard()
442 {
443  if (m_locked)
444  ndbcluster_global_schema_unlock(m_thd);
445 }
446 
447 
448 int Ndb_global_schema_lock_guard::lock(bool no_lock_queue,
449  bool report_cluster_disconnected)
450 {
451  /* only one lock call allowed */
452  assert(!m_locked);
453 
454  /*
455  Always set m_locked, even if lock fails. Since the
456  lock/unlock calls are reference counted, the number
457  of calls to lock and unlock need to match up.
458  */
459  m_locked= true;
460 
461  return ndbcluster_global_schema_lock(m_thd, no_lock_queue,
462  report_cluster_disconnected);
463 }