Provide DIC-friendly, concurrency-safe alternative(s) to `drupal_static()`

Created on 28 November 2022, about 2 years ago
Updated 27 March 2024, 9 months ago

Problem/Motivation

Broken out from #2218651-81: [meta] Make Drupal compatible with persistent app servers like ReactPHP, PHP-PM, PHPFastCGI β†’ . 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 🌱 [META] Remove all usages of drupal_static() & drupal_static_reset() Active .

Remaining tasks

User interface changes

API changes

Data model changes

Release notes snippet

πŸ“Œ Task
Status

Active

Version

11.0 πŸ”₯

Component
CacheΒ  β†’

Last updated 3 days ago

Created by

πŸ‡ΊπŸ‡ΈUnited States bradjones1 Digital Nomad Life

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • πŸ‡ΊπŸ‡ΈUnited States bradjones1 Digital Nomad Life

    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.

  • πŸ‡©πŸ‡ͺGermany geek-merlin Freiburg, Germany

    @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.

  • πŸ‡¦πŸ‡ΊAustralia kim.pepper πŸ„β€β™‚οΈπŸ‡¦πŸ‡ΊSydney, Australia

    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.

Production build 0.71.5 2024