File: query_response_time.cc

package info (click to toggle)
mariadb 1%3A11.8.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 772,520 kB
  • sloc: ansic: 2,414,714; cpp: 1,791,394; asm: 381,336; perl: 62,905; sh: 49,647; pascal: 40,897; java: 39,363; python: 20,791; yacc: 20,432; sql: 17,907; xml: 12,344; ruby: 8,544; cs: 6,542; makefile: 6,145; ada: 1,879; lex: 1,193; javascript: 996; objc: 80; tcl: 73; awk: 46; php: 22
file content (359 lines) | stat: -rw-r--r-- 10,766 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
#include "mysql_version.h"
#include "my_global.h"
#ifdef HAVE_RESPONSE_TIME_DISTRIBUTION
#include "mysql_com.h"
#include "rpl_tblmap.h"
#include "table.h"
#include "field.h"
#include "sql_show.h"
#include "query_response_time.h"

#define TIME_STRING_POSITIVE_POWER_LENGTH QRT_TIME_STRING_POSITIVE_POWER_LENGTH
#define TIME_STRING_NEGATIVE_POWER_LENGTH 6
#define TOTAL_STRING_POSITIVE_POWER_LENGTH QRT_TOTAL_STRING_POSITIVE_POWER_LENGTH
#define TOTAL_STRING_NEGATIVE_POWER_LENGTH 6
#define MINIMUM_BASE 2
#define MAXIMUM_BASE QRT_MAXIMUM_BASE
#define POSITIVE_POWER_FILLER QRT_POSITIVE_POWER_FILLER
#define NEGATIVE_POWER_FILLER QRT_NEGATIVE_POWER_FILLER
#define TIME_OVERFLOW   QRT_TIME_OVERFLOW
#define DEFAULT_BASE    QRT_DEFAULT_BASE

#define do_xstr(s) do_str(s)
#define do_str(s) #s
#define do_format(filler,width) "%" filler width "lld"
/*
  Format strings for snprintf. Generate from:
  POSITIVE_POWER_FILLER and TIME_STRING_POSITIVE_POWER_LENGTH
  NEFATIVE_POWER_FILLER and TIME_STRING_NEGATIVE_POWER_LENGTH
*/
#define TIME_STRING_POSITIVE_POWER_FORMAT do_format(POSITIVE_POWER_FILLER,do_xstr(TIME_STRING_POSITIVE_POWER_LENGTH))
#define TIME_STRING_NEGATIVE_POWER_FORMAT do_format(NEGATIVE_POWER_FILLER,do_xstr(TIME_STRING_NEGATIVE_POWER_LENGTH))
#define TIME_STRING_FORMAT		      TIME_STRING_POSITIVE_POWER_FORMAT "." TIME_STRING_NEGATIVE_POWER_FORMAT

#define TOTAL_STRING_POSITIVE_POWER_FORMAT do_format(POSITIVE_POWER_FILLER,do_xstr(TOTAL_STRING_POSITIVE_POWER_LENGTH))
#define TOTAL_STRING_NEGATIVE_POWER_FORMAT do_format(NEGATIVE_POWER_FILLER,do_xstr(TOTAL_STRING_NEGATIVE_POWER_LENGTH))
#define TOTAL_STRING_FORMAT		      TOTAL_STRING_POSITIVE_POWER_FORMAT "." TOTAL_STRING_NEGATIVE_POWER_FORMAT

#define TIME_STRING_LENGTH	QRT_TIME_STRING_LENGTH
#define TIME_STRING_BUFFER_LENGTH	(TIME_STRING_LENGTH + 1 /* '\0' */)

#define TOTAL_STRING_LENGTH	QRT_TOTAL_STRING_LENGTH
#define TOTAL_STRING_BUFFER_LENGTH	(TOTAL_STRING_LENGTH + 1 /* '\0' */)

/*
  Calculate length of "log linear"
  1)
  (MINIMUM_BASE ^ result) <= (10 ^ STRING_POWER_LENGTH) < (MINIMUM_BASE ^ (result + 1))

  2)
  (MINIMUM_BASE ^ result) <= (10 ^ STRING_POWER_LENGTH)
  and
  (MINIMUM_BASE ^ (result + 1)) > (10 ^ STRING_POWER_LENGTH)

  3)
  result     <= LOG(MINIMUM_BASE, 10 ^ STRING_POWER_LENGTH)= STRING_POWER_LENGTH * LOG(MINIMUM_BASE,10)
  result + 1 >  LOG(MINIMUM_BASE, 10 ^ STRING_POWER_LENGTH)= STRING_POWER_LENGTH * LOG(MINIMUM_BASE,10)

  4) STRING_POWER_LENGTH * LOG(MINIMUM_BASE,10) - 1 < result <= STRING_POWER_LENGTH * LOG(MINIMUM_BASE,10)

  MINIMUM_BASE= 2 always, LOG(MINIMUM_BASE,10)= 3.3219280948873626, result= (int)3.3219280948873626 * STRING_POWER_LENGTH

  Last counter always use for time overflow
*/
#define POSITIVE_POWER_COUNT ((int)(3.32192809 * TIME_STRING_POSITIVE_POWER_LENGTH))
#define NEGATIVE_POWER_COUNT ((int)(3.32192809 * TIME_STRING_NEGATIVE_POWER_LENGTH))
#define OVERALL_POWER_COUNT (NEGATIVE_POWER_COUNT + 1 + POSITIVE_POWER_COUNT)

#define MILLION ((unsigned long)1000 * 1000)

namespace query_response_time
{

class utility
{
public:
  utility() : m_base(0)
  {
    m_max_dec_value= MILLION;
    for(int i= 0; TIME_STRING_POSITIVE_POWER_LENGTH > i; ++i)
      m_max_dec_value *= 10;
    setup(DEFAULT_BASE);
  }
public:
  uint      base()            const { return m_base; }
  uint      negative_count()  const { return m_negative_count; }
  uint      positive_count()  const { return m_positive_count; }
  uint      bound_count()     const { return m_bound_count; }
  ulonglong max_dec_value()   const { return m_max_dec_value; }
  ulonglong bound(uint index) const { return m_bound[ index ]; }
public:
  void setup(uint base)
  {
    if(base != m_base)
    {
      m_base= base;

      const ulonglong million= 1000 * 1000;
      ulonglong value= million;
      m_negative_count= 0;
      while(value > 0)
      {
	m_negative_count += 1;
	value /= m_base;
      }
      m_negative_count -= 1;

      value= million;
      m_positive_count= 0;
      while(value < m_max_dec_value)
      {
	m_positive_count += 1;
	value *= m_base;
      }
      m_bound_count= m_negative_count + m_positive_count;

      value= million;
      for(uint i= 0; i < m_negative_count; ++i)
      {
	value /= m_base;
	m_bound[m_negative_count - i - 1]= value;
      }
      value= million;
      for(uint i= 0; i < m_positive_count;  ++i)
      {
	m_bound[m_negative_count + i]= value;
	value *= m_base;
      }
    }
  }
private:
  uint      m_base;
  uint      m_negative_count;
  uint      m_positive_count;
  uint      m_bound_count;
  ulonglong m_max_dec_value; /* for TIME_STRING_POSITIVE_POWER_LENGTH=7 is 10000000 */
  ulonglong m_bound[OVERALL_POWER_COUNT];
};

ATTRIBUTE_FORMAT(printf, 3, 0) static
size_t print_time(char* buffer, std::size_t buffer_size, const char* format,
                  uint64 value)
{
  ulonglong second=      (value / MILLION);
  ulonglong microsecond= (value % MILLION);
  return my_snprintf(buffer, buffer_size, format, second, microsecond);
}

class time_collector
{
  utility *m_utility;
  /*
    Counters for each query type. See QUERY_TYPE
  */
  Atomic_counter<uint32_t> m_count[QUERY_TYPES][OVERALL_POWER_COUNT + 1];
  Atomic_counter<uint64_t> m_total[QUERY_TYPES][OVERALL_POWER_COUNT + 1];

public:
  time_collector(utility& u): m_utility(&u) { flush_all(); }
  ~time_collector() = default;
  uint32_t count(QUERY_TYPE type, uint index) { return m_count[type][index]; }
  uint64_t total(QUERY_TYPE type, uint index) { return m_total[type][index]; }
  void flush(QUERY_TYPE type)
  {
    switch (type) {
    case ANY: flush_all(); break;
    case READ: flush_read(); break;
    case WRITE: flush_write(); break;
    }
  }
  void flush_all()
  {
    memset((void*)&m_count,0,sizeof(m_count));
    memset((void*)&m_total,0,sizeof(m_total));
  }
  void flush_read()
  {
    memset((void*)&m_count[READ],0,sizeof(m_count[READ]));
    memset((void*)&m_total[READ],0,sizeof(m_total[READ]));
    update_total();
  }
  void flush_write()
  {
    memset((void*)&m_count[WRITE],0,sizeof(m_count[WRITE]));
    memset((void*)&m_total[WRITE],0,sizeof(m_total[WRITE]));
    update_total();
  }
  void update_total()
  {
    int count, i;
    for (i=0, count= m_utility->bound_count(); i < count; ++i)
    {
      m_count[0][i]= m_count[1][i]+m_count[2][i];
      m_total[0][i]= m_total[1][i]+m_total[2][i];
    }
  }
  void collect(QUERY_TYPE type, uint64_t time)
  {
    DBUG_ASSERT(type != ANY);
    int i= 0;
    for(int count= m_utility->bound_count(); count > i; ++i)
    {
      if (m_utility->bound(i) > time)
      {
        m_count[0][i]++;
        m_total[0][i]+= time;
        m_count[type][i]++;
        m_total[type][i]+= time;
        return;
      }
    }
  }
};

class collector
{
public:
  collector() : m_time(m_utility)
  {
    m_utility.setup(DEFAULT_BASE);
    m_time.flush_all();
  }
public:
  void flush(QUERY_TYPE type)
  {
    if (opt_query_response_time_range_base != m_utility.base())
    {
      /* We have to flush everything if base changes */
      type= ANY;
      m_utility.setup(opt_query_response_time_range_base);
    }
    m_time.flush(type);
  }
  int fill(QUERY_TYPE type, THD* thd, TABLE_LIST *tables, COND *cond,
           bool extra_fields)
  {
    DBUG_ENTER("fill_schema_query_response_time");
    TABLE        *table= static_cast<TABLE*>(tables->table);
    Field        **fields= table->field;
    for(uint i= 0, count= bound_count() + 1 /* with overflow */; count > i; ++i)
    {
      char time[TIME_STRING_BUFFER_LENGTH];
      char total[TOTAL_STRING_BUFFER_LENGTH];
      size_t time_length, total_length;
      if(i == bound_count())
      {
        assert(sizeof(TIME_OVERFLOW) <= TIME_STRING_BUFFER_LENGTH);
        assert(sizeof(TIME_OVERFLOW) <= TOTAL_STRING_BUFFER_LENGTH);
        memcpy(time,TIME_OVERFLOW,sizeof(TIME_OVERFLOW));
        memcpy(total,TIME_OVERFLOW,sizeof(TIME_OVERFLOW));
        time_length= total_length= sizeof(TIME_OVERFLOW)-1;
      }
      else
      {
        time_length= print_time(time, sizeof(time), TIME_STRING_FORMAT,
                               this->bound(i));
        total_length= print_time(total, sizeof(total), TOTAL_STRING_FORMAT,
                                 this->total(type, i));
      }
      fields[0]->store(time, time_length, system_charset_info);
      fields[1]->store((longlong) this->count(type, i), true);
      fields[2]->store(total, total_length, system_charset_info);
      if (extra_fields)
      {
        fields[3]->store((longlong) this->count(WRITE, i), true);
        total_length= print_time(total, sizeof(total), TOTAL_STRING_FORMAT,
                                 this->total(WRITE, i));
        fields[4]->store(total, total_length, system_charset_info);
      }
      if (schema_table_store_record(thd, table))
      {
	DBUG_RETURN(1);
      }
    }
    DBUG_RETURN(0);
  }
  void collect(QUERY_TYPE type, ulonglong time)
  {
    m_time.collect(type, time);
  }
  uint bound_count() const
  {
    return m_utility.bound_count();
  }
  ulonglong bound(uint index)
  {
    return m_utility.bound(index);
  }
  ulonglong count(QUERY_TYPE type, uint index)
  {
    return m_time.count(type, index);
  }
  ulonglong total(QUERY_TYPE type, uint index)
  {
    return m_time.total(type, index);
  }
private:
  utility          m_utility;
  time_collector   m_time;
};

static collector g_collector;

} // namespace query_response_time

void query_response_time_init()
{
  query_response_time_flush_all();
}

void query_response_time_free()
{
  query_response_time::g_collector.flush(ANY);
}

int query_response_time_flush_all()
{
  query_response_time::g_collector.flush(ANY);
  return 0;
}

int query_response_time_flush_read()
{
  query_response_time::g_collector.flush(READ);
  return 0;
}

int query_response_time_flush_write()
{
  query_response_time::g_collector.flush(WRITE);
  return 0;
}

void query_response_time_collect(QUERY_TYPE type, ulonglong query_time)
{
  query_response_time::g_collector.collect(type, query_time);
}

int query_response_time_fill(THD *thd, TABLE_LIST *tables, COND *cond)
{
  return query_response_time::g_collector.fill(ANY, thd,tables, cond, 0);
}

int query_response_time_fill_read(THD *thd, TABLE_LIST *tables, COND *cond)
{
  return query_response_time::g_collector.fill(READ, thd, tables, cond, 0);
}

int query_response_time_fill_write(THD *thd, TABLE_LIST *tables, COND *cond)
{
  return query_response_time::g_collector.fill(WRITE, thd, tables, cond, 0);
}

int query_response_time_fill_read_write(THD *thd, TABLE_LIST *tables,
                                        COND *cond)
{
  /* write will also be filled as extra fields is 1 */
  return query_response_time::g_collector.fill(READ, thd, tables, cond, 1);
}
#endif // HAVE_RESPONSE_TIME_DISTRIBUTION