~posulliv/drizzle/optimizer-style-cleanup

« back to all changes in this revision

Viewing changes to plugin/logging_stats/logging_stats.cc

  • Committer: Padraig O'Sullivan
  • Date: 2010-04-17 01:38:47 UTC
  • mfrom: (1237.9.238 bad-staging)
  • Revision ID: osullivan.padraig@gmail.com-20100417013847-ibjioqsfbmf5yg4g
Merge trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (c) 2010, Joseph Daly <skinny.moey@gmail.com>
 
3
 * All rights reserved.
 
4
 *
 
5
 * Redistribution and use in source and binary forms, with or without
 
6
 * modification, are permitted provided that the following conditions are met:
 
7
 *
 
8
 *   * Redistributions of source code must retain the above copyright notice,
 
9
 *     this list of conditions and the following disclaimer.
 
10
 *   * Redistributions in binary form must reproduce the above copyright notice,
 
11
 *     this list of conditions and the following disclaimer in the documentation
 
12
 *     and/or other materials provided with the distribution.
 
13
 *   * Neither the name of Joseph Daly nor the names of its contributors
 
14
 *     may be used to endorse or promote products derived from this software
 
15
 *     without specific prior written permission.
 
16
 *
 
17
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 
18
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 
19
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 
20
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 
21
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 
22
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 
23
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 
24
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 
25
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 
26
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 
27
 * THE POSSIBILITY OF SUCH DAMAGE.
 
28
 */
 
29
 
 
30
/**
 
31
 * @details
 
32
 *
 
33
 * This plugin tracks the current user commands for a session, and copies
 
34
 * them into a cumulative vector of all commands run by a user over time. 
 
35
 * The commands are logged using the post() and postEnd() logging APIs.
 
36
 * User commands are stored in a Scoreboard where each active session
 
37
 * owns a ScoreboardSlot.  
 
38
 *
 
39
 * Scoreboard
 
40
 *
 
41
 * The scoreboard is a pre-allocated vector of vectors of ScoreboardSlots. It
 
42
 * can be thought of as a vector of buckets where each bucket contains
 
43
 * pre-allocated ScoreboardSlots. To determine which bucket gets used for
 
44
 * recording statistics the modulus operator is used on the session_id. This
 
45
 * will result in a bucket to search for a unused ScoreboardSlot.
 
46
 *
 
47
 * Locking  
 
48
 * 
 
49
 * Each vector in the Scoreboard has its own lock. This allows session 2 
 
50
 * to not have to wait for session 1 to locate a slot to use, as they
 
51
 * will be in different buckets.  A lock is taken to locate a open slot
 
52
 * in the scoreboard for a session or to locate the slot that the current
 
53
 * session has claimed. 
 
54
 *
 
55
 * A read lock is taken on the scoreboard vector when the table is queried 
 
56
 * in the data_dictionary.
 
57
 *
 
58
 * A lock is taken when a new user is added to the cumulative vector
 
59
 * repeat connections with a already used user will not use a lock. 
 
60
 * 
 
61
 * System Variables
 
62
 * 
 
63
 * logging_stats_scoreboard_size - the size of the scoreboard this corresponds
 
64
 *   to the maximum number of concurrent connections that can be tracked
 
65
 *
 
66
 * logging_stats_max_user_count - this is used for cumulative statistics it 
 
67
 *   represents the maximum users that can be tracked 
 
68
 * 
 
69
 * logging_stats_bucket_count - the number of buckets to have in the scoreboard
 
70
 *   this splits up locking across several buckets so the entire scoreboard is 
 
71
 *   not locked at a single point in time.
 
72
 * 
 
73
 * logging_stats_enabled - enable/disable plugin 
 
74
 * 
 
75
 * TODO 
 
76
 *
 
77
 * A pointer to the scoreboard slot could be added to the Session object.
 
78
 * This will avoid the session having to do multiple lookups in the scoreboard,
 
79
 * this will also avoid having to take a lock to locate the scoreboard slot 
 
80
 * being used by a particular session. 
 
81
 *
 
82
 * Allow expansion of Scoreboard and cumulative vector 
 
83
 *  
 
84
 */
 
85
 
 
86
#include "config.h"
 
87
#include "logging_stats.h"
 
88
#include "stats_schema.h"
 
89
#include <drizzled/session.h>
 
90
 
 
91
using namespace drizzled;
 
92
using namespace plugin;
 
93
using namespace std;
 
94
 
 
95
static bool sysvar_logging_stats_enabled= false;
 
96
 
 
97
static uint32_t sysvar_logging_stats_scoreboard_size= 2000;
 
98
 
 
99
static uint32_t sysvar_logging_stats_max_user_count= 10000;
 
100
 
 
101
static uint32_t sysvar_logging_stats_bucket_count= 10;
 
102
 
 
103
pthread_rwlock_t LOCK_cumulative_scoreboard_index;
 
104
 
 
105
LoggingStats::LoggingStats(string name_arg) : Logging(name_arg)
 
106
{
 
107
  (void) pthread_rwlock_init(&LOCK_cumulative_scoreboard_index, NULL);
 
108
  cumulative_stats_by_user_index= 0;
 
109
 
 
110
  current_scoreboard= new Scoreboard(sysvar_logging_stats_scoreboard_size, 
 
111
                                     sysvar_logging_stats_bucket_count);
 
112
 
 
113
  cumulative_stats_by_user_max= sysvar_logging_stats_max_user_count;
 
114
 
 
115
  cumulative_stats_by_user_vector= new vector<ScoreboardSlot *>(cumulative_stats_by_user_max);
 
116
  preAllocateScoreboardSlotVector(sysvar_logging_stats_max_user_count, 
 
117
                                  cumulative_stats_by_user_vector);
 
118
}
 
119
 
 
120
LoggingStats::~LoggingStats()
 
121
{
 
122
  (void) pthread_rwlock_destroy(&LOCK_cumulative_scoreboard_index); 
 
123
  deleteScoreboardSlotVector(cumulative_stats_by_user_vector);
 
124
  delete current_scoreboard;
 
125
}
 
126
 
 
127
void LoggingStats::preAllocateScoreboardSlotVector(uint32_t size, 
 
128
                                                   vector<ScoreboardSlot *> *scoreboard_slot_vector)
 
129
{
 
130
  vector<ScoreboardSlot *>::iterator it= scoreboard_slot_vector->begin();
 
131
  for (uint32_t j=0; j < size; ++j)
 
132
  {
 
133
    ScoreboardSlot *scoreboard_slot= new ScoreboardSlot();
 
134
    it= scoreboard_slot_vector->insert(it, scoreboard_slot);
 
135
  }
 
136
  scoreboard_slot_vector->resize(size);
 
137
}
 
138
 
 
139
void LoggingStats::deleteScoreboardSlotVector(vector<ScoreboardSlot *> *scoreboard_slot_vector)
 
140
{
 
141
  vector<ScoreboardSlot *>::iterator it= scoreboard_slot_vector->begin();
 
142
  for (; it < scoreboard_slot_vector->end(); ++it)
 
143
  {
 
144
    delete *it;
 
145
  }
 
146
  scoreboard_slot_vector->clear();
 
147
  delete scoreboard_slot_vector;
 
148
}
 
149
 
 
150
bool LoggingStats::isBeingLogged(Session *session)
 
151
{
 
152
  enum_sql_command sql_command= session->lex->sql_command;
 
153
 
 
154
  switch(sql_command)
 
155
  {
 
156
    case SQLCOM_UPDATE:
 
157
    case SQLCOM_DELETE:
 
158
    case SQLCOM_INSERT:
 
159
    case SQLCOM_ROLLBACK:
 
160
    case SQLCOM_COMMIT:
 
161
    case SQLCOM_CREATE_TABLE:
 
162
    case SQLCOM_ALTER_TABLE:
 
163
    case SQLCOM_DROP_TABLE:
 
164
    case SQLCOM_SELECT:
 
165
      return true;
 
166
    default:
 
167
      return false;
 
168
  }
 
169
 
170
 
 
171
void LoggingStats::updateCurrentScoreboard(ScoreboardSlot *scoreboard_slot,
 
172
                                           Session *session)
 
173
{
 
174
  enum_sql_command sql_command= session->lex->sql_command;
 
175
 
 
176
  UserCommands *user_commands= scoreboard_slot->getUserCommands();
 
177
 
 
178
  switch(sql_command)
 
179
  {
 
180
    case SQLCOM_UPDATE:
 
181
      user_commands->incrementUpdateCount();
 
182
      break;
 
183
    case SQLCOM_DELETE:
 
184
      user_commands->incrementDeleteCount();
 
185
      break;
 
186
    case SQLCOM_INSERT:
 
187
      user_commands->incrementInsertCount();
 
188
      break;
 
189
    case SQLCOM_ROLLBACK:
 
190
      user_commands->incrementRollbackCount();
 
191
      break;
 
192
    case SQLCOM_COMMIT:
 
193
      user_commands->incrementCommitCount();
 
194
      break;
 
195
    case SQLCOM_CREATE_TABLE:
 
196
      user_commands->incrementCreateCount();
 
197
      break;
 
198
    case SQLCOM_ALTER_TABLE:
 
199
      user_commands->incrementAlterCount();
 
200
      break;
 
201
    case SQLCOM_DROP_TABLE:
 
202
      user_commands->incrementDropCount();
 
203
      break;
 
204
    case SQLCOM_SELECT:
 
205
      user_commands->incrementSelectCount();
 
206
      break;
 
207
    default:
 
208
      return;
 
209
  }
 
210
}
 
211
 
 
212
bool LoggingStats::post(Session *session)
 
213
{
 
214
  if (! isEnabled() || (session->getSessionId() == 0))
 
215
  {
 
216
    return false;
 
217
  }
 
218
 
 
219
  /* exit early if we are not logging this type of command */
 
220
  if (isBeingLogged(session) == false)
 
221
  {
 
222
    return false;
 
223
  }
 
224
 
 
225
  ScoreboardSlot *scoreboard_slot= current_scoreboard->findScoreboardSlotToLog(session);
 
226
 
 
227
  /* Its possible that the scoreboard is full with active sessions in which case 
 
228
     this could be null */
 
229
  if (scoreboard_slot)
 
230
  {
 
231
    updateCurrentScoreboard(scoreboard_slot, session);
 
232
  }
 
233
  return false;
 
234
}
 
235
 
 
236
bool LoggingStats::postEnd(Session *session)
 
237
{
 
238
  if (! isEnabled() || (session->getSessionId() == 0))
 
239
  {
 
240
    return false;
 
241
  }
 
242
 
 
243
  ScoreboardSlot *scoreboard_slot= current_scoreboard->findAndResetScoreboardSlot(session);
 
244
 
 
245
  if (scoreboard_slot)
 
246
  {
 
247
    vector<ScoreboardSlot *>::iterator cumulative_it= cumulative_stats_by_user_vector->begin();
 
248
    bool found= false;
 
249
 
 
250
    /* Search if this is a pre-existing user */
 
251
    for (uint32_t h= 0; h < cumulative_stats_by_user_index; ++h)
 
252
    {
 
253
      ScoreboardSlot *cumulative_scoreboard_slot= *cumulative_it;
 
254
      string user= cumulative_scoreboard_slot->getUser();
 
255
      if (user.compare(scoreboard_slot->getUser()) == 0)
 
256
      {
 
257
        found= true;
 
258
        cumulative_scoreboard_slot->merge(scoreboard_slot);
 
259
        break;
 
260
      }
 
261
      ++cumulative_it;
 
262
    }
 
263
 
 
264
    /* this will add a new user */
 
265
    if (! found)
 
266
    {
 
267
      updateCumulativeStatsByUserVector(scoreboard_slot);
 
268
    }
 
269
    delete scoreboard_slot;
 
270
  }
 
271
 
 
272
  return false;
 
273
}
 
274
 
 
275
void LoggingStats::updateCumulativeStatsByUserVector(ScoreboardSlot *current_scoreboard_slot)
 
276
{
 
277
  /* Check twice if the user table is full, do not grab a lock each time */
 
278
  if (cumulative_stats_by_user_max > cumulative_stats_by_user_index)
 
279
  {
 
280
    pthread_rwlock_wrlock(&LOCK_cumulative_scoreboard_index);
 
281
 
 
282
    if (cumulative_stats_by_user_max > cumulative_stats_by_user_index)
 
283
    { 
 
284
      ScoreboardSlot *cumulative_scoreboard_slot=
 
285
        cumulative_stats_by_user_vector->at(cumulative_stats_by_user_index);
 
286
      string cumulative_scoreboard_user(current_scoreboard_slot->getUser());
 
287
      cumulative_scoreboard_slot->setUser(cumulative_scoreboard_user);
 
288
      cumulative_scoreboard_slot->merge(current_scoreboard_slot);
 
289
      ++cumulative_stats_by_user_index;
 
290
    }
 
291
 
 
292
    pthread_rwlock_unlock(&LOCK_cumulative_scoreboard_index);
 
293
  }
 
294
}
 
295
 
 
296
 
 
297
/* Plugin initialization and system variables */
 
298
 
 
299
static LoggingStats *logging_stats= NULL;
 
300
 
 
301
static CurrentCommandsTool *current_commands_tool= NULL;
 
302
 
 
303
static CumulativeCommandsTool *cumulative_commands_tool= NULL;
 
304
 
 
305
static void enable(Session *,
 
306
                   drizzle_sys_var *,
 
307
                   void *var_ptr,
 
308
                   const void *save)
 
309
{
 
310
  if (logging_stats)
 
311
  {
 
312
    if (*(bool *)save != false)
 
313
    {
 
314
      logging_stats->enable();
 
315
      *(bool *) var_ptr= (bool) true;
 
316
    }
 
317
    else
 
318
    {
 
319
      logging_stats->disable();
 
320
      *(bool *) var_ptr= (bool) false;
 
321
    }
 
322
  }
 
323
}
 
324
 
 
325
static bool initTable()
 
326
{
 
327
  current_commands_tool= new(nothrow)CurrentCommandsTool(logging_stats);
 
328
 
 
329
  if (! current_commands_tool)
 
330
  {
 
331
    return true;
 
332
  }
 
333
 
 
334
  cumulative_commands_tool= new(nothrow)CumulativeCommandsTool(logging_stats);
 
335
 
 
336
  if (! cumulative_commands_tool)
 
337
  {
 
338
    return true;
 
339
  }
 
340
 
 
341
  return false;
 
342
}
 
343
 
 
344
static int init(Context &context)
 
345
{
 
346
  logging_stats= new LoggingStats("logging_stats");
 
347
 
 
348
  if (initTable())
 
349
  {
 
350
    return 1;
 
351
  }
 
352
 
 
353
  context.add(logging_stats);
 
354
  context.add(current_commands_tool);
 
355
  context.add(cumulative_commands_tool);
 
356
 
 
357
  if (sysvar_logging_stats_enabled)
 
358
  {
 
359
    logging_stats->enable();
 
360
  }
 
361
 
 
362
  return 0;
 
363
}
 
364
 
 
365
static DRIZZLE_SYSVAR_UINT(max_user_count,
 
366
                           sysvar_logging_stats_max_user_count,
 
367
                           PLUGIN_VAR_RQCMDARG,
 
368
                           N_("Max number of users that will be logged"),
 
369
                           NULL, /* check func */
 
370
                           NULL, /* update func */
 
371
                           10000, /* default */
 
372
                           500, /* minimum */
 
373
                           50000,
 
374
                           0);
 
375
 
 
376
static DRIZZLE_SYSVAR_UINT(bucket_count,
 
377
                           sysvar_logging_stats_bucket_count,
 
378
                           PLUGIN_VAR_RQCMDARG,
 
379
                           N_("Max number of vector buckets to construct for logging"),
 
380
                           NULL, /* check func */
 
381
                           NULL, /* update func */
 
382
                           10, /* default */
 
383
                           5, /* minimum */
 
384
                           100,
 
385
                           0);
 
386
 
 
387
static DRIZZLE_SYSVAR_UINT(scoreboard_size,
 
388
                           sysvar_logging_stats_scoreboard_size,
 
389
                           PLUGIN_VAR_RQCMDARG,
 
390
                           N_("Max number of concurrent sessions that will be logged"),
 
391
                           NULL, /* check func */
 
392
                           NULL, /* update func */
 
393
                           2000, /* default */
 
394
                           10, /* minimum */
 
395
                           50000, 
 
396
                           0);
 
397
 
 
398
static DRIZZLE_SYSVAR_BOOL(enable,
 
399
                           sysvar_logging_stats_enabled,
 
400
                           PLUGIN_VAR_NOCMDARG,
 
401
                           N_("Enable Logging Statistics Collection"),
 
402
                           NULL, /* check func */
 
403
                           enable, /* update func */
 
404
                           false /* default */);
 
405
 
 
406
static drizzle_sys_var* system_var[]= {
 
407
  DRIZZLE_SYSVAR(max_user_count),
 
408
  DRIZZLE_SYSVAR(bucket_count),
 
409
  DRIZZLE_SYSVAR(scoreboard_size),
 
410
  DRIZZLE_SYSVAR(enable),
 
411
  NULL
 
412
};
 
413
 
 
414
DRIZZLE_DECLARE_PLUGIN
 
415
{
 
416
  DRIZZLE_VERSION_ID,
 
417
  "logging_stats",
 
418
  "0.1",
 
419
  "Joseph Daly",
 
420
  N_("User Statistics as DATA_DICTIONARY tables"),
 
421
  PLUGIN_LICENSE_BSD,
 
422
  init,   /* Plugin Init      */
 
423
  system_var, /* system variables */
 
424
  NULL    /* config options   */
 
425
}
 
426
DRIZZLE_DECLARE_PLUGIN_END;