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;
}

?>