Loading core/lib/Drupal/Core/Entity/EntityStorageBase.php +65 −7 Original line number Diff line number Diff line Loading @@ -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} */ Loading @@ -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 { Loading @@ -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. * Loading Loading @@ -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]); } } } Loading Loading @@ -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) { Loading @@ -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) { Loading Loading @@ -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(), Loading core/profiles/demo_umami/tests/src/FunctionalJavascript/OpenTelemetryPerformanceTest.php +4 −4 Original line number Diff line number Diff line Loading @@ -65,7 +65,7 @@ protected function testNodePageColdCache(): void { $expected = [ 'QueryCount' => 195, 'CacheGetCount' => 230, 'CacheGetCount' => 222, 'CacheSetCount' => 227, 'CacheDeleteCount' => 0, 'CacheTagLookupQueryCount' => 23, Loading Loading @@ -348,7 +348,7 @@ protected function testFrontPageColdCache(): void { $expected = [ 'QueryCount' => 178, 'CacheGetCount' => 238, 'CacheGetCount' => 230, 'CacheSetCount' => 241, 'CacheDeleteCount' => 0, 'CacheTagLookupQueryCount' => 24, Loading Loading @@ -526,9 +526,9 @@ protected function doTestNodePageAdministrator(): void { $expected = [ 'QueryCount' => 264, 'CacheGetCount' => 269, 'CacheGetCount' => 261, 'CacheGetCountByBin' => [ 'config' => 60, 'config' => 52, 'bootstrap' => 15, 'discovery' => 75, 'data' => 13, Loading core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php +61 −0 Original line number Diff line number Diff line Loading @@ -5,6 +5,7 @@ namespace Drupal\Tests\Core\Entity\Sql; use Drupal\Component\Datetime\Time; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Cache\MemoryCache\MemoryCache; use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\Entity\ContentEntityInterface; Loading Loading @@ -1390,6 +1391,66 @@ public function testLoadMultiplePersistentCacheMiss(): void { $this->assertEquals($entity, $entities[$id]); } /** * Tests load multiple with no result. */ public function testLoadMultipleNoResult(): void { $this->setUpModuleHandlerNoImplementations(); $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. */ Loading Loading
core/lib/Drupal/Core/Entity/EntityStorageBase.php +65 −7 Original line number Diff line number Diff line Loading @@ -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} */ Loading @@ -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 { Loading @@ -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. * Loading Loading @@ -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]); } } } Loading Loading @@ -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) { Loading @@ -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) { Loading Loading @@ -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(), Loading
core/profiles/demo_umami/tests/src/FunctionalJavascript/OpenTelemetryPerformanceTest.php +4 −4 Original line number Diff line number Diff line Loading @@ -65,7 +65,7 @@ protected function testNodePageColdCache(): void { $expected = [ 'QueryCount' => 195, 'CacheGetCount' => 230, 'CacheGetCount' => 222, 'CacheSetCount' => 227, 'CacheDeleteCount' => 0, 'CacheTagLookupQueryCount' => 23, Loading Loading @@ -348,7 +348,7 @@ protected function testFrontPageColdCache(): void { $expected = [ 'QueryCount' => 178, 'CacheGetCount' => 238, 'CacheGetCount' => 230, 'CacheSetCount' => 241, 'CacheDeleteCount' => 0, 'CacheTagLookupQueryCount' => 24, Loading Loading @@ -526,9 +526,9 @@ protected function doTestNodePageAdministrator(): void { $expected = [ 'QueryCount' => 264, 'CacheGetCount' => 269, 'CacheGetCount' => 261, 'CacheGetCountByBin' => [ 'config' => 60, 'config' => 52, 'bootstrap' => 15, 'discovery' => 75, 'data' => 13, Loading
core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php +61 −0 Original line number Diff line number Diff line Loading @@ -5,6 +5,7 @@ namespace Drupal\Tests\Core\Entity\Sql; use Drupal\Component\Datetime\Time; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Cache\MemoryCache\MemoryCache; use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\Entity\ContentEntityInterface; Loading Loading @@ -1390,6 +1391,66 @@ public function testLoadMultiplePersistentCacheMiss(): void { $this->assertEquals($entity, $entities[$id]); } /** * Tests load multiple with no result. */ public function testLoadMultipleNoResult(): void { $this->setUpModuleHandlerNoImplementations(); $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. */ Loading