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() .
Comments
Comment #2
andypostLooks like duplicate of #1577902: [META] Remove all usages of drupal_static() & drupal_static_reset()
Comment #3
cilefen commentedAlso #2260187: [PP-1] Deprecate drupal_static() and drupal_static_reset() exists. In that issue, there is no replacement except to use object properties.
Comment #4
bradjones1Updating 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.
Comment #5
bradjones1Seems 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.
Comment #6
andypostI still not sure we need API, but memory cache could be handy in few places
Comment #7
bradjones1Perhaps something like the "resettable API" proposed in #2729725: Reset API / ResettableInterface (brainstorming).
Comment #8
bradjones1What 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.
Comment #10
bradjones1Some notes from reading related issues: There's
ResetInterfacefrom Symfony, which exists to help reset objects to their original state.In addition, some Drupal modules such as Commerce use
SplObjectStorageto 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.
Comment #11
geek-merlin@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.
Comment #12
kim.pepperIf we do use
Symfony\Contracts\Service\ResetInterfacewe could collect all services implementing that interface at container build, and provide a simple way to callreset()on them all when needed.