Skip to content

Attachment lookup cache can exhaust frontend request memory #1139

Description

@pirate-bot

Summary

Frontend image processing can terminate with an allowed-memory-exhausted fatal while resolving attachment IDs.

Expected behavior: Processing a page with many distinct image URLs completes within the request memory limit.

Actual behavior: The request can exhaust its memory limit during attachment-ID lookup.

Impact: Affected frontend requests fail before page output is completed.

Customer context

  • Product / area: Optimole attachment-ID lookup used by DAM/media offload processing
  • Version: 4.2.11
  • Environment: WordPress 7.1, PHP 8.1.2; 128 MB PHP memory limit reported
  • Integration / third party: Not provided
  • Reported error / symptom: Allowed memory size of 134217728 bytes exhausted at the attachment-ID lookup call
  • Impact: 8 occurrences across 2 production sites in the telemetry window

Reproduction notes

Reported production workflow: a frontend request on Optimole 4.2.11 reaches attachment-ID resolution and exhausts a 128 MB PHP memory limit.

Reproduction has not been run locally. A likely reproduction is a frontend response that causes media-offload URL processing for many distinct image URLs in one PHP request.

Diagnosis

Conclusion

The release code keeps each distinct attachment-URL cache key in Optml_Attachment_Cache::$cache_map for the lifetime of the PHP request and provides no size limit or automatic clearing path. The crash occurs at the call that enters this cache during frontend image processing. This is a confirmed unbounded per-request memory-growth path; the telemetry does not reveal the number or shape of URLs required to reach the reported 128 MB limit.

Where this likely occurs

  • inc/traits/dam_offload_utils.php — Optml_Dam_Offload_Utils::attachment_url_to_post_id() lines 284–332 calls the cache for each input URL, then caches the lookup result.
  • inc/attachment_cache.php — Optml_Attachment_Cache::get_cached_attachment_id() lines 27–40 inserts a value into the request-static $cache_map for cache misses; set_cached_attachment_id() lines 51–62 also inserts into that same map without a capacity boundary.
  • inc/url_replacer.php — Optml_Url_Replacer::is_offloaded_url() lines 430–450 reaches attachment_url_to_post_id() while processing an offloaded source URL.
  • The same cache behavior is present in tag v4.2.11. Git history attributes the cache lookup to 4c726ce1 (fix: cache calls to attachment_url_to_postid); no inspected tag comparison establishes a previously working bounded implementation.

Engineering notes

The cache is intentionally request-local, as documented in Optml_Attachment_Cache::get_cached_attachment_id() around lines 29–38, so it is released after a normal PHP request. Its growth is nevertheless proportional to the number of distinct processed URLs in a single request, including negative cache lookups. The affected frontend path can be reached when media offload is enabled through Optml_Url_Replacer::is_offloaded_url(). The available shutdown fatal lacks request HTML and a structured stack trace, so the exact page size and URL diversity remain unknown.

Test coverage status

tests/test-media.php covers normal attachment lookup variants in Test_Media::test_replace_alternative_domain() around lines 483–498 and resets the static cache before editor-content tests around lines 499–563. No relevant coverage was found during inspection for a large number of distinct attachment URLs in one request or for a memory-growth bound.

What to verify or explore next

  • May be worth reproducing frontend replacement with media offload enabled and a page containing an increasing number of distinct local and non-local image URLs under a 128 MB memory limit.
  • If reproducible, checking the peak size of the request-local attachment cache and whether cache misses contribute materially would close the telemetry context gap.
  • May be worth running the targeted tests/test-media.php suite alongside a volume-oriented lookup scenario.

Unknowns / follow-up

  • The telemetry has no structured stack trace or request payload, so the exact caller and number of distinct URLs at failure are not known.
  • No release boundary proving a regression was identified.

Confidence

Confidence: 87/100

One confirmed Optimole defect explains the DAM lookup memory-exhaustion fingerprint: the v4.2.11 release retains every unique attachment lookup in a request-static cache without an eviction boundary. The other four fingerprints do not currently establish a shipped defect: both required target files exist in the tagged release and match the autoloader path, the reported output-buffering location does not contain buffering calls in the locked SDK source, and the SDK-start memory fatal remains limited to one site without a trace showing an unbounded SDK allocation.

Crash telemetry

Occurrences 8
Distinct sites 2
First seen 2026-09-05 21:28 UTC
Last seen 2026-09-06 16:55 UTC
Crash location product:inc/traits/dam_offload_utils.php:285
Request context frontend
Inside Themeisle SDK no
Product versions 4.2.11
WP versions 7.1
PHP versions 8.1.2-1ubuntu2.25
SDK versions 3.3.61

Source: automated crash report — optimole-wp, fingerprint a031c89fdc3708fad35d577ae2cf94df
Generated by bug-report-triage (ID: bug-report-triage_6a9e53b43c3721.46530364)

Activity

  1. added theissue type on Sep 7, 2026
  2. added
    bugThis label could be used to identify issues that are caused by a defect in the product.
    on Oct 19, 2026
  3. self-assigned this
    on Oct 20, 2026
  4. milicavs commented on Oct 20, 2026

    @milicavs

    🧪 QA steps and test links

    With Image Storage on, front-end pages with a very large number of distinct image URLs can run out of PHP memory and stop rendering (Allowed memory size of 134217728 bytes exhausted). Normal use can't trigger it: it took pages far bigger than a test site has, so the steps are a regression check of image lookups on a page with more than 2000 image URLs.

    Requires: a connected Optimole account with Image Storage available, and the Code Snippets plugin.

    Reproduce on Optimole 4.2.17:

    1. Go to Optimole → Settings → Image Storage, turn on offloading and offload the existing images.
    2. Create a post with 3 Media Library images, publish it and confirm they offload.
    3. In Snippets → Add New, add "QA 1139 many images", set it to run on the front end, activate it:
      add_filter( 'the_content', function ( $content ) {
      	if ( ! is_singular() || ! isset( $_GET['qa1139'] ) ) { return $content; }
      	$base = wp_get_upload_dir()['baseurl'];
      	$filler = '';
      	for ( $i = 0; $i < 2500; $i++ ) {
      		$filler .= '<img src="' . esc_url( $base . '/qa-1139/missing-' . $i . '.jpg' ) . '" width="10" height="10" alt="">';
      	}
      	return $content . $filler . $content;
      }, 99 );
    4. Open the post logged out with ?qa1139=1 added to its URL.
    5. Inspect the 3 real images in the first copy and in the second copy (after the filler).
    • Bug: only very large production pages hit the memory error above; this test page loads on the release too.
    • Fixed: the page loads without errors, and the real images in both copies still use their offloaded Optimole URLs.
    Edge cases
    • Reload the same URL within 10 minutes: same output, no errors in debug.log.
    • Image Storage off: the page with filler loads, real images still use Optimole CDN URLs.
    • An image inserted from the Optimole DAM tab in the same post: renders in both copies.
    • Open the post in the block editor with offloading on: images show, no errors.

    Assumption: Optimole looks up every Media Library-style image URL on the page while Image Storage is on, so the filler crosses the 2000-entry limit in one request.

    Fix: #1192 (open, draft; no build yet)

  5. rodica-andronache commented on Oct 22, 2026

    @rodica-andronache

    I wasn't able to replicate the issue, just tested that nothing else breaks

  6. pirate-bot commented on Oct 25, 2026

    @pirate-bot
    CollaboratorAuthor

    Automated crash telemetry captured this again: 57 occurrence(s) across 7 site(s), last seen 2026-10-25 02:43 UTC.

    Customers experience fatal PHP memory exhaustion errors on frontend page requests when processing many distinct image URLs on Optimole 4.2.17. Investigation reveals that Optml_Attachment_Cache::$cache_map in inc/attachment_cache.php grows unboundedly with each distinct URL lookup during a request, eventually exhausting available memory. A size bound was subsequently added in commit 8e4a54b, but v4.2.17 lacks this capacity limit.


    Source: automated crash report — optimole-wp, fingerprint aedac40c6d2a81e5db3b2e3bbe3e78ef
    Generated by bug-report-triage (ID: bug-report-triage_6add9b7a6db4c3.93016456)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugThis label could be used to identify issues that are caused by a defect in the product.bug-reportbug-report-triagecrash-report

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions