You are here

db_maintenance.module in DB Maintenance 6

Optimizes database tables during cron runs.

@author David Kent Norman @link http://deekayen.net/

@todo

  • some sort of backup dump saver/emailer/ftp/etc

File

db_maintenance.module
View source
<?php

/**
 * @file
 * Optimizes database tables during cron runs.
 *
 * @author David Kent Norman
 * @link http://deekayen.net/
 *
 * @todo
 * - some sort of backup dump saver/emailer/ftp/etc
 */

/**
 * Implementation of hook_help().
 *
 * @param $section string
 * @return string
 */
function db_maintenance_help($path, $arg) {
  switch ($path) {
    case 'admin/help#db_maintenance':
      return t('<p>DB maintenance performs an optimization query on selected tables.</p>
        <p>For MyISAM tables,
        OPTIMIZE TABLE repairs a table if it has deleted or split rows, sorts table indexes,
        and updates table statistics. For BDB and InnoDB, OPTIMIZE rebuilds the table. Note, MySQL
        locks tables during the time OPTIMIZE TABLE is running. OPTIMIZE works best on tables with
        large deletions (e.g. cache or watchdog), however MySQL will reuse old record positions,
        therefore in most setups, OPTIMIZE TABLE is unnecessary unless you just like defragmenting.</p>
        <p>The Overhead column in phpMyAdmin\'s database view is the most common way to determine the
        need of an OPTIMIZE TABLE query. It essentially shows the amount of disk space you would
        recover by running an optimize/defragmentation query.</p>
        <p>For PostgreSQL tables, VACUUM reclaims storage occupied by deleted tuples.
        In normal PostgreSQL operation, tuples that are deleted or obsoleted by an update are not
        physically removed from their table; they remain present until a VACUUM is done. Therefore
        it\'s necessary to do VACUUM periodically, especially on frequently-updated tables.</p>');
    case 'admin/settings/db_maintenance':
      return t('Executes an optimization query on database tables during cron runs.');
  }
}

/**
 * Implementation of hook_menu().
 *
 * @return array
 */
function db_maintenance_menu() {
  $items = array();
  $items['admin/settings/db_maintenance'] = array(
    'title' => 'DB maintenance',
    'description' => 'Executes a cron-based query to optimize database tables.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'db_maintenance_admin_settings',
    ),
    'access callback' => 'user_access',
    'access arguments' => array(
      'administer site configuration',
    ),
    'type' => MENU_NORMAL_ITEM,
  );
  $items['db_maintenance/optimize'] = array(
    'page callback' => 'db_maintenance_optimize_tables_page',
    'access callback' => 'user_access',
    'access arguments' => array(
      'administer site configuration',
    ),
    'type' => MENU_CALLBACK,
  );
  return $items;
}

/**
 * Callback page for manually optimizing tables.
 *
 */
function db_maintenance_optimize_tables_page() {
  db_maintenance_optimize_tables();
  drupal_set_message(t('Database tables optimized'));
  drupal_goto('admin/settings/db_maintenance');
}

/**
 * Get a list of all the tables in a database.
 *
 * @param string $db The name of the database connection to query for tables.
 * @return array representing the tables in the specified database.
 */
function _db_maintenance_list_tables($db) {
  $table_names = array();

  // Set the database to query.
  $previous = db_set_active($db);
  if (_db_maintenance_determine_software() == 'mysql') {
    $result = db_query('SHOW TABLES');
  }
  elseif (_db_maintenance_determine_software() == 'pgsql') {
    $result = db_query("SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' ORDER BY table_name");
  }

  // Return to the previously set database.
  db_set_active($previous);
  while ($table_name = db_fetch_array($result)) {
    $table_name = current($table_name);
    $table_names[$table_name] = $table_name;
  }
  return $table_names;
}

/**
 * Implementation of hook_cron().
 *
 */
function db_maintenance_cron() {
  $last_run = variable_get('db_maintenance_cron_last', 0);
  $interval = time() - variable_get('db_maintenance_cron_frequency', 86400);

  // Only run cron if enough time has elapsed.
  if ($interval > $last_run) {
    db_maintenance_optimize_tables();
  }
}

/**
 * Perform the maintenance.
 *
 */
function db_maintenance_optimize_tables() {
  global $db_url;
  global $db_prefix;
  if (is_array($db_prefix)) {
    $db_prefix = $db_prefix['default'];
  }
  $is_prefixed = strlen($db_prefix) > 0;

  // Set the databases array if not already set in $db_url.
  if (is_array($db_url)) {
    $databases = $db_url;
  }
  else {
    $databases['default'] = $db_url;
  }

  // Loop through each database optimizing any selected tables.
  foreach ($databases as $db => $connection) {
    $config_tables = variable_get('db_maintenance_table_list_' . $db, NULL);

    // Only proceed if tables are selected for this database.
    if (is_array($config_tables) && sizeof($config_tables) > 0) {
      $db_name = $db == 'default' ? 'Drupal' : $db;
      while (list(, $table_name) = each($config_tables)) {

        // Set the database to query.
        $previous = db_set_active($db);
        $table_check_name = $is_prefixed ? str_replace($db_prefix, '', $table_name) : $table_name;
        if (db_table_exists($table_check_name)) {
          if (_db_maintenance_determine_software() == 'mysql') {
            db_query('OPTIMIZE TABLE %s', $table_name);
          }
          elseif (_db_maintenance_determine_software() == 'pgsql') {
            db_query('VACUUM ANALYZE %s', $table_name);
          }
        }
        else {
          watchdog('db_maintenance', '@table table in @db database was configured to be optimized but does not exist.', array(
            '@db' => $db_name,
            '@table' => $table_name,
          ), WATCHDOG_NOTICE);
        }

        // Return to the previously set database.
        db_set_active($previous);
        if (variable_get('db_maintenance_log', 0)) {
          watchdog('db_maintenance', 'Optimized @table table in @db database.', array(
            '@db' => $db_name,
            '@table' => $table_name,
          ), WATCHDOG_DEBUG);
        }
      }
      if (variable_get('db_maintenance_log', 0)) {
        $tables = implode(', ', $config_tables);
        watchdog('db_maintenance', 'Optimized tables in @db database: @tables', array(
          '@db' => $db_name,
          '@tables' => $tables,
        ), WATCHDOG_INFO);
      }
    }
  }
  variable_set('db_maintenance_cron_last', time());
}

/**
 * Administration settings
 *
 * options: log each optimization
 *          multi-select list of tables to optimize
 *
 * @return array
 */
function db_maintenance_admin_settings() {
  global $db_url;
  $form = array();
  $form['db_maintenance_log'] = array(
    '#type' => 'checkbox',
    '#title' => 'Log OPTIMIZE queries',
    '#default_value' => variable_get('db_maintenance_log', 0),
    '#description' => t('If enabled, a watchdog entry will be made each time tables are optimized, containing information which tables were involved.'),
  );
  $options = array(
    0 => t('Run during every cron'),
    3600 => t('Hourly'),
    7200 => t('Bi-Hourly'),
    86400 => t('Daily'),
    172800 => t('Bi-Daily'),
    604800 => t('Weekly'),
    1209600 => t('Bi-Weekly'),
    2592000 => t('Monthly'),
    5184000 => t('Bi-Monthly'),
  );
  $form['db_maintenance_cron_frequency'] = array(
    '#type' => 'select',
    '#title' => t('Optimize tables'),
    '#options' => $options,
    '#default_value' => variable_get('db_maintenance_cron_frequency', 86400),
    '#description' => t('Select how often database tables should be optimized.') . ' ' . l(t('Optimize now.'), 'db_maintenance/optimize'),
  );

  // Set the databases array if not already set in $db_url.
  if (is_array($db_url)) {
    $databases = $db_url;
  }
  else {
    $databases['default'] = $db_url;
  }
  $options = array();

  // Loop through each database and list the possible tables to optimize.
  foreach ($databases as $db => $connection) {
    $options = _db_maintenance_list_tables($db);
    $form['db_maintenance_table_list_' . $db] = array(
      '#type' => 'select',
      '#title' => t('Tables in the !db database', array(
        '!db' => $db == 'default' ? 'Drupal' : $db,
      )),
      '#options' => $options,
      '#default_value' => variable_get('db_maintenance_table_list_' . $db, ''),
      '#description' => t('Selected tables will be optimized during cron runs.'),
      '#multiple' => TRUE,
      '#attributes' => array(
        'size' => count($options),
      ),
    );
  }
  return system_settings_form($form);
}

/**
 * Determine which database software is in use
 */
function _db_maintenance_determine_software() {
  global $db_type;
  static $db_engine;
  if (!empty($db_engine)) {
    return $db_engine;
  }
  elseif ($db_type == 'mysql' || $db_type == 'mysqli') {
    $db_engine = 'mysql';
    return $db_engine;
  }
  elseif ($db_type == 'pgsql') {
    $db_engine = 'pgsql';
    return $db_engine;
  }
  else {
    return FALSE;
  }
}

Functions

Namesort descending Description
db_maintenance_admin_settings Administration settings
db_maintenance_cron Implementation of hook_cron().
db_maintenance_help Implementation of hook_help().
db_maintenance_menu Implementation of hook_menu().
db_maintenance_optimize_tables Perform the maintenance.
db_maintenance_optimize_tables_page Callback page for manually optimizing tables.
_db_maintenance_determine_software Determine which database software is in use
_db_maintenance_list_tables Get a list of all the tables in a database.