Loading core/modules/system/tests/modules/performance_test/performance_test.services.yml +5 −0 Original line number Diff line number Diff line Loading @@ -7,3 +7,8 @@ services: arguments: ['@database'] tags: - { name: http_middleware, priority: 1000, responder: true } performance_test.cache_factory: class: Drupal\performance_test\Cache\CacheFactoryDecorator public: false decorates: cache_factory arguments: ['@performance_test.cache_factory.inner', '@Drupal\performance_test\PerformanceDataCollector'] core/modules/system/tests/modules/performance_test/src/Cache/CacheBackendDecorator.php 0 → 100644 +171 −0 Original line number Diff line number Diff line <?php declare(strict_types = 1); namespace Drupal\performance_test\Cache; use Drupal\Core\Cache\Cache; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Cache\CacheTagsInvalidatorInterface; use Drupal\performance_test\PerformanceDataCollector; /** * Wraps an existing cache backend to track calls to the cache backend. */ class CacheBackendDecorator implements CacheBackendInterface, CacheTagsInvalidatorInterface { public function __construct(protected readonly PerformanceDataCollector $performanceDataCollector, protected readonly CacheBackendInterface $cacheBackend, protected readonly string $bin) {} /** * Logs a cache operation. * * @param string|array $cids * The cache IDs. * @param float $start * The start microtime. * @param float $stop * The stop microtime. * @param string $operation * The type of operation being logged. * * @return void */ protected function logCacheOperation(string|array $cids, float $start, float $stop, string $operation): void { $this->performanceDataCollector->addCacheOperation([ 'operation' => $operation, 'cids' => implode(', ', (array) $cids), 'bin' => $this->bin, 'start' => $start, 'stop' => $stop, ]); } /** * {@inheritdoc} */ public function get($cid, $allow_invalid = FALSE): object|bool { $start = microtime(TRUE); $cache = $this->cacheBackend->get($cid, $allow_invalid); $stop = microtime(TRUE); $this->logCacheOperation($cid, $start, $stop, 'get'); return $cache; } /** * {@inheritdoc} */ public function getMultiple(&$cids, $allow_invalid = FALSE): array { $cids_copy = $cids; $start = microtime(TRUE); $cache = $this->cacheBackend->getMultiple($cids, $allow_invalid); $stop = microtime(TRUE); $this->logCacheOperation($cids_copy, $start, $stop, 'getMultiple'); return $cache; } /** * {@inheritdoc} */ public function set($cid, $data, $expire = Cache::PERMANENT, array $tags = []) { $start = microtime(TRUE); $this->cacheBackend->set($cid, $data, $expire, $tags); $stop = microtime(TRUE); $this->logCacheOperation($cid, $start, $stop, 'set'); } /** * {@inheritdoc} */ public function setMultiple(array $items) { $cids = array_keys($items); $start = microtime(TRUE); $this->cacheBackend->setMultiple($items); $stop = microtime(TRUE); $this->logCacheOperation($cids, $start, $stop, 'setMultiple'); } /** * {@inheritdoc} */ public function delete($cid) { $start = microtime(TRUE); $this->cacheBackend->delete($cid); $stop = microtime(TRUE); $this->logCacheOperation($cid, $start, $stop, 'delete'); } /** * {@inheritdoc} */ public function deleteMultiple(array $cids) { $start = microtime(TRUE); $this->cacheBackend->deleteMultiple($cids); $stop = microtime(TRUE); $this->logCacheOperation($cids, $start, $stop, 'deleteMultiple'); } /** * {@inheritdoc} */ public function deleteAll() { $start = microtime(TRUE); $this->cacheBackend->deleteAll(); $stop = microtime(TRUE); $this->logCacheOperation([], $start, $stop, 'deleteAll'); } /** * {@inheritdoc} */ public function invalidate($cid) { $start = microtime(TRUE); $this->cacheBackend->invalidate($cid); $stop = microtime(TRUE); $this->logCacheOperation($cid, $start, $stop, 'invalidate'); } /** * {@inheritdoc} */ public function invalidateMultiple(array $cids) { $start = microtime(TRUE); $this->cacheBackend->invalidateMultiple($cids); $stop = microtime(TRUE); $this->logCacheOperation($cids, $start, $stop, 'invalidateMultiple'); } /** * {@inheritdoc} */ public function invalidateTags(array $tags) { if ($this->cacheBackend instanceof CacheTagsInvalidatorInterface) { $this->cacheBackend->invalidateTags($tags); } } /** * {@inheritdoc} */ public function invalidateAll() { $start = microtime(TRUE); $this->cacheBackend->invalidateAll(); $stop = microtime(TRUE); $this->logCacheOperation([], $start, $stop, 'invalidateAll'); } /** * {@inheritdoc} */ public function garbageCollection() { $this->cacheBackend->garbageCollection(); } /** * {@inheritdoc} */ public function removeBin() { $this->cacheBackend->removeBin(); } } core/modules/system/tests/modules/performance_test/src/Cache/CacheFactoryDecorator.php 0 → 100644 +45 −0 Original line number Diff line number Diff line <?php declare(strict_types = 1); namespace Drupal\performance_test\Cache; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Cache\CacheFactoryInterface; use Drupal\Core\Cache\MemoryCache\MemoryCacheInterface; use Drupal\Core\Cache\MemoryBackend; use Drupal\performance_test\PerformanceDataCollector; /** * Decorates a cache factory to register all calls to the cache system. */ class CacheFactoryDecorator implements CacheFactoryInterface { /** * All wrapped cache backends. * * @var \Drupal\performance_data\Cache\CacheBackendDecorator[] */ protected array $cacheBackends = []; public function __construct(protected readonly CacheFactoryInterface $cacheFactory, protected readonly PerformanceDataCollector $performanceDataCollector) {} /** * {@inheritdoc} */ public function get($bin): CacheBackendInterface { if (!isset($this->cacheBackends[$bin])) { $cache_backend = $this->cacheFactory->get($bin); // Don't log memory cache operations. if (!$cache_backend instanceof MemoryCacheInterface && !$cache_backend instanceof MemoryBackend) { $this->cacheBackends[$bin] = new CacheBackendDecorator($this->performanceDataCollector, $cache_backend, $bin); } else { $this->cacheBackends[$bin] = $cache_backend; } } return $this->cacheBackends[$bin]; } } core/modules/system/tests/modules/performance_test/src/PerformanceDataCollector.php +27 −3 Original line number Diff line number Diff line Loading @@ -15,6 +15,11 @@ class PerformanceDataCollector implements EventSubscriberInterface, Destructable */ protected array $databaseEvents = []; /** * Cache operations collected during the request. */ protected array $cacheOperations = []; /** * {@inheritdoc} */ Loading @@ -32,6 +37,13 @@ public function onStatementExecutionEnd(StatementExecutionEndEvent $event): void $this->databaseEvents[] = $event; } /** * Adds a cache operation. */ public function addCacheOperation(array $operation) { $this->cacheOperations[] = $operation; } /** * {@inheritdoc} */ Loading @@ -40,12 +52,24 @@ public function destruct(): void { // logging does not become part of the recorded data. $database_events = $this->databaseEvents; // Deliberately do not use an injected key value service to avoid any // overhead up until this point. // Deliberately do not use an injected key value or lock service to avoid // any overhead up until this point. $lock = \Drupal::lock(); // This loop should be safe because we know a very finite number of requests // will be trying to acquire a lock at any one time. while (!$lock->acquire('performance_test')) { $lock->wait(); } $collection = \Drupal::keyValue('performance_test'); $existing_data = $collection->get('performance_test_data') ?? ['database_events' => []]; $existing_data = $collection->get('performance_test_data') ?? [ 'database_events' => [], 'cache_operations' => [], ]; $existing_data['database_events'] = array_merge($existing_data['database_events'], $database_events); $existing_data['cache_operations'] = array_merge($existing_data['cache_operations'], $this->cacheOperations); $collection->set('performance_test_data', $existing_data); $lock->release('performance_test'); } } core/profiles/demo_umami/tests/src/FunctionalJavascript/OpenTelemetryAuthenticatedPerformanceTest.php +2 −3 Original line number Diff line number Diff line Loading @@ -35,9 +35,8 @@ public function testFrontPageAuthenticatedWarmCache(): void { $performance_data = $this->collectPerformanceData(function () { $this->drupalGet('<front>'); }, 'authenticatedFrontPage'); $this->assertLessThanOrEqual(16, $performance_data->getQueryCount()); $this->assertGreaterThanOrEqual(15, $performance_data->getQueryCount()); $this->assertSame(15, $performance_data->getCacheGetCount()); $this->assertSame(15, $performance_data->getQueryCount()); $this->assertSame(43, $performance_data->getCacheGetCount()); $this->assertSame(0, $performance_data->getCacheSetCount()); $this->assertSame(0, $performance_data->getCacheDeleteCount()); } Loading Loading
core/modules/system/tests/modules/performance_test/performance_test.services.yml +5 −0 Original line number Diff line number Diff line Loading @@ -7,3 +7,8 @@ services: arguments: ['@database'] tags: - { name: http_middleware, priority: 1000, responder: true } performance_test.cache_factory: class: Drupal\performance_test\Cache\CacheFactoryDecorator public: false decorates: cache_factory arguments: ['@performance_test.cache_factory.inner', '@Drupal\performance_test\PerformanceDataCollector']
core/modules/system/tests/modules/performance_test/src/Cache/CacheBackendDecorator.php 0 → 100644 +171 −0 Original line number Diff line number Diff line <?php declare(strict_types = 1); namespace Drupal\performance_test\Cache; use Drupal\Core\Cache\Cache; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Cache\CacheTagsInvalidatorInterface; use Drupal\performance_test\PerformanceDataCollector; /** * Wraps an existing cache backend to track calls to the cache backend. */ class CacheBackendDecorator implements CacheBackendInterface, CacheTagsInvalidatorInterface { public function __construct(protected readonly PerformanceDataCollector $performanceDataCollector, protected readonly CacheBackendInterface $cacheBackend, protected readonly string $bin) {} /** * Logs a cache operation. * * @param string|array $cids * The cache IDs. * @param float $start * The start microtime. * @param float $stop * The stop microtime. * @param string $operation * The type of operation being logged. * * @return void */ protected function logCacheOperation(string|array $cids, float $start, float $stop, string $operation): void { $this->performanceDataCollector->addCacheOperation([ 'operation' => $operation, 'cids' => implode(', ', (array) $cids), 'bin' => $this->bin, 'start' => $start, 'stop' => $stop, ]); } /** * {@inheritdoc} */ public function get($cid, $allow_invalid = FALSE): object|bool { $start = microtime(TRUE); $cache = $this->cacheBackend->get($cid, $allow_invalid); $stop = microtime(TRUE); $this->logCacheOperation($cid, $start, $stop, 'get'); return $cache; } /** * {@inheritdoc} */ public function getMultiple(&$cids, $allow_invalid = FALSE): array { $cids_copy = $cids; $start = microtime(TRUE); $cache = $this->cacheBackend->getMultiple($cids, $allow_invalid); $stop = microtime(TRUE); $this->logCacheOperation($cids_copy, $start, $stop, 'getMultiple'); return $cache; } /** * {@inheritdoc} */ public function set($cid, $data, $expire = Cache::PERMANENT, array $tags = []) { $start = microtime(TRUE); $this->cacheBackend->set($cid, $data, $expire, $tags); $stop = microtime(TRUE); $this->logCacheOperation($cid, $start, $stop, 'set'); } /** * {@inheritdoc} */ public function setMultiple(array $items) { $cids = array_keys($items); $start = microtime(TRUE); $this->cacheBackend->setMultiple($items); $stop = microtime(TRUE); $this->logCacheOperation($cids, $start, $stop, 'setMultiple'); } /** * {@inheritdoc} */ public function delete($cid) { $start = microtime(TRUE); $this->cacheBackend->delete($cid); $stop = microtime(TRUE); $this->logCacheOperation($cid, $start, $stop, 'delete'); } /** * {@inheritdoc} */ public function deleteMultiple(array $cids) { $start = microtime(TRUE); $this->cacheBackend->deleteMultiple($cids); $stop = microtime(TRUE); $this->logCacheOperation($cids, $start, $stop, 'deleteMultiple'); } /** * {@inheritdoc} */ public function deleteAll() { $start = microtime(TRUE); $this->cacheBackend->deleteAll(); $stop = microtime(TRUE); $this->logCacheOperation([], $start, $stop, 'deleteAll'); } /** * {@inheritdoc} */ public function invalidate($cid) { $start = microtime(TRUE); $this->cacheBackend->invalidate($cid); $stop = microtime(TRUE); $this->logCacheOperation($cid, $start, $stop, 'invalidate'); } /** * {@inheritdoc} */ public function invalidateMultiple(array $cids) { $start = microtime(TRUE); $this->cacheBackend->invalidateMultiple($cids); $stop = microtime(TRUE); $this->logCacheOperation($cids, $start, $stop, 'invalidateMultiple'); } /** * {@inheritdoc} */ public function invalidateTags(array $tags) { if ($this->cacheBackend instanceof CacheTagsInvalidatorInterface) { $this->cacheBackend->invalidateTags($tags); } } /** * {@inheritdoc} */ public function invalidateAll() { $start = microtime(TRUE); $this->cacheBackend->invalidateAll(); $stop = microtime(TRUE); $this->logCacheOperation([], $start, $stop, 'invalidateAll'); } /** * {@inheritdoc} */ public function garbageCollection() { $this->cacheBackend->garbageCollection(); } /** * {@inheritdoc} */ public function removeBin() { $this->cacheBackend->removeBin(); } }
core/modules/system/tests/modules/performance_test/src/Cache/CacheFactoryDecorator.php 0 → 100644 +45 −0 Original line number Diff line number Diff line <?php declare(strict_types = 1); namespace Drupal\performance_test\Cache; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Cache\CacheFactoryInterface; use Drupal\Core\Cache\MemoryCache\MemoryCacheInterface; use Drupal\Core\Cache\MemoryBackend; use Drupal\performance_test\PerformanceDataCollector; /** * Decorates a cache factory to register all calls to the cache system. */ class CacheFactoryDecorator implements CacheFactoryInterface { /** * All wrapped cache backends. * * @var \Drupal\performance_data\Cache\CacheBackendDecorator[] */ protected array $cacheBackends = []; public function __construct(protected readonly CacheFactoryInterface $cacheFactory, protected readonly PerformanceDataCollector $performanceDataCollector) {} /** * {@inheritdoc} */ public function get($bin): CacheBackendInterface { if (!isset($this->cacheBackends[$bin])) { $cache_backend = $this->cacheFactory->get($bin); // Don't log memory cache operations. if (!$cache_backend instanceof MemoryCacheInterface && !$cache_backend instanceof MemoryBackend) { $this->cacheBackends[$bin] = new CacheBackendDecorator($this->performanceDataCollector, $cache_backend, $bin); } else { $this->cacheBackends[$bin] = $cache_backend; } } return $this->cacheBackends[$bin]; } }
core/modules/system/tests/modules/performance_test/src/PerformanceDataCollector.php +27 −3 Original line number Diff line number Diff line Loading @@ -15,6 +15,11 @@ class PerformanceDataCollector implements EventSubscriberInterface, Destructable */ protected array $databaseEvents = []; /** * Cache operations collected during the request. */ protected array $cacheOperations = []; /** * {@inheritdoc} */ Loading @@ -32,6 +37,13 @@ public function onStatementExecutionEnd(StatementExecutionEndEvent $event): void $this->databaseEvents[] = $event; } /** * Adds a cache operation. */ public function addCacheOperation(array $operation) { $this->cacheOperations[] = $operation; } /** * {@inheritdoc} */ Loading @@ -40,12 +52,24 @@ public function destruct(): void { // logging does not become part of the recorded data. $database_events = $this->databaseEvents; // Deliberately do not use an injected key value service to avoid any // overhead up until this point. // Deliberately do not use an injected key value or lock service to avoid // any overhead up until this point. $lock = \Drupal::lock(); // This loop should be safe because we know a very finite number of requests // will be trying to acquire a lock at any one time. while (!$lock->acquire('performance_test')) { $lock->wait(); } $collection = \Drupal::keyValue('performance_test'); $existing_data = $collection->get('performance_test_data') ?? ['database_events' => []]; $existing_data = $collection->get('performance_test_data') ?? [ 'database_events' => [], 'cache_operations' => [], ]; $existing_data['database_events'] = array_merge($existing_data['database_events'], $database_events); $existing_data['cache_operations'] = array_merge($existing_data['cache_operations'], $this->cacheOperations); $collection->set('performance_test_data', $existing_data); $lock->release('performance_test'); } }
core/profiles/demo_umami/tests/src/FunctionalJavascript/OpenTelemetryAuthenticatedPerformanceTest.php +2 −3 Original line number Diff line number Diff line Loading @@ -35,9 +35,8 @@ public function testFrontPageAuthenticatedWarmCache(): void { $performance_data = $this->collectPerformanceData(function () { $this->drupalGet('<front>'); }, 'authenticatedFrontPage'); $this->assertLessThanOrEqual(16, $performance_data->getQueryCount()); $this->assertGreaterThanOrEqual(15, $performance_data->getQueryCount()); $this->assertSame(15, $performance_data->getCacheGetCount()); $this->assertSame(15, $performance_data->getQueryCount()); $this->assertSame(43, $performance_data->getCacheGetCount()); $this->assertSame(0, $performance_data->getCacheSetCount()); $this->assertSame(0, $performance_data->getCacheDeleteCount()); } Loading