Some Drupal Acceleration Tips

  • Tutorial
Drupal is constantly blamed for its slowness, for the huge number of database queries and slowness. This is most often solved using Memcached or Varnish. In this article, I would like to add some more tips, the use of which will prevent Drupal from making it even slower. Those who are interested, please welcome under the cat.

We use the JS module


Along with the functionality of the site, both the amount of memory consumed and the number of SQL queries needed to complete a full Drupal download are growing. If you need to execute just one SQL query using AJAX, Drupal can spend a lot of time loading and executing code that might never be used in this query. The JS module solves this problem by providing an alternative way to load Drupal only to the level necessary to complete a specific task. Including allowing you to connect the necessary files and modules to process the request.

Drupal loads itself on every request, going through a series of bootstrap phases. All boot phases are defined in the bootstarp.inc file:
  • DRUPAL_BOOTSTRAP_CONFIGURATION: In this phase, the Drupal configuration array is filled in, the base URL is set, the settings.php file is analyzed, etc.
  • DRUPAL_BOOTSTRAP_PAGE_CACHE: Attempting to provide a page from the cache if page caching is enabled for anonymous users.
  • DRUPAL_BOOTSTRAP_DATABASE: The type of database is determined and a connection is established to perform database queries.
  • DRUPAL_BOOTSTRAP_VARIABLES: Initialize a variable system.
  • DRUPAL_BOOTSTRAP_SESSION: Initialize session processing.
  • DRUPAL_BOOTSTRAP_PAGE_HEADER: Set page title.
  • DRUPAL_BOOTSTRAP_LANGUAGE: Determine the language of the page.
  • DRUPAL_BOOTSTRAP_FULL: Loading modules and initializing the theme.

For example, if you only need to use the variable_get () function in the AJAX callback, the DRUPAL_BOOTSTRAP_VARIABLES level will suffice, and if you need access to the current $ user object, you need to use DRUPAL_BOOTSTRAP_SESSION, etc.

To work with the JS module, it is enough to implement the hook_js () hook, in which to describe which modules you need to connect, which bootstrap phase to use:
/**
 * Implements hook_js().
 */
function js_example_js() {
  return array(
    'results' => array(
      'callback' => 'js_example_ajax_results', // Функция обратного вызова необходимая для отображения результатов
      'includes' => array('unicode', 'locale', 'language'), // Какие файлы необходимо загрузить из директории /includes
      'dependencies' => array(), // Список модулей, которые необходимо загрузить
      'bootstrap' => DRUPAL_BOOTSTRAP_DATABASE, // Необходимая фаза загрузки Drupal
      'file' => 'js_example.ajax.inc', // Файл в котором находится callback
      'access callback' => 'js_example_ajax_results_access', // Функция обратного вызова для проверки прав доступа
      'access arguments' => array(), // Список аргументов для access callback
      'page arguments' => array(), // Список аргументов для callback
      'skip_hook_init' => TRUE, // Флаг, который позволяет пропустить выполнение hook_init(),
      'i18n' => FALSE, // Флаг, который позволяет включить или выключить поддержку i18n
    ),
  );
}

It is important to understand that it is quite difficult to control user access rights at the initial stages of Drupal loading, so you need to carefully monitor the security of your code.

Multiple Entity Loading


Very often it is necessary to add fields from the user profile to the node and the simplest solution for this is to use the template_preprocess_node () hook:
template_preprocess_node(&$variables) {
  $node = $variables['node'];
  $variables['account'] = user_load($node->uid);
}

But when displaying a large number of nodes, this approach will create a large number of queries to the database. You can get the same functionality without sacrificing performance using the hook_entity_prepare_view () hook:
hook_entity_prepare_view($entities, $type, $langcode) {
  if ($type != 'node') {
    return;
  }
  $uids = array();
  foreach ($entities as $entity) {
    $uids[] = $entity->uid;
  }
  $accounts = user_load_multiple($uids);
  foreach ($entities as $entity) {
    $entity->account = $accounts[$entity->uid];
  }
}

After that, $ entity-> account will be available in the preprocess:
template_preprocess_node(&$vars) {
  $account = $vars['node']->account;
}


Use drupal_static ()


When the code is executed several times during the same request, it is very convenient to use static variables for caching. (read more about static variables in PHP here and here ). The Drupal core provides an excellent solution for implementing static caching - the drupal_static () function. The drupal_static () function provides a central static variable for storing data. The first call to drupal_static () will return NULL, but any changes to this variable will be saved for the next call to this function. Thus, we can check whether the variable has already been set and get it instantly practically without doing any work.
function my_module_function() {
  $foo = &drupal_static(__FUNCTION__);
  global $user;
  if (!isset($foo[$user->uid])) {
    $foo[$user->uid] = something_expensive();
  }
  return $foo[$user->uid];
}


Frequent use of variable_set () affects performance


Variables in Drupal are stored in a special table in the format: name - serialized value. With each request, all variables are loaded from the cache into the global variable $ conf.
While saving each variable, the following happens:
  • Database entry updated
  • Cleared cache
  • When the following query finds that there is no cache for the variable table, all variables are loaded and written to the cache.

With a large number of variables, this can take a lot of time. Drupal implements a blocking system and any long-running operations, in parallel with which, most likely, other requests will come, should try to get a block before starting work. If the previous query cleared the variable cache, the next query will rebuild it, so the very frequent use of the variable_set () function can lead to massive blocking of the table, because dozens of requests are waiting for a new variable cache table entry, which may become outdated before it will be removed for use.

Session table shortening


Drupal stores user sessions in a database, not in files, so if on sites with high traffic this table can grow to huge sizes very quickly. If the session table has become very large, you can increase the garbage collection frequency for PHP sessions in settings.php:
ini_set('session.gc_maxlifetime', 86400); 


I hope that not all of this list is very obvious things and it will come in handy for someone.

Also popular now: