Commit 0f10d21c authored by catch's avatar catch
Browse files

Issue #3116478 by clayfreeman, jungle, mrinalini9, jonathanshaw, neclimdul,...

Issue #3116478 by clayfreeman, jungle, mrinalini9, jonathanshaw, neclimdul, Charlie ChX Negyesi, longwave, alexpott, catch, andypost: Add a way to silently keep an item locked when processing a queue via cron
parent 856ab5fa
Loading
Loading
Loading
Loading
+14 −0
Changes for core/lib/Drupal/Core/Cron.php: 14 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -8,7 +8,9 @@
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Lock\LockBackendInterface;
use Drupal\Core\Queue\QueueFactory;
use Drupal\Core\Queue\DelayableQueueInterface;
use Drupal\Core\Queue\QueueWorkerManagerInterface;
use Drupal\Core\Queue\DelayedRequeueException;
use Drupal\Core\Queue\RequeueException;
use Drupal\Core\Queue\SuspendQueueException;
use Drupal\Core\Session\AccountSwitcherInterface;
@@ -180,6 +182,18 @@ protected function processQueues() {
            $queue_worker->processItem($item->data);
            $queue->deleteItem($item);
          }
          catch (DelayedRequeueException $e) {
            // The worker requested the task not be immediately re-queued.
            // - If the queue doesn't support ::delayItem(), we should leave the
            // item's current expiry time alone.
            // - If the queue does support ::delayItem(), we should allow the
            // queue to update the item's expiry using the requested delay.
            if ($queue instanceof DelayableQueueInterface) {
              // This queue can handle a custom delay; use the duration provided
              // by the exception.
              $queue->delayItem($item, $e->getDelay());
            }
          }
          catch (RequeueException $e) {
            // The worker requested the task be immediately requeued.
            $queue->releaseItem($item);
+30 −3
Changes for core/lib/Drupal/Core/Queue/DatabaseQueue.php: 30 added lines, 3 removed lines.
Original line number Diff line number Diff line
@@ -11,7 +11,7 @@
 *
 * @ingroup queue
 */
class DatabaseQueue implements ReliableQueueInterface, QueueGarbageCollectionInterface {
class DatabaseQueue implements ReliableQueueInterface, QueueGarbageCollectionInterface, DelayableQueueInterface {

  use DependencySerializationTrait;

@@ -89,7 +89,7 @@ protected function doCreateItem($data) {
        'data' => serialize($data),
        // We cannot rely on REQUEST_TIME because many items might be created
        // by a single request which takes longer than 1 second.
        'created' => time(),
        'created' => \Drupal::time()->getCurrentTime(),
      ]);
    // Return the new serial ID, or FALSE on failure.
    return $query->execute();
@@ -140,7 +140,7 @@ public function claimItem($lease_time = 30) {
      // should really expire.
      $update = $this->connection->update(static::TABLE_NAME)
        ->fields([
          'expire' => time() + $lease_time,
          'expire' => \Drupal::time()->getCurrentTime() + $lease_time,
        ])
        ->condition('item_id', $item->item_id)
        ->condition('expire', 0);
@@ -171,6 +171,33 @@ public function releaseItem($item) {
    }
  }

  /**
   * {@inheritdoc}
   */
  public function delayItem($item, int $delay) {
    // Only allow a positive delay interval.
    if ($delay < 0) {
      throw new \InvalidArgumentException('$delay must be non-negative');
    }

    try {
      // Add the delay relative to the current time.
      $expire = \Drupal::time()->getCurrentTime() + $delay;
      // Update the expiry time of this item.
      $update = $this->connection->update(static::TABLE_NAME)
        ->fields([
          'expire' => $expire,
        ])
        ->condition('item_id', $item->item_id);
      return $update->execute();
    }
    catch (\Exception $e) {
      $this->catchException($e);
      // If the table doesn't exist we should consider the item nonexistent.
      return TRUE;
    }
  }

  /**
   * {@inheritdoc}
   */
+34 −0
Changes for core/lib/Drupal/Core/Queue/DelayableQueueInterface.php: 34 added lines, 0 removed lines.
Original line number Diff line number Diff line
<?php

namespace Drupal\Core\Queue;

/**
 * Delayable queue interface.
 *
 * Classes implementing this interface allow an item to be released on a delay.
 *
 * @ingroup queue
 */
interface DelayableQueueInterface extends QueueInterface {

  /**
   * Delay an item so it runs in the future.
   *
   * @param object $item
   *   The item returned by \Drupal\Core\Queue\QueueInterface::claimItem().
   * @param int $delay
   *   A delay before the item's lock should expire (in seconds). Relative to
   *   the current time, not the item's current expiry.
   *
   * @throws \InvalidArgumentException
   *   When a negative $delay is provided; $delay must be non-negative.
   *
   * @see \Drupal\Core\Queue\QueueInterface::releaseItem()
   *   To immediately release an item without delay.
   *
   * @return bool
   *   TRUE if the item has been updated, FALSE otherwise.
   */
  public function delayItem($item, int $delay);

}
+53 −0
Changes for core/lib/Drupal/Core/Queue/DelayedRequeueException.php: 53 added lines, 0 removed lines.
Original line number Diff line number Diff line
<?php

namespace Drupal\Core\Queue;

/**
 * Throw this exception to leave an item in the queue until its lock expires.
 *
 * @see \Drupal\Core\Cron::processQueues()
 *   For more information about how this exception interacts with Drupal's queue
 *   processing via the built-in cron service.
 * @see \Drupal\Core\Queue\DelayableQueueInterface
 *   Queues must implement this interface to support custom delay intervals; if
 *   this interface is missing, any custom delay interval specified for this
 *   exception will be ignored and the remaining time in the original lease will
 *   be used as the duration of the delay interval.
 * @see \Drupal\Core\Queue\RequeueException
 *   For use when an item needs to be requeued immediately.
 */
class DelayedRequeueException extends \RuntimeException {

  /**
   * The interval of time that the item should remain locked (in seconds).
   *
   * @var int
   */
  protected $delay = 0;

  /**
   * Constructs a DelayedRequeueException.
   *
   * @param int $delay
   *   The desired delay interval for this item.
   */
  public function __construct(int $delay = 0) {
    if ($delay > 0) {
      $this->delay = $delay;
    }
  }

  /**
   * Get the desired delay interval for this item.
   *
   * @see self::$delay
   *   For recommended value usage in a queue processor.
   *
   * @return int
   *   The desired delay interval for this item.
   */
  public function getDelay(): int {
    return $this->delay;
  }

}
+3 −2
Changes for core/lib/Drupal/Core/Queue/Memory.php: 3 added lines, 2 removed lines.
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@
 * @ingroup queue
 */
class Memory implements QueueInterface {

  /**
   * The queue data.
   *
@@ -44,7 +45,7 @@ public function createItem($data) {
    $item = new \stdClass();
    $item->item_id = $this->idSequence++;
    $item->data = $data;
    $item->created = time();
    $item->created = \Drupal::time()->getCurrentTime();
    $item->expire = 0;
    $this->queue[$item->item_id] = $item;
    return $item->item_id;
@@ -63,7 +64,7 @@ public function numberOfItems() {
  public function claimItem($lease_time = 30) {
    foreach ($this->queue as $key => $item) {
      if ($item->expire == 0) {
        $item->expire = time() + $lease_time;
        $item->expire = \Drupal::time()->getCurrentTime() + $lease_time;
        $this->queue[$key] = $item;
        return $item;
      }
Loading