3

I am using Drupal Queue (Class DrupalQueue) to store items (a simple objects).

How can I check if there is already the same object in the queue before adding a new one ?

0

5 Answers 5

2

I initially used the answer provided by George, to do a text search for the given queue item. However, I decided that this would often end up with more overhead than just skipping the queue altogether.

So in the end, I changed it so that each queue item does it's job, then uses a similar query to clear out any queue items that are duplicate to itself. This puts the heavy query into the queue processing.

0
1

You cannot find the previously entered values in the queue, since the DrupalQueueInterface interface doesn't expose a method to retrieve a previously added value. You cannot even rely on the fact the value is added in a database table, since that is an implementation detail that can change with the class actually implementing the queue, which can be easily changed from a module.

That is the reason why modules like the Aggregator module take their data from their own database table, where they record the last time they added those values in the queue. (See the comment that says Add timestamp to avoid queueing item more than once.)

  $result = db_query('SELECT * FROM {aggregator_feed} WHERE queued = 0 AND checked + refresh < :time AND refresh <> :never', array(
    ':time' => REQUEST_TIME,
    ':never' => AGGREGATOR_CLEAR_NEVER
  ));
  $queue = DrupalQueue::get('aggregator_feeds');
  foreach ($result as $feed) {
    if ($queue->createItem($feed)) {
      // Add timestamp to avoid queueing item more than once.
      db_update('aggregator_feed')
        ->fields(array('queued' => REQUEST_TIME))
        ->condition('fid', $feed->fid)
        ->execute();
    }
  }

  // Remove queued timestamp after 6 hours assuming the update has failed.
  db_update('aggregator_feed')
    ->fields(array('queued' => 0))
    ->condition('queued', REQUEST_TIME - (3600 * 6), '<')
    ->execute();

You could implement a queue class that checks the value has been already inserted, and use that as default queue class, but a module cannot rely on such implementation details.

0

Michal, Here is how I solved this for myself: prior to calling createItem I was making a DB request to queue table searching for a value in data field.

Here is a piece of code that is working for my site: function

__my_custom_queue_add_id_for_worker_achievements_guides_flags_updates ($uid) {
    // But 1st confirm that this user is not already scheduled for recalc
    if (!db_query( // Returns Queue ID if found
        'SELECT item_id  FROM {queue} WHERE name = :name AND data LIKE :nid LIMIT 1',
        array(
            ':name' => 'achievements_guides_flags_updates',
            ':nid' => '%"' . $uid . '%',
        )
    )->fetchField()) {
        $queue = DrupalQueue::get('achievements_guides_flags_updates');
        $queue->createItem($uid);
    }
}
1
  • Just a heads up that text comparison is quite slow in SQL, so to compare the data in larger queues can result in performance issues. Feb 28, 2016 at 14:58
0

Hi I stumbled upon this question looking for an answer myself, and my team and I came up with a viable, performant solution using static caching.

$queue_cache = &drupal_static($cache_name, []);
if (!empty($queue_cache[$entity->id()])) {
  return;
}

... Build your queue data here ...

$queue->createItem($data);
$queue_cache[$entity->id()] = TRUE;

What this does is it creates a static cache during runtime when queueing items. It will first check if the entity id exists, and if so, skips it. Otherwise, it queues it normally. No need for querying blobs in the queue table or anything like that.

0

Create a class (or you can put it in the .module file if you want):

/**
 * Custom queue implementation to merge on creation, preventing duplicates.
 *
 * See https://www.webomelette.com/overriding-queues-drupal-7-or-how-not-create-duplicate-queue-items
 */
class MergeQueue extends SystemQueue {

  /**
   * Overriding the method to make sure no duplicate queue items get created.
   */
  public function createItem($data) {

    $serialized = serialize($data);

    // Merge instead of insert to prevent duplicates. An item is updated if it
    // already exists.
    $query = db_merge('queue')
      ->key(array('name' => $this->name, 'data' => $serialized))
      ->fields(array(
        'name' => $this->name,
        'data' => $serialized,
        'created' => time(),
      ));
    return (bool) $query->execute();
  }

}

if (module_exists('queue_ui')) {
  /**
   * Support Queue UI inspection.
   */
  class QueueUIMergeQueue extends QueueUISystemQueue {}
}

And in the .install file:

/**
 * Implements hook_install().
 */
function my_module_install() {
  variable_set('queue_class_merge_queue', 'MergeQueue');
}

/**
 * Implements hook_uninstall().
 */
function my_module_uninstall() {
  variable_del('queue_class_merge_queue');
}

See https://www.webomelette.com/overriding-queues-drupal-7-or-how-not-create-duplicate-queue-items

Your Answer

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

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