1 /*
2 Copyright (c) 2011, 2016, 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, version 2.0,
6 as published by the Free Software Foundation.
7
8 This program is also distributed with certain software (including
9 but not limited to OpenSSL) that is licensed under separate terms,
10 as designated in a particular file or component or in included license
11 documentation. The authors of MySQL hereby grant you an additional
12 permission to link the program and your derivative works with the
13 separately licensed software that they have included with MySQL.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License, version 2.0, for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24
25 #include "ha_ndbcluster_glue.h"
26
27 #ifdef WITH_NDBCLUSTER_STORAGE_ENGINE
28
29 #include "ha_ndbcluster.h"
30 #include "ha_ndb_index_stat.h"
31 #include <mysql/plugin.h>
32 #include <ctype.h>
33
34 // copied from ha_ndbcluster_binlog.h
35
36 extern handlerton *ndbcluster_hton;
37
38 inline
39 void
set_thd_ndb(THD * thd,Thd_ndb * thd_ndb)40 set_thd_ndb(THD *thd, Thd_ndb *thd_ndb)
41 { thd_set_ha_data(thd, ndbcluster_hton, thd_ndb); }
42
43 // Typedefs for long names
44 typedef NdbDictionary::Table NDBTAB;
45 typedef NdbDictionary::Index NDBINDEX;
46
47 struct Ndb_index_stat {
48 enum {
49 LT_Undef= 0,
50 LT_New= 1, /* new entry added by a table handler */
51 LT_Update = 2, /* force kernel update from analyze table */
52 LT_Read= 3, /* read or reread stats into new query cache */
53 LT_Idle= 4, /* stats exist */
54 LT_Check= 5, /* check for new stats */
55 LT_Delete= 6, /* delete the entry */
56 LT_Error= 7, /* error, on hold for a while */
57 LT_Count= 8
58 };
59 NdbIndexStat* is;
60 int index_id;
61 int index_version;
62 #ifndef DBUG_OFF
63 char id[32];
64 #endif
65 time_t access_time; /* by any table handler */
66 time_t load_time; /* when stats were created by kernel */
67 time_t read_time; /* when stats were read by us (>= load_time) */
68 uint sample_version; /* goes with read_time */
69 time_t check_time; /* when checked for updated stats (>= read_time) */
70 bool cache_clean; /* old caches have been deleted */
71 uint force_update; /* one-time force update from analyze table */
72 bool no_stats; /* have detected that no stats exist */
73 NdbIndexStat::Error error;
74 time_t error_time;
75 int error_count;
76 struct Ndb_index_stat *share_next; /* per-share list */
77 int lt;
78 int lt_old; /* for info only */
79 struct Ndb_index_stat *list_next;
80 struct Ndb_index_stat *list_prev;
81 struct NDB_SHARE *share;
82 Ndb_index_stat();
83 };
84
85 struct Ndb_index_stat_list {
86 const char *name;
87 int lt;
88 struct Ndb_index_stat *head;
89 struct Ndb_index_stat *tail;
90 uint count;
91 Ndb_index_stat_list(int the_lt, const char* the_name);
92 };
93
94 extern Ndb_index_stat_list ndb_index_stat_list[];
95
96 time_t ndb_index_stat_time_now= 0;
97
98 time_t
ndb_index_stat_time()99 ndb_index_stat_time()
100 {
101 time_t now= time(0);
102
103 if (unlikely(ndb_index_stat_time_now == 0))
104 ndb_index_stat_time_now= now;
105
106 if (unlikely(now < ndb_index_stat_time_now))
107 {
108 DBUG_PRINT("index_stat", ("time moved backwards %d seconds",
109 int(ndb_index_stat_time_now - now)));
110 now= ndb_index_stat_time_now;
111 }
112
113 ndb_index_stat_time_now= now;
114 return now;
115 }
116
117 bool ndb_index_stat_allow_flag= false;
118
119 bool
ndb_index_stat_allow(int flag=-1)120 ndb_index_stat_allow(int flag= -1)
121 {
122 if (flag != -1) {
123 pthread_mutex_lock(&ndb_index_stat_list_mutex);
124 ndb_index_stat_allow_flag= (bool)flag;
125 pthread_mutex_unlock(&ndb_index_stat_list_mutex);
126 }
127 return ndb_index_stat_allow_flag;
128 }
129
130 /* Options */
131
132 /* Options in string format buffer size */
133 static const uint ndb_index_stat_option_sz= 512;
134 void ndb_index_stat_opt2str(const struct Ndb_index_stat_opt&, char*);
135
136 struct Ndb_index_stat_opt {
137 enum Unit {
138 Ubool = 1,
139 Usize = 2,
140 Utime = 3,
141 Umsec = 4
142 };
143 enum Flag {
144 Freadonly = (1 << 0)
145 };
146 struct Val {
147 const char* name;
148 uint val;
149 uint minval;
150 uint maxval;
151 Unit unit;
152 uint flag;
153 };
154 enum Idx {
155 Iloop_checkon = 0,
156 Iloop_idle = 1,
157 Iloop_busy = 2,
158 Iupdate_batch = 3,
159 Iread_batch = 4,
160 Iidle_batch = 5,
161 Icheck_batch = 6,
162 Icheck_delay = 7,
163 Idelete_batch = 8,
164 Iclean_delay = 9,
165 Ierror_batch = 10,
166 Ierror_delay = 11,
167 Ievict_batch = 12,
168 Ievict_delay = 13,
169 Icache_limit = 14,
170 Icache_lowpct = 15,
171 Imax = 16
172 };
173 Val val[Imax];
174 /* Options in string format (SYSVAR ndb_index_stat_option) */
175 char *option;
176 Ndb_index_stat_opt(char* buf);
getNdb_index_stat_opt177 uint get(Idx i) const {
178 assert(i < Imax);
179 return val[i].val;
180 }
181 };
182
Ndb_index_stat_opt(char * buf)183 Ndb_index_stat_opt::Ndb_index_stat_opt(char* buf) :
184 option(buf)
185 {
186 #define ival(aname, aval, aminval, amaxval, aunit, aflag) \
187 val[I##aname].name = #aname; \
188 val[I##aname].val = aval; \
189 val[I##aname].minval = aminval; \
190 val[I##aname].maxval = amaxval; \
191 val[I##aname].unit = aunit; \
192 val[I##aname].flag = aflag
193 ival(loop_checkon, 1000, 0, ~0, Umsec, 0);
194 ival(loop_idle, 1000, 0, ~0, Umsec, 0);
195 ival(loop_busy, 100, 0, ~0, Umsec, 0);
196 ival(update_batch, 1, 1, ~0, Usize, 0);
197 ival(read_batch, 4, 1, ~0, Usize, 0);
198 ival(idle_batch, 32, 1, ~0, Usize, 0);
199 ival(check_batch, 32, 1, ~0, Usize, 0);
200 ival(check_delay, 60, 0, ~0, Utime, 0);
201 ival(clean_delay, 0, 0, ~0, Utime, 0);
202 ival(delete_batch, 8, 1, ~0, Usize, 0);
203 ival(error_batch, 4, 1, ~0, Usize, 0);
204 ival(error_delay, 60, 0, ~0, Utime, 0);
205 ival(evict_batch, 8, 1, ~0, Usize, 0);
206 ival(evict_delay, 60, 0, ~0, Utime, 0);
207 ival(cache_limit, 32*1024*1024, 1024*1024, ~0, Usize, 0);
208 ival(cache_lowpct, 90, 0, 100, Usize, 0);
209 #undef ival
210
211 ndb_index_stat_opt2str(*this, option);
212 }
213
214 /* Hard limits */
215 static const uint ndb_index_stat_max_evict_batch = 32;
216
217 char ndb_index_stat_option_buf[ndb_index_stat_option_sz];
218 Ndb_index_stat_opt ndb_index_stat_opt(ndb_index_stat_option_buf);
219
220 /* Copy option struct to string buffer */
221 void
ndb_index_stat_opt2str(const Ndb_index_stat_opt & opt,char * str)222 ndb_index_stat_opt2str(const Ndb_index_stat_opt& opt, char* str)
223 {
224 DBUG_ENTER("ndb_index_stat_opt2str");
225
226 char buf[ndb_index_stat_option_sz];
227 char *const end= &buf[sizeof(buf)];
228 char* ptr= buf;
229 *ptr= 0;
230
231 const uint imax= Ndb_index_stat_opt::Imax;
232 for (uint i= 0; i < imax; i++)
233 {
234 const Ndb_index_stat_opt::Val& v= opt.val[i];
235 ptr+= strlen(ptr);
236 const char* sep= (ptr == buf ? "" : ",");
237 const uint sz= ptr < end ? end - ptr : 0;
238
239 switch (v.unit) {
240 case Ndb_index_stat_opt::Ubool:
241 {
242 DBUG_ASSERT(v.val == 0 || v.val == 1);
243 if (v.val == 0)
244 my_snprintf(ptr, sz, "%s%s=OFF", sep, v.name);
245 else
246 my_snprintf(ptr, sz, "%s%s=ON", sep, v.name);
247 }
248 break;
249
250 case Ndb_index_stat_opt::Usize:
251 {
252 uint m;
253 if (v.val == 0)
254 my_snprintf(ptr, sz, "%s%s=0", sep, v.name);
255 else if (v.val % (m= 1024*1024*1024) == 0)
256 my_snprintf(ptr, sz, "%s%s=%uG", sep, v.name, v.val / m);
257 else if (v.val % (m= 1024*1024) == 0)
258 my_snprintf(ptr, sz, "%s%s=%uM", sep, v.name, v.val / m);
259 else if (v.val % (m= 1024) == 0)
260 my_snprintf(ptr, sz, "%s%s=%uK", sep, v.name, v.val / m);
261 else
262 my_snprintf(ptr, sz, "%s%s=%u", sep, v.name, v.val);
263 }
264 break;
265
266 case Ndb_index_stat_opt::Utime:
267 {
268 uint m;
269 if (v.val == 0)
270 my_snprintf(ptr, sz, "%s%s=0", sep, v.name);
271 else if (v.val % (m= 60*60*24) == 0)
272 my_snprintf(ptr, sz, "%s%s=%ud", sep, v.name, v.val / m);
273 else if (v.val % (m= 60*60) == 0)
274 my_snprintf(ptr, sz, "%s%s=%uh", sep, v.name, v.val / m);
275 else if (v.val % (m= 60) == 0)
276 my_snprintf(ptr, sz, "%s%s=%um", sep, v.name, v.val / m);
277 else
278 my_snprintf(ptr, sz, "%s%s=%us", sep, v.name, v.val);
279 }
280 break;
281
282 case Ndb_index_stat_opt::Umsec:
283 {
284 if (v.val == 0)
285 my_snprintf(ptr, sz, "%s%s=0", sep, v.name);
286 else
287 my_snprintf(ptr, sz, "%s%s=%ums", sep, v.name, v.val);
288 }
289 break;
290
291 default:
292 DBUG_ASSERT(false);
293 break;
294 }
295 }
296
297 memset(str, 0, ndb_index_stat_option_sz);
298 strcpy(str, buf);
299 DBUG_PRINT("index_stat", ("str: \"%s\"", str));
300 DBUG_VOID_RETURN;
301 }
302
303 int
ndb_index_stat_option_parse(char * p,Ndb_index_stat_opt & opt)304 ndb_index_stat_option_parse(char* p, Ndb_index_stat_opt& opt)
305 {
306 DBUG_ENTER("ndb_index_stat_option_parse");
307
308 char *r= strchr(p, '=');
309 if (r == 0)
310 DBUG_RETURN(-1);
311 *r++= 0;
312
313 while (isspace(*r))
314 *r++= 0;
315 if (*r == 0)
316 DBUG_RETURN(-1);
317
318 const uint imax= Ndb_index_stat_opt::Imax;
319 for (uint i= 0; i < imax; i++)
320 {
321 Ndb_index_stat_opt::Val& v= opt.val[i];
322 if (strcmp(p, v.name) != 0)
323 continue;
324
325 char *s;
326 for (s= r; *s != 0; s++)
327 *s= tolower(*s);
328 ulonglong val= strtoull(r, &s, 10);
329
330 switch (v.unit) {
331 case Ndb_index_stat_opt::Ubool:
332 {
333 if ((s > r && *s == 0 && val == 0) ||
334 strcmp(r, "off") == 0 ||
335 strcmp(r, "false") == 0)
336 val= 0;
337 else if ((s > r && *s == 0 && val == 1) ||
338 strcmp(r, "on") == 0 ||
339 strcmp(r, "true") == 0)
340 val= 1;
341 else
342 DBUG_RETURN(-1);
343 v.val= (uint)val;
344 }
345 break;
346
347 case Ndb_index_stat_opt::Usize:
348 {
349 if (s == r)
350 DBUG_RETURN(-1);
351 if (strcmp(s, "") == 0)
352 ;
353 else if (strcmp(s, "k") == 0)
354 val*= 1024;
355 else if (strcmp(s, "m") == 0)
356 val*= 1024*1024;
357 else if (strcmp(s, "g") == 0)
358 val*= 1024*1024*1024;
359 else
360 DBUG_RETURN(-1);
361 if (val < v.minval || val > v.maxval)
362 DBUG_RETURN(-1);
363 v.val= (uint)val;
364 }
365 break;
366
367 case Ndb_index_stat_opt::Utime:
368 {
369 if (s == r)
370 DBUG_RETURN(-1);
371 if (strcmp(s, "") == 0)
372 ;
373 else if (strcmp(s, "s") == 0)
374 ;
375 else if (strcmp(s, "m") == 0)
376 val*= 60;
377 else if (strcmp(s, "h") == 0)
378 val*= 60*60;
379 else if (strcmp(s, "d") == 0)
380 val*= 24*60*60;
381 else
382 DBUG_RETURN(-1);
383 if (val < v.minval || val > v.maxval)
384 DBUG_RETURN(-1);
385 v.val= (uint)val;
386 }
387 break;
388
389 case Ndb_index_stat_opt::Umsec:
390 {
391 if (s == r)
392 DBUG_RETURN(-1);
393 if (strcmp(s, "") == 0)
394 ;
395 else if (strcmp(s, "ms") == 0)
396 ;
397 else
398 DBUG_RETURN(-1);
399 if (val < v.minval || val > v.maxval)
400 DBUG_RETURN(-1);
401 v.val= (uint)val;
402 }
403 break;
404
405 default:
406 DBUG_ASSERT(false);
407 break;
408 }
409 }
410 DBUG_RETURN(0);
411 }
412
413 /* Copy option string to option struct */
414 int
ndb_index_stat_str2opt(const char * str,Ndb_index_stat_opt & opt)415 ndb_index_stat_str2opt(const char *str, Ndb_index_stat_opt& opt)
416 {
417 DBUG_ENTER("ndb_index_stat_str2opt");
418 DBUG_PRINT("index_stat", ("str: \"%s\"", str));
419
420 char buf[ndb_index_stat_option_sz];
421
422 assert(str != 0);
423 if (strlen(str) >= sizeof(buf))
424 DBUG_RETURN(-1);
425 strcpy(buf, str);
426
427 char *p= buf;
428 while (1)
429 {
430 while (isspace(*p))
431 p++;
432 if (*p == 0)
433 break;
434
435 char *q= strchr(p, ',');
436 if (q == p)
437 DBUG_RETURN(-1);
438 if (q != 0)
439 *q= 0;
440
441 DBUG_PRINT("index_stat", ("parse: %s", p));
442 if (ndb_index_stat_option_parse(p, opt) == -1)
443 DBUG_RETURN(-1);
444
445 if (q == 0)
446 break;
447 p= q + 1;
448 }
449
450 ndb_index_stat_opt2str(opt, opt.option);
451 DBUG_RETURN(0);
452 }
453
454 /* Thanks to ha_innodb.cc */
455
456 /* Need storage between check and update (assume locked) */
457 char ndb_index_stat_option_tmp[ndb_index_stat_option_sz];
458
459 int
ndb_index_stat_option_check(MYSQL_THD,struct st_mysql_sys_var * var,void * save,struct st_mysql_value * value)460 ndb_index_stat_option_check(MYSQL_THD,
461 struct st_mysql_sys_var *var,
462 void *save,
463 struct st_mysql_value *value)
464 {
465 DBUG_ENTER("ndb_index_stat_option_check");
466 char buf[ndb_index_stat_option_sz];
467 int len= sizeof(buf);
468 const char *str= value->val_str(value, buf, &len);
469 if (str != 0)
470 {
471 /* Seems to be nothing in buf */
472 DBUG_PRINT("index_stat", ("str: %s len: %d", str, len));
473 char buf2[ndb_index_stat_option_sz];
474 Ndb_index_stat_opt opt(buf2);
475 if (ndb_index_stat_str2opt(str, opt) == 0)
476 {
477 /* Passed to update */
478 strcpy(ndb_index_stat_option_tmp, str);
479 *(const char**)save= ndb_index_stat_option_tmp;
480 DBUG_RETURN(0);
481 }
482 }
483 DBUG_RETURN(1);
484 }
485
486 void
ndb_index_stat_option_update(MYSQL_THD,struct st_mysql_sys_var * var,void * var_ptr,const void * save)487 ndb_index_stat_option_update(MYSQL_THD,
488 struct st_mysql_sys_var *var,
489 void *var_ptr,
490 const void *save)
491 {
492 DBUG_ENTER("ndb_index_stat_option_update");
493 const char *str= *(const char**)save;
494 DBUG_PRINT("index_stat", ("str: %s", str));
495 Ndb_index_stat_opt& opt= ndb_index_stat_opt;
496 int ret= ndb_index_stat_str2opt(str, opt);
497 assert(ret == 0); NDB_IGNORE_VALUE(ret);
498 *(const char**)var_ptr= ndb_index_stat_opt.option;
499 DBUG_VOID_RETURN;
500 }
501
502 /* Global stuff */
503
504 struct Ndb_index_stat_glob {
505 uint list_count[Ndb_index_stat::LT_Count]; /* Temporary use */
506 uint total_count;
507 uint force_update;
508 uint wait_update;
509 uint no_stats;
510 uint cache_query_bytes; /* In use */
511 uint cache_clean_bytes; /* Obsolete versions not yet removed */
Ndb_index_stat_globNdb_index_stat_glob512 Ndb_index_stat_glob() :
513 total_count(0),
514 force_update(0),
515 wait_update(0),
516 no_stats(0),
517 cache_query_bytes(0),
518 cache_clean_bytes(0)
519 {
520 }
set_list_countNdb_index_stat_glob521 void set_list_count()
522 {
523 total_count= 0;
524 int lt;
525 for (lt= 0; lt < Ndb_index_stat::LT_Count; lt++)
526 {
527 const Ndb_index_stat_list &list= ndb_index_stat_list[lt];
528 list_count[lt]= list.count;
529 total_count++;
530 }
531 }
set_status_variablesNdb_index_stat_glob532 void set_status_variables()
533 {
534 g_ndb_status_index_stat_cache_query= cache_query_bytes;
535 g_ndb_status_index_stat_cache_clean= cache_clean_bytes;
536 }
537 };
538
539 Ndb_index_stat_glob ndb_index_stat_glob;
540
541 /* Shared index entries */
542
Ndb_index_stat()543 Ndb_index_stat::Ndb_index_stat()
544 {
545 is= 0;
546 index_id= 0;
547 index_version= 0;
548 #ifndef DBUG_OFF
549 memset(id, 0, sizeof(id));
550 #endif
551 access_time= 0;
552 load_time= 0;
553 read_time= 0;
554 sample_version= 0;
555 check_time= 0;
556 cache_clean= false;
557 force_update= 0;
558 no_stats= false;
559 error_time= 0;
560 error_count= 0;
561 share_next= 0;
562 lt= 0;
563 lt_old= 0;
564 list_next= 0;
565 list_prev= 0;
566 share= 0;
567 }
568
569 void
ndb_index_stat_error(Ndb_index_stat * st,const char * place,int line)570 ndb_index_stat_error(Ndb_index_stat *st, const char* place, int line)
571 {
572 time_t now= ndb_index_stat_time();
573 NdbIndexStat::Error error= st->is->getNdbError();
574 if (error.code == 0)
575 {
576 // XXX why this if
577 NdbIndexStat::Error error2;
578 error= error2;
579 error.code= NdbIndexStat::InternalError;
580 error.status= NdbError::TemporaryError;
581 }
582 st->error= error;
583 st->error_time= now;
584 st->error_count++;
585
586 DBUG_PRINT("index_stat", ("%s line %d: error %d line %d extra %d",
587 place, line, error.code, error.line, error.extra));
588 }
589
590 void
ndb_index_stat_clear_error(Ndb_index_stat * st)591 ndb_index_stat_clear_error(Ndb_index_stat *st)
592 {
593 st->error.code= 0;
594 st->error.status= NdbError::Success;
595 }
596
597 /* Lists across shares */
598
Ndb_index_stat_list(int the_lt,const char * the_name)599 Ndb_index_stat_list::Ndb_index_stat_list(int the_lt, const char* the_name)
600 {
601 lt= the_lt;
602 name= the_name;
603 head= 0;
604 tail= 0;
605 count= 0;
606 }
607
608 Ndb_index_stat_list ndb_index_stat_list[Ndb_index_stat::LT_Count] = {
609 Ndb_index_stat_list(0, 0),
610 Ndb_index_stat_list(Ndb_index_stat::LT_New, "New"),
611 Ndb_index_stat_list(Ndb_index_stat::LT_Update, "Update"),
612 Ndb_index_stat_list(Ndb_index_stat::LT_Read, "Read"),
613 Ndb_index_stat_list(Ndb_index_stat::LT_Idle, "Idle"),
614 Ndb_index_stat_list(Ndb_index_stat::LT_Check, "Check"),
615 Ndb_index_stat_list(Ndb_index_stat::LT_Delete, "Delete"),
616 Ndb_index_stat_list(Ndb_index_stat::LT_Error, "Error")
617 };
618
619 void
ndb_index_stat_list_add(Ndb_index_stat * st,int lt)620 ndb_index_stat_list_add(Ndb_index_stat* st, int lt)
621 {
622 assert(st != 0 && st->lt == 0);
623 assert(st->list_next == 0 && st->list_prev == 0);
624 assert(1 <= lt && lt < Ndb_index_stat::LT_Count);
625 Ndb_index_stat_list &list= ndb_index_stat_list[lt];
626
627 DBUG_PRINT("index_stat", ("st %s -> %s", st->id, list.name));
628
629 if (list.count == 0)
630 {
631 assert(list.head == 0 && list.tail == 0);
632 list.head= st;
633 list.tail= st;
634 }
635 else
636 {
637 assert(list.tail != 0 && list.tail->list_next == 0);
638 st->list_prev= list.tail;
639 list.tail->list_next= st;
640 list.tail= st;
641 }
642 list.count++;
643
644 st->lt= lt;
645 }
646
647 void
ndb_index_stat_list_remove(Ndb_index_stat * st)648 ndb_index_stat_list_remove(Ndb_index_stat* st)
649 {
650 assert(st != 0);
651 int lt= st->lt;
652 assert(1 <= lt && lt < Ndb_index_stat::LT_Count);
653 Ndb_index_stat_list &list= ndb_index_stat_list[lt];
654
655 DBUG_PRINT("index_stat", ("st %s <- %s", st->id, list.name));
656
657 Ndb_index_stat* next= st->list_next;
658 Ndb_index_stat* prev= st->list_prev;
659
660 if (list.head == st)
661 list.head= next;
662 if (list.tail == st)
663 list.tail= prev;
664 assert(list.count != 0);
665 list.count--;
666
667 if (next != 0)
668 next->list_prev= prev;
669 if (prev != 0)
670 prev->list_next= next;
671
672 st->lt= 0;
673 st->lt_old= 0;
674 st->list_next= 0;
675 st->list_prev= 0;
676 }
677
678 void
ndb_index_stat_list_move(Ndb_index_stat * st,int lt)679 ndb_index_stat_list_move(Ndb_index_stat *st, int lt)
680 {
681 assert(st != 0);
682 ndb_index_stat_list_remove(st);
683 ndb_index_stat_list_add(st, lt);
684 }
685
686 /* Stats entry changes (must hold stat_mutex) */
687
688 void
ndb_index_stat_force_update(Ndb_index_stat * st,bool onoff)689 ndb_index_stat_force_update(Ndb_index_stat *st, bool onoff)
690 {
691 Ndb_index_stat_glob &glob= ndb_index_stat_glob;
692 if (onoff)
693 {
694 /* One more request */
695 glob.force_update++;
696 st->force_update++;
697 }
698 else
699 {
700 /* All done */
701 assert(glob.force_update >= st->force_update);
702 glob.force_update-= st->force_update;
703 st->force_update= 0;
704 }
705 }
706
707 void
ndb_index_stat_no_stats(Ndb_index_stat * st,bool flag)708 ndb_index_stat_no_stats(Ndb_index_stat *st, bool flag)
709 {
710 Ndb_index_stat_glob &glob= ndb_index_stat_glob;
711 if (st->no_stats != flag)
712 {
713 if (flag)
714 {
715 glob.no_stats++;
716 st->no_stats= true;
717 }
718 else
719 {
720 assert(glob.no_stats >= 1);
721 glob.no_stats-= 1;
722 st->no_stats= false;
723 }
724 }
725 }
726
727 /* Find or add entry under the share */
728
729 Ndb_index_stat*
ndb_index_stat_alloc(const NDBINDEX * index,const NDBTAB * table,int & err_out)730 ndb_index_stat_alloc(const NDBINDEX *index,
731 const NDBTAB *table,
732 int &err_out)
733 {
734 err_out= 0;
735 Ndb_index_stat *st= new Ndb_index_stat;
736 NdbIndexStat *is= new NdbIndexStat;
737 if (st != 0 && is != 0)
738 {
739 st->is= is;
740 st->index_id= index->getObjectId();
741 st->index_version= index->getObjectVersion();
742 #ifndef DBUG_OFF
743 my_snprintf(st->id, sizeof(st->id), "%d.%d", st->index_id, st->index_version);
744 #endif
745 if (is->set_index(*index, *table) == 0)
746 return st;
747 ndb_index_stat_error(st, "set_index", __LINE__);
748 err_out= st->error.code;
749 }
750 else
751 {
752 err_out= NdbIndexStat::NoMemError;
753 }
754 if (is != 0)
755 delete is;
756 if (st != 0)
757 delete st;
758 return 0;
759 }
760
761 /* Subroutine, have lock */
762 Ndb_index_stat*
ndb_index_stat_find_share(NDB_SHARE * share,const NDBINDEX * index,Ndb_index_stat * & st_last)763 ndb_index_stat_find_share(NDB_SHARE *share,
764 const NDBINDEX *index,
765 Ndb_index_stat *&st_last)
766 {
767 struct Ndb_index_stat *st= share->index_stat_list;
768 st_last= 0;
769 while (st != 0)
770 {
771 assert(st->share == share);
772 assert(st->is != 0);
773 NdbIndexStat::Head head;
774 st->is->get_head(head);
775 if (head.m_indexId == (uint)index->getObjectId() &&
776 head.m_indexVersion == (uint)index->getObjectVersion())
777 break;
778 st_last= st;
779 st= st->share_next;
780 }
781 return st;
782 }
783
784 /* Subroutine, have lock */
785 void
ndb_index_stat_add_share(NDB_SHARE * share,Ndb_index_stat * st,Ndb_index_stat * st_last)786 ndb_index_stat_add_share(NDB_SHARE *share,
787 Ndb_index_stat *st,
788 Ndb_index_stat *st_last)
789 {
790 st->share= share;
791 if (st_last == 0)
792 share->index_stat_list= st;
793 else
794 st_last->share_next= st;
795 }
796
797 Ndb_index_stat*
ndb_index_stat_get_share(NDB_SHARE * share,const NDBINDEX * index,const NDBTAB * table,int & err_out,bool allow_add,bool force_update)798 ndb_index_stat_get_share(NDB_SHARE *share,
799 const NDBINDEX *index,
800 const NDBTAB *table,
801 int &err_out,
802 bool allow_add,
803 bool force_update)
804 {
805 pthread_mutex_lock(&share->mutex);
806 pthread_mutex_lock(&ndb_index_stat_list_mutex);
807 pthread_mutex_lock(&ndb_index_stat_stat_mutex);
808 time_t now= ndb_index_stat_time();
809 err_out= 0;
810
811 struct Ndb_index_stat *st= 0;
812 struct Ndb_index_stat *st_last= 0;
813 do
814 {
815 if (unlikely(!ndb_index_stat_allow()))
816 {
817 err_out= Ndb_index_stat_error_NOT_ALLOW;
818 break;
819 }
820 st= ndb_index_stat_find_share(share, index, st_last);
821 if (st == 0)
822 {
823 if (!allow_add)
824 {
825 err_out= Ndb_index_stat_error_NOT_FOUND;
826 break;
827 }
828 st= ndb_index_stat_alloc(index, table, err_out);
829 if (st == 0)
830 {
831 assert(err_out != 0);
832 break;
833 }
834 ndb_index_stat_add_share(share, st, st_last);
835 ndb_index_stat_list_add(st, Ndb_index_stat::LT_New);
836 }
837 if (force_update)
838 ndb_index_stat_force_update(st, true);
839 st->access_time= now;
840 }
841 while (0);
842
843 pthread_mutex_unlock(&ndb_index_stat_stat_mutex);
844 pthread_mutex_unlock(&ndb_index_stat_list_mutex);
845 pthread_mutex_unlock(&share->mutex);
846 return st;
847 }
848
849 void
ndb_index_stat_free(Ndb_index_stat * st)850 ndb_index_stat_free(Ndb_index_stat *st)
851 {
852 pthread_mutex_lock(&ndb_index_stat_list_mutex);
853 NDB_SHARE *share= st->share;
854 assert(share != 0);
855
856 Ndb_index_stat *st_head= 0;
857 Ndb_index_stat *st_tail= 0;
858 Ndb_index_stat *st_loop= share->index_stat_list;
859 bool found= false;
860 while (st_loop != 0) {
861 if (st == st_loop) {
862 st->share= 0;
863 assert(st->lt != 0);
864 assert(st->lt != Ndb_index_stat::LT_Delete);
865 ndb_index_stat_list_move(st, Ndb_index_stat::LT_Delete);
866 st_loop= st_loop->share_next;
867 assert(!found);
868 found++;
869 } else {
870 if (st_head == 0)
871 st_head= st_loop;
872 else
873 st_tail->share_next= st_loop;
874 st_tail= st_loop;
875 st_loop= st_loop->share_next;
876 st_tail->share_next= 0;
877 }
878 }
879 assert(found);
880 share->index_stat_list= st_head;
881 pthread_mutex_unlock(&ndb_index_stat_list_mutex);
882 }
883
884 void
ndb_index_stat_free(NDB_SHARE * share)885 ndb_index_stat_free(NDB_SHARE *share)
886 {
887 pthread_mutex_lock(&ndb_index_stat_list_mutex);
888 Ndb_index_stat *st;
889 while ((st= share->index_stat_list) != 0)
890 {
891 share->index_stat_list= st->share_next;
892 st->share= 0;
893 assert(st->lt != 0);
894 assert(st->lt != Ndb_index_stat::LT_Delete);
895 ndb_index_stat_list_move(st, Ndb_index_stat::LT_Delete);
896 }
897 pthread_mutex_unlock(&ndb_index_stat_list_mutex);
898 }
899
900 /* Find entry across shares */
901 /* wl4124_todo mutex overkill, hash table, can we find table share */
902 Ndb_index_stat*
ndb_index_stat_find_entry(int index_id,int index_version,int table_id)903 ndb_index_stat_find_entry(int index_id, int index_version, int table_id)
904 {
905 DBUG_ENTER("ndb_index_stat_find_entry");
906 pthread_mutex_lock(&ndbcluster_mutex);
907 pthread_mutex_lock(&ndb_index_stat_list_mutex);
908 DBUG_PRINT("index_stat", ("find index:%d version:%d table:%d",
909 index_id, index_version, table_id));
910
911 int lt;
912 for (lt=1; lt < Ndb_index_stat::LT_Count; lt++)
913 {
914 Ndb_index_stat *st=ndb_index_stat_list[lt].head;
915 while (st != 0)
916 {
917 if (st->index_id == index_id &&
918 st->index_version == index_version)
919 {
920 pthread_mutex_unlock(&ndb_index_stat_list_mutex);
921 pthread_mutex_unlock(&ndbcluster_mutex);
922 DBUG_RETURN(st);
923 }
924 st= st->list_next;
925 }
926 }
927
928 pthread_mutex_unlock(&ndb_index_stat_list_mutex);
929 pthread_mutex_unlock(&ndbcluster_mutex);
930 DBUG_RETURN(0);
931 }
932
933 /* Statistics thread sub-routines */
934
935 void
ndb_index_stat_cache_move(Ndb_index_stat * st)936 ndb_index_stat_cache_move(Ndb_index_stat *st)
937 {
938 Ndb_index_stat_glob &glob= ndb_index_stat_glob;
939 NdbIndexStat::CacheInfo infoBuild;
940 NdbIndexStat::CacheInfo infoQuery;
941
942 st->is->get_cache_info(infoBuild, NdbIndexStat::CacheBuild);
943 st->is->get_cache_info(infoQuery, NdbIndexStat::CacheQuery);
944 const uint new_query_bytes= infoBuild.m_totalBytes;
945 const uint old_query_bytes= infoQuery.m_totalBytes;
946 DBUG_PRINT("index_stat", ("st %s cache move: query:%u clean:%u",
947 st->id, new_query_bytes, old_query_bytes));
948 st->is->move_cache();
949 assert(glob.cache_query_bytes >= old_query_bytes);
950 glob.cache_query_bytes-= old_query_bytes;
951 glob.cache_query_bytes+= new_query_bytes;
952 glob.cache_clean_bytes+= old_query_bytes;
953 glob.set_status_variables();
954 }
955
956 void
ndb_index_stat_cache_clean(Ndb_index_stat * st)957 ndb_index_stat_cache_clean(Ndb_index_stat *st)
958 {
959 Ndb_index_stat_glob &glob= ndb_index_stat_glob;
960 NdbIndexStat::CacheInfo infoClean;
961
962 st->is->get_cache_info(infoClean, NdbIndexStat::CacheClean);
963 const uint old_clean_bytes= infoClean.m_totalBytes;
964 DBUG_PRINT("index_stat", ("st %s cache clean: clean:%u",
965 st->id, old_clean_bytes));
966 st->is->clean_cache();
967 assert(glob.cache_clean_bytes >= old_clean_bytes);
968 glob.cache_clean_bytes-= old_clean_bytes;
969 glob.set_status_variables();
970 }
971
972 /* Misc in/out parameters for process steps */
973 struct Ndb_index_stat_proc {
974 NdbIndexStat* is_util; // For metadata and polling
975 Ndb *ndb;
976 time_t now;
977 int lt;
978 bool busy;
979 bool end;
Ndb_index_stat_procNdb_index_stat_proc980 Ndb_index_stat_proc() :
981 is_util(0),
982 ndb(0),
983 now(0),
984 lt(0),
985 busy(false),
986 end(false)
987 {}
988 };
989
990 void
ndb_index_stat_proc_new(Ndb_index_stat_proc & pr,Ndb_index_stat * st)991 ndb_index_stat_proc_new(Ndb_index_stat_proc &pr, Ndb_index_stat *st)
992 {
993 if (st->error.code != 0)
994 pr.lt= Ndb_index_stat::LT_Error;
995 else if (st->force_update)
996 pr.lt= Ndb_index_stat::LT_Update;
997 else
998 pr.lt= Ndb_index_stat::LT_Read;
999 }
1000
1001 void
ndb_index_stat_proc_new(Ndb_index_stat_proc & pr)1002 ndb_index_stat_proc_new(Ndb_index_stat_proc &pr)
1003 {
1004 pthread_mutex_lock(&ndb_index_stat_list_mutex);
1005 const int lt= Ndb_index_stat::LT_New;
1006 Ndb_index_stat_list &list= ndb_index_stat_list[lt];
1007
1008 Ndb_index_stat *st_loop= list.head;
1009 while (st_loop != 0)
1010 {
1011 Ndb_index_stat *st= st_loop;
1012 st_loop= st_loop->list_next;
1013 DBUG_PRINT("index_stat", ("st %s proc %s", st->id, list.name));
1014 ndb_index_stat_proc_new(pr, st);
1015 ndb_index_stat_list_move(st, pr.lt);
1016 }
1017 pthread_mutex_unlock(&ndb_index_stat_list_mutex);
1018 }
1019
1020 void
ndb_index_stat_proc_update(Ndb_index_stat_proc & pr,Ndb_index_stat * st)1021 ndb_index_stat_proc_update(Ndb_index_stat_proc &pr, Ndb_index_stat *st)
1022 {
1023 if (st->is->update_stat(pr.ndb) == -1)
1024 {
1025 ndb_index_stat_error(st, "update_stat", __LINE__);
1026 pr.lt= Ndb_index_stat::LT_Error;
1027 return;
1028 }
1029 pr.lt= Ndb_index_stat::LT_Read;
1030 }
1031
1032 void
ndb_index_stat_proc_update(Ndb_index_stat_proc & pr)1033 ndb_index_stat_proc_update(Ndb_index_stat_proc &pr)
1034 {
1035 const int lt= Ndb_index_stat::LT_Update;
1036 Ndb_index_stat_list &list= ndb_index_stat_list[lt];
1037 const Ndb_index_stat_opt &opt= ndb_index_stat_opt;
1038 const uint batch= opt.get(Ndb_index_stat_opt::Iupdate_batch);
1039
1040 Ndb_index_stat *st_loop= list.head;
1041 uint cnt= 0;
1042 while (st_loop != 0 && cnt < batch)
1043 {
1044 Ndb_index_stat *st= st_loop;
1045 st_loop= st_loop->list_next;
1046 DBUG_PRINT("index_stat", ("st %s proc %s", st->id, list.name));
1047 ndb_index_stat_proc_update(pr, st);
1048 ndb_index_stat_list_move(st, pr.lt);
1049 cnt++;
1050 }
1051 if (cnt == batch)
1052 pr.busy= true;
1053 }
1054
1055 void
ndb_index_stat_proc_read(Ndb_index_stat_proc & pr,Ndb_index_stat * st)1056 ndb_index_stat_proc_read(Ndb_index_stat_proc &pr, Ndb_index_stat *st)
1057 {
1058 NdbIndexStat::Head head;
1059 if (st->is->read_stat(pr.ndb) == -1)
1060 {
1061 pthread_mutex_lock(&ndb_index_stat_stat_mutex);
1062 ndb_index_stat_error(st, "read_stat", __LINE__);
1063 const uint force_update= st->force_update;
1064 ndb_index_stat_force_update(st, false);
1065
1066 /* no stats is not unexpected error, unless analyze was done */
1067 if (st->is->getNdbError().code == NdbIndexStat::NoIndexStats &&
1068 force_update == 0)
1069 {
1070 ndb_index_stat_no_stats(st, true);
1071 pr.lt= Ndb_index_stat::LT_Idle;
1072 }
1073 else
1074 {
1075 pr.lt= Ndb_index_stat::LT_Error;
1076 }
1077
1078 pthread_cond_broadcast(&ndb_index_stat_stat_cond);
1079 pthread_mutex_unlock(&ndb_index_stat_stat_mutex);
1080 return;
1081 }
1082
1083 pthread_mutex_lock(&ndb_index_stat_stat_mutex);
1084 pr.now= ndb_index_stat_time();
1085 st->is->get_head(head);
1086 st->load_time= head.m_loadTime;
1087 st->read_time= pr.now;
1088 st->sample_version= head.m_sampleVersion;
1089
1090 ndb_index_stat_force_update(st, false);
1091 ndb_index_stat_no_stats(st, false);
1092
1093 ndb_index_stat_cache_move(st);
1094 st->cache_clean= false;
1095 pr.lt= Ndb_index_stat::LT_Idle;
1096 pthread_cond_broadcast(&ndb_index_stat_stat_cond);
1097 pthread_mutex_unlock(&ndb_index_stat_stat_mutex);
1098 }
1099
1100 void
ndb_index_stat_proc_read(Ndb_index_stat_proc & pr)1101 ndb_index_stat_proc_read(Ndb_index_stat_proc &pr)
1102 {
1103 const int lt= Ndb_index_stat::LT_Read;
1104 Ndb_index_stat_list &list= ndb_index_stat_list[lt];
1105 const Ndb_index_stat_opt &opt= ndb_index_stat_opt;
1106 const uint batch= opt.get(Ndb_index_stat_opt::Iread_batch);
1107
1108 Ndb_index_stat *st_loop= list.head;
1109 uint cnt= 0;
1110 while (st_loop != 0 && cnt < batch)
1111 {
1112 Ndb_index_stat *st= st_loop;
1113 st_loop= st_loop->list_next;
1114 DBUG_PRINT("index_stat", ("st %s proc %s", st->id, list.name));
1115 ndb_index_stat_proc_read(pr, st);
1116 ndb_index_stat_list_move(st, pr.lt);
1117 cnt++;
1118 }
1119 if (cnt == batch)
1120 pr.busy= true;
1121 }
1122
1123 // wl4124_todo detect force_update faster
1124 void
ndb_index_stat_proc_idle(Ndb_index_stat_proc & pr,Ndb_index_stat * st)1125 ndb_index_stat_proc_idle(Ndb_index_stat_proc &pr, Ndb_index_stat *st)
1126 {
1127 const Ndb_index_stat_opt &opt= ndb_index_stat_opt;
1128 const int clean_delay= opt.get(Ndb_index_stat_opt::Iclean_delay);
1129 const int check_delay= opt.get(Ndb_index_stat_opt::Icheck_delay);
1130 const time_t clean_wait=
1131 st->cache_clean ? 0 : st->read_time + clean_delay - pr.now;
1132 const time_t check_wait=
1133 st->check_time == 0 ? 0 : st->check_time + check_delay - pr.now;
1134
1135 DBUG_PRINT("index_stat", ("st %s check wait:%lds force update:%u"
1136 " clean wait:%lds cache clean:%d",
1137 st->id, (long)check_wait, st->force_update,
1138 (long)clean_wait, st->cache_clean));
1139
1140 if (!st->cache_clean && clean_wait <= 0)
1141 {
1142 ndb_index_stat_cache_clean(st);
1143 st->cache_clean= true;
1144 }
1145 if (st->force_update)
1146 {
1147 pr.lt= Ndb_index_stat::LT_Update;
1148 return;
1149 }
1150 if (check_wait <= 0)
1151 {
1152 pr.lt= Ndb_index_stat::LT_Check;
1153 return;
1154 }
1155 pr.lt= Ndb_index_stat::LT_Idle;
1156 }
1157
1158 void
ndb_index_stat_proc_idle(Ndb_index_stat_proc & pr)1159 ndb_index_stat_proc_idle(Ndb_index_stat_proc &pr)
1160 {
1161 const int lt= Ndb_index_stat::LT_Idle;
1162 Ndb_index_stat_list &list= ndb_index_stat_list[lt];
1163 const Ndb_index_stat_opt &opt= ndb_index_stat_opt;
1164 const uint batch= opt.get(Ndb_index_stat_opt::Iidle_batch);
1165 pr.now= ndb_index_stat_time();
1166
1167 Ndb_index_stat *st_loop= list.head;
1168 uint cnt= 0;
1169 while (st_loop != 0 && cnt < batch)
1170 {
1171 Ndb_index_stat *st= st_loop;
1172 st_loop= st_loop->list_next;
1173 DBUG_PRINT("index_stat", ("st %s proc %s", st->id, list.name));
1174 ndb_index_stat_proc_idle(pr, st);
1175 // rotates list if entry remains LT_Idle
1176 ndb_index_stat_list_move(st, pr.lt);
1177 cnt++;
1178 }
1179 if (cnt == batch)
1180 pr.busy= true;
1181 }
1182
1183 void
ndb_index_stat_proc_check(Ndb_index_stat_proc & pr,Ndb_index_stat * st)1184 ndb_index_stat_proc_check(Ndb_index_stat_proc &pr, Ndb_index_stat *st)
1185 {
1186 pr.now= ndb_index_stat_time();
1187 st->check_time= pr.now;
1188 NdbIndexStat::Head head;
1189 if (st->is->read_head(pr.ndb) == -1)
1190 {
1191 ndb_index_stat_error(st, "read_head", __LINE__);
1192 /* no stats is not unexpected error */
1193 if (st->is->getNdbError().code == NdbIndexStat::NoIndexStats)
1194 {
1195 ndb_index_stat_no_stats(st, true);
1196 pr.lt= Ndb_index_stat::LT_Idle;
1197 }
1198 else
1199 {
1200 pr.lt= Ndb_index_stat::LT_Error;
1201 }
1202 return;
1203 }
1204 st->is->get_head(head);
1205 const uint version_old= st->sample_version;
1206 const uint version_new= head.m_sampleVersion;
1207 if (version_old != version_new)
1208 {
1209 DBUG_PRINT("index_stat", ("st %s sample version old:%u new:%u",
1210 st->id, version_old, version_new));
1211 pr.lt= Ndb_index_stat::LT_Read;
1212 return;
1213 }
1214 pr.lt= Ndb_index_stat::LT_Idle;
1215 }
1216
1217 void
ndb_index_stat_proc_check(Ndb_index_stat_proc & pr)1218 ndb_index_stat_proc_check(Ndb_index_stat_proc &pr)
1219 {
1220 const int lt= Ndb_index_stat::LT_Check;
1221 Ndb_index_stat_list &list= ndb_index_stat_list[lt];
1222 const Ndb_index_stat_opt &opt= ndb_index_stat_opt;
1223 const uint batch= opt.get(Ndb_index_stat_opt::Icheck_batch);
1224
1225 Ndb_index_stat *st_loop= list.head;
1226 uint cnt= 0;
1227 while (st_loop != 0 && cnt < batch)
1228 {
1229 Ndb_index_stat *st= st_loop;
1230 st_loop= st_loop->list_next;
1231 DBUG_PRINT("index_stat", ("st %s proc %s", st->id, list.name));
1232 ndb_index_stat_proc_check(pr, st);
1233 ndb_index_stat_list_move(st, pr.lt);
1234 cnt++;
1235 }
1236 if (cnt == batch)
1237 pr.busy= true;
1238 }
1239
1240 void
ndb_index_stat_proc_evict(Ndb_index_stat_proc & pr,Ndb_index_stat * st)1241 ndb_index_stat_proc_evict(Ndb_index_stat_proc &pr, Ndb_index_stat *st)
1242 {
1243 NdbIndexStat::Head head;
1244 NdbIndexStat::CacheInfo infoBuild;
1245 NdbIndexStat::CacheInfo infoQuery;
1246 NdbIndexStat::CacheInfo infoClean;
1247 st->is->get_head(head);
1248 st->is->get_cache_info(infoBuild, NdbIndexStat::CacheBuild);
1249 st->is->get_cache_info(infoQuery, NdbIndexStat::CacheQuery);
1250 st->is->get_cache_info(infoClean, NdbIndexStat::CacheClean);
1251
1252 DBUG_PRINT("index_stat",
1253 ("evict table: %u index: %u version: %u"
1254 " sample version: %u"
1255 " cache bytes build:%u query:%u clean:%u",
1256 head.m_tableId, head.m_indexId, head.m_indexVersion,
1257 head.m_sampleVersion,
1258 infoBuild.m_totalBytes, infoQuery.m_totalBytes, infoClean.m_totalBytes));
1259
1260 /* Twice to move all caches to clean */
1261 ndb_index_stat_cache_move(st);
1262 ndb_index_stat_cache_move(st);
1263 ndb_index_stat_cache_clean(st);
1264 }
1265
1266 bool
ndb_index_stat_proc_evict()1267 ndb_index_stat_proc_evict()
1268 {
1269 const Ndb_index_stat_opt &opt= ndb_index_stat_opt;
1270 Ndb_index_stat_glob &glob= ndb_index_stat_glob;
1271 uint curr_size= glob.cache_query_bytes + glob.cache_clean_bytes;
1272 const uint cache_lowpct= opt.get(Ndb_index_stat_opt::Icache_lowpct);
1273 const uint cache_limit= opt.get(Ndb_index_stat_opt::Icache_limit);
1274 if (100 * curr_size <= cache_lowpct * cache_limit)
1275 return false;
1276 return true;
1277 }
1278
1279 void
ndb_index_stat_proc_evict(Ndb_index_stat_proc & pr,int lt)1280 ndb_index_stat_proc_evict(Ndb_index_stat_proc &pr, int lt)
1281 {
1282 Ndb_index_stat_list &list= ndb_index_stat_list[lt];
1283 const Ndb_index_stat_opt &opt= ndb_index_stat_opt;
1284 const uint batch= opt.get(Ndb_index_stat_opt::Ievict_batch);
1285 const int evict_delay= opt.get(Ndb_index_stat_opt::Ievict_delay);
1286 pr.now= ndb_index_stat_time();
1287
1288 if (!ndb_index_stat_proc_evict())
1289 return;
1290
1291 /* Create a LRU batch */
1292 Ndb_index_stat* st_lru_arr[ndb_index_stat_max_evict_batch + 1];
1293 uint st_lru_cnt= 0;
1294 Ndb_index_stat *st_loop= list.head;
1295 while (st_loop != 0 && st_lru_cnt < batch)
1296 {
1297 Ndb_index_stat *st= st_loop;
1298 st_loop= st_loop->list_next;
1299 if (st->read_time + evict_delay <= pr.now)
1300 {
1301 /* Insertion sort into the batch from the end */
1302 if (st_lru_cnt == 0)
1303 st_lru_arr[st_lru_cnt++]= st;
1304 else
1305 {
1306 uint i= st_lru_cnt;
1307 while (i != 0)
1308 {
1309 if (st_lru_arr[i-1]->access_time < st->access_time)
1310 break;
1311 i--;
1312 }
1313 if (i < st_lru_cnt)
1314 {
1315 uint j= st_lru_cnt; /* There is place for one more at end */
1316 while (j > i)
1317 {
1318 st_lru_arr[j]= st_lru_arr[j-1];
1319 j--;
1320 }
1321 st_lru_arr[i]= st;
1322 if (st_lru_cnt < batch)
1323 st_lru_cnt++;
1324 }
1325 }
1326 }
1327 }
1328
1329 /* Process the LRU batch */
1330 uint cnt= 0;
1331 while (cnt < st_lru_cnt)
1332 {
1333 if (!ndb_index_stat_proc_evict())
1334 break;
1335
1336 Ndb_index_stat *st= st_lru_arr[cnt];
1337 DBUG_PRINT("index_stat", ("st %s proc evict %s", st->id, list.name));
1338 ndb_index_stat_proc_evict(pr, st);
1339 ndb_index_stat_free(st);
1340 cnt++;
1341 }
1342 if (cnt == batch)
1343 pr.busy= true;
1344 }
1345
1346 void
ndb_index_stat_proc_evict(Ndb_index_stat_proc & pr)1347 ndb_index_stat_proc_evict(Ndb_index_stat_proc &pr)
1348 {
1349 ndb_index_stat_proc_evict(pr, Ndb_index_stat::LT_Error);
1350 ndb_index_stat_proc_evict(pr, Ndb_index_stat::LT_Idle);
1351 }
1352
1353 void
ndb_index_stat_proc_delete(Ndb_index_stat_proc & pr)1354 ndb_index_stat_proc_delete(Ndb_index_stat_proc &pr)
1355 {
1356 const int lt= Ndb_index_stat::LT_Delete;
1357 Ndb_index_stat_list &list= ndb_index_stat_list[lt];
1358 const Ndb_index_stat_opt &opt= ndb_index_stat_opt;
1359 const uint delete_batch= opt.get(Ndb_index_stat_opt::Idelete_batch);
1360 const uint batch= !pr.end ? delete_batch : 0xFFFFFFFF;
1361
1362 Ndb_index_stat *st_loop= list.head;
1363 uint cnt= 0;
1364 while (st_loop != 0 && cnt < batch)
1365 {
1366 Ndb_index_stat *st= st_loop;
1367 st_loop= st_loop->list_next;
1368 DBUG_PRINT("index_stat", ("st %s proc %s", st->id, list.name));
1369 ndb_index_stat_proc_evict(pr, st);
1370 ndb_index_stat_list_remove(st);
1371 delete st->is;
1372 delete st;
1373 cnt++;
1374 }
1375 if (cnt == batch)
1376 pr.busy= true;
1377 }
1378
1379 void
ndb_index_stat_proc_error(Ndb_index_stat_proc & pr,Ndb_index_stat * st)1380 ndb_index_stat_proc_error(Ndb_index_stat_proc &pr, Ndb_index_stat *st)
1381 {
1382 const Ndb_index_stat_opt &opt= ndb_index_stat_opt;
1383 const int error_delay= opt.get(Ndb_index_stat_opt::Ierror_delay);
1384 const time_t error_wait= st->error_time + error_delay - pr.now;
1385
1386 if (error_wait <= 0 ||
1387 /* Analyze issued after previous error */
1388 st->force_update)
1389 {
1390 DBUG_PRINT("index_stat", ("st %s error wait:%ds error count:%u"
1391 " force update:%u",
1392 st->id, (int)error_wait, st->error_count,
1393 st->force_update));
1394 ndb_index_stat_clear_error(st);
1395 if (st->force_update)
1396 pr.lt= Ndb_index_stat::LT_Update;
1397 else
1398 pr.lt= Ndb_index_stat::LT_Read;
1399 return;
1400 }
1401 pr.lt= Ndb_index_stat::LT_Error;
1402 }
1403
1404 void
ndb_index_stat_proc_error(Ndb_index_stat_proc & pr)1405 ndb_index_stat_proc_error(Ndb_index_stat_proc &pr)
1406 {
1407 const int lt= Ndb_index_stat::LT_Error;
1408 Ndb_index_stat_list &list= ndb_index_stat_list[lt];
1409 const Ndb_index_stat_opt &opt= ndb_index_stat_opt;
1410 const uint batch= opt.get(Ndb_index_stat_opt::Ierror_batch);
1411 pr.now= ndb_index_stat_time();
1412
1413 Ndb_index_stat *st_loop= list.head;
1414 uint cnt= 0;
1415 while (st_loop != 0 && cnt < batch)
1416 {
1417 Ndb_index_stat *st= st_loop;
1418 st_loop= st_loop->list_next;
1419 DBUG_PRINT("index_stat", ("st %s proc %s", st->id, list.name));
1420 ndb_index_stat_proc_error(pr, st);
1421 ndb_index_stat_list_move(st, pr.lt);
1422 cnt++;
1423 }
1424 if (cnt == batch)
1425 pr.busy= true;
1426 }
1427
1428 void
ndb_index_stat_proc_event(Ndb_index_stat_proc & pr,Ndb_index_stat * st)1429 ndb_index_stat_proc_event(Ndb_index_stat_proc &pr, Ndb_index_stat *st)
1430 {
1431 /*
1432 Put on Check list if idle.
1433 We get event also for our own analyze but this should not matter.
1434 */
1435 pr.lt= st->lt;
1436 if (st->lt == Ndb_index_stat::LT_Idle ||
1437 st->lt == Ndb_index_stat::LT_Error)
1438 pr.lt= Ndb_index_stat::LT_Check;
1439 }
1440
1441 void
ndb_index_stat_proc_event(Ndb_index_stat_proc & pr)1442 ndb_index_stat_proc_event(Ndb_index_stat_proc &pr)
1443 {
1444 NdbIndexStat *is= pr.is_util;
1445 Ndb *ndb= pr.ndb;
1446 int ret;
1447 ret= is->poll_listener(ndb, 0);
1448 DBUG_PRINT("index_stat", ("poll_listener ret: %d", ret));
1449 if (ret == -1)
1450 {
1451 // wl4124_todo report error
1452 DBUG_ASSERT(false);
1453 return;
1454 }
1455 if (ret == 0)
1456 return;
1457
1458 while (1)
1459 {
1460 ret= is->next_listener(ndb);
1461 DBUG_PRINT("index_stat", ("next_listener ret: %d", ret));
1462 if (ret == -1)
1463 {
1464 // wl4124_todo report error
1465 DBUG_ASSERT(false);
1466 return;
1467 }
1468 if (ret == 0)
1469 break;
1470
1471 NdbIndexStat::Head head;
1472 is->get_head(head);
1473 DBUG_PRINT("index_stat", ("next_listener eventType: %d indexId: %u",
1474 head.m_eventType, head.m_indexId));
1475
1476 Ndb_index_stat *st= ndb_index_stat_find_entry(head.m_indexId,
1477 head.m_indexVersion,
1478 head.m_tableId);
1479 /*
1480 Another process can update stats for an index which is not found
1481 in this mysqld. Ignore it.
1482 */
1483 if (st != 0)
1484 {
1485 DBUG_PRINT("index_stat", ("st %s proc %s", st->id, "Event"));
1486 ndb_index_stat_proc_event(pr, st);
1487 if (pr.lt != st->lt)
1488 ndb_index_stat_list_move(st, pr.lt);
1489 }
1490 else
1491 {
1492 DBUG_PRINT("index_stat", ("entry not found in this mysqld"));
1493 }
1494 }
1495 }
1496
1497 #ifndef DBUG_OFF
1498 void
ndb_index_stat_report(const Ndb_index_stat_glob & old_glob)1499 ndb_index_stat_report(const Ndb_index_stat_glob& old_glob)
1500 {
1501 Ndb_index_stat_glob new_glob= ndb_index_stat_glob;
1502 new_glob.set_list_count();
1503
1504 /* List counts */
1505 {
1506 const uint (&old_count)[Ndb_index_stat::LT_Count]= old_glob.list_count;
1507 const uint (&new_count)[Ndb_index_stat::LT_Count]= new_glob.list_count;
1508 bool any= false;
1509 int lt;
1510 for (lt=1; lt < Ndb_index_stat::LT_Count; lt++)
1511 {
1512 const Ndb_index_stat_list &list= ndb_index_stat_list[lt];
1513 const char* name= list.name;
1514 if (old_count[lt] != new_count[lt])
1515 {
1516 DBUG_PRINT("index_stat", ("%s: %u -> %u",
1517 name, old_count[lt], new_count[lt]));
1518 any= true;
1519 }
1520 }
1521 if (any)
1522 {
1523 const uint bufsz= 20 * Ndb_index_stat::LT_Count;
1524 char buf[bufsz];
1525 char *ptr= buf;
1526 for (lt= 1; lt < Ndb_index_stat::LT_Count; lt++)
1527 {
1528 const Ndb_index_stat_list &list= ndb_index_stat_list[lt];
1529 const char* name= list.name;
1530 sprintf(ptr, " %s:%u", name, new_count[lt]);
1531 ptr+= strlen(ptr);
1532 }
1533 DBUG_PRINT("index_stat", ("list:%s", buf));
1534 }
1535 }
1536
1537 /* Cache summary */
1538 {
1539 const Ndb_index_stat_opt &opt= ndb_index_stat_opt;
1540 uint query_size= new_glob.cache_query_bytes;
1541 uint clean_size= new_glob.cache_clean_bytes;
1542 uint total_size= query_size + clean_size;
1543 const uint limit= opt.get(Ndb_index_stat_opt::Icache_limit);
1544 double pct= 100.0;
1545 if (limit != 0)
1546 pct= 100.0 * (double)total_size / (double)limit;
1547 DBUG_PRINT("index_stat", ("cache query:%u clean:%u (%.2f pct)",
1548 query_size, clean_size, pct));
1549 }
1550
1551 /* Updates waited for and forced updates */
1552 {
1553 uint wait_update= new_glob.wait_update;
1554 uint force_update= new_glob.force_update;
1555 uint no_stats= new_glob.no_stats;
1556 DBUG_PRINT("index_stat", ("wait update:%u force update:%u no stats:%u",
1557 wait_update, force_update, no_stats));
1558 }
1559 }
1560 #endif
1561
1562 void
ndb_index_stat_proc(Ndb_index_stat_proc & pr)1563 ndb_index_stat_proc(Ndb_index_stat_proc &pr)
1564 {
1565 #ifndef DBUG_OFF
1566 Ndb_index_stat_glob old_glob= ndb_index_stat_glob;
1567 old_glob.set_list_count();
1568 #endif
1569
1570 DBUG_ENTER("ndb_index_stat_proc");
1571
1572 ndb_index_stat_proc_new(pr);
1573 ndb_index_stat_proc_update(pr);
1574 ndb_index_stat_proc_read(pr);
1575 ndb_index_stat_proc_idle(pr);
1576 ndb_index_stat_proc_check(pr);
1577 ndb_index_stat_proc_evict(pr);
1578 ndb_index_stat_proc_delete(pr);
1579 ndb_index_stat_proc_error(pr);
1580 ndb_index_stat_proc_event(pr);
1581
1582 #ifndef DBUG_OFF
1583 ndb_index_stat_report(old_glob);
1584 #endif
1585 DBUG_VOID_RETURN;
1586 }
1587
1588 void
ndb_index_stat_end()1589 ndb_index_stat_end()
1590 {
1591 DBUG_ENTER("ndb_index_stat_end");
1592 Ndb_index_stat_proc pr;
1593 pr.end= true;
1594
1595 /*
1596 * Shares have been freed so any index stat entries left should be
1597 * in LT_Delete. The first two steps here should be unnecessary.
1598 */
1599
1600 ndb_index_stat_allow(0);
1601
1602 int lt;
1603 for (lt= 1; lt < Ndb_index_stat::LT_Count; lt++)
1604 {
1605 if (lt == (int)Ndb_index_stat::LT_Delete)
1606 continue;
1607 Ndb_index_stat_list &list= ndb_index_stat_list[lt];
1608 Ndb_index_stat *st_loop= list.head;
1609 while (st_loop != 0)
1610 {
1611 Ndb_index_stat *st= st_loop;
1612 st_loop= st_loop->list_next;
1613 DBUG_PRINT("index_stat", ("st %s end %s", st->id, list.name));
1614 pr.lt= Ndb_index_stat::LT_Delete;
1615 ndb_index_stat_list_move(st, pr.lt);
1616 }
1617 }
1618
1619 /* Real free */
1620 ndb_index_stat_proc_delete(pr);
1621 DBUG_VOID_RETURN;
1622 }
1623
1624 /* Index stats thread */
1625
1626 int
ndb_index_stat_check_or_create_systables(Ndb_index_stat_proc & pr)1627 ndb_index_stat_check_or_create_systables(Ndb_index_stat_proc &pr)
1628 {
1629 DBUG_ENTER("ndb_index_stat_check_or_create_systables");
1630
1631 NdbIndexStat *is= pr.is_util;
1632 Ndb *ndb= pr.ndb;
1633
1634 if (is->check_systables(ndb) == 0)
1635 {
1636 DBUG_PRINT("index_stat", ("using existing index stats tables"));
1637 DBUG_RETURN(0);
1638 }
1639
1640 if (is->create_systables(ndb) == 0)
1641 {
1642 DBUG_PRINT("index_stat", ("created index stats tables"));
1643 DBUG_RETURN(0);
1644 }
1645
1646 if (is->getNdbError().code == 721 ||
1647 is->getNdbError().code == 4244)
1648 {
1649 // race between mysqlds, maybe
1650 DBUG_PRINT("index_stat", ("create index stats tables failed: error %d line %d",
1651 is->getNdbError().code, is->getNdbError().line));
1652 DBUG_RETURN(-1);
1653 }
1654
1655 sql_print_warning("create index stats tables failed: error %d line %d",
1656 is->getNdbError().code, is->getNdbError().line);
1657 DBUG_RETURN(-1);
1658 }
1659
1660 int
ndb_index_stat_check_or_create_sysevents(Ndb_index_stat_proc & pr)1661 ndb_index_stat_check_or_create_sysevents(Ndb_index_stat_proc &pr)
1662 {
1663 DBUG_ENTER("ndb_index_stat_check_or_create_sysevents");
1664
1665 NdbIndexStat *is= pr.is_util;
1666 Ndb *ndb= pr.ndb;
1667
1668 if (is->check_sysevents(ndb) == 0)
1669 {
1670 DBUG_PRINT("index_stat", ("using existing index stats events"));
1671 DBUG_RETURN(0);
1672 }
1673
1674 if (is->create_sysevents(ndb) == 0)
1675 {
1676 DBUG_PRINT("index_stat", ("created index stats events"));
1677 DBUG_RETURN(0);
1678 }
1679
1680 if (is->getNdbError().code == 746)
1681 {
1682 // race between mysqlds, maybe
1683 DBUG_PRINT("index_stat", ("create index stats events failed: error %d line %d",
1684 is->getNdbError().code, is->getNdbError().line));
1685 DBUG_RETURN(-1);
1686 }
1687
1688 sql_print_warning("create index stats events failed: error %d line %d",
1689 is->getNdbError().code, is->getNdbError().line);
1690 DBUG_RETURN(-1);
1691 }
1692
1693 int
ndb_index_stat_start_listener(Ndb_index_stat_proc & pr)1694 ndb_index_stat_start_listener(Ndb_index_stat_proc &pr)
1695 {
1696 DBUG_ENTER("ndb_index_stat_start_listener");
1697
1698 NdbIndexStat *is= pr.is_util;
1699 Ndb *ndb= pr.ndb;
1700
1701 if (is->create_listener(ndb) == -1)
1702 {
1703 sql_print_warning("create index stats listener failed: error %d line %d",
1704 is->getNdbError().code, is->getNdbError().line);
1705 DBUG_RETURN(-1);
1706 }
1707
1708 if (is->execute_listener(ndb) == -1)
1709 {
1710 sql_print_warning("execute index stats listener failed: error %d line %d",
1711 is->getNdbError().code, is->getNdbError().line);
1712 DBUG_RETURN(-1);
1713 }
1714
1715 DBUG_RETURN(0);
1716 }
1717
1718 int
ndb_index_stat_stop_listener(Ndb_index_stat_proc & pr)1719 ndb_index_stat_stop_listener(Ndb_index_stat_proc &pr)
1720 {
1721 DBUG_ENTER("ndb_index_stat_stop_listener");
1722
1723 NdbIndexStat *is= pr.is_util;
1724 Ndb *ndb= pr.ndb;
1725
1726 if (is->drop_listener(ndb) == -1)
1727 {
1728 sql_print_warning("drop index stats listener failed: error %d line %d",
1729 is->getNdbError().code, is->getNdbError().line);
1730 DBUG_RETURN(-1);
1731 }
1732
1733 DBUG_RETURN(0);
1734 }
1735
1736 pthread_handler_t
ndb_index_stat_thread_func(void * arg MY_ATTRIBUTE ((unused)))1737 ndb_index_stat_thread_func(void *arg MY_ATTRIBUTE((unused)))
1738 {
1739 THD *thd; /* needs to be first for thread_stack */
1740 struct timespec abstime;
1741 Thd_ndb *thd_ndb= NULL;
1742
1743 my_thread_init();
1744 DBUG_ENTER("ndb_index_stat_thread_func");
1745
1746 Ndb_index_stat_proc pr;
1747
1748 bool have_listener;
1749 have_listener= false;
1750
1751 // wl4124_todo remove useless stuff copied from utility thread
1752
1753 pthread_mutex_lock(&LOCK_ndb_index_stat_thread);
1754
1755 thd= new THD; /* note that contructor of THD uses DBUG_ */
1756 if (thd == NULL)
1757 {
1758 my_errno= HA_ERR_OUT_OF_MEM;
1759 DBUG_RETURN(NULL);
1760 }
1761 THD_CHECK_SENTRY(thd);
1762 pthread_detach_this_thread();
1763 ndb_index_stat_thread= pthread_self();
1764
1765 thd->thread_stack= (char*)&thd; /* remember where our stack is */
1766 if (thd->store_globals())
1767 goto ndb_index_stat_thread_fail;
1768 lex_start(thd);
1769 thd->init_for_queries();
1770 #ifndef NDB_THD_HAS_NO_VERSION
1771 thd->version=refresh_version;
1772 #endif
1773 thd->client_capabilities = 0;
1774 thd->security_ctx->skip_grants();
1775 my_net_init(&thd->net, 0);
1776
1777 CHARSET_INFO *charset_connection;
1778 charset_connection= get_charset_by_csname("utf8",
1779 MY_CS_PRIMARY, MYF(MY_WME));
1780 thd->variables.character_set_client= charset_connection;
1781 thd->variables.character_set_results= charset_connection;
1782 thd->variables.collation_connection= charset_connection;
1783 thd->update_charset();
1784
1785 /* Signal successful initialization */
1786 ndb_index_stat_thread_running= 1;
1787 pthread_cond_signal(&COND_ndb_index_stat_ready);
1788 pthread_mutex_unlock(&LOCK_ndb_index_stat_thread);
1789
1790 /*
1791 wait for mysql server to start
1792 */
1793 mysql_mutex_lock(&LOCK_server_started);
1794 while (!mysqld_server_started)
1795 {
1796 set_timespec(abstime, 1);
1797 mysql_cond_timedwait(&COND_server_started, &LOCK_server_started,
1798 &abstime);
1799 if (ndbcluster_terminating)
1800 {
1801 mysql_mutex_unlock(&LOCK_server_started);
1802 pthread_mutex_lock(&LOCK_ndb_index_stat_thread);
1803 goto ndb_index_stat_thread_end;
1804 }
1805 }
1806 mysql_mutex_unlock(&LOCK_server_started);
1807
1808 /*
1809 Wait for cluster to start
1810 */
1811 pthread_mutex_lock(&LOCK_ndb_index_stat_thread);
1812 while (!g_ndb_status.cluster_node_id && (ndbcluster_hton->slot != ~(uint)0))
1813 {
1814 /* ndb not connected yet */
1815 pthread_cond_wait(&COND_ndb_index_stat_thread, &LOCK_ndb_index_stat_thread);
1816 if (ndbcluster_terminating)
1817 goto ndb_index_stat_thread_end;
1818 }
1819 pthread_mutex_unlock(&LOCK_ndb_index_stat_thread);
1820
1821 /* Get instance used for sys objects check and create */
1822 if (!(pr.is_util= new NdbIndexStat))
1823 {
1824 sql_print_error("Could not allocate NdbIndexStat is_util object");
1825 pthread_mutex_lock(&LOCK_ndb_index_stat_thread);
1826 goto ndb_index_stat_thread_end;
1827 }
1828
1829 /* Get thd_ndb for this thread */
1830 if (!(thd_ndb= Thd_ndb::seize(thd)))
1831 {
1832 sql_print_error("Could not allocate Thd_ndb object");
1833 pthread_mutex_lock(&LOCK_ndb_index_stat_thread);
1834 goto ndb_index_stat_thread_end;
1835 }
1836 set_thd_ndb(thd, thd_ndb);
1837 thd_ndb->options|= TNO_NO_LOG_SCHEMA_OP;
1838 if (thd_ndb->ndb->setDatabaseName(NDB_INDEX_STAT_DB) == -1)
1839 {
1840 sql_print_error("Could not change index stats thd_ndb database to %s",
1841 NDB_INDEX_STAT_DB);
1842 pthread_mutex_lock(&LOCK_ndb_index_stat_thread);
1843 goto ndb_index_stat_thread_end;
1844 }
1845 pr.ndb= thd_ndb->ndb;
1846
1847 ndb_index_stat_allow(1);
1848 bool enable_ok;
1849 enable_ok= false;
1850
1851 set_timespec(abstime, 0);
1852 for (;;)
1853 {
1854 pthread_mutex_lock(&LOCK_ndb_index_stat_thread);
1855 if (!ndbcluster_terminating) {
1856 int ret= pthread_cond_timedwait(&COND_ndb_index_stat_thread,
1857 &LOCK_ndb_index_stat_thread,
1858 &abstime);
1859 const char* reason= ret == ETIMEDOUT ? "timed out" : "wake up";
1860 (void)reason; // USED
1861 DBUG_PRINT("index_stat", ("loop: %s", reason));
1862 }
1863 if (ndbcluster_terminating) /* Shutting down server */
1864 goto ndb_index_stat_thread_end;
1865 pthread_mutex_unlock(&LOCK_ndb_index_stat_thread);
1866
1867 /* const bool enable_ok_new= THDVAR(NULL, index_stat_enable); */
1868 const bool enable_ok_new= ndb_index_stat_get_enable(NULL);
1869
1870 do
1871 {
1872 if (enable_ok != enable_ok_new)
1873 {
1874 DBUG_PRINT("index_stat", ("global enable: %d -> %d",
1875 enable_ok, enable_ok_new));
1876
1877 if (enable_ok_new)
1878 {
1879 // at enable check or create stats tables and events
1880 if (ndb_index_stat_check_or_create_systables(pr) == -1 ||
1881 ndb_index_stat_check_or_create_sysevents(pr) == -1 ||
1882 ndb_index_stat_start_listener(pr) == -1)
1883 {
1884 // try again in next loop
1885 break;
1886 }
1887 have_listener= true;
1888 }
1889 else
1890 {
1891 // not a normal use-case
1892 if (have_listener)
1893 {
1894 if (ndb_index_stat_stop_listener(pr) == 0)
1895 have_listener= false;
1896 }
1897 }
1898 enable_ok= enable_ok_new;
1899 }
1900
1901 if (!enable_ok)
1902 break;
1903
1904 pr.busy= false;
1905 ndb_index_stat_proc(pr);
1906 } while (0);
1907
1908 /* Calculate new time to wake up */
1909
1910 const Ndb_index_stat_opt &opt= ndb_index_stat_opt;
1911 uint msecs= 0;
1912 if (!enable_ok)
1913 msecs= opt.get(Ndb_index_stat_opt::Iloop_checkon);
1914 else if (!pr.busy)
1915 msecs= opt.get(Ndb_index_stat_opt::Iloop_idle);
1916 else
1917 msecs= opt.get(Ndb_index_stat_opt::Iloop_busy);
1918 DBUG_PRINT("index_stat", ("sleep %dms", msecs));
1919
1920 set_timespec_nsec(abstime, msecs * 1000000ULL);
1921 }
1922
1923 ndb_index_stat_thread_end:
1924 net_end(&thd->net);
1925
1926 ndb_index_stat_thread_fail:
1927 if (have_listener)
1928 {
1929 if (ndb_index_stat_stop_listener(pr) == 0)
1930 have_listener= false;
1931 }
1932 if (pr.is_util)
1933 {
1934 delete pr.is_util;
1935 pr.is_util= 0;
1936 }
1937 if (thd_ndb)
1938 {
1939 Thd_ndb::release(thd_ndb);
1940 set_thd_ndb(thd, NULL);
1941 }
1942 thd->cleanup();
1943 delete thd;
1944
1945 /* signal termination */
1946 ndb_index_stat_thread_running= 0;
1947 pthread_cond_signal(&COND_ndb_index_stat_ready);
1948 pthread_mutex_unlock(&LOCK_ndb_index_stat_thread);
1949 DBUG_PRINT("exit", ("ndb_index_stat_thread"));
1950
1951 DBUG_LEAVE;
1952 my_thread_end();
1953 pthread_exit(0);
1954 return NULL;
1955 }
1956
1957 /* Optimizer queries */
1958
1959 static ulonglong
ndb_index_stat_round(double x)1960 ndb_index_stat_round(double x)
1961 {
1962 char buf[100];
1963 if (x < 0.0)
1964 x= 0.0;
1965 // my_snprintf has no float and windows has no snprintf
1966 sprintf(buf, "%.0f", x);
1967 /* mysql provides strtoull */
1968 ulonglong n= strtoull(buf, 0, 10);
1969 return n;
1970 }
1971
1972 int
ndb_index_stat_wait(Ndb_index_stat * st,uint sample_version,bool from_analyze)1973 ndb_index_stat_wait(Ndb_index_stat *st,
1974 uint sample_version,
1975 bool from_analyze)
1976 {
1977 DBUG_ENTER("ndb_index_stat_wait");
1978
1979 pthread_mutex_lock(&ndb_index_stat_stat_mutex);
1980 int err= 0;
1981 uint count= 0;
1982 struct timespec abstime;
1983 while (true)
1984 {
1985 int ret= 0;
1986 if (count == 0)
1987 {
1988 if (st->lt == Ndb_index_stat::LT_Error && !from_analyze)
1989 {
1990 err= Ndb_index_stat_error_HAS_ERROR;
1991 break;
1992 }
1993 ndb_index_stat_clear_error(st);
1994 }
1995 if (st->no_stats && !from_analyze)
1996 {
1997 /* Have detected no stats now or before */
1998 err= NdbIndexStat::NoIndexStats;
1999 break;
2000 }
2001 if (st->error.code != 0)
2002 {
2003 /* A new error has occured */
2004 err= st->error.code;
2005 break;
2006 }
2007 if (st->sample_version > sample_version)
2008 break;
2009 DBUG_PRINT("index_stat", ("st %s wait count:%u",
2010 st->id, ++count));
2011 pthread_mutex_lock(&LOCK_ndb_index_stat_thread);
2012 pthread_cond_signal(&COND_ndb_index_stat_thread);
2013 pthread_mutex_unlock(&LOCK_ndb_index_stat_thread);
2014 set_timespec(abstime, 1);
2015 ret= pthread_cond_timedwait(&ndb_index_stat_stat_cond,
2016 &ndb_index_stat_stat_mutex,
2017 &abstime);
2018 if (ret != 0 && ret != ETIMEDOUT)
2019 {
2020 err= ret;
2021 break;
2022 }
2023 }
2024 pthread_mutex_unlock(&ndb_index_stat_stat_mutex);
2025 if (err != 0)
2026 {
2027 DBUG_PRINT("index_stat", ("st %s wait error: %d",
2028 st->id, err));
2029 DBUG_RETURN(err);
2030 }
2031 DBUG_PRINT("index_stat", ("st %s wait ok: sample_version %u -> %u",
2032 st->id, sample_version, st->sample_version));
2033 DBUG_RETURN(0);
2034 }
2035
2036 int
ndb_index_stat_query(uint inx,const key_range * min_key,const key_range * max_key,NdbIndexStat::Stat & stat,int from)2037 ha_ndbcluster::ndb_index_stat_query(uint inx,
2038 const key_range *min_key,
2039 const key_range *max_key,
2040 NdbIndexStat::Stat& stat,
2041 int from)
2042 {
2043 DBUG_ENTER("ha_ndbcluster::ndb_index_stat_query");
2044
2045 const KEY *key_info= table->key_info + inx;
2046 const NDB_INDEX_DATA &data= m_index[inx];
2047 const NDBINDEX *index= data.index;
2048 DBUG_PRINT("index_stat", ("index: %u name: %s", inx, index->getName()));
2049
2050 int err= 0;
2051
2052 /* Create an IndexBound struct for the keys */
2053 NdbIndexScanOperation::IndexBound ib;
2054 compute_index_bounds(ib, key_info, min_key, max_key, from);
2055 ib.range_no= 0;
2056
2057 Ndb_index_stat *st=
2058 ndb_index_stat_get_share(m_share, index, m_table, err, true, false);
2059 if (st == 0)
2060 DBUG_RETURN(err);
2061
2062 /* Pass old version 0 so existing stats terminates wait at once */
2063 err= ndb_index_stat_wait(st, 0, false);
2064 if (err != 0)
2065 DBUG_RETURN(err);
2066
2067 if (st->read_time == 0)
2068 {
2069 DBUG_PRINT("index_stat", ("no index stats"));
2070 pthread_mutex_lock(&LOCK_ndb_index_stat_thread);
2071 pthread_cond_signal(&COND_ndb_index_stat_thread);
2072 pthread_mutex_unlock(&LOCK_ndb_index_stat_thread);
2073 DBUG_RETURN(NdbIndexStat::NoIndexStats);
2074 }
2075
2076 uint8 bound_lo_buffer[NdbIndexStat::BoundBufferBytes];
2077 uint8 bound_hi_buffer[NdbIndexStat::BoundBufferBytes];
2078 NdbIndexStat::Bound bound_lo(st->is, bound_lo_buffer);
2079 NdbIndexStat::Bound bound_hi(st->is, bound_hi_buffer);
2080 NdbIndexStat::Range range(bound_lo, bound_hi);
2081
2082 const NdbRecord* key_record= data.ndb_record_key;
2083 if (st->is->convert_range(range, key_record, &ib) == -1)
2084 {
2085 ndb_index_stat_error(st, "convert_range", __LINE__);
2086 DBUG_RETURN(st->error.code);
2087 }
2088 if (st->is->query_stat(range, stat) == -1)
2089 {
2090 /* Invalid cache - should remove the entry */
2091 ndb_index_stat_error(st, "query_stat", __LINE__);
2092 DBUG_RETURN(st->error.code);
2093 }
2094
2095 DBUG_RETURN(0);
2096 }
2097
2098 int
ndb_index_stat_get_rir(uint inx,key_range * min_key,key_range * max_key,ha_rows * rows_out)2099 ha_ndbcluster::ndb_index_stat_get_rir(uint inx,
2100 key_range *min_key,
2101 key_range *max_key,
2102 ha_rows *rows_out)
2103 {
2104 DBUG_ENTER("ha_ndbcluster::ndb_index_stat_get_rir");
2105 uint8 stat_buffer[NdbIndexStat::StatBufferBytes];
2106 NdbIndexStat::Stat stat(stat_buffer);
2107 int err= ndb_index_stat_query(inx, min_key, max_key, stat, 1);
2108 if (err == 0)
2109 {
2110 double rir= -1.0;
2111 NdbIndexStat::get_rir(stat, &rir);
2112 ha_rows rows= ndb_index_stat_round(rir);
2113 /* Estimate only so cannot return exact zero */
2114 if (rows == 0)
2115 rows= 1;
2116 *rows_out= rows;
2117 #ifndef DBUG_OFF
2118 char rule[NdbIndexStat::RuleBufferBytes];
2119 NdbIndexStat::get_rule(stat, rule);
2120 #endif
2121 DBUG_PRINT("index_stat", ("rir: %u rule: %s", (uint)rows, rule));
2122 DBUG_RETURN(0);
2123 }
2124 DBUG_RETURN(err);
2125 }
2126
2127 int
ndb_index_stat_set_rpk(uint inx)2128 ha_ndbcluster::ndb_index_stat_set_rpk(uint inx)
2129 {
2130 DBUG_ENTER("ha_ndbcluster::ndb_index_stat_set_rpk");
2131
2132 KEY *key_info= table->key_info + inx;
2133 int err= 0;
2134
2135 uint8 stat_buffer[NdbIndexStat::StatBufferBytes];
2136 NdbIndexStat::Stat stat(stat_buffer);
2137 const key_range *min_key= 0;
2138 const key_range *max_key= 0;
2139 err= ndb_index_stat_query(inx, min_key, max_key, stat, 2);
2140 if (err == 0)
2141 {
2142 uint k;
2143 for (k= 0; k < key_info->user_defined_key_parts; k++)
2144 {
2145 double rpk= -1.0;
2146 NdbIndexStat::get_rpk(stat, k, &rpk);
2147 ulonglong recs= ndb_index_stat_round(rpk);
2148 key_info->rec_per_key[k]= (ulong)recs;
2149 #ifndef DBUG_OFF
2150 char rule[NdbIndexStat::RuleBufferBytes];
2151 NdbIndexStat::get_rule(stat, rule);
2152 #endif
2153 DBUG_PRINT("index_stat", ("rpk[%u]: %u rule: %s", k, (uint)recs, rule));
2154 }
2155 DBUG_RETURN(0);
2156 }
2157 DBUG_RETURN(err);
2158 }
2159
2160 int
ndb_index_stat_analyze(Ndb * ndb,uint * inx_list,uint inx_count)2161 ha_ndbcluster::ndb_index_stat_analyze(Ndb *ndb,
2162 uint *inx_list,
2163 uint inx_count)
2164 {
2165 DBUG_ENTER("ha_ndbcluster::ndb_index_stat_analyze");
2166
2167 struct {
2168 uint sample_version;
2169 uint error_count;
2170 } old[MAX_INDEXES];
2171
2172 int err= 0;
2173 uint i;
2174
2175 /* Force stats update on each index */
2176 for (i= 0; i < inx_count; i++)
2177 {
2178 uint inx= inx_list[i];
2179 const NDB_INDEX_DATA &data= m_index[inx];
2180 const NDBINDEX *index= data.index;
2181 DBUG_PRINT("index_stat", ("force update: %s", index->getName()));
2182
2183 Ndb_index_stat *st=
2184 ndb_index_stat_get_share(m_share, index, m_table, err, true, true);
2185 if (st == 0)
2186 DBUG_RETURN(err);
2187
2188 old[i].sample_version= st->sample_version;
2189 old[i].error_count= st->error_count;
2190 }
2191
2192 /* Wait for each update (or error) */
2193 for (i = 0; i < inx_count; i++)
2194 {
2195 uint inx= inx_list[i];
2196 const NDB_INDEX_DATA &data= m_index[inx];
2197 const NDBINDEX *index= data.index;
2198 DBUG_PRINT("index_stat", ("wait for update: %s", index->getName()));
2199
2200 Ndb_index_stat *st=
2201 ndb_index_stat_get_share(m_share, index, m_table, err, false, false);
2202 if (st == 0)
2203 DBUG_RETURN(err);
2204
2205 err= ndb_index_stat_wait(st, old[i].sample_version, true);
2206 if (err != 0)
2207 DBUG_RETURN(err);
2208 }
2209
2210 DBUG_RETURN(0);
2211 }
2212
2213 #endif
2214