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