Preciso de uma tarefa cron para processar uma fila?

32

Tenho uma tarefa que leva cerca de 45 minutos para ser concluída e precisa acontecer todos os dias (sincronizando usuários com vários bancos de dados externos, etc.).

Para lidar com o trabalho, configurei uma fila cron com hook_cron_queue_info()o seguinte:

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

Encho a fila usando esta função:

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

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

A função de preenchimento de fila é chamada como uma tarefa cron. Eu uso o Elysia Cron , então minha implementação hook_cronapi()é:

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;
}

A função de trabalho para cada item da fila, definida em, mymodule_cron_queue_infoé como:

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

Minha pergunta é: quando o cron realmente começará a processar a fila?

Digamos que eu preencha a fila todos os dias às 3 da manhã e queira processá-la em 120 segundos a cada 30 minutos até que ela termine - preciso criar outra tarefa cron?

joe_flash
fonte
Devo mencionar que estou usando Drupal 7.
joe_flash
1
Também estou curioso sobre essas questões. Gostaria de ouvir sim ou não. Usamos a API da fila fortemente em um de nossos projetos D7. Visualmente, vi linhas da tabela {fila} serem limpas quando o cron for executado. Então, presuma que sim.
Sivaji

Respostas:

19

Quando o Drupal executa tarefas cron, ele lida automaticamente com qualquer fila cron definida nos módulos, in drupal_cron_run(); as primeiras hook_cron()implementações são invocadas e as filas cron são esvaziadas.

Na implementação hook_cronapi(), você pode adicionar uma entrada para outra função que lida com a fila cron do seu 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);
    }
  }
}

A alternativa é permitir que o Drupal lide com a fila cron para você, mas isso acontece quando as tarefas do cron Drupal são executadas. Se você quiser esvaziar a fila do cron do seu módulo com mais frequência, poderá adicionar apenas uma nova tarefa do cron manipulada pelo módulo Elysia Cron.

O módulo Elysia Cron lida com as filas cron elysia_cron_run(); esta função está sendo chamada de elysia_cron_cron()(uma implementação de hook_cron()), drush_elysia_cron_run_wrapper()(um retorno de chamada do comando Drush) e de seu próprio cron.php . Se você seguiu as instruções no arquivo INSTALL.txt (em particular na "ETAPA B: ALTERAR O SISTEMA CRONTAB (OPCIONAL)") e substituiu qualquer chamada de http://example.com/cron.php por http: // example .com / sites / all / modules / elysia_cron / cron.php , o módulo Elysia Cron já deve estar lidando com as filas do cron. O código sugerido pode ser usado para acelerar o processamento das filas cron usadas no seu módulo, se houver efetivamente a necessidade de fazê-lo.

// 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
fonte
Ah, obrigado @kiam! Era disso que eu suspeitava, mas não conseguia entender bem o meu cérebro.
Joe_flash
Na verdade, acho que estou perdendo alguma coisa aqui. Você disse que a alternativa é deixar o Drupal cuidar da fila cron para mim; Eu acho que parte da minha pergunta original é quando isso realmente acontece ? Cada vez que o crontab solicita cron.php? Se for esse o caso, isso acontece a cada minuto (veja meu primeiro comentário na resposta de @ David).
joe_flash
1
Vale ressaltar que parece que o Elysia cron possui sua própria implementação de processador cron_queue, elysia_cron_runcom filas cron sendo processadas automaticamente toda vez que o cron.php do Elysia é solicitado.
David Thomas
@joe_flash Me desculpe: eu deveria ter sido mais claro. Sim, o Drupal executa tarefas cron quando o cron.php é chamado (para todas as versões do Drupal até o Drupal 7). No Drupal 8, o cron.php não está mais presente e as tarefas do cron são executadas usando um caminho de sistema diferente.
kiamlaluno
2

A fila será preenchida por meio do gancho Elysia cronapi no horário definido.

No entanto, a fila será processada sempre que ocorrer a execução cron padrão do Drupal.

Veja este fragmento de processamento de retorno de chamada do trabalhador no final do 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
fonte
David, talvez Elysia introduza um pouco de complicação aqui? Eu configurei o crontab para acionar o cron.phpscript Elysia a cada minuto , o que permite que o Elysia controle o tempo das tarefas com uma resolução minuciosa. Porém, nenhuma tarefa é executada a cada minuto - e foi isso que me fez pensar que precisava criar uma tarefa para trabalhar especificamente em filas?
Joe_flash
@joe_flash enquanto drupal_cron_runestiver sendo chamado, seu retorno de chamada do trabalhador da fila cron será processado.
David Thomas
Ah, acho que você está certo. No entanto, drupal_cron_runnão é chamado a partir do cron.phpscript Elysia (quando o Elysia está ativado); elysia_cron_runé usado em seu lugar.
Joe_flash
Nesse caso, parece que você não pode usar o hook_cron_queue_infoElysia cron, a menos que especifique seu próprio retorno de chamada do trabalhador, conforme o drupal_cron_runsnippet da função principal acima.
David Thomas
elysia_cron_runnão chama drupal_cron_run, mas faz chamada module_invoke_all('cron_queue_info')e faz algumas fantasia-calças manipulação que faz fumaça sair meus ouvidos multi-canal.
Joe_flash
1

conforme indicado acima, ao usar o Elysia Cron, suas filas não são processadas.

você (e drupal) não tem acesso a filas que seriam executadas em drupal_run_cron

a solução alternativa é criar uma tarefa cron personalizada - (isso será visível para elysia cron) para processar todas as filas ou uma que você deseja e chamar o processamento da fila lá. ou seja:

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);
      }
    }
  }
}

agora o processamento de filas pode ser controlado pelo ElysiaCron

masterperoo
fonte
0

Não uso o Elysia, mas minha solução sempre foi assim:

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);
    }
  }
}

Ele lida apenas com um item, para cada execução do cron. Talvez você queira mudar isso.

Martin Poulsen
fonte
0

Eu também tenho tentado entender isso, pois estou usando a API da fila pela primeira vez junto com o Elysia cron. Após uma inspeção mais detalhada, você pode ver que o Elysia cron executa itens da fila quando a função elysia_cron_run é chamada. Veja este trecho da linha 1044 no arquivo 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);
    }
  }
}

Isso ajudou a desmistificar o processamento da fila para mim ao usar o Elysia cron.

Alex Kirsten
fonte