You are here

apdqc.module in Asynchronous Prefetch Database Query Cache 7

Asynchronous Prefetch Database Query Cache module.

File

apdqc.module
View source
<?php

/**
 * @file
 * Asynchronous Prefetch Database Query Cache module.
 */

/**
 * Should this code try to prefetch cids before they are needed.
 */
define('APDQC_PREFETCH', TRUE);

/**
 * How long to wait until apdqc cron will run again. Default is 24 hours.
 */
define('APDQC_CRON_FREQUENCY', 86400);

/**
 * Should we use async database connection to write to watchdog table.
 */
define('APDQC_DBLOG_WATCHDOG', TRUE);

/**
 * Last time cron was ran.
 */
define('APDQC_CRON_TIMESTAMP', 0);

/**
 * Output a lot of extra info in the devel output.
 */
if (!defined('APDQC_VERBOSE_DEVEL_OUTPUT')) {
  define('APDQC_VERBOSE_DEVEL_OUTPUT', TRUE);
}

/**
 * TRUE if APDQC altered the semaphore table.
 */
define('APDQC_SEMAPHORE_MEMORY', FALSE);

/**
 * TRUE if APDQC altered the cache table collations.
 */
define('APDQC_TABLE_COLLATIONS', FALSE);

/**
 * TRUE if APDQC altered the cache table indexes.
 */
define('APDQC_TABLE_INDEXES', FALSE);

/**
 * Set to TRUE to ignore the MySQL version checking on status report page.
 */
define('APDQC_INSTALL_IGNORE_MYSQL_VERSION', FALSE);

/**
 * Set to TRUE to prefetch all views unpack cached items.
 */
define('APDQC_PREFETCH_VIEWS_UNPACK', FALSE);

/**
 * TRUE if APDQC altered the cache table engine to be innodb.
 */
define('APDQC_INNODB', FALSE);

/**
 * OFF if APDQC checked and innodb_file_per_table was off at that time.
 */
define('APDQC_INNODB_FILE_PER_TABLE', FALSE);

/**
 * TRUE if APDQC altered the sessions table schema.
 */
define('APDQC_SESSIONS_SCHEMA', FALSE);

/**
 * TRUE if APDQC altered the semaphore table schema.
 */
define('APDQC_SEMAPHORE_SCHEMA', FALSE);

/**
 * TRUE if APDQC will skip NULL entity loads.
 */
define('APDQC_ENTITY_LOAD_SKIP_NULL', FALSE);

/**
 * Implements hook_menu().
 */
function apdqc_menu() {
  $file_path = drupal_get_path('module', 'apdqc');
  $items['admin/config/development/performance/default'] = array(
    'title' => 'Performance',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'file path' => drupal_get_path('module', 'system'),
    'weight' => -10,
  );
  $items['admin/config/development/performance/apdqc'] = array(
    'title' => 'APDQC',
    'description' => 'Configuration for Asynchronous Prefetch Database Query Cache.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'apdqc_admin_settings_form',
    ),
    'type' => MENU_LOCAL_TASK,
    'access arguments' => array(
      'administer site configuration',
    ),
    'file path' => $file_path,
    'file' => 'apdqc.admin.inc',
    'weight' => 1,
  );
  $items['admin/config/development/performance/apdqc/default'] = array(
    'title' => 'Configuration',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -10,
  );
  $items['admin/config/development/performance/apdqc/operations'] = array(
    'title' => 'Operations',
    'description' => 'Convert table types.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'apdqc_admin_operations_form',
    ),
    'type' => MENU_LOCAL_TASK,
    'access arguments' => array(
      'administer site configuration',
    ),
    'file path' => $file_path,
    'file' => 'apdqc.admin.inc',
    'weight' => 20,
  );
  return $items;
}

/**
 * Implements hook_modules_installed().
 */
function apdqc_modules_installed($modules) {
  $collation = variable_get('apdqc_table_collations', APDQC_TABLE_COLLATIONS);
  $innodb = variable_get('apdqc_innodb', APDQC_INNODB);
  $table_indexes = variable_get('apdqc_table_indexes', APDQC_TABLE_INDEXES);
  $sessions_schema = variable_get('apdqc_sessions_schema', APDQC_SESSIONS_SCHEMA);
  if (empty($collation) && !empty($innodb) && !empty($table_indexes)) {

    // Cache tables have not been altered.
    return;
  }
  if ($collation === TRUE) {
    $collation = 'utf8_bin';
  }
  $ascii = array(
    'binary' => TRUE,
    'collation' => 'ascii_bin',
    'charset' => 'ascii',
    'mysql_character_set' => 'ascii',
  );

  // Ensure we have the admin functions.
  module_load_include('admin.inc', 'apdqc');

  // Check for cache tables in the recently enabled modules.
  foreach ($modules as $module) {
    $schema = drupal_get_schema_unprocessed($module);
    if (empty($schema)) {
      continue;
    }
    _drupal_schema_initialize($schema, $module, FALSE);
    foreach ($schema as $table_name => &$values) {
      if ($sessions_schema) {
        $changed = FALSE;
        foreach ($values['fields'] as $column => &$attributes) {

          // Convert other session schemas to match the new session schema.
          if (($column === 'sid' || $column === 'ssid') && !empty($attributes['length']) && $attributes['length'] >= 43 && stripos($attributes['type'], 'char') !== FALSE) {
            $attributes['length'] = 43;
            $attributes['type'] = 'char';
            $attributes += $ascii;
            $changed = TRUE;
          }
        }
        if ($changed) {
          apdqc_admin_sessions_table_duplicates(TRUE, FALSE, array(
            $table_name => $values,
          ));
        }
      }
      if (strpos($table_name, 'cache') !== 0) {

        // Remove if not a cache* table.
        unset($schema[$table_name]);
        continue;
      }
      if (empty($schema[$table_name]['fields']['cid'])) {

        // Remove if no cid field.
        unset($schema[$table_name]);
        continue;
      }
    }
    if (empty($schema)) {

      // Skip if this module doesn't have any cache tables.
      continue;
    }
    $schema_keys = array_keys($schema);
    if (!empty($innodb)) {
      apdqc_admin_change_table_engine(TRUE, $schema_keys);
    }
    if (!empty($collation)) {
      apdqc_admin_change_table_collation(TRUE, $collation, $schema);
    }
    if (!empty($table_indexes)) {

      // Drop the expire index; use expire_created.
      $before = array(
        'expire',
      );
      $after = array(
        'expire',
        'created',
      );
      apdqc_convert_cache_index($before, $after, $schema_keys);
    }
  }
}

/**
 * Implements hook_cron().
 *
 * Empties out the __truncated_tables on cron.
 */
function apdqc_cron() {

  // Execute once a day (24 hours).
  if (variable_get('apdqc_cron_timestamp', APDQC_CRON_TIMESTAMP) > REQUEST_TIME - variable_get('apdqc_cron_frequency', APDQC_CRON_FREQUENCY)) {
    return;
  }
  variable_set('apdqc_cron_timestamp', REQUEST_TIME);

  // Get all cache tables.
  module_load_include('admin.inc', 'apdqc');
  if (!function_exists('apdqc_get_db_object')) {
    module_load_include('mysql.inc', 'apdqc');
  }
  $tables = apdqc_get_cache_tables();
  foreach ($tables as $table) {
    $mysqli = apdqc_get_db_object(array(
      $table,
    ), array(
      '*',
    ));

    // Skip tables if it doesn't end in __truncated_table.
    if (strpos(strrev($table), strrev('__truncated_table')) === FALSE) {
      continue;
    }

    // Get the real table name.
    $real_table_name_truncated = Database::getConnection()
      ->prefixTables("{" . db_escape_table($table) . "}");

    // Remove any values from the *__truncated_table if needed.
    $results = $mysqli
      ->query("SELECT 1 FROM {$real_table_name_truncated} LIMIT 1");

    // mysqli_result::fetch_row returns NULL when there are no more rows.
    if ($results !== FALSE && !is_null($results
      ->fetch_row())) {

      // Empty the truncated_table since it is not empty.
      apdqc_query(array(
        $real_table_name_truncated,
      ), array(
        '*',
      ), "TRUNCATE {$real_table_name_truncated}");
    }
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Give more detail on cache-related variables and expose
 * cache_garbage_collection_frequency variable provided by this module.
 */
function apdqc_form_system_performance_settings_alter(&$form, &$form_state) {
  module_load_include('admin.inc', 'apdqc');
  apdqc_admin_system_performance_settings_form($form, $form_state);
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Exposes additional devel settings for tracking prefetched items.
 */
function apdqc_form_devel_admin_settings_alter(&$form, &$form_state) {
  module_load_include('admin.inc', 'apdqc');
  apdqc_admin_devel_admin_settings_form($form, $form_state);
}

/**
 * Implements hook_module_implements_alter().
 */
function apdqc_module_implements_alter(&$implementations, $hook) {

  // Move apdqc to the top.
  if ($hook === 'theme_registry_alter' && array_key_exists('apdqc', $implementations)) {
    $item = array(
      'apdqc' => $implementations['apdqc'],
    );
    unset($implementations['apdqc']);
    $implementations = array_merge($item, $implementations);
  }

  // Put apdqc_before_entity_info_alter() at the top and
  // apdqc_after_entity_info_alter() at the end.
  if ($hook === 'entity_info_alter') {
    $item = array(
      'apdqc_before' => FALSE,
    );
    $implementations = array_merge($item, $implementations);
    $implementations['apdqc_after'] = FALSE;
  }

  // Remove dblog, add apdqc_dblog.
  if ($hook === 'watchdog' && isset($implementations['dblog'])) {
    unset($implementations['dblog']);
    $implementations['_apdqc_dblog'] = FALSE;
  }
}

/**
 * Implements hook_watchdog().
 *
 * Note: Some values may be truncated to meet database column size restrictions.
 */
function _apdqc_dblog_watchdog(array $log_entry) {

  // Pass through if apdqc_dblog_watchdog is disabled.
  // Or if apdqc_run_prefetch_array is not defined; apdqc.cache.inc not loaded.
  if (!variable_get('apdqc_dblog_watchdog', APDQC_DBLOG_WATCHDOG) || !function_exists('apdqc_run_prefetch_array') || !function_exists('apdqc_escape_string')) {
    if (function_exists('dblog_watchdog')) {
      return dblog_watchdog($log_entry);
    }
    return;
  }

  // Escape data.
  $field_data = array(
    'uid' => apdqc_escape_string($log_entry['uid']),
    'type' => apdqc_escape_string(substr($log_entry['type'], 0, 64)),
    'message' => apdqc_escape_string($log_entry['message']),
    'variables' => apdqc_escape_string(serialize($log_entry['variables'])),
    'severity' => apdqc_escape_string($log_entry['severity']),
    'link' => apdqc_escape_string(substr($log_entry['link'], 0, 255)),
    'location' => apdqc_escape_string($log_entry['request_uri']),
    'referer' => apdqc_escape_string($log_entry['referer']),
    'hostname' => apdqc_escape_string(substr($log_entry['ip'], 0, 128)),
    'timestamp' => apdqc_escape_string($log_entry['timestamp']),
  );

  // Get table name.
  $table = Database::getConnection()
    ->prefixTables("{" . db_escape_table('watchdog') . "}");

  // Build query.
  $query = "\n    INSERT INTO {$table} (\n      uid,\n      type,\n      message,\n      variables,\n      severity,\n      link,\n      location,\n      referer,\n      hostname,\n      timestamp\n    )\n    VALUES (\n      '{$field_data['uid']}',\n      '{$field_data['type']}',\n      '{$field_data['message']}',\n      '{$field_data['variables']}',\n      '{$field_data['severity']}',\n      '{$field_data['link']}',\n      '{$field_data['location']}',\n      '{$field_data['referer']}',\n      '{$field_data['hostname']}',\n      '{$field_data['timestamp']}'\n    )\n  ";

  // Insert info into watchdog table in a non blocking manner.
  apdqc_query(array(
    $table,
  ), array(
    microtime(TRUE),
  ), $query, array(
    'async' => TRUE,
  ));
}

/**
 * Implements hook_schema_alter().
 */
function apdqc_schema_alter(&$schema) {
  $collation = variable_get('apdqc_table_collations', APDQC_TABLE_COLLATIONS);
  $table_indexes = variable_get('apdqc_table_indexes', APDQC_TABLE_INDEXES);
  $ascii = array(
    'binary' => TRUE,
    'collation' => 'ascii_bin',
    'charset' => 'ascii',
    'mysql_character_set' => 'ascii',
  );
  $utf8 = array(
    'binary' => TRUE,
    'collation' => 'utf8_bin',
    'charset' => 'utf8',
    'mysql_character_set' => 'utf8',
  );
  foreach ($schema as $table_name => &$values) {
    if (strpos($table_name, 'semaphore') === 0) {
      $schema[$table_name]['primary key'] = array(
        'name',
        'value',
        'expire',
      );
      $schema[$table_name]['fields']['name'] += $ascii;
      if (function_exists('apdqc_lock_base85_encode')) {

        // Length of _apdqc_lock_id() is always 20 or less.
        $schema[$table_name]['fields']['value']['length'] = 20;
      }
      else {

        // Length of _lock_id() is always 33 or less.
        $schema[$table_name]['fields']['value']['length'] = 33;
      }
      $schema[$table_name]['fields']['value'] += $ascii;
      continue;
    }
    if (strpos($table_name, 'session') === 0) {

      // Length of drupal_random_key() is always 43.
      $schema[$table_name]['fields']['sid']['length'] = 43;
      $schema[$table_name]['fields']['sid']['type'] = 'char';
      $schema[$table_name]['fields']['sid'] += $ascii;
      $schema[$table_name]['fields']['ssid']['length'] = 43;
      $schema[$table_name]['fields']['ssid']['type'] = 'char';
      $schema[$table_name]['fields']['ssid'] += $ascii;

      // Max length of IPv6 tunneled is 45.
      $schema[$table_name]['fields']['hostname']['length'] = 45;
      $schema[$table_name]['fields']['hostname'] += $ascii;
      continue;
    }
    foreach ($values['fields'] as $column => &$attributes) {

      // Convert other session schemas to match the new session schema.
      if (($column === 'sid' || $column === 'ssid') && !empty($attributes['length']) && $attributes['length'] >= 43 && stripos($attributes['type'], 'char') !== FALSE) {
        $attributes['length'] = 43;
        $attributes['type'] = 'char';
        $attributes += $ascii;
      }
    }
    if (strpos($table_name, 'cache') !== 0) {

      // Skip if not a cache table.
      continue;
    }
    if (empty($schema[$table_name]['fields']['cid'])) {

      // Skip if not a cache table.
      continue;
    }
    if (!empty($collation)) {
      if (!empty($schema[$table_name]['fields']['cid'])) {

        // Force db collation to be *_bin for the cid of cache tables.
        $schema[$table_name]['fields']['cid']['binary'] = TRUE;
        if ($collation === 'ascii_bin') {
          $schema[$table_name]['fields']['cid'] += $ascii;
        }
        else {
          $schema[$table_name]['fields']['cid'] += $utf8;
        }
      }
    }
    if (!empty($table_indexes)) {
      if (!empty($schema[$table_name]['indexes']['expire'])) {

        // Create a expire_created index.
        $schema[$table_name]['indexes']['expire_created'] = array(
          'expire',
          'created',
        );
        unset($schema[$table_name]['indexes']['expire']);
      }
    }

    // Add cache*__truncated_table to the schema.
    if (strpos(strrev($table_name), strrev('__truncated_table')) !== 0 && db_table_exists($table_name . '__truncated_table')) {
      $schema[$table_name . '__truncated_table'] = $schema[$table_name];
    }
  }
}

/**
 * Implements hook_modules_uninstalled().
 */
function apdqc_modules_uninstalled($modules) {

  // Check for orphaned cache*__truncated_table's.
  $table_names = array_keys(db_query('SHOW TABLE STATUS')
    ->fetchAllAssoc('Name'));
  foreach ($table_names as $key => $table_name) {
    if (strpos($table_name, 'cache') !== 0) {

      // Remove if not a cache table.
      unset($table_names[$key]);
      continue;
    }
    if (strpos(strrev($table_name), strrev('__truncated_table')) !== 0) {

      // Skip if table name doesn't end in __truncated_table.
      continue;
    }
    $base_table = substr($table_name, 0, strpos($table_name, '__truncated_table'));
    if (in_array($base_table, $table_names)) {

      // Remove if __truncated_table has a base a cache table.
      unset($table_names[$key]);
      continue;
    }

    // Kill the orphaned *__truncated_table from the DB.
    if (db_table_exists($table_name)) {
      db_drop_table($table_name);
    }
  }
}

/**
 * Implements hook_apdqc_install_check_boot_exit_hooks_alter().
 */
function apdqc_apdqc_install_check_boot_exit_hooks_alter(&$hook_boot, &$hook_exit) {

  // Whitelist.
  $boot_whitelist = array(
    'apdqc',
    'httprl',
    'devel',
    'elysia_cron',
    'botcha',
    'logintoboggan',
    'moopapi',
    'variable',
  );
  $exit_whitelist = array(
    'image_effects_text',
    'feeds',
    'rules',
    'cdn',
  );

  // Remove items from the whitelist.
  $hook_boot = array_diff($hook_boot, $boot_whitelist);
  $hook_exit = array_diff($hook_exit, $exit_whitelist);
}

/**
 * Implements hook_entity_info_alter().
 */
function apdqc_before_entity_info_alter(&$entity_info) {
  $entity_controller_class_info =& drupal_static('apdqc_entity_info_alter');
  foreach ($entity_info as $type => $info) {
    if (isset($info['fieldable'])) {
      $classes = class_implements($info['controller class']);

      // Only use prefetch shim if class only implements
      // DrupalEntityControllerInterface.
      if (count($classes) == 1 && isset($classes['DrupalEntityControllerInterface'])) {
        $entity_controller_class_info[$type] = $info['controller class'];
      }
    }
  }
}

/**
 * Implements hook_entity_info_alter().
 */
function apdqc_after_entity_info_alter(&$entity_info) {
  $entity_controller_class_info =& drupal_static('apdqc_entity_info_alter');
  foreach ($entity_info as $type => $info) {
    if (isset($info['fieldable']) && !empty($entity_controller_class_info[$type]) && $entity_controller_class_info[$type] === $info['controller class']) {
      $classes = class_implements($info['controller class']);

      // Only use prefetch shim if class only implements
      // DrupalEntityControllerInterface.
      if (count($classes) == 1 && isset($classes['DrupalEntityControllerInterface'])) {

        // Controller class is not being overwritten; put in prefetch class.
        $entity_info[$type]['original controller class'] = $info['controller class'];
        $entity_info[$type]['controller class'] = 'ApdqcPrefetchDrupalDefaultEntityController';
      }
    }
  }
}

/**
 * Passthrough DrupalDefaultEntityController class for prefetching cache ids.
 */
class ApdqcPrefetchDrupalDefaultEntityController {
  public $originalBaseClass;
  public $originalEntityType;
  public $originalEntityInfo;

  /**
   * Get the entity type and create the original controller object.
   *
   * @param string $entity_type
   *   Entity type.
   */
  public function __construct($entity_type) {
    $entity_info = entity_get_info($entity_type);
    $classname = $entity_info['original controller class'];
    $this->originalEntityInfo = $entity_info;
    $this->originalEntityType = $entity_type;
    $this->originalBaseClass = new $classname($entity_type);
  }

  /**
   * Magic method; forward all calls to the original base class.
   *
   * On load calls prefetch the field data.
   *
   * @param string $name
   *   Name of the method called.
   * @param array $arguments
   *   Array of parameters passed to the method.
   */
  public function __call($name, array $arguments) {
    if ($name == 'load' && function_exists('apdqc_run_prefetch_array') && function_exists('apdqc_escape_string')) {
      if (apdqc_get_bin_class_name('cache_field') === 'APDQCache') {

        // Use the advanced drupal_static() pattern, since this is called very
        // often.
        static $drupal_static_fast;
        if (!isset($drupal_static_fast)) {
          $drupal_static_fast['cache_field'] =& drupal_static('apdqc_cache_prefetch_ids');
        }
        $table_keys_cache_prefetch = array();
        if (!empty($arguments[0])) {
          foreach ($arguments[0] as $id) {
            if (is_string($id) || is_numeric($id)) {
              $string = apdqc_escape_string('field:' . $this->originalEntityType . ':' . $id);
              if (!isset($drupal_static_fast['cache_field'][$string])) {
                $drupal_static_fast['cache_field'][$string] = TRUE;
                $table_keys_cache_prefetch['cache_field'][] = $string;
              }
            }
          }
        }

        // Prefetch cache.
        if (!empty($table_keys_cache_prefetch)) {
          apdqc_run_prefetch_array($table_keys_cache_prefetch);
        }
      }
    }
    if ($name == 'load' && !empty($arguments[0]) && is_array($arguments[0]) && count($arguments[0]) == 1 && variable_get('apdqc_entity_load_skip_null', APDQC_ENTITY_LOAD_SKIP_NULL)) {
      $test = reset($arguments[0]);
      if (empty($test) && is_null($test)) {
        return array();
      }
    }
    $return = call_user_func_array(array(
      $this->originalBaseClass,
      $name,
    ), $arguments);
    return $return;
  }

}

/**
 * Implements hook_panelizer_defaults_alter().
 */
function apdqc_panelizer_defaults_alter($items) {
  if (!function_exists('apdqc_run_prefetch_array') || !function_exists('apdqc_escape_string')) {
    return;
  }
  $table_keys_cache_prefetch = array();
  if (apdqc_get_bin_class_name('cache_field') === 'APDQCache') {
    foreach ($items as $values) {
      $table_keys_cache_prefetch['cache_field'][] = "field_info:bundle:{$values->panelizer_type}:{$values->panelizer_key}";
    }
  }
  apdqc_run_prefetch_array($table_keys_cache_prefetch);
}

/**
 * Implements hook_menu_get_item_alter().
 */
function apdqc_menu_get_item_alter($router_item, $path, $original_map) {
  if (!function_exists('apdqc_run_prefetch_array') || !function_exists('apdqc_escape_string')) {
    return;
  }
  static $run_once;
  if (apdqc_get_bin_class_name('cache_views') === 'APDQCache') {
    if ($router_item['page_callback'] === 'views_page' || $router_item['access_callback'] === 'views_access') {
      if (!isset($run_once['views'])) {
        $run_once['views'] = TRUE;
        $table_keys_cache_prefetch = apdqc_views_get_unpack_options();
      }
      $args = $router_item['page_arguments'];
      $data = @unserialize($router_item['page_arguments']);
      if ($router_item['page_arguments'] === 'b:0;' || $data !== FALSE) {
        $args = $data;
      }
      $table_keys_cache_prefetch['cache_views'][] = 'ctools_export:views_view:' . apdqc_escape_string($args[0]);
      if ($args[0] === 'project_issue_project_searchapi') {
        $lang_shorthand = apdqc_get_lang();
        $table_keys_cache_prefetch['cache_views'][] = 'views_data:search_api_index_project_issues:' . $lang_shorthand;
        if (apdqc_get_bin_class_name('cache_field') === 'APDQCache') {
          $table_keys_cache_prefetch['cache_field'][] = 'field_info:bundle:node:project_module';
        }
      }
      if ($args[0] === 'project_issue_search_project_searchapi') {
        $lang_shorthand = apdqc_get_lang();
        $table_keys_cache_prefetch['cache_views'][] = 'views_data:search_api_index_project_issues:' . $lang_shorthand;
      }
    }
  }
  if (apdqc_get_bin_class_name('cache_views') === 'APDQCache') {
    if ($router_item['page_callback'] === 'theme') {
      $args = $router_item['page_arguments'];
      $data = @unserialize($router_item['page_arguments']);
      if ($router_item['page_arguments'] === 'b:0;' || $data !== FALSE) {
        $args = $data;
      }
      if ($args[0] === 'drupalorg_download') {
        $table_keys_cache_prefetch = apdqc_views_get_unpack_options();
        $table_keys_cache_prefetch['cache_views'][] = 'ctools_export:views_view:project_index';
        if (apdqc_get_bin_class_name('cache_field') === 'APDQCache') {
          $table_keys_cache_prefetch['cache_field'][] = 'field_info:bundle:node:project_module';
        }
      }
    }
  }
  if (apdqc_get_bin_class_name('cache_field') === 'APDQCache') {
    if ($router_item['page_callback'] === 'project_issue_node_add') {
      $table_keys_cache_prefetch['cache_field'][] = 'field_info:bundle:node:project_issue';
      $table_keys_cache_prefetch['cache_field'][] = 'field_info:bundle_extra:node:project_issue';
      $table_keys_cache_prefetch['cache_field'][] = 'field_info:bundle:comment:comment_node_project_issue';
      $table_keys_cache_prefetch['cache_field'][] = 'field_info:bundle_extra:comment:comment_node_project_issue';
    }
  }

  // Prefetch cache.
  if (!empty($table_keys_cache_prefetch)) {
    apdqc_run_prefetch_array($table_keys_cache_prefetch);
  }
}

/**
 * Implements hook_views_pre_view().
 */
function apdqc_views_pre_view($view) {
  if (!function_exists('apdqc_run_prefetch_array') || !function_exists('apdqc_escape_string')) {
    return;
  }
  static $lang_shorthand;
  if (!isset($lang_shorthand)) {
    $lang_shorthand = apdqc_get_lang();
  }
  $table_keys_cache_prefetch = array();
  if (apdqc_get_bin_class_name('cache_views') === 'APDQCache') {
    if (!empty($view->relationship)) {
      foreach ($view->relationship as $values) {
        if (!empty($values->table)) {
          $table_keys_cache_prefetch['cache_views'][] = "views_data:{$values->table}:{$lang_shorthand}";
        }
      }
    }
    if (!empty($view->filter)) {
      foreach ($view->filter as $values) {
        if (!empty($values->table)) {
          $table_keys_cache_prefetch['cache_views'][] = "views_data:{$values->table}:{$lang_shorthand}";
        }
      }
    }
    if (!empty($view->display_handler->display->display_options['relationships'])) {
      foreach ($view->display_handler->display->display_options['relationships'] as $values) {
        if (!empty($values['table'])) {
          $table_keys_cache_prefetch['cache_views'][] = "views_data:{$values['table']}:{$lang_shorthand}";
        }
      }
    }
    if (!empty($view->display_handler->default_display->options['relationships'])) {
      foreach ($view->display_handler->default_display->options['relationships'] as $values) {
        if (!empty($values['table'])) {
          $table_keys_cache_prefetch['cache_views'][] = "views_data:{$values['table']}:{$lang_shorthand}";
        }
      }
    }
    if (!empty($view->display_handler->options['filters'])) {
      foreach ($view->display_handler->options['filters'] as $values) {
        if (!empty($values['table'])) {
          $table_keys_cache_prefetch['cache_views'][] = "views_data:{$values['table']}:{$lang_shorthand}";
        }
      }
    }
    if (!empty($view->display['default']->display_options['filters'])) {
      foreach ($view->display['default']->display_options['filters'] as $values) {
        if (!empty($values['table'])) {
          $table_keys_cache_prefetch['cache_views'][] = "views_data:{$values['table']}:{$lang_shorthand}";
        }
      }
    }
  }

  // Prefetch cache.
  apdqc_run_prefetch_array($table_keys_cache_prefetch);
}

/**
 * Implements hook_views_pre_build().
 */
function apdqc_views_pre_build($view) {
  if (!function_exists('apdqc_run_prefetch_array') || !function_exists('apdqc_escape_string')) {
    return;
  }
  static $run_once;
  static $lang_shorthand;
  if (!isset($lang_shorthand)) {
    $lang_shorthand = apdqc_get_lang();
  }
  $table_keys_cache_prefetch = array();
  if (apdqc_get_bin_class_name('cache_views') === 'APDQCache') {
    $table_keys_cache_prefetch['cache_views'][] = 'ctools_export:views_view:' . $view->name;
  }
  foreach ($view->display as $values) {

    // Get field data.
    if (!empty($values->handler->options['fields'])) {
      foreach ($values->handler->options['fields'] as $field_name => $info) {
        if (apdqc_get_bin_class_name('cache_views') === 'APDQCache') {
          if (strpos($field_name, 'field_') === 0) {
            if (empty($run_once['field_data_' . $field_name])) {
              $table_keys_cache_prefetch['cache_views'][] = 'views_data:field_data_' . apdqc_escape_string($field_name) . ':' . $lang_shorthand;
              $run_once['field_data_' . $field_name] = TRUE;
            }
          }
        }
        if (apdqc_get_bin_class_name('cache') === 'APDQCache') {

          // One off for VBO.
          if ($field_name == 'views_bulk_operations' && empty($run_once['ctools_plugin_files:views_bulk_operations:operation_types'])) {
            $table_keys_cache_prefetch['cache'][] = 'ctools_plugin_files:views_bulk_operations:operation_types';
            $run_once['ctools_plugin_files:views_bulk_operations:operation_types'] = TRUE;
          }
        }
      }
    }
    if (apdqc_get_bin_class_name('cache_views') === 'APDQCache') {

      // Get table data.
      if (!empty($values->handler->options['filters'])) {
        foreach ($values->handler->options['filters'] as $info) {
          if (empty($run_once[$info['table']])) {
            $table_keys_cache_prefetch['cache_views'][] = 'views_data:' . apdqc_escape_string($info['table']) . ':' . $lang_shorthand;
            $run_once[$info['table']] = TRUE;
          }
        }
      }
    }
  }
  if (apdqc_get_bin_class_name('cache_field') === 'APDQCache') {
    if (!empty($view->display_handler->handlers['field'])) {
      foreach ($view->display_handler->handlers['field'] as $values) {
        if (isset($values->field_info['bundles'])) {
          foreach ($values->field_info['bundles'] as $entity_name => $entity_types) {
            foreach ($entity_types as $entity_type) {
              $table_keys_cache_prefetch['cache_field'][] = "field_info:bundle:{$entity_name}:{$entity_type}";
            }
          }
        }
      }
    }
  }
  if (apdqc_get_bin_class_name('cache_views') === 'APDQCache') {
    if (empty($run_once[$view->name])) {
      $table_keys_cache_prefetch['cache_views'][] = 'ctools_export:views_view:' . apdqc_escape_string($view->name);
      $run_once[$view->name] = TRUE;
    }
  }

  // Prefetch cache.
  apdqc_run_prefetch_array($table_keys_cache_prefetch);
}

/**
 * Implements hook_views_post_execute().
 */
function apdqc_views_post_execute($view) {
  if (!function_exists('apdqc_run_prefetch_array') || !function_exists('apdqc_escape_string')) {
    return;
  }
  if (apdqc_get_bin_class_name('cache_field') !== 'APDQCache') {
    return;
  }

  // Use the advanced drupal_static() pattern, since this is called very often.
  static $drupal_static_fast;
  if (!isset($drupal_static_fast)) {
    $drupal_static_fast['cache_field'] =& drupal_static('apdqc_cache_prefetch_ids');
  }
  $table_keys_cache_prefetch = array();
  foreach ($view->result as $row) {
    if (isset($row->nid)) {
      $string = 'field:node:' . apdqc_escape_string($row->nid);
      if (!isset($drupal_static_fast['cache_field'][$string])) {
        $drupal_static_fast['cache_field'][$string] = TRUE;
        $table_keys_cache_prefetch['cache_field'][] = $string;
      }
    }
  }

  // Prefetch cache.
  apdqc_run_prefetch_array($table_keys_cache_prefetch);
}

/**
 * Implements hook_views_pre_render().
 */
function apdqc_views_pre_render($view) {
  if (!function_exists('apdqc_run_prefetch_array') || !function_exists('apdqc_escape_string')) {
    return;
  }
  if (apdqc_get_bin_class_name('cache_field') !== 'APDQCache') {
    return;
  }
  $table_keys_cache_prefetch = array();
  if (isset($view->style_plugin->row_plugin->nodes)) {
    $node_types = array();
    foreach ($view->style_plugin->row_plugin->nodes as $node) {
      $node_types[$node->type] = TRUE;
    }
    if (!empty($node_types)) {
      foreach ($node_types as $type => $empty) {
        $type = apdqc_escape_string($type);
        $table_keys_cache_prefetch['cache_field'][] = 'field_info:bundle:node:' . $type;
        $table_keys_cache_prefetch['cache_field'][] = 'field_info:bundle_extra:node:' . $type;
      }
    }
  }

  // Prefetch cache.
  apdqc_run_prefetch_array($table_keys_cache_prefetch);
}

/**
 * Prefetch unpack_options & return some cache_views cids to prefetch.
 *
 * @return array
 *   Multidimensional array containing tables and cache keys to prefectch.
 */
function apdqc_views_get_unpack_options() {
  if (!function_exists('apdqc_run_prefetch_array') || !function_exists('apdqc_escape_string')) {
    return array();
  }
  static $run_once;
  $table_keys_cache_prefetch = array();
  if (!isset($run_once)) {
    $run_once = TRUE;
    if (apdqc_get_bin_class_name('cache_views') !== 'APDQCache') {
      return;
    }
    $lang_shorthand = apdqc_get_lang();
    if (variable_get('apdqc_prefetch_views_unpack', APDQC_PREFETCH_VIEWS_UNPACK)) {

      // Prefetch cache; OR with a LIKE % results in slow queries.
      $table_keys_cache_prefetch['cache_views'][] = 'unpack_options:%:' . $lang_shorthand;
      apdqc_run_prefetch_array($table_keys_cache_prefetch);
      $table_keys_cache_prefetch = array();
    }
    $table_keys_cache_prefetch['cache_views'][] = 'views_data:node:' . $lang_shorthand;
    $table_keys_cache_prefetch['cache_views'][] = 'views_data:' . $lang_shorthand;
  }
  return $table_keys_cache_prefetch;
}

/**
 * Implements hook_ctools_plugin_pre_alter().
 */
function apdqc_ctools_plugin_pre_alter(&$plugin, &$plugin_type_info) {
  if (!function_exists('apdqc_run_prefetch_array') || !function_exists('apdqc_escape_string')) {
    return;
  }
  static $lang_shorthand;
  static $run_once;
  if (!isset($lang_shorthand)) {
    $lang_shorthand = apdqc_get_lang();
  }
  if ($plugin['name'] == 'user') {
    if (!isset($run_once['user'])) {
      $run_once['user'] = TRUE;
    }
  }
  $table_keys_cache_prefetch = array();
  if ($plugin['file'] === 'views_panes.inc' || $plugin['file'] === 'view_pane.inc') {
    if (!isset($run_once['views_panes'])) {
      $run_once['views_panes'] = TRUE;
      if (apdqc_get_bin_class_name('cache_views') === 'APDQCache') {
        $table_keys_cache_prefetch += apdqc_views_get_unpack_options();
      }
    }
  }
  if ($plugin['file'] === 'view.inc' || $plugin['file'] === 'views.inc') {
    if (!isset($run_once['views'])) {
      $run_once['views'] = TRUE;
      if (apdqc_get_bin_class_name('cache_views') === 'APDQCache') {
        $table_keys_cache_prefetch += apdqc_views_get_unpack_options();
        $data = cache_get('ctools_export_index:views_view', 'cache_views');
        if (!empty($data) && is_array($data->data) && !empty($data->data)) {
          foreach ($data->data as $name) {
            $table_keys_cache_prefetch['cache_views'][] = 'ctools_export:views_view:' . apdqc_escape_string($name);
          }
        }
        if (!empty($run_once['user'])) {
          $table_keys_cache_prefetch['cache_views'][] = 'views_data:users:' . $lang_shorthand;
        }
      }
    }
  }
  if ($plugin['name'] == 'entity_from_field') {
    if (!isset($run_once['entity_from_field'])) {
      $run_once['entity_from_field'] = TRUE;
      if (apdqc_get_bin_class_name('cache_field') === 'APDQCache') {
        $table_keys_cache_prefetch['cache_field'][] = 'field_info:fields';
        $table_keys_cache_prefetch['cache_field'][] = 'field_info:instances';
      }
    }
  }
  if (module_exists('panels')) {
    if (!isset($run_once['views_content_panes'])) {
      $run_once['views_content_panes'] = TRUE;
      if (apdqc_get_bin_class_name('cache_views') === 'APDQCache') {
        $table_keys_cache_prefetch['cache_views'][] = 'views_content_panes:' . $lang_shorthand;
      }
    }
  }
  if ($plugin['name'] == 'download_releases_link') {
    if (!isset($run_once['download_releases_link'])) {
      $run_once['download_releases_link'] = TRUE;
      if (apdqc_get_bin_class_name('cache_views') === 'APDQCache') {
        $table_keys_cache_prefetch['cache_views'][] = 'ctools_export:views_view:project_downloads';
        $table_keys_cache_prefetch['cache_views'][] = 'views_data:field_data_field_release_project:' . $lang_shorthand;
        $table_keys_cache_prefetch['cache_views'][] = 'views_data:field_data_field_release_files:' . $lang_shorthand;
        $table_keys_cache_prefetch['cache_views'][] = 'views_data:field_data_field_release_file:' . $lang_shorthand;
        $table_keys_cache_prefetch['cache_views'][] = 'views_data:file_managed:' . $lang_shorthand;
        $table_keys_cache_prefetch['cache_views'][] = 'views_data:field_data_field_release_version:' . $lang_shorthand;
        $table_keys_cache_prefetch['cache_views'][] = 'views_data:field_data_field_release_short_description:' . $lang_shorthand;
        $table_keys_cache_prefetch['cache_views'][] = 'views_data:field_data_field_release_version_major:' . $lang_shorthand;
        $table_keys_cache_prefetch['cache_views'][] = 'views_data:field_data_field_release_version_minor:' . $lang_shorthand;
        $table_keys_cache_prefetch['cache_views'][] = 'views_data:field_data_field_release_version_patch:' . $lang_shorthand;
        $table_keys_cache_prefetch['cache_views'][] = 'views_data:field_data_field_release_version_ext_weight:' . $lang_shorthand;
        $table_keys_cache_prefetch['cache_views'][] = 'views_data:field_data_field_release_version_extra:' . $lang_shorthand;
        $table_keys_cache_prefetch['cache_views'][] = 'views_data:field_data_field_release_version_ext_delta:' . $lang_shorthand;
        $table_keys_cache_prefetch['cache_views'][] = 'views_data:field_data_field_release_recommended:' . $lang_shorthand;
        $table_keys_cache_prefetch['cache_views'][] = 'views_data:field_data_field_release_update_status:' . $lang_shorthand;
        $table_keys_cache_prefetch['cache_views'][] = 'views_data:field_data_field_release_build_type:' . $lang_shorthand;
        $table_keys_cache_prefetch['cache_views'][] = 'views_data:project_release_supported_versions:' . $lang_shorthand;
      }
      if (apdqc_get_bin_class_name('cache_field') === 'APDQCache') {
        $table_keys_cache_prefetch['cache_field'][] = 'field_info:bundle:node:project_release';
        $table_keys_cache_prefetch['cache_field'][] = 'field_info:bundle:node:page';
        $table_keys_cache_prefetch['cache_field'][] = 'field_info:bundle:node:section';
      }
    }
  }
  if ($plugin['name'] === 'taxonomy_term') {
    if (!isset($run_once['taxonomy_term'])) {
      $run_once['taxonomy_term'] = TRUE;
      if (apdqc_get_bin_class_name('cache_views') === 'APDQCache') {
        $table_keys_cache_prefetch['cache_views'][] = 'views_data:taxonomy_term_data:' . $lang_shorthand;
        $table_keys_cache_prefetch['cache_views'][] = 'views_data:taxonomy_index:' . $lang_shorthand;
      }
    }
  }

  // Prefetch cache.
  apdqc_run_prefetch_array($table_keys_cache_prefetch);
}

/**
 * Implements hook_views_api().
 */
function apdqc_views_api() {
  if (!function_exists('apdqc_run_prefetch_array') || !function_exists('apdqc_escape_string')) {
    return;
  }
  if (apdqc_get_bin_class_name('cache_views') !== 'APDQCache') {
    return;
  }
  static $run_once;
  if (!isset($run_once)) {
    $run_once = TRUE;
    $lang_shorthand = apdqc_get_lang();
    $table_keys_cache_prefetch = array();
    $table_keys_cache_prefetch['cache_views'][] = 'entity_base_tables:' . $lang_shorthand;
    $table_keys_cache_prefetch['cache_views'][] = 'views_data:' . $lang_shorthand;
    $table_keys_cache_prefetch['cache_views'][] = 'views_data:views:' . $lang_shorthand;
    $table_keys_cache_prefetch['cache_views'][] = 'views_content_all:' . $lang_shorthand;

    // Prefetch cache.
    apdqc_run_prefetch_array($table_keys_cache_prefetch);
  }
}

/**
 * Implements hook_entity_load().
 */
function apdqc_entity_load($entities, $type) {
  if (!function_exists('apdqc_run_prefetch_array') || !function_exists('apdqc_escape_string')) {
    return;
  }
  if (apdqc_get_bin_class_name('cache_field') !== 'APDQCache') {
    return;
  }
  $bundle_type = apdqc_escape_string($type);
  $table_keys_cache_prefetch = array();
  foreach ($entities as $entity) {
    if (!is_object($entity) || empty($entity->type)) {
      continue;
    }
    $type = apdqc_escape_string($entity->type);
    $table_keys_cache_prefetch['cache_field'][] = "field_info:bundle:{$bundle_type}:{$type}";
    $table_keys_cache_prefetch['cache_field'][] = "field_info:bundle_extra:{$bundle_type}:{$type}";
    foreach ($entity as $key => $fields) {
      if (strpos($key, 'field_') !== 0 || empty($fields[LANGUAGE_NONE])) {
        continue;
      }
      foreach ($fields[LANGUAGE_NONE] as $values) {
        if (empty($values['entity']) || !is_object($values['entity']) || empty($values['entity']->type)) {
          continue;
        }
        $type = apdqc_escape_string($values['entity']->type);
        $table_keys_cache_prefetch['cache_field'][] = "field_info:bundle:{$bundle_type}:{$type}";
        $table_keys_cache_prefetch['cache_field'][] = "field_info:bundle_extra:{$bundle_type}:{$type}";
      }
    }
  }

  // Prefetch cache.
  apdqc_run_prefetch_array($table_keys_cache_prefetch);
}

/**
 * Implements hook_boot().
 */
function apdqc_boot() {
  if (!function_exists('apdqc_run_prefetch_array') || !function_exists('apdqc_escape_string')) {
    require_once 'apdqc.cache.inc';
    $db_type = apdqc_fast_get_db_type();
    if ($db_type === 'mysql') {
      require_once 'apdqc.mysql.inc';
    }
  }

  // Bail if this is a cached page hit.
  $header_list = headers_list();
  if (in_array('X-Drupal-Cache: HIT', $header_list)) {
    return;
  }
  $table_keys_cache_prefetch = array();
  if (apdqc_get_bin_class_name('cache_bootstrap') === 'APDQCache') {

    // Prefetch the rest of cache_bootstrap on a cache_page miss.
    // List the cids we already have.
    $table_keys_cache_prefetch['cache_bootstrap']['*'] = array(
      'variables',
      'bootstrap_modules',
      'lookup_cache',
      'system_list',
      'module_implements',
    );
  }

  // Get defaults for language & theme.
  $lang_shorthand = apdqc_get_lang();
  $default_theme = apdqc_escape_string(variable_get('theme_default', 'bartik'));
  if (apdqc_get_bin_class_name('cache') === 'APDQCache') {
    $table_keys_cache_prefetch['cache'][] = 'theme_registry:runtime:' . $default_theme;

    // Get cache bin keys.
    $table_keys_cache_prefetch['cache'][] = 'entity_info:' . $lang_shorthand;
    $table_keys_cache_prefetch['cache'][] = 'node_types:' . $lang_shorthand;
  }
  if (apdqc_get_bin_class_name('cache_field') === 'APDQCache') {
    $uid = apdqc_escape_string($GLOBALS['user']->uid);
    $table_keys_cache_prefetch['cache_field'][] = 'field:user:' . $uid;
    $table_keys_cache_prefetch['cache_field'][] = 'field_info:fields';
    $table_keys_cache_prefetch['cache_field'][] = 'field_info:bundle:user:user';
    $table_keys_cache_prefetch['cache_field'][] = 'field_info:instances';
    $table_keys_cache_prefetch['cache_field'][] = 'field_info:field_map';
    $table_keys_cache_prefetch['cache_field'][] = 'field_groups';

    // Prefetch node field info.
    $page = apdqc_escape_string($_GET['q']);
    $bin = 'cache_field';
    $query = " SELECT '{$bin}' AS bin, {$bin}.cid, {$bin}.data, {$bin}.created, {$bin}.expire, {$bin}.serialized FROM " . apdqc_fast_prefix_tables('{' . apdqc_fast_escape_table($bin) . '}') . " AS {$bin} ";

    // Get node type.
    $matches = array();
    if (preg_match("/node\\/(\\d+)/i", $page, $matches)) {

      // If page starts with node/NID.
      $nid = apdqc_escape_string($matches[1]);
      $query .= " INNER JOIN " . apdqc_fast_prefix_tables('{' . apdqc_fast_escape_table('node') . '}') . " AS node ON node.nid = {$nid}";
    }
    else {

      // Page might be an alias to a node.
      $query .= " INNER JOIN " . apdqc_fast_prefix_tables('{' . apdqc_fast_escape_table('url_alias') . '}') . " AS url_alias ON (url_alias.alias = '{$page}' AND SUBSTRING(url_alias.source, 1, 5) = 'node/')";
      $query .= " INNER JOIN " . apdqc_fast_prefix_tables('{' . apdqc_fast_escape_table('node') . '}') . " AS node ON node.nid = SUBSTRING(url_alias.source, 6)";
    }
    $query .= " WHERE cid LIKE 'field_info:bundle:node:%' AND cid = CONCAT('field_info:bundle:node:', node.type)";
    apdqc_query(array(
      'cache_field',
    ), array(
      'field_info:bundle:node:%',
    ), $query, array(
      'async' => TRUE,
    ));
  }

  // Prefetch cache.
  apdqc_run_prefetch_array($table_keys_cache_prefetch);
}

/**
 * Implements hook_stream_wrappers().
 */
function apdqc_stream_wrappers() {
  if (!function_exists('apdqc_run_prefetch_array') || !function_exists('apdqc_escape_string')) {
    require_once 'apdqc.cache.inc';
    $db_type = apdqc_fast_get_db_type();
    if ($db_type === 'mysql') {
      require_once 'apdqc.mysql.inc';
    }
  }
  static $run_once;
  if (!empty($run_once)) {
    return;
  }
  $run_once = TRUE;

  // Run before menu_get_item() && hook_init but inside full bootstrap.
  $lang_shorthand = apdqc_get_lang();
  $table_keys_cache_prefetch = array();
  if (apdqc_get_bin_class_name('cache_menu') === 'APDQCache') {
    $wrappers = stream_get_wrappers();
    if (module_exists('block') && in_array('public', $wrappers)) {
      drupal_path_initialize();

      // Preload menus in the blocks.
      $system_menus = menu_list_system_menus();
      $main = variable_get('menu_main_links_source', 'main-menu');
      $secondary = variable_get('menu_secondary_links_source', 'user-menu');
      if (!array_key_exists($main, $system_menus)) {
        $system_menus[$main] = 'Main Menu';
      }
      if ($main !== $secondary) {
        $system_menus[$secondary] = 'Secondary Menu';
      }
      foreach ($system_menus as $menu_name => $title) {
        $active_path = menu_tree_get_path($menu_name);

        // Load the menu item corresponding to the current page.
        $item = menu_get_item($active_path);
        if ($item) {
          $menu_name = apdqc_escape_string($menu_name);

          // Generate a cache ID (cid) specific for this page.
          $table_keys_cache_prefetch['cache_menu'][] = 'links:' . $menu_name . ':page:' . apdqc_escape_string($item['href']) . ':' . $lang_shorthand . ':' . (int) $item['access'] . ':1';
          $table_keys_cache_prefetch['cache_menu'][] = 'links:' . $menu_name . ':page:' . apdqc_escape_string($item['href']) . ':' . $lang_shorthand . ':' . (int) $item['access'] . ':0';
          $table_keys_cache_prefetch['cache_menu'][] = 'links:' . $menu_name . ':page:' . apdqc_escape_string($item['href']) . ':' . $lang_shorthand . ':' . (int) $item['access'] . ':1:trail';
          $table_keys_cache_prefetch['cache_menu'][] = 'links:' . $menu_name . ':page:' . apdqc_escape_string($item['href']) . ':' . $lang_shorthand . ':' . (int) $item['access'] . ':0:trail';
        }
      }
    }
  }
  if (apdqc_get_bin_class_name('cache') === 'APDQCache') {
    $table_keys_cache_prefetch['cache'][] = 'schema:runtime:1';
    if (module_exists('ctools')) {
      $table_keys_cache_prefetch['cache'][] = 'ctools_plugin_type_info';
    }
    if (module_exists('context')) {
      $table_keys_cache_prefetch['cache'][] = 'plugins:context:plugins';
    }
    if (module_exists('mailsystem')) {
      $table_keys_cache_prefetch['cache'][] = 'mailsystem_get_classes';
    }
    if (module_exists('filter')) {
      $table_keys_cache_prefetch['cache'][] = 'filter_formats:' . $lang_shorthand;
    }
    if (module_exists('context')) {
      $table_keys_cache_prefetch['cache'][] = 'context';
    }
    if (module_exists('ctools')) {
      $table_keys_cache_prefetch['cache'][] = 'ctools_plugin_files:ctools:arguments';
      $table_keys_cache_prefetch['cache'][] = 'ctools_plugin_files:ctools:contexts';
      if (module_exists('page_manager')) {
        $table_keys_cache_prefetch['cache'][] = 'ctools_plugin_files:page_manager:tasks';
      }
    }
  }

  // Get cache_path bin keys.
  $page = $_GET['q'];
  if (empty($page)) {
    $page = variable_get('site_frontpage', 'node');
  }
  $source = drupal_lookup_path('source', $page);
  if (empty($source)) {
    $source = $page;
  }
  if (apdqc_get_bin_class_name('cache') === 'APDQCache') {
    $table_keys_cache_prefetch['cache_path'][] = apdqc_escape_string($source);
  }
  if (apdqc_get_bin_class_name('cache_field') === 'APDQCache') {
    $matched = apdqc_entity_urls($source);
    if (!empty($matched)) {

      // Use the advanced drupal_static() pattern, since this is called very
      // often.
      static $drupal_static_fast;
      if (!isset($drupal_static_fast)) {
        $drupal_static_fast['cache_field'] =& drupal_static('apdqc_cache_prefetch_ids');
      }
      $string = 'field:' . $matched[0] . ':' . apdqc_escape_string(substr($source, strlen($matched[1])));
      if (!isset($drupal_static_fast['cache_field'][$string])) {
        $drupal_static_fast['cache_field'][$string] = TRUE;
        $table_keys_cache_prefetch['cache_field'][] = $string;
      }
      if ($matched[0] == 'user') {
        $table_keys_cache_prefetch['cache_field'][] = 'field_info:bundle:user:user';
      }
    }
    $table_keys_cache_prefetch['cache_field'][] = 'field_info_types:' . $lang_shorthand;
  }
  if (apdqc_get_bin_class_name('cache_token') === 'APDQCache') {
    if (module_exists('token')) {
      $table_keys_cache_prefetch['cache_token'][] = 'info:' . $lang_shorthand;
    }
  }

  // Prefetch cache.
  apdqc_run_prefetch_array($table_keys_cache_prefetch);
}

/**
 * Implements hook_init().
 */
function apdqc_init() {
  if (!function_exists('apdqc_run_prefetch_array') || !function_exists('apdqc_escape_string')) {
    require_once 'apdqc.cache.inc';
    $db_type = apdqc_fast_get_db_type();
    if ($db_type === 'mysql') {
      require_once 'apdqc.mysql.inc';
    }
  }
  $lang_shorthand = apdqc_get_lang();
  $table_keys_cache_prefetch = array();
  if (apdqc_get_bin_class_name('cache') === 'APDQCache') {
    if (module_exists('filter')) {
      $table_keys_cache_prefetch['cache'][] = 'filter_list_format';
    }
    if (module_exists('image')) {
      $table_keys_cache_prefetch['cache'][] = 'image_styles';
    }
    if (module_exists('cdn')) {
      $table_keys_cache_prefetch['cache'][] = 'cdn_blacklist';
    }
    if (module_exists('entity')) {
      $table_keys_cache_prefetch['cache'][] = 'entity_property_info:' . $lang_shorthand;
    }
    if (module_exists('ctools')) {
      $table_keys_cache_prefetch['cache'][] = 'ctools_plugin_files:ctools:access';
      $table_keys_cache_prefetch['cache'][] = 'ctools_plugin_files:ctools:content_types';
      $table_keys_cache_prefetch['cache'][] = 'ctools_plugin_files:ctools:relationships';
      if (module_exists('panels')) {
        $table_keys_cache_prefetch['cache'][] = 'ctools_plugin_files:panels:styles';
        $table_keys_cache_prefetch['cache'][] = 'ctools_plugin_files:panels:layouts';
        $table_keys_cache_prefetch['cache'][] = 'ctools_plugin_files:panels:display_renderers';
      }
      if (module_exists('page_manager')) {
        $table_keys_cache_prefetch['cache'][] = 'ctools_plugin_files:page_manager:tasks';
        $table_keys_cache_prefetch['cache'][] = 'ctools_plugin_files:page_manager:task_handlers';
      }
    }
    $default_theme = apdqc_escape_string(variable_get('theme_default', 'bartik'));
    $global_theme = apdqc_escape_string($GLOBALS['theme']);
    if ($default_theme != $global_theme) {
      $table_keys_cache_prefetch['cache'][] = 'theme_registry:runtime:' . $global_theme;
    }
    $table_keys_cache_prefetch['cache'][] = 'theme_registry:' . $global_theme;
  }
  if (apdqc_get_bin_class_name('cache_rules') === 'APDQCache') {
    if (module_exists('rules')) {
      $table_keys_cache_prefetch['cache_rules'][] = 'data:' . $lang_shorthand;
    }
  }
  if (apdqc_get_bin_class_name('cache_token') === 'APDQCache') {
    if (module_exists('token')) {
      $table_keys_cache_prefetch['cache_token'][] = 'info:' . $lang_shorthand;
      $table_keys_cache_prefetch['cache_token'][] = 'field:info';
    }
  }
  if (apdqc_get_bin_class_name('cache_field') === 'APDQCache') {
    if (function_exists('menu_get_item')) {
      $item = menu_get_item();
    }
    if (!empty($item) && !empty($item['page_callback']) && $item['page_callback'] === 'drupalorg_home') {
      $table_keys_cache_prefetch['cache_field'][] = "field_info:bundle:node:post";
    }
    if (!empty($item) && !empty($item['page_callback']) && $item['page_callback'] === 'project_solr_browse_page') {
      if (apdqc_get_bin_class_name('cache_views') === 'APDQCache') {
        $table_keys_cache_prefetch += apdqc_views_get_unpack_options();
        if (!empty($item['path']) && ($item['path'] === 'project/project_module' || $item['path'] === 'project/project_distribution' || $item['path'] === 'project/project_theme')) {
          $table_keys_cache_prefetch['cache_views'][] = 'ctools_export:views_view:project_issue_user_projects';
          $table_keys_cache_prefetch['cache_views'][] = 'views_data:field_data_field_issue_assigned:' . $lang_shorthand;
          $table_keys_cache_prefetch['cache_views'][] = 'views_data:field_data_field_project:' . $lang_shorthand;
          $table_keys_cache_prefetch['cache_views'][] = 'views_data:field_data_field_project_machine_name:' . $lang_shorthand;
          $table_keys_cache_prefetch['cache_views'][] = 'views_data:history:' . $lang_shorthand;
          $table_keys_cache_prefetch['cache_views'][] = 'views_data:field_data_field_issue_status:' . $lang_shorthand;
          $table_keys_cache_prefetch['cache_views'][] = 'views_data:field_data_field_issue_priority:' . $lang_shorthand;
          $table_keys_cache_prefetch['cache_views'][] = 'views_data:field_data_field_issue_category:' . $lang_shorthand;
          $table_keys_cache_prefetch['cache_views'][] = 'views_data:field_data_field_issue_version:' . $lang_shorthand;
          $table_keys_cache_prefetch['cache_views'][] = 'views_data:node_comment_statistics:' . $lang_shorthand;
          $table_keys_cache_prefetch['cache_views'][] = 'views_data:users:' . $lang_shorthand;
          $table_keys_cache_prefetch['cache_views'][] = 'views_data:search_index:' . $lang_shorthand;
          $table_keys_cache_prefetch['cache_views'][] = 'views_data:project_maintainer:' . $lang_shorthand;
        }
      }
      if (!empty($item['path']) && $item['path'] === 'project/project_module') {
        $table_keys_cache_prefetch['cache_field'][] = "field_info:bundle:node:project_module";
        $table_keys_cache_prefetch['cache_field'][] = "field_info:bundle_extra:node:project_module";
      }
    }
    if (!empty($item) && !empty($item['page_arguments'][0]->type) && ($item['page_arguments'][0]->type === 'project_module' || $item['page_arguments'][0]->type === 'project_distribution' || $item['page_arguments'][0]->type === 'project_theme') && !empty($item['page_arguments'][0]->versioncontrol_project) && !empty($item['page_arguments'][0]->project)) {
      if (apdqc_get_bin_class_name('cache_views') === 'APDQCache') {
        $table_keys_cache_prefetch += apdqc_views_get_unpack_options();
        $table_keys_cache_prefetch['cache_views'][] = 'ctools_export:views_view:project_issue_metrics_new_issues';
        $table_keys_cache_prefetch['cache_views'][] = 'views_data:sampler_project_issue_new_issues_comments_by_project:' . $lang_shorthand;
        $table_keys_cache_prefetch['cache_views'][] = 'ctools_export:views_view:project_issue_metrics_response_rate';
        $table_keys_cache_prefetch['cache_views'][] = 'views_data:sampler_project_issue_responses_by_project:' . $lang_shorthand;
        $table_keys_cache_prefetch['cache_views'][] = 'ctools_export:views_view:project_issue_metrics_open_bugs';
        $table_keys_cache_prefetch['cache_views'][] = 'views_data:sampler_project_issue_opened_vs_closed_by_category:' . $lang_shorthand;
        $table_keys_cache_prefetch['cache_views'][] = 'ctools_export:views_view:project_issue_metrics_participants';
        $table_keys_cache_prefetch['cache_views'][] = 'views_data:sampler_project_issue_reporters_participants_by_project:' . $lang_shorthand;
        $table_keys_cache_prefetch['cache_views'][] = 'ctools_export:views_view:project_issue_metrics_first_response';
      }
      $table_keys_cache_prefetch['cache_field'][] = "field_info:bundle:node:organization";
    }
    if (!empty($item) && !empty($item['page_arguments'][0]) && is_string($item['page_arguments'][0]) && $item['page_arguments'][0] === 'project_issue_project_searchapi' && !empty($item['page_arguments'][1]) && is_string($item['page_arguments'][1]) && $item['page_arguments'][1] === 'page') {
      $table_keys_cache_prefetch['cache_field'][] = "field_info:bundle:node:page";
      $table_keys_cache_prefetch['cache_field'][] = "field_info:bundle:node:project_release";
      $table_keys_cache_prefetch['cache_field'][] = "field_info:bundle:node:section";
      $table_keys_cache_prefetch['cache_field'][] = "field_info:bundle:node:changenotice";
    }
    if (!empty($item) && !empty($item['page_arguments'][0]) && is_object($item['page_arguments'][0]) && !empty($item['page_arguments'][0]->type) && $item['page_arguments'][0]->type === 'project_issue') {
      $table_keys_cache_prefetch['cache_field'][] = "field_info:bundle:node:page";
      $table_keys_cache_prefetch['cache_field'][] = "field_info:bundle:node:project_release";
      $table_keys_cache_prefetch['cache_field'][] = "field_info:bundle:node:section";
      $table_keys_cache_prefetch['cache_field'][] = "field_info:bundle:node:changenotice";
      if (apdqc_get_bin_class_name('cache_views') === 'APDQCache') {
        $table_keys_cache_prefetch += apdqc_views_get_unpack_options();
        $table_keys_cache_prefetch['cache_views'][] = 'ctools_export:views_view:change_records';
      }
    }
    $source = $_GET['q'];
    $matched = apdqc_entity_urls($source);
    if (!empty($matched)) {
      $entity_name = $matched[0];
      $entity_id = substr($source, strlen($matched[1]));
      $entity_bundle = '';
      if (!empty($item) && !empty($item['original_map'])) {
        $map_key = array_search($entity_id, $item['original_map']);
        if (!empty($item['map'][$map_key]->type)) {
          $entity_bundle = $item['map'][$map_key]->type;
        }
        elseif ($entity_name === 'user') {
          $entity_bundle = 'user';
        }
      }
      if (!empty($entity_bundle)) {
        $table_keys_cache_prefetch['cache_field'][] = "field_info:bundle:{$entity_name}:{$entity_bundle}";
        $table_keys_cache_prefetch['cache_field'][] = "field_info:bundle_extra:{$entity_name}:{$entity_bundle}";
        if (module_exists('comment') && !empty($item['map'][$map_key]->comment)) {

          // Add in comment pre-loading if this entity has comments enabled.
          $table_keys_cache_prefetch['cache_field'][] = "field_info:bundle:comment:comment_{$entity_name}_{$entity_bundle}";
          $table_keys_cache_prefetch['cache_field'][] = "field_info:bundle_extra:comment:comment_{$entity_name}_{$entity_bundle}";
        }
        if (module_exists('field_collection')) {
          if (!empty($item['page_arguments'][0]) && is_object($item['page_arguments'][0])) {
            $prefetch = apdqc_async_data(FALSE, 'cache_field', array(
              'field_info:field_map',
            ));
            $parameters = array();
            if (!empty($prefetch['field_info:field_map']['data'])) {
              apdqc_inflate_unserialize($prefetch['field_info:field_map']);
              $parameters = $prefetch['field_info:field_map']['data'];
            }
            foreach ($parameters as $key => $values) {
              if ($values['type'] === 'field_collection' && !empty($item['page_arguments'][0]->{$key}[LANGUAGE_NONE])) {
                $table_keys_cache_prefetch['cache_field'][] = 'field_info:bundle:field_collection_item:' . apdqc_escape_string($key);
                $table_keys_cache_prefetch['cache_field'][] = 'field_info:bundle_extra:field_collection_item:' . apdqc_escape_string($key);
                foreach ($item['page_arguments'][0]->{$key}[LANGUAGE_NONE] as $values) {
                  if (!empty($values['value'])) {
                    $table_keys_cache_prefetch['cache_field'][] = "field:field_collection_item:" . apdqc_escape_string($values['value']);
                  }
                }
              }
            }
            if (apdqc_get_bin_class_name('cache_views') === 'APDQCache') {
              $table_keys_cache_prefetch['cache_views'][] = 'views_data:field_collection_item:' . $lang_shorthand;
            }
          }
        }
      }
    }
    if (module_exists('googleanalytics')) {
      $table_keys_cache_prefetch['cache_field'][] = "field_info:bundle:user:user";
    }
  }
  if (module_exists('admin_menu')) {
    $cid = 'admin_menu:' . apdqc_escape_string($GLOBALS['user']->uid . ':' . session_id() . ':' . $GLOBALS['language']->language);
    if (apdqc_get_bin_class_name('cache_menu') === 'APDQCache') {
      $table_keys_cache_prefetch['cache_menu'][] = $cid;
    }
    if (apdqc_get_bin_class_name('cache_admin_menu') === 'APDQCache') {
      $table_keys_cache_prefetch['cache_admin_menu'][] = $cid;
    }
  }
  if (apdqc_get_bin_class_name('cache_views') === 'APDQCache') {
    if (module_exists('views')) {
      $table_keys_cache_prefetch['cache_views'][] = 'ctools_export_index:views_view';
    }
  }
  if (apdqc_get_bin_class_name('cache_field') === 'APDQCache') {
    if (module_exists('field_group')) {
      $table_keys_cache_prefetch['cache_field'][] = 'field_groups';
    }
  }
  if (apdqc_get_bin_class_name('cache_menu') === 'APDQCache') {
    $table_keys_cache_prefetch['cache_menu'][] = 'menu_custom';

    // Get router item.
    if (function_exists('menu_get_item')) {
      $router_item = menu_get_item();
    }

    // If this router item points to its parent, start from the parents to
    // compute tabs and actions.
    if (!empty($router_item) && $router_item['type'] & MENU_LINKS_TO_PARENT) {
      $router_item = menu_get_item($router_item['tab_parent_href']);
    }

    // If we failed to fetch a router item or the current user doesn't have
    // access to it, don't bother computing the tabs.
    if (!empty($router_item) && $router_item['access']) {

      // Get all tabs (also known as local tasks) and the root page.
      $table_keys_cache_prefetch['cache_menu'][] = 'local_tasks:' . apdqc_escape_string($router_item['tab_root']);
    }

    // Load up system blocks.
    if (module_exists('block')) {

      // Preload menus in the blocks.
      $system_menus = menu_list_system_menus();
      $main = variable_get('menu_main_links_source', 'main-menu');
      $secondary = variable_get('menu_secondary_links_source', 'user-menu');
      if (!array_key_exists($main, $system_menus)) {
        $system_menus[$main] = 'Main Menu';
      }
      if ($main !== $secondary) {
        $system_menus[$secondary] = 'Secondary Menu';
      }
      $cids = array();
      foreach ($system_menus as $menu_name => $title) {
        $active_path = menu_tree_get_path($menu_name);

        // Load the menu item corresponding to the current page.
        $item = menu_get_item($active_path);
        if ($item) {
          $menu_name = apdqc_escape_string($menu_name);
          $href = apdqc_escape_string($item['href']);
          $cids[] = 'links:' . $menu_name . ':page:' . $href . ':' . $lang_shorthand . ':' . (int) $item['access'] . ':1';
          $cids[] = 'links:' . $menu_name . ':page:' . $href . ':' . $lang_shorthand . ':' . (int) $item['access'] . ':0';
          $cids[] = 'links:' . $menu_name . ':page:' . $href . ':' . $lang_shorthand . ':' . (int) $item['access'] . ':1:trail';
          $cids[] = 'links:' . $menu_name . ':page:' . $href . ':' . $lang_shorthand . ':' . (int) $item['access'] . ':0:trail';
        }
      }
      $prefetch = apdqc_async_data(FALSE, 'cache_menu', $cids);
      foreach ($prefetch as $cid => $cache) {
        if (empty($cache)) {
          continue;
        }
        $menu_name = substr($cid, 6, strpos($cid, ':', 6) - 6);
        apdqc_inflate_unserialize($cache);
        $parameters = $cache['data'];
        if (isset($parameters['expanded'])) {
          sort($parameters['expanded']);
        }
        $hash = hash('sha256', serialize($parameters));
        $table_keys_cache_prefetch['cache_menu'][] = "links:{$menu_name}:tree-data:{$lang_shorthand}:{$hash}";
      }
    }
  }

  // Prefetch cache.
  apdqc_run_prefetch_array($table_keys_cache_prefetch, FALSE);
}

/**
 * Returns an array of entity URL prefixes.
 *
 * @param string $match
 *   URL prefix to match.
 *
 * @return array
 *   All entity names with the prefix if $match is empty.
 *   An array($name, $first_part_uri) if $match was matched.
 *   NULL if $match was not matched.
 */
function apdqc_entity_urls($match = '') {
  static $entities;
  static $return;
  if (!isset($entities)) {
    $entities = array();
    if (module_exists('comment')) {
      $entities['comment'] = 'comment/';
    }
    if (module_exists('heartbeat')) {
      $entities['heartbeat'] = 'heartbeat/message/';
    }
    if (module_exists('mollom_test')) {
      $entities['mollom_test'] = 'mollom-test/form/';
    }
    if (module_exists('node')) {
      $entities['node'] = 'node/';
      $entities['_node'] = 'node';
    }
    if (module_exists('privatemsg_message')) {
      $entities['privatemsg_message'] = 'messages/view/';
    }
    if (module_exists('relation')) {
      $entities['relation'] = 'relation/';
    }
    if (module_exists('search_api')) {
      $entities['search_api_server'] = 'admin/config/search/search_api/server/';
      $entities['search_api_index'] = 'admin/config/search/search_api/index/';
    }
    if (module_exists('taxonomy')) {
      $entities['taxonomy_term'] = 'taxonomy/term/';
    }
    if (module_exists('uc_order')) {
      $entities['uc_order'] = 'admin/store/orders/';
    }
    if (module_exists('user')) {
      $entities['user'] = 'user/';
      $entities['_user'] = 'user';
    }
  }
  if (empty($match)) {
    return $entities;
  }
  foreach ($entities as $name => $first_part_uri) {
    if (strpos($match, $first_part_uri) === 0) {
      $return[$match] = array(
        ltrim($name, '_'),
        $first_part_uri,
      );
      break;
    }
  }
  if (isset($return[$match])) {
    return $return[$match];
  }
}

/**
 * Returns the language shorthand escaped for database use.
 *
 * @return string
 *   Shorthand for the given language.
 */
function apdqc_get_lang() {
  static $lang_shorthand;
  if (!isset($lang_shorthand)) {
    $language = language_default();
    if (isset($language->language)) {
      if (function_exists('apdqc_escape_string')) {
        $lang_shorthand = apdqc_escape_string($language->language);
      }
    }
  }
  if (isset($lang_shorthand)) {
    return $lang_shorthand;
  }
  return 'en';
}

/**
 * Returns an int depending on the state of the semaphore table.
 *
 * @return int
 *   0 - No changes needed.
 *   1 - Convert to MEMORY.
 *   2 - Convert to InnoDB.
 */
function apdqc_semaphore_conversion() {
  $conversion = 0;
  $results = db_query("SELECT VERSION()")
    ->fetchAssoc();
  $version = reset($results);
  $real_table_name_semaphore = str_replace("`", "'", Database::getConnection()
    ->prefixTables("{" . db_escape_table('semaphore') . "}"));
  $results = db_query("SHOW TABLE STATUS WHERE Name = {$real_table_name_semaphore}")
    ->fetchAssoc();
  if (version_compare($version, '5.6.0', '<=')) {
    if (strcasecmp($results['Engine'], 'MEMORY') != 0) {
      $conversion = 1;
    }
  }
  elseif (strcasecmp($results['Engine'], 'InnoDB') != 0) {
    $conversion = 2;
  }
  return $conversion;
}

Functions

Namesort descending Description
apdqc_after_entity_info_alter Implements hook_entity_info_alter().
apdqc_apdqc_install_check_boot_exit_hooks_alter Implements hook_apdqc_install_check_boot_exit_hooks_alter().
apdqc_before_entity_info_alter Implements hook_entity_info_alter().
apdqc_boot Implements hook_boot().
apdqc_cron Implements hook_cron().
apdqc_ctools_plugin_pre_alter Implements hook_ctools_plugin_pre_alter().
apdqc_entity_load Implements hook_entity_load().
apdqc_entity_urls Returns an array of entity URL prefixes.
apdqc_form_devel_admin_settings_alter Implements hook_form_FORM_ID_alter().
apdqc_form_system_performance_settings_alter Implements hook_form_FORM_ID_alter().
apdqc_get_lang Returns the language shorthand escaped for database use.
apdqc_init Implements hook_init().
apdqc_menu Implements hook_menu().
apdqc_menu_get_item_alter Implements hook_menu_get_item_alter().
apdqc_modules_installed Implements hook_modules_installed().
apdqc_modules_uninstalled Implements hook_modules_uninstalled().
apdqc_module_implements_alter Implements hook_module_implements_alter().
apdqc_panelizer_defaults_alter Implements hook_panelizer_defaults_alter().
apdqc_schema_alter Implements hook_schema_alter().
apdqc_semaphore_conversion Returns an int depending on the state of the semaphore table.
apdqc_stream_wrappers Implements hook_stream_wrappers().
apdqc_views_api Implements hook_views_api().
apdqc_views_get_unpack_options Prefetch unpack_options & return some cache_views cids to prefetch.
apdqc_views_post_execute Implements hook_views_post_execute().
apdqc_views_pre_build Implements hook_views_pre_build().
apdqc_views_pre_render Implements hook_views_pre_render().
apdqc_views_pre_view Implements hook_views_pre_view().
_apdqc_dblog_watchdog Implements hook_watchdog().

Constants

Namesort descending Description
APDQC_CRON_FREQUENCY How long to wait until apdqc cron will run again. Default is 24 hours.
APDQC_CRON_TIMESTAMP Last time cron was ran.
APDQC_DBLOG_WATCHDOG Should we use async database connection to write to watchdog table.
APDQC_ENTITY_LOAD_SKIP_NULL TRUE if APDQC will skip NULL entity loads.
APDQC_INNODB TRUE if APDQC altered the cache table engine to be innodb.
APDQC_INNODB_FILE_PER_TABLE OFF if APDQC checked and innodb_file_per_table was off at that time.
APDQC_INSTALL_IGNORE_MYSQL_VERSION Set to TRUE to ignore the MySQL version checking on status report page.
APDQC_PREFETCH Should this code try to prefetch cids before they are needed.
APDQC_PREFETCH_VIEWS_UNPACK Set to TRUE to prefetch all views unpack cached items.
APDQC_SEMAPHORE_MEMORY TRUE if APDQC altered the semaphore table.
APDQC_SEMAPHORE_SCHEMA TRUE if APDQC altered the semaphore table schema.
APDQC_SESSIONS_SCHEMA TRUE if APDQC altered the sessions table schema.
APDQC_TABLE_COLLATIONS TRUE if APDQC altered the cache table collations.
APDQC_TABLE_INDEXES TRUE if APDQC altered the cache table indexes.

Classes

Namesort descending Description
ApdqcPrefetchDrupalDefaultEntityController Passthrough DrupalDefaultEntityController class for prefetching cache ids.