You are here

trait CacheTagsChecksumTrait in Drupal 8

Same name and namespace in other branches
  1. 9 core/lib/Drupal/Core/Cache/CacheTagsChecksumTrait.php \Drupal\Core\Cache\CacheTagsChecksumTrait

A trait for cache tag checksum implementations.

Handles delayed cache tag invalidations.

Hierarchy

File

core/lib/Drupal/Core/Cache/CacheTagsChecksumTrait.php, line 10

Namespace

Drupal\Core\Cache
View source
trait CacheTagsChecksumTrait {

  /**
   * A list of tags that have already been invalidated in this request.
   *
   * Used to prevent the invalidation of the same cache tag multiple times.
   *
   * @var bool[]
   */
  protected $invalidatedTags = [];

  /**
   * The set of cache tags whose invalidation is delayed.
   *
   * @var string[]
   */
  protected $delayedTags = [];

  /**
   * Contains already loaded tag invalidation counts from the storage.
   *
   * @var int[]
   */
  protected $tagCache = [];

  /**
   * Callback to be invoked just after a database transaction gets committed.
   *
   * Executes all delayed tag invalidations.
   *
   * @param bool $success
   *   Whether or not the transaction was successful.
   */
  public function rootTransactionEndCallback($success) {
    if ($success) {
      $this
        ->doInvalidateTags($this->delayedTags);
    }
    $this->delayedTags = [];
  }

  /**
   * Implements \Drupal\Core\Cache\CacheTagsChecksumInterface::invalidateTags()
   */
  public function invalidateTags(array $tags) {

    // Only invalidate tags once per request unless they are written again.
    foreach ($tags as $key => $tag) {
      if (isset($this->invalidatedTags[$tag])) {
        unset($tags[$key]);
      }
      else {
        $this->invalidatedTags[$tag] = TRUE;
        unset($this->tagCache[$tag]);
      }
    }
    if (!$tags) {
      return;
    }
    $in_transaction = $this
      ->getDatabaseConnection()
      ->inTransaction();
    if ($in_transaction) {
      if (empty($this->delayedTags)) {
        $this
          ->getDatabaseConnection()
          ->addRootTransactionEndCallback([
          $this,
          'rootTransactionEndCallback',
        ]);
      }
      $this->delayedTags = Cache::mergeTags($this->delayedTags, $tags);
    }
    else {
      $this
        ->doInvalidateTags($tags);
    }
  }

  /**
   * Implements \Drupal\Core\Cache\CacheTagsChecksumInterface::getCurrentChecksum()
   */
  public function getCurrentChecksum(array $tags) {

    // Any cache writes in this request containing cache tags whose invalidation
    // has been delayed due to an in-progress transaction must not be read by
    // any other request, so use a nonsensical checksum which will cause any
    // written cache items to be ignored.
    if (!empty(array_intersect($tags, $this->delayedTags))) {
      return CacheTagsChecksumInterface::INVALID_CHECKSUM_WHILE_IN_TRANSACTION;
    }

    // Remove tags that were already invalidated during this request from the
    // static caches so that another invalidation can occur later in the same
    // request. Without that, written cache items would not be invalidated
    // correctly.
    foreach ($tags as $tag) {
      unset($this->invalidatedTags[$tag]);
    }
    return $this
      ->calculateChecksum($tags);
  }

  /**
   * Implements \Drupal\Core\Cache\CacheTagsChecksumInterface::isValid()
   */
  public function isValid($checksum, array $tags) {

    // Any cache reads in this request involving cache tags whose invalidation
    // has been delayed due to an in-progress transaction are not allowed to use
    // data stored in cache; it must be assumed to be stale. This forces those
    // results to be computed instead. Together with the logic in
    // ::getCurrentChecksum(), it also prevents that computed data from being
    // written to the cache.
    if (!empty(array_intersect($tags, $this->delayedTags))) {
      return FALSE;
    }
    return $checksum == $this
      ->calculateChecksum($tags);
  }

  /**
   * Calculates the current checksum for a given set of tags.
   *
   * @param string[] $tags
   *   The array of tags to calculate the checksum for.
   *
   * @return int
   *   The calculated checksum.
   */
  protected function calculateChecksum(array $tags) {
    $checksum = 0;
    $query_tags = array_diff($tags, array_keys($this->tagCache));
    if ($query_tags) {
      $tag_invalidations = $this
        ->getTagInvalidationCounts($query_tags);
      $this->tagCache += $tag_invalidations;

      // Fill static cache with empty objects for tags not found in the storage.
      $this->tagCache += array_fill_keys(array_diff($query_tags, array_keys($tag_invalidations)), 0);
    }
    foreach ($tags as $tag) {
      $checksum += $this->tagCache[$tag];
    }
    return $checksum;
  }

  /**
   * Implements \Drupal\Core\Cache\CacheTagsChecksumInterface::reset()
   */
  public function reset() {
    $this->tagCache = [];
    $this->invalidatedTags = [];
  }

  /**
   * Fetches invalidation counts for cache tags.
   *
   * @param string[] $tags
   *   The list of tags to fetch invalidations for.
   *
   * @return int[]
   *   List of invalidation counts keyed by the respective cache tag.
   */
  protected abstract function getTagInvalidationCounts(array $tags);

  /**
   * Returns the database connection.
   *
   * @return \Drupal\Core\Database\Connection
   *   The database connection.
   */
  protected abstract function getDatabaseConnection();

  /**
   * Marks cache items with any of the specified tags as invalid.
   *
   * @param string[] $tags
   *   The set of tags for which to invalidate cache items.
   */
  protected abstract function doInvalidateTags(array $tags);

}

Members

Namesort descending Modifiers Type Description Overrides
CacheTagsChecksumTrait::$delayedTags protected property The set of cache tags whose invalidation is delayed.
CacheTagsChecksumTrait::$invalidatedTags protected property A list of tags that have already been invalidated in this request.
CacheTagsChecksumTrait::$tagCache protected property Contains already loaded tag invalidation counts from the storage.
CacheTagsChecksumTrait::calculateChecksum protected function Calculates the current checksum for a given set of tags.
CacheTagsChecksumTrait::doInvalidateTags abstract protected function Marks cache items with any of the specified tags as invalid. 1
CacheTagsChecksumTrait::getCurrentChecksum public function Implements \Drupal\Core\Cache\CacheTagsChecksumInterface::getCurrentChecksum()
CacheTagsChecksumTrait::getDatabaseConnection abstract protected function Returns the database connection. 1
CacheTagsChecksumTrait::getTagInvalidationCounts abstract protected function Fetches invalidation counts for cache tags. 1
CacheTagsChecksumTrait::invalidateTags public function Implements \Drupal\Core\Cache\CacheTagsChecksumInterface::invalidateTags()
CacheTagsChecksumTrait::isValid public function Implements \Drupal\Core\Cache\CacheTagsChecksumInterface::isValid()
CacheTagsChecksumTrait::reset public function Implements \Drupal\Core\Cache\CacheTagsChecksumInterface::reset()
CacheTagsChecksumTrait::rootTransactionEndCallback public function Callback to be invoked just after a database transaction gets committed.