TransWikia.com

How can execute batch api over cron jobs

Drupal Answers Asked by JohnTang on September 6, 2020

I am working on Drupal 8 and batch API. I created the batch api calling importingmatch and it should automatically importing every 1 hour.

My solution is use Drush for make it works without the browser, but I don’t know how I can get the batch id.

Here is my code about the Batch API:

// Set up the Batch API
    $batch = array(
        'operations' => $operations,
        'finished' => 'importingmatch_finishedBatch',
        'title' => t('Import match'),
        'init_message' => t('Starting import match.....'),
        'progress_message' => t('Completed @current step of @total.'),
        'error_message' => t('Import match deletion has encountered an error.'),
    );

    batch_set($batch);

Currently, this batch is runing perfect if I access from my PC. Url: www.mysite.com/importing_match
I am not sure how make it working from the server Centos with Drush, I need the batch ID for execute: Drush batch-process

UPDATE:
– Interesting with the Command for Drush, I created custom Command from my module. Following from here: https://www.chapterthree.com/blog/how-to-create-custom-drush-commands. But it’s still not working, it’s showing:

The drush command '<name>' could not be found.

I changed from localhost to 127.0.0.1 from the settings.php but it’s not helpful. Any idea?

3 Answers

Instead of using Cron API you could provide your own custom Drush command and implement Batch API there. And then execute this Drush command using your server's crontab.

Following comes a sample command that implements a batch. Inside module_name.drush.inc put:

function module_name_drush_command() {
  $items = array();
  $items['migrate-batch-list'] = array(
    'description' => 'Migrate import match',
    'examples' => array(
      'drush migrate-batch-list' => 'Migrate import match',
    ),
  );
return $items;
}

function drush_module_name_migrate_batch_list() {
  drush_print('Migrate import match list.');

  $operations = array(...something here you want.);
  $batch = array(
    'operations' => $operations,
    'finished' => 'importingmatch_finishedBatch',
    'title' => t('Import match'),
    'init_message' => t('Starting import match.....'),
    'progress_message' => t('Completed @current step of @total.'),
    'error_message' => t('Import match deletion has encountered an error.'),
  );

  // Initialize the batch.
  batch_set($batch);

  // Start the batch process.
  drush_backend_batch_process();
}

Note: You can run this from terminal as drush migrate-batch-list

Correct answer by Ashish Deynap on September 6, 2020

Sometimes it's just simply not possible to avoid batch process in cron jobs (user_cancel using batch...). My case was to delete users periodically if they were inactive for a certain time. Here is how I solved it, it can give a good idea of how you can use it, I've stripped out the totally irrelevant parts leaving some guides for easier adaption.

function MODULE_cron() {
  [...]
  $to_delete_users = User::loadMultiple(/* User ids to be deleted */);

  $edit = [
    'user_cancel_notify' => FALSE,
  ];
  $failed_deletion_ids = [];
  foreach ($to_delete_users as $user) {
    [...]

    try {
      // Mark user for cancel.
      user_cancel($edit, $user->id(), 'user_cancel_reassign');

      // Drush check is failing, due wrong condition, however this may not
      // correct either, isset($batch['sets']) could be better.
      $batch =& batch_get();
      if (count($batch) > 0) {
        // Process the deletion with batch.
        // This is not ideal, but in this way we won't notify users that
        // wasn't deleted at the end also it doesn't break the whole process,
        // max the current loop will fail.
        if (function_exists('drush_backend_batch_process')) {
          // For drush.
          drush_backend_batch_process();
        }
        else {
          // For the rest.
          $batch['progressive'] = FALSE;
          batch_process();
        }
      }

      $mail_manager->mail(/* Parameters for mail sending */);
    }
    catch (Exception $e) {
      $failed_deletion_ids[] = $user->id();
      Drupal::logger('cron')->error('Failed to cancel %uid user account. Please check the error message: %message in %file file at line %line and the backtrace: %backtrace NOTE: this error may not the original one!', [
        '%uid' => $user->id(),
        '%message' => $e->getMessage(),
        '%file' => $e->getFile(),
        '%line' => $e->getLine(),
        '%backtrace' => $e->getTraceAsString(),
      ]);
    }
    finally {
      // Reset batch.
      $batch = [];
    }
  }
  /* Some other stuff and make a report */
  [...]
}

There are two important things to note here.
First, if you using the drush function like it was suggested in the other answer, Behat can fail (if you have or the UI cron execution), because in that case drush is not loaded. That's why I've checked if it exists or not.
Second, if you want to run another batch process after a finished one, first you need to reset back to default the batch variable, or it will hang on the second run/call/loop.
This solution should also work when you call cron from the UI.

Answered by golddragon007 on September 6, 2020

You don't. Batch API is to do long-running processing with user-interaction.

For cron, you either use queue API or you just process for N seconds/things in your hook_cron() implementation, and in both cases, cron will be calling you repeatedly until it is eventually done.

If you want to process as fast as possible then either use a different queue processing that doesn't run on cron or make a custom script/drush command that you execute separate from cron, so that you don't block other cron jobs.

Answered by Berdir on September 6, 2020

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP