You are here

public function PurgersService::invalidate in Purge 8.3

Invalidate content from external caches.

Implementations of this method have the responsibility of invalidating the given list of invalidation objects from their external caches. Besides the invalidation itself, it also needs to call ::setState() on each object to reflect the correct state after invalidation.

You can set it to the following states:

PROCESSING is a special state only intended to be used on caching platforms where more time is required than 1-2 seconds to clear its cache. Usually CDNs with special status API calls where you can later find out if the object succeeded invalidation. When set to this state, the object flows back to the queue to be offered to your plugin again later.

NOT_SUPPORTED will be rarely needed, as invalidation types not listed as supported by your plugin will already be put to this state before it is offered to your plugin by PurgersServiceInterface::invalidate(). However, if there is any technical reason why you couldn't support a particular invalidation at that given time, you can set it as such and it will be offered again later.

Parameters

\Drupal\purge\Plugin\Purge\Processor\ProcessorInterface $processor: The processor plugin that is initiating cache invalidation.

\Drupal\purge\Plugin\Purge\Invalidation\InvalidationInterface[] $invalidations: Non-associative array of invalidation objects that each describe what needs to be invalidated by the external caching system. Usually these objects originate from the queue but direct invalidation is also possible, in either cases the behavior of your plugin stays the same.

The number of objects given is dictated by the outer limit of Purge's capacity tracking mechanism and is dynamically calculated. The lower your ::getTimeHint() implementation returns, the more that will be offered at once. However, your real execution time can and should never exceed the defined hint, to protect system stability.

Throws

\Drupal\purge\Plugin\Purge\Purger\Exception\BadBehaviorException Thrown when $invalidations contains other data than derivatives of \Drupal\purge\Plugin\Purge\Invalidation\InvalidationInterface.

\Drupal\purge\Plugin\Purge\Purger\Exception\DiagnosticsException Thrown by ::invalidate after a diagnostic of type SEVERITY_ERROR has been detected, which is established after calling DiagnosticsServiceInterface::::isSystemOnFire. Errors by definition force all cache invalidation to be prevented, until the user resolved the issue.

\Drupal\purge\Plugin\Purge\Purger\Exception\CapacityException Thrown when the capacity tracker's global resource limit returns zero or when more $invalidations are given exceeding this limit. Any claimed objects should be released back to the queue (or will expire naturally) and your code should depend on the next processing window.

\Drupal\purge\Plugin\Purge\Purger\Exception\LockException Thrown when other processors are currently busy.

Overrides PurgersServiceInterface::invalidate

See also

\Drupal\purge\Plugin\Purge\Invalidation\InvalidationInterface::setState()

\Drupal\purge\Plugin\Purge\Purger\PurgerCapacityDataInterface::getTimeHint()

File

src/Plugin/Purge/Purger/PurgersService.php, line 409

Class

PurgersService
Provides the service that distributes access to one or more purgers.

Namespace

Drupal\purge\Plugin\Purge\Purger

Code

public function invalidate(ProcessorInterface $processor, array $invalidations) {
  $execution_time_start = microtime(TRUE);

  // Perform various (exception-throwing) pre-flight checks before we start.
  if (!$this
    ->checksBeforeTakeoff($invalidations)) {
    return;
  }

  // Remove states from old purgers that got uninstalled.
  $purger_ids = array_keys($this
    ->getPluginsEnabled());
  foreach ($invalidations as $invalidation) {
    foreach ($invalidation
      ->getStateContexts() as $purger_id) {
      if (!in_array($purger_id, $purger_ids)) {
        $invalidation
          ->removeStateContext($purger_id);
      }
    }
  }

  // Discover types in need of processing and - just to be sure - reset state.
  $types = [];
  foreach ($invalidations as $i => $invalidation) {
    $types[$i] = $invalidation
      ->getType();
    $invalidation
      ->setStateContext(NULL);
  }

  // Iterate the purgers and start invalidating the items each one supports.
  $typesByPurger = $this
    ->getTypesByPurger();
  foreach ($this->purgers as $id => $purger) {
    $groups = [];

    // Set context and presort the invalidations that this purger supports.
    foreach ($invalidations as $i => $invalidation) {
      $invalidation
        ->setStateContext($id);
      if ($invalidation
        ->getState() == InvalidationInterface::SUCCEEDED) {
        continue;
      }
      if (!in_array($types[$i], $typesByPurger[$id])) {
        $invalidation
          ->setState(InvalidationInterface::NOT_SUPPORTED);
        continue;
      }
    }

    // Filter supported objects and group them by the right purger methods.
    foreach ($typesByPurger[$id] as $type) {
      $method = $purger
        ->routeTypeToMethod($type);
      if (!isset($groups[$method])) {
        $groups[$method] = [];
      }
      foreach ($types as $i => $invalidation_type) {
        if ($invalidation_type === $type) {
          $groups[$method][$i] = $invalidations[$i];
        }
      }
    }

    // Invalidate objects by offering each group to its method on the purger.
    foreach ($groups as $method => $offers) {
      if (!count($offers)) {
        continue;
      }

      // Leave a trace in the logs, when errors occur, its likely after this.
      $this->logger
        ->debug("offering @no items to @plugin's @id::@method()", [
        '@no' => count($offers),
        '@id' => $id,
        '@plugin' => $purger
          ->getPluginId(),
        '@method' => $method,
      ]);

      // Feed the offers either with runtime measurement running or without.
      $has_runtime_measurement = $purger
        ->hasRuntimeMeasurement();
      if ($has_runtime_measurement) {
        $instrument = $purger
          ->getRuntimeMeasurement();
        $instrument
          ->start();
        $purger
          ->{$method}($offers);
        $instrument
          ->stop($offers);
      }
      else {
        $purger
          ->{$method}($offers);
      }
    }

    // Wait configured cooldown time before other purgers kick in.
    if (count($groups)) {
      $this
        ->capacityTracker()
        ->waitCooldownTime($id);
    }
  }

  // As processing finished we have the obligation to reset context. A call to
  // getState() will now lead to evaluation of the outcome for each object.
  foreach ($invalidations as $i => $invalidation) {
    $invalidation
      ->setStateContext(NULL);
  }

  // Update the counters with runtime information and release the lock.
  $this
    ->capacityTracker()
    ->spentInvalidations()
    ->increment(count($invalidations));
  $this
    ->capacityTracker()
    ->spentExecutionTime()
    ->increment(microtime(TRUE) - $execution_time_start);
  $this->lock
    ->release(self::LOCKNAME);
}