throttle.module 6.56 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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
    // 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
    if ($users > $max_users) {
      if (!$throttle) {
        variable_set('throttle_level', 1);
        watchdog('special', t('Throttle: %users %user accessing site; throttle enabled.', array('%users' => "<em>$users</em>", '%user' => format_plural($users, 'user', 'users'))));
      }
    }
    elseif ($guests >= $max_guests) {
      if (!$throttle) {
        variable_set('throttle_level', 1);
        watchdog('special', t('Throttle: %guests %guest accessing site; throttle enabled.', array('%guests' => "<em>$guests</em>", '%guest' => format_plural($guests, 'guest', 'guests'))));
      }
    }
    else {
      if ($throttle) {
        variable_set('throttle_level', 0);
        watchdog('special', 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'))));
      }
    }
Dries's avatar
   
Dries committed
84
85
86
  }
}

Dries's avatar
Dries committed
87
88
89
/**
 * Implementation of hook_perm().
 */
Dries's avatar
   
Dries committed
90
function throttle_perm() {
Dries's avatar
Dries committed
91
  return array('access throttle block');
Dries's avatar
   
Dries committed
92
93
}

Dries's avatar
Dries committed
94
95
96
97
/**
 * Implementation of hook_help().
 */
function throttle_help($section) {
Dries's avatar
   
Dries committed
98
  switch ($section) {
Dries's avatar
   
Dries committed
99
    case 'admin/modules#description':
Dries's avatar
Dries committed
100
      return t('Allows configuration of congestion control auto-throttle mechanism.');
101
    case 'admin/settings/throttle':
102
      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 mechanism for automatically detecting a surge in incoming traffic.  This mechanism is utilized by other Drupal models to automatically optimize their performance by temporarily disabling CPU-intensive functionality.  To use the auto-throttle, the statistics module\'s <a href="%access">access log</a> must be enabled.', array('%access' => url('admin/settings/statistics')));
Dries's avatar
   
Dries committed
103
  }
Dries's avatar
   
Dries committed
104
105
}

Dries's avatar
Dries committed
106
107
108
/**
 * Implementation of hook_settings().
 */
109
function throttle_settings() {
Dries's avatar
Dries committed
110
  // Tune auto-throttle.
111
112
113
  $numbers = array(0 => t('disabled')) + drupal_map_assoc(array(25, 50, 75, 100, 125, 150, 175, 200, 250, 300, 350, 400, 450, 500, 550, 600, 650, 700, 750, 800, 850, 900, 950, 1000, 1500, 2000, 2500, 3000, 3500, 4000, 4500, 5000));
  $group = form_select(t('Auto-throttle on anonymous users'), 'throttle_anonymous', variable_get('throttle_anonymous', 0), $numbers, t('The congestion control throttle can be automatically enabled when the selected number of anonymous users currently visiting your site exceeds the specified threshold.  You can inspect the number of anonymous users using the "Who\'s online" block.'));
  $group .= form_select(t('Auto-throttle on authenticated users'), 'throttle_user', variable_get('throttle_user', 0), $numbers, t('The congestion control throttle can be automatically enabled when the selected number of authenticated users currently visiting your site exceeds the specified threshold.  You can inspect the number of authenticated users using the "Who\'s online" block.'));
Dries's avatar
Dries committed
114
  $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%');
115
  $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
116
117
  $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
118

Dries's avatar
   
Dries committed
119
120
121
122
  return $output;
}

?>