0

I created module to import users from other DB to drupal. I used batch API, tried all kind of combinations but It is usually very slow in doing this and also constantly breaking with error

General error: 1205 Lock wait timeout exceeded; try restarting transaction: INSERT INTO {users}...

I also tried changing "batch config" with raising

if ($batch['progressive'] && timer_read('batch_processing') > 10000)

to 10 seconds or so (found this advice somewhere) but it didnt help. What I realised is the user_save(null, $newUser) makes this happen. For some reason saving new users data makes this problem, when I remove it batch goes without the problem to the end.

Here is the full module code, with comments and lots of testing code and different variables that can be set, but whatever I changed the same situation happens.

<?php


/**
 * Implementation of hook_menu()
 */
function import_user_migration_menu() {
  $items['admin/config/import_user_migration_form'] = array(
    'title' => t('Migrate users from old site.'),
    'page callback' => 'drupal_get_form',
    'page arguments' => array('import_user_migration_form'),
    'access arguments' => array('administer site configuration'),
    );

  return $items;
}

function import_user_migration_form($form, &$form_state) {
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Start processing...'),
    );

  return $form;
}

function import_user_migration_form_submit($form, &$form_state) {
  //switch to old user db
  _import_user_migration_switch_to_old();

$start = 0;
$end_count = 50;
  $query = db_select('lelo_customer_temp', 't');
  $query->fields('t', array('customerId', 'email_address', 'password', 'cust_name'));
  $query->condition('t.password', '', '<>');
  $query->range($start, $end_count);
  //$query->condition('t.Cust_Bill_Email', '', '<>');

  $results = $query->execute()->fetchCol();
  $user_count = count($results);
  //$user_count = 450;
  $batch_size = 50;

  //Break up all of our data so each process does not time out.
  $chunks = array_chunk($results, $batch_size);
  $operations = array();
  $count_chunks = count($chunks);
  //switch to new db
  db_set_active('default');


  //for every chunk, assign some method to run on that chunk of data
  $i = 0;
  foreach ($chunks as $chunk) {
    $i++;
    $operations[] = array("import_user_migration_batch_process", array( $chunk ,'details'=> t('(Importing chunk @chunk  of @count)', array('@chunk '=>$i, '@count'=>$count_chunks)), $batch_size));
  }
  $batch = array(
    'operations' => $operations,
    'init_message' => t('Batch is starting.'),
    'progress_message' => t('Processed @current out of @total.'),
    'error_message' => t('Batch has encountered an error.'),
    'finished' => 'import_user_migration_batch_finished',
    );
  batch_set($batch);
}

function import_user_migration_batch_process($uids, $operation_details, $batch_size,  &$context) {

//set defaults for the first time
  if (!isset($context['sandbox']['progress'])) {
    $context['sandbox']['progress'] = 0;
    $context['sandbox']['current_user'] = 0;
    $context['sandbox']['max'] =  $batch_size ;
  }

  $context['message'] = $operation_details; //Will show what chunk we're on.

  //dpm($context);

  foreach ($uids as $uid) {

    _import_user_migration_switch_to_old();
    $old_user = _import_user_migration_user_load($uid);
    db_set_active('default');
    $user_info = array(
      'uid' => $uid,
      'name' => $old_user['cust_name'],
      'pass' => $old_user['password'],
      'mail' => check_plain($old_user['email_address']),
      );

      // Update our progress information.
    $context['sandbox']['progress']++; 
    $context['sandbox']['current_user'] = $uid;

  $context['message'] = "Working on " . $old_user['cust_name'] . ' : ' . check_plain( $old_user['email_address']);
  $context['results'][] = $old_user['cust_name'] . ' : ' . check_plain( $old_user['email_address']);


    //Notice: Trying to get property of non-object in import_user_migration_batch_process() (line 94 of /Applications/MAMP/htdocs/lelo-pht/sites/all/modules/custom/import_user_migration/import_user_migration.module).
    if (user_load_by_mail(check_plain($old_user['email_address']))) {
    //user found
    } else {
    //user NOT found
      _import_user_migration_create_user($user_info);
    }
  }

   // Inform the batch engine that we are not finished,
  // and provide an estimation of the completion level we reached.
  if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
    $context['finished'] = ($context['sandbox']['progress'] >= $context['sandbox']['max']);
  }
    _import_user_migration_update_http_requests();
}

function import_user_migration_batch_finished($success, $results, $operations) {
  if ($success) {
    // Here we could do something meaningful with the results.
    // We just display the number of nodes we processed...
    drupal_set_message(t('@count results processed in @requests HTTP requests.', array('@count' => count($results), '@requests' => _import_user_migration_get_http_requests())));
    drupal_set_message(t('The final result was "%final"', array('%final' => end($results))));
  }
  else {
    // An error occurred.
    // $operations contains the operations that remained unprocessed.
    $error_operation = reset($operations);
    drupal_set_message(t('An error occurred while processing @operation with arguments : @args', array(
      '@operation' => $error_operation[0],
      '@args' => print_r($error_operation[0], TRUE),
    )));
  }
}

/**
 * Switch to database import_user
 */
function _import_user_migration_switch_to_old() {
  $other_database = array(
    'database' => 'users',
    'username' => 'root',
    'password' => 'root',
    'host' => 'localhost',
    'driver' => 'mysql',
    );
  Database::addConnectionInfo('old', 'default', $other_database);
  db_set_active('old');
}

/**
 * Create a user in new site
 * 
 * @param array $user_info
 *   array('uid', 'name', 'pass', 'mail')
 */
function _import_user_migration_create_user($user_info) {
  $newUser = array(
    'name' => $user_info['name'],
    'pass' => $user_info['pass'],
    'mail' => $user_info['mail'],
    'status' => 1,
    'init' => $user_info['mail'],
    'roles' => array(DRUPAL_AUTHENTICATED_RID => 'authenticated user'),
    );
    user_save(null, $newUser);
}

/**
 * Load old user data
 * 
 * @return array 
 *   array('uid', 'name', 'pass', 'mail')
 */
function _import_user_migration_user_load($uid) {
  $query = db_select('lelo_customer_temp', 't');
  $query->fields('t', array('customerId', 'cust_name', 'password', 'email_address'));
  $query->condition('t.customerId', $uid);

  $results = $query->execute()->fetchObject();

  return (array) $results;
}

function _import_user_migration_update_http_requests() {
  $_SESSION['http_request_count']++;
}

function _import_user_migration_get_http_requests() {
  return !empty($_SESSION['http_request_count']) ? $_SESSION['http_request_count'] : 0;
}
3
  • Maybe this some kind of DB locking described here stackoverflow.com/questions/22931722/… ?Try to set wait_timeout in mysql.
    – FriOne
    Nov 25, 2014 at 20:01
  • Checkout slides 74-106 from my DIY Drupal 7 Performance presentation. Should give you some ways to solve it. My guess is you need to set the transaction iso level to READ COMMITTED.
    – mikeytown2
    Nov 25, 2014 at 21:55
  • If I add the recommended setting to settings.php ($databases['default']['default']['init_commands'] = array( 'isolation' => "SET SESSION tx_isolation='READ-COMMITTED'" ); I get a broken site with this error :( Additional uncaught exception thrown while handling exception. Original PDOException: SQLSTATE[HY000]: General error: 1665 Cannot execute statement: impossible to write to binary log since BINLOG_FORMAT = STATEMENT and at least one table uses a storage engine limited to row-based logging
    – Blissful
    Nov 26, 2014 at 8:23

1 Answer 1

0

Finally this is the answer to my problems.

User_save is very slow for no apparent reason

so that problem (cURL slow response) made the above.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.