Skip to content

fix(symfony): clear SkolemIriConverter state between requests via ResetInterface#7829

Merged
soyuka merged 3 commits intoapi-platform:4.2from
gwini:fix/skolem-iri-converter-memory-leak
Mar 13, 2026
Merged

fix(symfony): clear SkolemIriConverter state between requests via ResetInterface#7829
soyuka merged 3 commits intoapi-platform:4.2from
gwini:fix/skolem-iri-converter-memory-leak

Conversation

@gwini
Copy link

@gwini gwini commented Mar 8, 2026

Q A
Branch? 4.2
Tickets
License MIT
Doc PR

SkolemIriConverter uses SplObjectStorage $objectHashMap and array $classHashMap to cache IRI mappings for non-entity objects during serialization. In long-running processes (FrankenPHP worker mode,
Swoole, RoadRunner), these maps accumulate entries across requests and are never cleared, causing a memory leak.

How I found it

Running Symfony 7.4 with API Platform 4.2.20 in FrankenPHP worker mode, I observed ~2.7 MB/request memory growth when calling an endpoint that serializes ~665 DTO objects (each containing two CarbonImmutable
properties). Memory grew linearly with every request and was never reclaimed by GC.

Using a deep object-graph scanner with reflection across all container services, I traced the leaked references to SkolemIriConverter::$objectHashMap — it held strong references to every serialized DTO and
its nested objects across all previous requests. The class never implements ResetInterface, so services_resetter never clears it.

After applying this fix, memory stabilized at ~76 MB across 20+ consecutive requests.

Changes

  • SkolemIriConverter now implements ResetInterface with a reset() method that clears both $objectHashMap and $classHashMap
  • The service is tagged with kernel.reset so Symfony automatically calls reset() between requests

Note: The Laravel variant (ApiPlatform\Laravel\Routing\SkolemIriConverter) has the same issue — its $objectHashMap and $classHashMap also grow unbounded across requests when running under Octane or other
long-running setups. I'm not familiar enough with Laravel's service lifecycle to implement the correct reset mechanism there. A follow-up PR for the Laravel side would be appreciated.

@gwini gwini marked this pull request as draft March 8, 2026 15:48
@gwini gwini changed the base branch from main to 4.2 March 8, 2026 15:49
@gwini gwini marked this pull request as ready for review March 8, 2026 15:49
@soyuka soyuka merged commit c20a41c into api-platform:4.2 Mar 13, 2026
147 of 150 checks passed
@soyuka
Copy link
Member

soyuka commented Mar 13, 2026

Thanks good catch!

soyuka added a commit to soyuka/core that referenced this pull request Mar 13, 2026
| Q             | A
| ------------- | ---
| Branch?       | 4.2
| Tickets       | api-platform#7829
| License       | MIT
| Doc PR        | ∅

* Add reset() method to clear $objectHashMap and $classHashMap
* Listen to RequestHandled event to reset state, preventing memory leak in
Octane/long-running setups
soyuka added a commit to soyuka/core that referenced this pull request Mar 13, 2026
| Q             | A
| ------------- | ---
| Branch?       | 4.2
| Tickets       | api-platform#7829
| License       | MIT
| Doc PR        | ∅

* Add reset() method to clear $objectHashMap and $classHashMap
* Listen to RequestHandled event to reset state, preventing memory leak in
Octane/long-running setups
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants