Commit 3e27fd4c authored by Youri van Koppen's avatar Youri van Koppen
Browse files

Issue #3132198 by MegaChriz: Clean queue tasks when unlocking a feed to...

Issue #3132198 by MegaChriz: Clean queue tasks when unlocking a feed to prevent potential data loss.
parent 0f49a1ce
Loading
Loading
Loading
Loading
+5 −1
Changes for feeds.services.yml: 5 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -53,10 +53,14 @@ services:
    arguments: ['@entity_type.manager', '@entity.repository']
  feeds.lock:
    class: Drupal\feeds\Lock\FeedsLockBackend
    arguments: ['@database', '@entity_type.manager', '@config.factory']
    arguments: ['@database', '@entity_type.manager', '@config.factory', '@logger.channel.feeds']
    tags:
      - { name: backend_overridable }

  feeds_plugin_form_factory:
    class: Drupal\feeds\Plugin\PluginFormFactory
    arguments: ['@class_resolver']

  logger.channel.feeds:
    parent: logger.channel_base
    arguments: ['feeds']
+16 −6
Changes for src/Entity/Feed.php: 16 added lines, 6 removed lines.
Original line number Diff line number Diff line
@@ -312,6 +312,15 @@ class Feed extends ContentEntityBase implements FeedInterface {
      ->hasQueueTasks($this);
  }

  /**
   * {@inheritdoc}
   */
  public function clearQueueTasks(): void {
    $this->entityTypeManager()
      ->getHandler('feeds_feed', 'feed_import')
      ->clearQueueTasks($this);
  }

  /**
   * {@inheritdoc}
   */
@@ -348,10 +357,6 @@ class Feed extends ContentEntityBase implements FeedInterface {
    // Allow other modules to react upon finishing importing.
    $this->eventDispatcher()->dispatch(new ImportFinishedEvent($this), FeedsEvents::IMPORT_FINISHED);

    // Cleanup.
    $this->clearStates();
    $this->setQueuedTime(0);

    $this->set('imported', $time);

    $interval = $this->getType()->getImportPeriod();
@@ -359,8 +364,8 @@ class Feed extends ContentEntityBase implements FeedInterface {
      $this->set('next', $interval + $time);
    }

    $this->save();
    $this->unlock();
    $this->save();
  }

  /**
@@ -514,8 +519,13 @@ class Feed extends ContentEntityBase implements FeedInterface {
   */
  public function unlock() {
    \Drupal::service('feeds.lock')->release("feeds_feed:{$this->id()}");
    // @todo clean up stale queue tasks.
    Cache::invalidateTags(['feeds_feed_locked']);

    // Clean up import states and stale queue tasks.
    $this->clearStates();
    $this->setQueuedTime(0);
    $this->clearQueueTasks();
    $this->save();
  }

  /**
+16 −1
Changes for src/FeedImportHandler.php: 16 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -100,7 +100,7 @@ class FeedImportHandler extends FeedHandlerBase {

    $result = $this->database->select('queue')
      ->fields('queue', [])
      ->condition('data', 'a:3:{i:0;i:' . $feed->id() . '%', 'LIKE')
      ->condition('data', 'a:3:{i:0;i:' . $feed->id() . ';%', 'LIKE')
      ->countQuery()
      ->execute()
      ->fetchField();
@@ -108,6 +108,21 @@ class FeedImportHandler extends FeedHandlerBase {
    return $result > 0;
  }

  /**
   * Removes all queue tasks for the given feed.
   *
   * @param \Drupal\feeds\FeedInterface $feed
   *   The feed for which to remove queue tasks.
   */
  public function clearQueueTasks(FeedInterface $feed): void {
    if (!$this->database->schema()->tableExists('queue')) {
      return;
    }
    $this->database->delete('queue')
      ->condition('data', 'a:3:{i:0;i:' . $feed->id() . ';%', 'LIKE')
      ->execute();
  }

  /**
   * Checks if there was recent import activity.
   *
+5 −0
Changes for src/FeedInterface.php: 5 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -173,6 +173,11 @@ interface FeedInterface extends ContentEntityInterface, EntityChangedInterface,
   */
  public function hasQueueTasks(): bool;

  /**
   * Removes all queue tasks for the current feed.
   */
  public function clearQueueTasks(): void;

  /**
   * Checks if there was recent import activity.
   *
+19 −1
Changes for src/Lock/FeedsLockBackend.php: 19 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@ use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Lock\PersistentDatabaseLockBackend;
use Psr\Log\LoggerInterface;

/**
 * Lock backend for Feeds imports.
@@ -29,6 +30,13 @@ class FeedsLockBackend extends PersistentDatabaseLockBackend {
   */
  protected $timeout;

  /**
   * A logger instance.
   *
   * @var \Psr\Log\LoggerInterface
   */
  protected $logger;

  /**
   * Constructs a new FeedsLockBackend.
   *
@@ -38,11 +46,14 @@ class FeedsLockBackend extends PersistentDatabaseLockBackend {
   *   The entity type manager.
   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
   *   The config factory.
   * @param \Psr\Log\LoggerInterface $logger
   *   A logger instance.
   */
  public function __construct(Connection $database, EntityTypeManagerInterface $entity_type_manager, ConfigFactoryInterface $config_factory) {
  public function __construct(Connection $database, EntityTypeManagerInterface $entity_type_manager, ConfigFactoryInterface $config_factory, LoggerInterface $logger) {
    parent::__construct($database);
    $this->feedStorage = $entity_type_manager->getStorage('feeds_feed');
    $this->timeout = $config_factory->get('feeds.settings')->get('lock_timeout');
    $this->logger = $logger;
  }

  /**
@@ -115,6 +126,13 @@ class FeedsLockBackend extends PersistentDatabaseLockBackend {
      throw new \Exception('The semaphore table could not be created.');
    }

    // Imports running for a long time could potentially indicate issues, though
    // it can also happen that the import is very large and just takes long to
    // complete. So only log a notice of this and not a warning.
    $this->logger->notice('Lock @name got extended.', [
      '@name' => $name,
    ]);

    $this->locks[$name] = TRUE;
    return $this->acquire($name, $timeout);
  }
Loading