Problem/Motivation

Broken out from #2218651-81: [meta] Make Drupal compatible with persistent app servers like ReactPHP, PHP-PM, PHPFastCGI, FrankenPHP, Swoole. In short, there are myriad places in Drupal core and contrib that leverage some type of static caching. This is undeniably great for performance, but requires a lot of boilerplate and special knowledge of service internals in situations where you want concurrency (e.g., async/fiber-based web servers) or in tests.

Way back in Drupal 7 we got drupal_static() (#254491: Standardize static caching) and I was kinda assuming that got deprecated at some point with a lot of other global "helper" functions but... it didn't.

Interestingly, drupal_static_reset() contains a few special-case deprecations that speak to the need for an issue like this. Taxonomy, Node, and Book modules all used drupal_static() for static cache management and for its clearing function... but there are lots of other static caches that simply use a static class property.

The result is that we have an inconsistent application of static caching across core, and I'm sure in contrib, which means you must know to clear this or that cache manually if you want to clean up this global state between requests.

As the referenced comment states,

I think we end up with a handful of patterns, and maybe a few dozen services to touch.
Some static caches can be moved to MemoryCache backends, some others and some services need to be moved to the request object.
This can be done with thin wrapper services or methods.
As of now, it looks it a bit of work, but no big complexity.
(If no new dragons appear...)

I agree this isn't the most significant lift ever, but whatever alternatives we propose need to be easy enough to use and clearly communicated as to their proper use and alternatives.

Steps to reproduce

Proposed resolution

Other issues exist to deprecate drupal_static(), however there's nothing concrete proposed to replace it. This could potentially be helpful in chipping away refactors for the various services that will need to be touched in #1577902: [META] Remove all usages of drupal_static() & drupal_static_reset() .

Remaining tasks

User interface changes

API changes

Data model changes

Release notes snippet

Comments

bradjones1 created an issue. See original summary.

cilefen’s picture

Also #2260187: [PP-1] Deprecate drupal_static() and drupal_static_reset() exists. In that issue, there is no replacement except to use object properties.

bradjones1’s picture

Title: Deprecate drupal_static(), replace with DIC-friendly, concurrency-safe alternative(s) » Provide DIC-friendly, concurrency-safe alternative(s) to `drupal_static()`
Issue summary: View changes
Related issues: +#1577902: [META] Remove all usages of drupal_static() & drupal_static_reset()

Updating title and IS to make it clear this is to provide an API for static caching/alternatives to one-off static caching that make Drupal more async/concurrency friendly.

bradjones1’s picture

Seems like #3047289: Standardize how we implement in-memory caches is IMO the closest true analog to what I'm trying to express here... however I think the missing component here is to solidify an API/convention for how such services have their per-request/in-memory caches invalidated between requests. Or even to incorporate into policy that Drupal's service contract to memory caches is that they should not persist between main requests (but OK for subrequests, for instance.)

Happy to have this issue closed as a duplicate, of course... just trying to coalesce the current state of this feature and make connections.

andypost’s picture

I still not sure we need API, but memory cache could be handy in few places

bradjones1’s picture

Perhaps something like the "resettable API" proposed in #2729725: Reset API / ResettableInterface (brainstorming).

bradjones1’s picture

I still not sure we need API, but memory cache could be handy in few places

What I'm struggling with is the question of how to address two main sticking points for MemoryCache:

So I'm having trouble with the idea that we could deprecate drupal_static() with no replacement that allows for a global reset of such caches in the same process.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

bradjones1’s picture

Some notes from reading related issues: There's ResetInterface from Symfony, which exists to help reset objects to their original state.

In addition, some Drupal modules such as Commerce use SplObjectStorage to map a request to a resolved value. There's still the question of how long this should stay in memory, but it's prior art worth noting.

It seems the "correct" thing to do is require services be stateless, however that's a pretty big change in paradigm for Drupal overall and would in theory require us some way of validating and enforcing this requirement. It's also a likely security hotspot.

geek-merlin’s picture

@bradjones1 Yes, very relevant questions.

So we have existing code that relies on carrying request-specific state, in a world where that assumption does not hold anymore.
Whar are our minimally invasive solutions?

Build a fresh container, and give each request a deep copy of it, to throw away in the end. Saves us at least container-building, but the deep copy is the expensive part.

Then add a blessed stateless_service tag, and only swap out the remaining services for a new request.

Something like $kernel->rebuildContainerForNewRequest(), as minimal API for now. And maybe in the end we can live with throwing away a handful of services.

kim.pepper’s picture

If we do use Symfony\Contracts\Service\ResetInterface we could collect all services implementing that interface at container build, and provide a simple way to call reset() on them all when needed.

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.