throttle.module 6.82 KB
Newer Older
Dries's avatar
   
Dries committed
1
<?php
Dries's avatar
   
Dries committed
2
// $Id$
Dries's avatar
   
Dries committed
3

Dries's avatar
   
Dries committed
4
5
6
7
8
/**
 * @file
 * Allows configuration of congestion control auto-throttle mechanism.
 */

Dries's avatar
Dries committed
9
10
11
12
13
/**
 * Determine the current load on the site.
 *
 * Call the throttle_status() function from your own modules, themes, blocks,
 * etc. to determine the current throttle status. For example, in your theme
Dries's avatar
   
Dries committed
14
15
16
 * you might choose to disable pictures when your site is too busy (reducing
 * bandwidth), or in your modules you might choose to disable some complicated
 * logic when your site is too busy (reducing CPU utilization).
Dries's avatar
Dries committed
17
18
 *
 * @return
19
20
21
 *   0 or 1.  0 means that the throttle is currently disabled.  1 means that
 *   the throttle is currently enabled.  When the throttle is enabled, CPU
 *   and bandwidth intensive functionality should be disabled.
Dries's avatar
   
Dries committed
22
23
 */
function throttle_status() {
Dries's avatar
   
Dries committed
24
  return variable_get('throttle_level', 0);
Dries's avatar
   
Dries committed
25
26
}

Dries's avatar
Dries committed
27
28
29
30
31
/**
 * Implementation of hook_exit().
 *
 * Changes the current throttle level based on page hits.
 */
Dries's avatar
   
Dries committed
32
function throttle_exit() {
Dries's avatar
Dries committed
33
34
35
  // The following logic determines what the current throttle level should
  //  be, and can be disabled by the admin.  If enabled, the rand() function
  //  returns a number between 0 and N, N being specified by the admin. If
36
37
  //  0 is returned, the throttle logic is run, adding two additional database
  //  queries.  Otherwise, the following logic is skipped.  This mechanism is
Dries's avatar
Dries committed
38
39
40
41
42
43
44
45
  //  referred to in the admin page as the 'probability limiter', roughly
  //  limiting throttle related database calls to 1 in N.
  if (!rand(0, variable_get('throttle_probability_limiter', 9))) {
    // Note:  The rand() function is supported by PHP 3+.  However, prior to
    // PHP 4.2.0 it needs to be seeded with a call to srand().  It is important
    // that this only happens once, so this should be managed by the Drupal
    // engine, not this module.  The Drupal engine should use phpversion() to
    // detect and automatically seed pre-4.2.0 systems.
Dries's avatar
   
Dries committed
46

47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
    // Count users with activity in the past n seconds, defined in user module
    $time_period = variable_get('user_block_seconds_online', 2700);

    $throttle = module_invoke('throttle', 'status');

    if ($max_guests = variable_get('throttle_anonymous', 0)) {
      $guests = db_result(db_query('SELECT COUNT(sid) AS count FROM {sessions} WHERE timestamp >= %d AND uid = 0', time() - $time_period));
    }
    else {
      $guests = 0;
    }
    if ($max_users = variable_get('throttle_user', 0)) {
      $users = db_result(db_query('SELECT COUNT(DISTINCT(uid)) AS count FROM {sessions} WHERE timestamp >= %d AND uid != 0 GROUP BY uid ORDER BY timestamp DESC', time() - $time_period));
    }
    else {
      $users = 0;
    }

    // update the throttle status
66
    if ($max_users && $users > $max_users) {
67
68
      if (!$throttle) {
        variable_set('throttle_level', 1);
69
        cache_clear_all();
70
        watchdog('throttle', t('Throttle: %users %user accessing site; throttle enabled.', array('%users' => "<em>$users</em>", '%user' => format_plural($users, 'user', 'users'))));
71
72
      }
    }
73
    elseif ($max_guests && $guests > $max_guests) {
74
75
      if (!$throttle) {
        variable_set('throttle_level', 1);
76
        cache_clear_all();
77
        watchdog('throttle', t('Throttle: %guests %guest accessing site; throttle enabled.', array('%guests' => "<em>$guests</em>", '%guest' => format_plural($guests, 'guest', 'guests'))));
78
79
80
81
82
      }
    }
    else {
      if ($throttle) {
        variable_set('throttle_level', 0);
83
        cache_clear_all();
84
        watchdog('throttle', t('Throttle: %users %user, %guests %guest accessing site; throttle disabled.', array('%users' => "<em>$users</em>", '%user' => format_plural($users, 'user', 'users'), '%guests' => "<em>$guests</em>", '%guest' => format_plural($guests, 'guest', 'guests'))));
85
86
      }
    }
Dries's avatar
   
Dries committed
87
88
89
  }
}

90
91
92
93
94
95
96
97
function _throttle_validate($value, $form) {
  if ($value != NULL) {
    if (!is_numeric($value) || $value < 0) {
      form_set_error($form, t("'%value' is not a valid auto-throttle setting.  Please enter a positive numeric value.", array('%value' => $value)));
    }
  }
}

Dries's avatar
Dries committed
98
99
100
101
/**
 * Implementation of hook_help().
 */
function throttle_help($section) {
Dries's avatar
   
Dries committed
102
  switch ($section) {
Dries's avatar
   
Dries committed
103
    case 'admin/modules#description':
104
      return t('Handles the auto-throttling mechanism, to control site congestion.');
105
    case 'admin/settings/throttle':
Dries's avatar
Dries committed
106
      return t('If your site gets linked to by a popular website, or otherwise comes under a "Denial of Service" (DoS) attack, your webserver might become overwhelmed.  This module provides a congestion control throttling mechanism for automatically detecting a surge in incoming traffic.  This mechanism is utilized by other Drupal modules to automatically optimize their performance by temporarily disabling CPU-intensive functionality.');
Dries's avatar
   
Dries committed
107
  }
Dries's avatar
   
Dries committed
108
109
}

Dries's avatar
Dries committed
110
111
112
/**
 * Implementation of hook_settings().
 */
113
function throttle_settings() {
Dries's avatar
Dries committed
114
  // Tune auto-throttle.
115
116
117
118
  _throttle_validate(variable_get('throttle_anonymous', ''), 'throttle_anonymous');
  _throttle_validate(variable_get('throttle_user', ''), 'throttle_user');
  $group = form_textfield(t('Auto-throttle on anonymous users'), 'throttle_anonymous', variable_get('throttle_anonymous', ''), 5, 6, t('The congestion control throttle can be automatically enabled when the number of anonymous users currently visiting your site exceeds the specified threshold.  Leave this value blank or set to "0" if you do not wish to auto-throttle on anonymous users.  You can inspect the current number of anonymous users using the "Who\'s online" block.'));
  $group .= form_textfield(t('Auto-throttle on authenticated users'), 'throttle_user', variable_get('throttle_user', ''), 5, 6, t('The congestion control throttle can be automatically enabled when the number of authenticated users currently visiting your site exceeds the specified threshold.  Leave this value blank or set to "0" if you do not wish to auto-throttle on authenticated users.  You can inspect the current number of authenticated users using the "Who\'s online" block.'));
Dries's avatar
Dries committed
119
  $probabilities = array(0 => '100%', 1 => '50%', 2 => '33.3%', 3 => '25%', 4 => '20%', 5 => '16.6%', 7 => '12.5%', 9 => '10%', 19 => '5%', 99 => '1%', 199 => '.5%', 399 => '.25%', 989 => '.1%');
120
  $group .= form_select(t('Auto-throttle probability limiter'), 'throttle_probability_limiter', variable_get('throttle_probability_limiter', 9), $probabilities, t('The auto-throttle probability limiter is an efficiency mechanism to statistically reduce the overhead of the auto-throttle.  The limiter is expressed as a percentage of page views, so for example if set to the default of 10% we only perform the extra database queries to update the throttle status 1 out of every 10 page views.  The busier your site, the lower you should set the limiter value.'));
Dries's avatar
Dries committed
121
122
  $period = drupal_map_assoc(array(1800, 3600, 7200, 10800, 14400, 18000, 21600, 43200, 64800, 86400, 172800, 259200, 604800), 'format_interval');
  $output .= form_group(t('Auto-throttle tuning'), $group);
Dries's avatar
   
Dries committed
123

Dries's avatar
   
Dries committed
124
125
126
127
  return $output;
}

?>