Verified Commit 36e3ad42 authored by godotislate's avatar godotislate
Browse files

task: #3587565 Static cache entities that aren't loaded

By: catch
By: berdir
By: godotislate
parent 77247452
Loading
Loading
Loading
Loading
Loading
+65 −7
Original line number Diff line number Diff line
@@ -154,6 +154,20 @@ protected function buildCacheId($id) {
    return "values:{$this->entityTypeId}:$id";
  }

  /**
   * Builds the cache ID entity IDs that are not found.
   *
   * @param string|int $id
   *   Entity ID for which the cache ID should be built.
   *
   * @return string
   *   Cache ID that can be passed to the cache backend.
   */
  protected function buildNotFoundCacheId(string|int $id): string {
    // Ensure that any overrides of ::buildCacheId() are reflected here too.
    return "not_found:" . $this->buildCacheId($id);
  }

  /**
   * {@inheritdoc}
   */
@@ -172,6 +186,7 @@ public function resetCache(?array $ids = NULL) {
      $this->memoryCache->invalidateTags([$this->uuidMemoryCacheTag]);
      foreach ($ids as $id) {
        $this->memoryCache->delete($this->buildCacheId($id));
        $this->memoryCache->delete($this->buildNotFoundCacheId($id));
      }
    }
    else {
@@ -180,6 +195,39 @@ public function resetCache(?array $ids = NULL) {
    }
  }

  /**
   * Gets not found entities from the static cache.
   *
   * @param array $ids
   *   IDs to check against the cache of not found entity IDs.
   *
   * @return array
   *   Array of not found entity IDs.
   */
  protected function getNotFoundFromStaticCache(array $ids): array {
    $not_found = [];
    foreach ($ids as $id) {
      if ($this->memoryCache->get($this->buildNotFoundCacheId($id))) {
        $not_found[] = $id;
      }
    }
    return $not_found;
  }

  /**
   * Stores not found entities in the static entity cache.
   *
   * @param array $remaining_ids
   *   Not found entity IDs to store in the cache.
   */
  protected function setNotFoundStaticCache(array $remaining_ids): void {
    if ($this->entityType->isStaticallyCacheable()) {
      foreach ($remaining_ids as $id) {
        $this->memoryCache->set($this->buildNotFoundCacheId($id), FALSE, MemoryCacheInterface::CACHE_PERMANENT, [$this->memoryCacheTag]);
      }
    }
  }

  /**
   * Gets entities from the static cache.
   *
@@ -209,12 +257,12 @@ protected function getFromStaticCache(array $ids) {
  protected function setStaticCache(array $entities) {
    $has_uuid = $this->entityType->hasKey('uuid');
    if ($this->entityType->isStaticallyCacheable()) {
      foreach ($entities as $entity) {
        $this->memoryCache->set($this->buildCacheId($entity->id()), $entity, MemoryCacheInterface::CACHE_PERMANENT, [$this->memoryCacheTag]);
      foreach ($entities as $id => $entity) {
        $this->memoryCache->set($this->buildCacheId($id), $entity, MemoryCacheInterface::CACHE_PERMANENT, [$this->memoryCacheTag]);
        if ($has_uuid) {
          // Pre-cache the UUID of this entity to speed up ::loadEntityByUuid
          // @see ::loadByProperties
          $this->memoryCache->set(\sprintf('uuid_lookup:%s:%s', $this->entityTypeId, $entity->uuid()), [$entity->id()], MemoryCacheInterface::CACHE_PERMANENT, [$this->uuidMemoryCacheTag]);
          $this->memoryCache->set(\sprintf('uuid_lookup:%s:%s', $this->entityTypeId, $entity->uuid()), [$id], MemoryCacheInterface::CACHE_PERMANENT, [$this->uuidMemoryCacheTag]);
        }
      }
    }
@@ -300,9 +348,10 @@ public function loadMultiple(?array $ids = NULL) {
    // static caching.
    if ($ids && $this->entityType->isStaticallyCacheable()) {
      $entities += $this->getFromStaticCache($ids);
      // If any entities were in the static cache remove them from the
      // remaining IDs.
      $ids = array_diff($ids, array_keys($entities));
      $not_found = $this->getNotFoundFromStaticCache($ids);
      // Remove any statically cached entity lookups from the remaining IDs to
      // load.
      $ids = array_diff($ids, array_keys($entities), $not_found);

      $fiber = \Fiber::getCurrent();
      if ($ids && $fiber !== NULL) {
@@ -317,7 +366,8 @@ public function loadMultiple(?array $ids = NULL) {
        // deferring these may allow them to be loaded with more other entities
        // later.
        $entities += $this->getFromStaticCache($ids);
        $ids = array_diff($ids, array_keys($entities));
        $not_found = $this->getNotFoundFromStaticCache($ids);
        $ids = array_diff($ids, array_keys($entities), $not_found);

        // Otherwise load additional entities now.
        if ($ids && $this->entityIdsToLoad) {
@@ -353,6 +403,14 @@ public function loadMultiple(?array $ids = NULL) {
    // load.
    if ($ids === NULL || $ids) {
      $queried_entities = $this->doLoadMultiple($ids);

      // Statically cache entities that were not loaded.
      if ($ids) {
        $remaining_ids = array_diff_key($ids, $queried_entities);
        if ($remaining_ids) {
          $this->setNotFoundStaticCache($remaining_ids);
        }
      }
    }

    // Pass all entities loaded from the database through $this->postLoad(),
+4 −4
Original line number Diff line number Diff line
@@ -65,7 +65,7 @@ protected function testNodePageColdCache(): void {

    $expected = [
      'QueryCount' => 195,
      'CacheGetCount' => 225,
      'CacheGetCount' => 217,
      'CacheSetCount' => 226,
      'CacheDeleteCount' => 0,
      'CacheTagLookupQueryCount' => 24,
@@ -347,7 +347,7 @@ protected function testFrontPageColdCache(): void {

    $expected = [
      'QueryCount' => 178,
      'CacheGetCount' => 233,
      'CacheGetCount' => 225,
      'CacheSetCount' => 240,
      'CacheDeleteCount' => 0,
      'CacheTagLookupQueryCount' => 25,
@@ -525,9 +525,9 @@ protected function doTestNodePageAdministrator(): void {

    $expected = [
      'QueryCount' => 264,
      'CacheGetCount' => 265,
      'CacheGetCount' => 257,
      'CacheGetCountByBin' => [
        'config' => 60,
        'config' => 52,
        'bootstrap' => 15,
        'discovery' => 75,
        'data' => 14,
+61 −0
Original line number Diff line number Diff line
@@ -1414,6 +1414,67 @@ public function testLoadMultiplePersistentCacheMiss(): void {
    $this->assertEquals($entity, $entities[$id]);
  }

  /**
   * Tests load multiple with no result.
   */
  public function testLoadMultipleNoResult(): void {
    $this->setUpModuleHandlerNoImplementations();
    $this->setUpMockEntityType();

    $this->entityType
      ->method('isPersistentlyCacheable')
      ->willReturn(TRUE);
    $this->entityType
      ->method('isStaticallyCacheable')
      ->willReturn(TRUE);
    $this->entityType->expects($this->atLeastOnce())
      ->method('id')
      ->willReturn($this->entityTypeId);

    // Override the cache backend so we can set expectations.
    $this->cache = $this->createMock(CacheBackendInterface::class);
    // When the entity is not loaded at all, this will be recorded in the
    // static cache but not the persistent cache.
    $id = 1;
    $key = 'values:' . $this->entityTypeId . ':1';
    $this->cache->expects($this->once())
      ->method('getMultiple')
      ->with([$key])
      ->willReturn([]);
    $this->cache->expects($this->never())
      ->method('setMultiple');

    $this->entityTypeManager
      ->getActiveDefinition($this->entityType->id())
      ->willReturn($this->entityType);

    $entity_storage = $this->getMockBuilder('Drupal\Core\Entity\Sql\SqlContentEntityStorage')
      ->setConstructorArgs([
        $this->entityType,
        $this->connection,
        $this->entityFieldManager->reveal(),
        $this->cache,
        $this->languageManager,
        new MemoryCache(new Time()), $this->entityTypeBundleInfo, $this->entityTypeManager->reveal(),
      ])
      ->onlyMethods(['getFromStorage', 'invokeStorageLoadHook', 'initTableLayout'])
      ->getMock();
    $entity_storage->method('invokeStorageLoadHook')
      ->willReturn(NULL);
    $entity_storage->method('initTableLayout')
      ->willReturn(NULL);
    $entity_storage->expects($this->once())
      ->method('getFromStorage')
      ->with([$id])
      ->willReturn([]);

    // Loading the same missing entity ID twice should only result in a
    // single persistent cache get.
    $entities = $entity_storage->loadMultiple([$id]);
    $entities = $entity_storage->loadMultiple([$id]);
    $this->assertSame($entities, []);
  }

  /**
   * Tests has data.
   */