Hero

Como crear cola de ejecución para el cron en Drupal 7

Abril 08, 2013

kenneth
Drupal

En algunas ocasiones debemos ejecutar una o varias acciones las cuales en conjunto podrían sobrepasar el tiempo de ejecución configurado en nuestro servidor web y esto puede llevar a colapsar nuestro servidor ya que son procesos muy pesados. En estos casos particulares Drupal nos ofrece la facilidad de crear colas de ejecución para el cron, donde podemos definir los procesos que se efectuaran de tal manera que Drupal administre como manejarlo evitando sobrecargar el servidor para lograr finalizar las tareas.

Imaginemos que tenemos un sitio web donde hacemos publicaciones de noticias y tenemos muchos usuarios registrados, entonces se nos presenta el siguiente requerimiento: “Notificar a todos los usuarios sobre una nueva noticia cuando esta sea creada y catalogada con la taxonomía de carácter importante***”.***

  1. Crear una cola en Drupal.

Lo primero que definiremos es una constante con el nombre de nuestra cola, que sera el valor inequívoco para poder identificar nuestra nueva cola e implementaremos el hook hook_queue_info donde podríamos definir mas de una cola de procesos e indicaremos cual función se ejecutara para cada uno de los items de la cola, el código de implementación se podría ver como el siguiente:

<pre title="Implementar hook_queue_info">define('QUEUE', 'notificar_todo_usuario');

function modulo_personalizado_cron_queue_info() {
  $queues = array();
  $queues[QUEUE] = array(
    'worker callback' => 'modulo_personalizado_notificar_usuarios_worker',
    'time' => 60,
  );

  return $queues;
}
  1. Definir función de procesamiento para la cola.

Ahora definiremos la función modulo_personalizado_notificar_usuarios_worker donde podremos ejecutar las notificaciones a los usuarios definiendo un tope de cuantos usuarios notificar por cada llamada, ahora bien, también allí mismo crearemos otro item para que sea ejecutado nuevamente a por la misma cola, generando un ciclo hasta que se termine completamente la tarea y todos los usuario hayan sido notificados, la función se puede ver de la siguiente manera:

<pre title="Definiendo funcion para notificar">define('MAXIMO_POR_PROCESO', 10);

function modulo_personalizado_notificar_usuarios_worker($item) {

  $uids = $item['uids'];
  for ($index = 0; $index < MAXIMO_POR_PROCESO; $index++) {
    /*
      Codigo para enviar una notificacion personalizada....
     */
    unset($uids[$index]);
  }

  // Crear nuevamente un elemento en la cola para volver a ser ejecutado, 
  // si aun faltan usuario por notificar,
  if (!empty($uids)) {
    $queue = DrupalQueue::get(QUEUE);
    $element = array(
      'nid' => $item['nid'],
      'uids' => $uids,
    );
    $queue->createItem($element);
  }
}

Con esto podemos estar seguros que todos los usuarios almacenados serán notificados sobre la nueva noticia creada, y lo mas importante cumplir con esta tarea evitando que el servidor se sature, aun así debemos hacer el primer llamado a la cola donde definiremos todos los usuarios quienes sera notificados.

  1. Colocar elementos en la cola.

Utilizaremos el hook hook_node_insert para verificar cuando un nuevo nodo es agregado y que además contenga la taxonomía importante, dentro de la implementación se busca a todos los identificadores de los usuarios existentes excluyendo el usuario actual y el usuario anónimo para enviarles un correo electrónico con la notificación, entonces para evitar saturar el servidor, debemos crear una cola quien se encargara del trabajo.

El código con la implementacion de este hook se podría ver de la siguiente manera:

<pre title="Implementar hook_node_insert">define('CATEGORIA_IMPORTANTE', 'importante');

function modulo_personalizado_node_insert($node) {

  // Verificar:
  // *Si el nodo es del tipo "noticia".
  // *Si el nodo contiene categorias (taxonomias) relacionadas.
  if (isset($node->type) && $node->type == 'noticia' && (isset($node->field_categorias) && !empty($node->field_categorias))) {

    // Recorer todas las categorias relacionadas.
    foreach ($node->field_categorias[LANGUAGE_NONE] as $key => $value) {
      $term = (object) $value;
      if (!isset($term->name)) {
        $term = taxonomy_term_load($term->tid);
      }

      if (CATEGORIA_IMPORTANTE == $term->name) {
        // Notificar a todos los usuarios usando una cola.
        global $user;

        $uids = db_select('users', 'u')
                ->fields('u', array('uid'))
                ->condition('uid', $user->uid, '!=')
                ->condition('uid', 0, '!=')
                ->execute()->fetchCol();

        $queue = DrupalQueue::get(QUEUE);
        $element = array(
          'nid' => $node->nid,
          'uids' => $uids,
        );
        $queue->createItem($element);
      }
    }
  }
}

Espero que esto les sea de mucha ayuda para sus tareas masivas que pueden consumir muchos recursos y de esta manera evitar que el servidor falle,

Saludos,

Recibe consejos y oportunidades de trabajo 100% remotas y en dólares de weKnow Inc.