¿Necesito una tarea cron para procesar una cola?

32

Tengo una tarea que tarda unos 45 minutos en completarse y debe realizarse todos los días (sincronización de usuarios con varias bases de datos externas, etc.).

Para manejar el trabajo, he configurado una cola cron con hook_cron_queue_info()lo siguiente:

function mymodule_cron_queue_info() {
  $queues = array();
  $queues['update_users_queue'] = array(
    'worker callback' => '_mymodule_process_user_queue_item',
    'time' => 120,
  );
  return $queues;
}

Lleno la cola usando esta función:

function mymodule_queue_all_users_for_synching() {
  //...query for users...

  $queue = DrupalQueue::get('update_users_queue');
  foreach($users as $user) {
    $queue->createItem($user);
  }
}

La función de llenado de cola se llama como una tarea cron. Uso Elysia Cron , por lo que mi implementación de hook_cronapi()es:

function mymodule_cronapi($op, $job = NULL) {
  $items = array();
  $items['queue_users_for_synch'] = array(
    'description' => 'Queue all user accounts for synching.',
    'rule' => '0 3 * * *', // Run this job every day at 3am.
    'callback' => 'mymodule_queue_all_users_for_synching',
  );
  return $items;
}

La función de trabajo para cada elemento de la cola, definida en mymodule_cron_queue_infoes como:

function _mymodule_process_user_queue_item($item) {
  //...synchronize user ($item)...
}

Mi pregunta es, ¿ cuándo cron realmente comenzará a procesar la cola?

Supongamos que lleno la cola todos los días a las 3 a.m. y quiero procesarla 120 segundos de cada 30 minutos hasta que esté lista. ¿Necesito crear otra tarea cron?

joe_flash
fuente
Debo mencionar que estoy usando Drupal 7.
joe_flash
1
También tengo curiosidad por estas preguntas. Quisiera escuchar si o no. Usamos API de cola en gran medida en uno de nuestros proyectos D7. Visualmente he visto que las filas de la tabla {cola} se borran cuando se ejecuta el cron. Entonces supongamos que sí.
Sivaji

Respuestas:

19

Cuando Drupal ejecuta tareas cron, maneja automáticamente cualquier cola cron definida a partir de módulos, en drupal_cron_run(); primero hook_cron()se invocan implementaciones y luego se vacían las colas cron.

Por ejemplo hook_cronapi(), puede agregar una entrada para otra función que maneja la cola cron de su módulo.

function mymodule_cronapi($op, $job = NULL) {
  $items = array();

  $items['queue_users_for_synch'] = array(
    'description' => 'Queue all user accounts for synching.',
    'rule' => '0 3 * * *', // Run this job every day at 3am.
    'callback' => 'mymodule_queue_all_users_for_synching',
  );

  $items['clean_queue'] = array(
    'description' => 'Clean the queue for the user synching.',
    'rule' => '0 4 * * *', // Run this job every day at 4 AM.
    'callback' => 'mymodule_clean_queue',
  );

  return $items;
}

function mymodule_clean_queue() {
  $queues = module_invoke('mymodule', 'cron_queue_info');
  drupal_alter('cron_queue_info', $queues);

  // Make sure every queue exists. There is no harm in trying to recreate an
  // existing queue.
  foreach ($queues as $queue_name => $info) {
    DrupalQueue::get($queue_name)->createQueue();
  }

  foreach ($queues as $queue_name => $info) {
    $function = $info['worker callback'];
    $end = time() + (isset($info['time']) ? $info['time'] : 15);
    $queue = DrupalQueue::get($queue_name);
    while (time() < $end && ($item = $queue->claimItem())) {
      $function($item->data);
      $queue->deleteItem($item);
    }
  }
}

La alternativa es dejar que Drupal maneje la cola cron por usted, pero eso sucede cuando se ejecutan las tareas cron de Drupal. Si desea vaciar la cola cron de su módulo con más frecuencia, solo puede agregar una nueva tarea cron manejada por el módulo Elysia Cron.

El módulo Elysia Cron maneja las colas cron en elysia_cron_run(); esta función se invoca desde elysia_cron_cron()(una implementación de hook_cron()), drush_elysia_cron_run_wrapper()(una devolución de llamada de comando Drush) y desde su propio cron.php . Si siguió las instrucciones en el archivo INSTALL.txt (en particular en "PASO B: CAMBIO DE CRONTAB DEL SISTEMA (OPCIONAL)") y reemplazó cualquier invocación de http://example.com/cron.php con http: // ejemplo .com / sites / all / modules / elysia_cron / cron.php , el módulo Elysia Cron ya debería estar manejando las colas cron. El código que sugerí podría usarse para acelerar el manejo de las colas cron utilizadas desde su módulo, si efectivamente existe la necesidad de hacerlo.

// This code is part of the code executed from modules/elysia_cron/cron.php.
define('DRUPAL_ROOT', getcwd());

include_once DRUPAL_ROOT . '/includes/bootstrap.inc';
drupal_override_server_variables(array(
  'SCRIPT_NAME' => '/cron.php',
));
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);

if (!isset($_GET['cron_key']) || variable_get('cron_key', 'drupal') != $_GET['cron_key']) {
  watchdog('cron', 'Cron could not run because an invalid key was used.', array(), WATCHDOG_NOTICE);
  drupal_access_denied();
}
elseif (variable_get('maintenance_mode', 0)) {
  watchdog('cron', 'Cron could not run because the site is in maintenance mode.', array(), WATCHDOG_NOTICE);
  drupal_access_denied();
}
else {
  if (function_exists('elysia_cron_run')) {
    elysia_cron_run();
  }
  else {
    drupal_cron_run();
  }
}
kiamlaluno
fuente
Ah, gracias @kiam! Eso es lo que sospechaba, pero no podía entenderlo.
joe_flash
En realidad, creo que me estoy perdiendo algo aquí. Dijiste que la alternativa es dejar que Drupal maneje la cola cron por mí; Supongo que parte de mi pregunta original es ¿ cuándo sucede eso realmente ? Cada vez que crontab solicita cron.php? Si ese es el caso, eso sucede cada minuto (vea mi primer comentario sobre la respuesta de @ David).
joe_flash
1
Vale la pena mencionar que parece que Elysia cron tiene su propia implementación de procesador cron_queue elysia_cron_runcon colas cron que se procesan automáticamente cada vez que se solicita cron.php de Elysia.
David Thomas
@joe_flash Lo siento: debería haber sido más claro. Sí, Drupal ejecuta tareas cron cuando se invoca cron.php (para cada versión de Drupal hasta Drupal 7). En Drupal 8, cron.php ya no está presente, y las tareas cron se ejecutan usando una ruta de sistema diferente.
kiamlaluno
2

La cola se completará a través del gancho Elysia cronapi a la hora establecida.

Sin embargo, la cola se procesará siempre que se produzca la ejecución estándar de Drupal cron.

Vea este fragmento de procesamiento de devolución de llamada de trabajo al final del núcleo: drupal_cron_run

 foreach ($queues as $queue_name => $info) {
    $function = $info['worker callback'];
    $end = time() + (isset($info['time']) ? $info['time'] : 15);
    $queue = DrupalQueue::get($queue_name);
    while (time() < $end && ($item = $queue->claimItem())) {
      $function($item->data);
      $queue->deleteItem($item);
    }
  }
David Thomas
fuente
David, ¿quizás Elysia introduce un poco de complicación aquí? He configurado crontab para activar el cron.phpscript de Elysia cada minuto , lo que le permite a Elysia controlar los tiempos de las tareas con una resolución minuto a minuto. Sin embargo, ninguna tarea se ejecuta cada minuto, ¿qué es lo que me hizo pensar que necesitaba crear una tarea para trabajar específicamente en colas?
joe_flash
@joe_flash mientras drupal_cron_runse llame, se procesará la devolución de llamada del trabajador de la cola cron.
David Thomas
Ah, creo que tienes razón. Sin embargo, drupal_cron_runno se llama desde el cron.phpscript Elysia (cuando Elysia está habilitado); elysia_cron_runse usa en su lugar.
joe_flash
En ese caso, parece que no puede usarlo hook_cron_queue_infocon Elysia cron, a menos que especifique su propia devolución de llamada de trabajo, según el drupal_cron_runfragmento de función principal anterior.
David Thomas
elysia_cron_runno llama drupal_cron_run, pero se hace la llamada module_invoke_all('cron_queue_info')y hace unos pantalones de fantasía manejo que hace que el humo salga mis oídos multicanal.
joe_flash
1

como se indicó anteriormente al usar Elysia Cron, sus colas no se procesan.

usted (y drupal) no tienen acceso a las colas que de otro modo se ejecutarían en drupal_run_cron

la solución consiste en crear una tarea cron personalizada (esto será visible para elysia cron) para procesar todas las colas o una que desee e invocar el procesamiento de colas allí. es decir:

function mymodule_cron() {
// below is copied from drupal_cron_run() in common.inc

// Grab the defined cron queues.
$queues = module_invoke_all('cron_queue_info');
drupal_alter('cron_queue_info', $queues);

//if you want to target only one queue you can remove 'foreach'
and your $info = $queues['your_queue'];

  foreach ($queues as $queue_name => $info) {
    if (!empty($info['skip on cron'])) {
      // Do not run if queue wants to skip.
      continue;
    }
    $callback = $info['worker callback'];
    $end = time() + (isset($info['time']) ? $info['time'] : 15);
    $queue = DrupalQueue::get($queue_name);
     while (time() < $end && ($item = $queue->claimItem())) {
      try {
        call_user_func($callback, $item->data);
        $queue->deleteItem($item);
      }
      catch (Exception $e) {
        // In case of exception log it and leave the item in the queue
        // to be processed again later.
        watchdog_exception('cron', $e);
      }
    }
  }
}

ahora el procesamiento de colas puede ser controlado por ElysiaCron

masterperoo
fuente
0

No uso Elysia, pero mi solución siempre ha sido algo como esto:

function mymodule_cron() {
  $queue = DrupalQueue::get('mymoudule_queue');
  $queue->createQueue();
  $item = $queue->claimItem(300);

  if (!empty($item->data)) {

    // Do the work.

    if ($sucess) {
      $queue->deleteItem($item);
      watchdog('mymodule', 'It worked.');
    }
    else {
      watchdog('mymodule', 'It did not work!', array(), WATCHDOG_ALERT);
    }
  }
}

Solo maneja un elemento, para cada ejecución cron. Tal vez quieras cambiar eso.

Martin Poulsen
fuente
0

También he estado tratando de entender esto, ya que estoy usando la API de cola por primera vez junto con Elysia cron. Tras una inspección más cercana, puede ver que Elysia cron ejecuta elementos de la cola cuando se llama a la función elysia_cron_run . Vea este fragmento de la línea 1044 dentro del archivo elysia_cron.module :

if (EC_DRUPAL_VERSION >= 7) {
  // D7 Queue processing
  foreach ($queues as $queue_name => $info) {
    $function = $info['worker callback'];
    $end = time() + (isset($info['time']) ? $info['time'] : 15);
    $queue = DrupalQueue::get($queue_name);
    while (time() < $end && ($item = $queue->claimItem())) {
      $function($item->data);
      $queue->deleteItem($item);
    }
  }
}

Esto me ayudó a desmitificar el procesamiento de colas cuando utilicé Elysia cron.

Alex Kirsten
fuente