From e6661502340139ac11d8e85286b7b8795ceb2e77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Sun, 21 Jul 2024 09:51:12 +0200 Subject: [PATCH 1/2] perf: use XXH3 instead of MD5 --- CONTRIBUTING.md | 6 +++--- src/Doctrine/Odm/Tests/DoctrineMongoDbOdmSetup.php | 2 +- src/Elasticsearch/Paginator.php | 2 +- src/HttpCache/State/AddHeadersProcessor.php | 2 +- .../Property/Factory/CachedPropertyMetadataFactory.php | 2 +- .../Factory/CachedPropertyNameCollectionFactory.php | 2 +- .../Factory/CachedResourceMetadataCollectionFactory.php | 2 +- .../Factory/CachedPropertyMetadataFactoryTest.php | 2 +- .../Factory/CachedPropertyNameCollectionFactoryTest.php | 2 +- tests/TestSuiteConfigCache.php | 8 ++++---- 10 files changed, 15 insertions(+), 15 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3cf80935f71..9b74748781f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -127,7 +127,7 @@ Recommendations: * don't change existing tests if possible * always add a new `ApiResource` or a new `Entity/Document` to add a new test instead of changing an existing class -* as of API Platform 3 each component has it's own test directory, avoid the `tests/` directory except for functional tests +* as of API Platform 3 each component has its own test directory, avoid the `tests/` directory except for functional tests * dependencies between components must be kept at its minimal (`api-platform/metadata`, `api-platform/state`) except for bridges (Doctrine, Symfony, Laravel etc.) * for functional testing with phpunit (see `tests/Functional`, add your ApiResource to `ApiPlatform\Tests\Fixtures\PhpUnitResourceNameCollectionFactory`) @@ -139,11 +139,11 @@ https://github.com/api-platform/core/blob/002c8b25283c9c06a085945f6206052a99a5fb To launch unit tests: - vendor/bin/simple-phpunit --stop-on-defect -vvv + vendor/bin/phpunit --stop-on-defect If you want coverage, you will need the `pcov` PHP extension and run: - vendor/bin/simple-phpunit --coverage-html coverage -vvv --stop-on-failure + vendor/bin/phpunit --coverage-html coverage --stop-on-defect Sometimes there might be an error with too many open files when generating coverage. To fix this, you can increase the `ulimit`, for example: diff --git a/src/Doctrine/Odm/Tests/DoctrineMongoDbOdmSetup.php b/src/Doctrine/Odm/Tests/DoctrineMongoDbOdmSetup.php index 0746d8665ff..3ab49d9f4cd 100644 --- a/src/Doctrine/Odm/Tests/DoctrineMongoDbOdmSetup.php +++ b/src/Doctrine/Odm/Tests/DoctrineMongoDbOdmSetup.php @@ -93,7 +93,7 @@ private static function createCacheConfiguration(bool $isDevMode, string $proxyD $namespace .= ':'; } - $cache->setNamespace($namespace.'dc2_'.md5($proxyDir.$hydratorDir).'_'); // to avoid collisions + $cache->setNamespace($namespace.'dc2_'.hash('xxh3', $proxyDir.$hydratorDir).'_'); // to avoid collisions return $cache; } diff --git a/src/Elasticsearch/Paginator.php b/src/Elasticsearch/Paginator.php index 2635be0b62d..2a1c6edc70e 100644 --- a/src/Elasticsearch/Paginator.php +++ b/src/Elasticsearch/Paginator.php @@ -95,7 +95,7 @@ public function getIterator(): \Traversable $denormalizationContext = array_merge([AbstractNormalizer::ALLOW_EXTRA_ATTRIBUTES => true], $this->denormalizationContext); foreach ($this->documents['hits']['hits'] ?? [] as $document) { - $cacheKey = isset($document['_index'], $document['_id']) ? md5("{$document['_index']}_{$document['_id']}") : null; + $cacheKey = isset($document['_index'], $document['_id']) ? hash('xxh3', "{$document['_index']}_{$document['_id']}") : null; if ($cacheKey && \array_key_exists($cacheKey, $this->cachedDenormalizedDocuments)) { $object = $this->cachedDenormalizedDocuments[$cacheKey]; diff --git a/src/HttpCache/State/AddHeadersProcessor.php b/src/HttpCache/State/AddHeadersProcessor.php index e1c008b7618..c11c23d7b79 100644 --- a/src/HttpCache/State/AddHeadersProcessor.php +++ b/src/HttpCache/State/AddHeadersProcessor.php @@ -53,7 +53,7 @@ public function process(mixed $data, Operation $operation, array $uriVariables = $resourceCacheHeaders = $operation->getCacheHeaders() ?? []; if ($this->etag && !$response->getEtag()) { - $response->setEtag(md5((string) $content)); + $response->setEtag(hash('xxh3', (string) $content)); } if (null !== ($maxAge = $resourceCacheHeaders['max_age'] ?? $this->maxAge) && !$response->headers->hasCacheControlDirective('max-age')) { diff --git a/src/Metadata/Property/Factory/CachedPropertyMetadataFactory.php b/src/Metadata/Property/Factory/CachedPropertyMetadataFactory.php index d9dbe14cb5d..3640ce671b6 100644 --- a/src/Metadata/Property/Factory/CachedPropertyMetadataFactory.php +++ b/src/Metadata/Property/Factory/CachedPropertyMetadataFactory.php @@ -38,7 +38,7 @@ public function __construct(CacheItemPoolInterface $cacheItemPool, private reado */ public function create(string $resourceClass, string $property, array $options = []): ApiProperty { - $cacheKey = self::CACHE_KEY_PREFIX.md5(serialize([$resourceClass, $property, $options])); + $cacheKey = self::CACHE_KEY_PREFIX.hash('xxh3', serialize([$resourceClass, $property, $options])); return $this->getCached($cacheKey, fn (): ApiProperty => $this->decorated->create($resourceClass, $property, $options)); } diff --git a/src/Metadata/Property/Factory/CachedPropertyNameCollectionFactory.php b/src/Metadata/Property/Factory/CachedPropertyNameCollectionFactory.php index fca23f9e826..4a7f97092de 100644 --- a/src/Metadata/Property/Factory/CachedPropertyNameCollectionFactory.php +++ b/src/Metadata/Property/Factory/CachedPropertyNameCollectionFactory.php @@ -38,7 +38,7 @@ public function __construct(CacheItemPoolInterface $cacheItemPool, private reado */ public function create(string $resourceClass, array $options = []): PropertyNameCollection { - $cacheKey = self::CACHE_KEY_PREFIX.md5(serialize([$resourceClass, $options])); + $cacheKey = self::CACHE_KEY_PREFIX.hash('xxh3', serialize([$resourceClass, $options])); return $this->getCached($cacheKey, fn (): PropertyNameCollection => $this->decorated->create($resourceClass, $options)); } diff --git a/src/Metadata/Resource/Factory/CachedResourceMetadataCollectionFactory.php b/src/Metadata/Resource/Factory/CachedResourceMetadataCollectionFactory.php index 523d9d6ebd2..88ffb94a9de 100644 --- a/src/Metadata/Resource/Factory/CachedResourceMetadataCollectionFactory.php +++ b/src/Metadata/Resource/Factory/CachedResourceMetadataCollectionFactory.php @@ -36,7 +36,7 @@ public function __construct(private readonly CacheItemPoolInterface $cacheItemPo */ public function create(string $resourceClass): ResourceMetadataCollection { - $cacheKey = self::CACHE_KEY_PREFIX.md5($resourceClass); + $cacheKey = self::CACHE_KEY_PREFIX.hash('xxh3', $resourceClass); if (\array_key_exists($cacheKey, $this->localCache)) { return new ResourceMetadataCollection($resourceClass, $this->localCache[$cacheKey]); } diff --git a/src/Metadata/Tests/Property/Factory/CachedPropertyMetadataFactoryTest.php b/src/Metadata/Tests/Property/Factory/CachedPropertyMetadataFactoryTest.php index 385e2a7f871..81946fff365 100644 --- a/src/Metadata/Tests/Property/Factory/CachedPropertyMetadataFactoryTest.php +++ b/src/Metadata/Tests/Property/Factory/CachedPropertyMetadataFactoryTest.php @@ -92,6 +92,6 @@ public function testCreateWithGetCacheItemThrowsCacheException(): void private function generateCacheKey(string $resourceClass = Dummy::class, string $property = 'dummy', array $options = []): string { - return CachedPropertyMetadataFactory::CACHE_KEY_PREFIX.md5(serialize([$resourceClass, $property, $options])); + return CachedPropertyMetadataFactory::CACHE_KEY_PREFIX.hash('xxh3', serialize([$resourceClass, $property, $options])); } } diff --git a/src/Metadata/Tests/Property/Factory/CachedPropertyNameCollectionFactoryTest.php b/src/Metadata/Tests/Property/Factory/CachedPropertyNameCollectionFactoryTest.php index 177551019fc..88ef2b3742a 100644 --- a/src/Metadata/Tests/Property/Factory/CachedPropertyNameCollectionFactoryTest.php +++ b/src/Metadata/Tests/Property/Factory/CachedPropertyNameCollectionFactoryTest.php @@ -92,6 +92,6 @@ public function testCreateWithGetCacheItemThrowsCacheException(): void private function generateCacheKey(string $resourceClass = Dummy::class, array $options = []): string { - return CachedPropertyNameCollectionFactory::CACHE_KEY_PREFIX.md5(serialize([$resourceClass, $options])); + return CachedPropertyNameCollectionFactory::CACHE_KEY_PREFIX.hash('xxh3', serialize([$resourceClass, $options])); } } diff --git a/tests/TestSuiteConfigCache.php b/tests/TestSuiteConfigCache.php index 3c4f6a21b95..e26db511af0 100644 --- a/tests/TestSuiteConfigCache.php +++ b/tests/TestSuiteConfigCache.php @@ -18,7 +18,7 @@ final class TestSuiteConfigCache implements ConfigCacheInterface { /** @var array */ - public static $md5 = []; + public static array $hashes = []; public function __construct(private readonly ConfigCacheInterface $decorated) { @@ -32,8 +32,8 @@ public function getPath(): string public function isFresh(): bool { $p = $this->getPath(); - if (!isset(static::$md5[$p]) || static::$md5[$p] !== $this->getHash()) { - static::$md5[$p] = $this->getHash(); + if (!isset(self::$hashes[$p]) || self::$hashes[$p] !== $this->getHash()) { + self::$hashes[$p] = $this->getHash(); return false; } @@ -48,6 +48,6 @@ public function write(string $content, ?array $metadata = null): void private function getHash(): string { - return md5_file(__DIR__.'/Fixtures/app/var/resources.php'); + return hash_file('xxh3', __DIR__.'/Fixtures/app/var/resources.php'); } } From 67e8ead87bf04a7327c08c0276e3e8d068985b9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Sun, 21 Jul 2024 11:08:41 +0200 Subject: [PATCH 2/2] fix Behat test --- features/http_cache/headers.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/http_cache/headers.feature b/features/http_cache/headers.feature index b14b412bdbd..2fe42bd14c3 100644 --- a/features/http_cache/headers.feature +++ b/features/http_cache/headers.feature @@ -7,6 +7,6 @@ Feature: Default values of HTTP cache headers Scenario: Cache headers default value When I send a "GET" request to "/relation_embedders" Then the response status code should be 200 - And the header "Etag" should be equal to '"7bfa587950d675e222660f68623f5f89"' + And the header "Etag" should be equal to '"032297ac74d75a50"' And the header "Cache-Control" should be equal to "max-age=60, public, s-maxage=3600" And the header "Vary" should be equal to "Accept, Cookie"