Am 18.05.2026, 13:00:02 schrieb Go Kudo <[email protected]>:

>
>
> 2026年5月17日(日) 5:14 Larry Garfield <[email protected]>:
>
>> On Sat, May 16, 2026, at 10:19 AM, Go Kudo wrote:
>> > Hi internals,
>> >
>> > I'd like to start the discussion for a new RFC, OPcache Static Cache.
>> >
>> > RFC: https://wiki.php.net/rfc/opcache_static_cache
>> > Implementation: https://github.com/php/php-src/pull/22052
>> >
>> > The proposal adds an OPcache-managed shared-memory cache for explicit
>> > userland values and for selected PHP static state. It introduces
>> > explicit functions under the OPcache namespace (volatile_* and
>> > persistent_*) and two attributes, #[OPcache\VolatileStatic] and
>> > #[OPcache\PersistentStatic], that let selected static properties and
>> > method static variables survive across requests. The feature is
>> > disabled by default and only activates once memory is allocated through
>> > the new INI directives.
>> >
>> > The RFC covers the motivation, the deliberate split between the two
>> > backends, the trust model (one PHP runtime = one trust domain; this is
>> > not a tenant isolation boundary), and benchmarks against APCu on NTS
>> > php-fpm and ZTS FrankenPHP. The PR is the full implementation, with
>> > PHPT coverage summarized in the Validation section.
>> >
>> > One thing to flag on the implementation status: the Windows build is
>> > currently broken. I don't have a Windows development environment
>> > available yet — one is being arranged through work, and I'll get the
>> > Windows side fixed once that's in place.
>> >
>> > Feedback welcome.
>> >
>> > Best Regards,
>> > Go Kudo
>>
>> Interesting!  I can definitely see uses for it, and I appreciate the
>> level of detail in the RFC.
>>
>> Some thoughts, though:
>>
>> - atomic-decrement throwing if a value doesn't exist sounds like a
>> footgun.  For something like an up/down voting widget, I could very easily
>> see someone hitting the Down Vote button first, which would cause it to
>> crash.  Having to always check _exists() on decrement but not on increment
>> is inconsistent and likely to confuse people.
>>
>> - Are either of these stores purged on reboot?
>>
>> - "persistent cache and returns void on success." - No, returns null.
>> You can't return void.  A function can have a void return type, whether
>> it's successful or not.  Not quite the same thing.
>>
>> - Having the locks automatically self-unlock sure sounds elegant at
>> first, but the lack of symmetry in the API feels very error prone.  People
>> will want to use it like a transaction, but since it unlocks on the first
>> write there's no way to make it one.  It just silently unlocks if certain
>> functions are called.  But if they're not called, there's no way to unlock
>> it.  That's even more of an issue in a persistent-process use case, where
>> you could easily not hit the process-end for minutes or hours, so the lock
>> never automatically clears.
>>
>> Use case: You need to update some lookup table, so you lock the stored
>> key, compute the new table, then write the new table.  But if the compute
>> step fails for some reason, you now have a locked value with no way to
>> unlock it, but no new value to write to it.  It's better in many cases to
>> leave the stale data there rather than delete it, but this API doesn't
>> offer a way to do that.
>>
>> - It's not made clear: Do objects have their __serialize() methods called
>> when storing (and vice versa on load), or no?  "They have to be
>> serializable" is not something that can be otherwise determined.
>>
>> - Status API: Uh, what are the keys?  No arrays here please.  Please make
>> it an object with defined readonly properties.  Please.
>>
>> - You realize you're effectively adding a Memoize attribute to PHP by
>> another name, right?  Just making sure. :-)
>>
>> - A property using the volatile-tracking strategy, if run in a persistent
>> process, seems like it would never get written.  That feels like a problem.
>>
>> - The section on write times is rather abstract and academic, so a bit
>> hard to follow.  If I read correctly, though, it means that writing to a
>> sub-property of an array/object on a cached property won't trigger a
>> resave?  That feels like another footgun waiting to happen.
>>
>> - It's not clear if there's a way to clear an attribute-cached value
>> other than nuking the entire volatile/persistent cache.  Is there not?  It
>> feels like there should be one...  Especially for "persistent," I don't
>> want to have to nuke my entire "persistent" cache from orbit because one
>> value got corrupted somehow.
>>
>> - Defaulting off... I can see the argument for that, but that means it
>> will be off for most users.  That means I, as a library author of, say, a
>> routing system or a DI container, cannot use it, because I have to assume
>> most users won't have it.  That kneecaps the usefulness of this feature
>> dramatically.  I would strongly recommend setting at least some default-on
>> amount, even if it's only the minimum 8 MB, if we want this feature to
>> actually be used.  (And I can already think of a few places where I'd want
>> to use it myself.)
>>
>> - Related, what happens if they're disabled but someone tries to use this
>> functionality?  Does it operate like every read is a cache miss, or does it
>> error?  If the latter, that means any code that uses the attributes
>> REQUIRES that the ini directives be turned on.  We generally try to avoid
>> this kind of "your code may or may not work depending on ini settings"
>> issues.  (Hello, magic_quotes!)
>>
>> - What's the development experience with this?  Frequently, in dev mode
>> frameworks will disable caches.  If the volatile cache just misses silently
>> that would work there, but not for the persistent cache.  How would I have
>> a persistent route cache that is automatically rebuilt on every request
>> during development while I'm messing with routes?
>>
>> - I understand the value of keeping it simple by making it
>> single-tenant.  However, I can very easily see different 3rd party
>> libraries wanting to make use of the cache at the same time.  That poses a
>> risk of key-space collision, though that's resolvable by a convention to
>> use a key prefix.  What it does not resolve is cases where one library
>> wants to wipe-and-rebuild a dynamic list of keys, but some other library
>> isn't expecting a total purge.  There's a high risk of libraries stepping
>> on each other here.
>>
>> - Currently, this is just a basic key/value store.  That's great for many
>> things, but not very queryable.  This is absolutely scope creep, but would
>> there be some way to extend this (in a future RFC, I'm sure) to allow, say,
>> a persistent memory-resident SQLite database?  Currently you can write one
>> to disk, but then you have to deal with disk permissions.  A memory
>> resident database now is request-specific, so not useful outside of
>> testing.  It would be lovely if there were some way to extend in that
>> direction.
>>
>> --Larry Garfield
>>
>
> Hi Larry,
>
> Thank you for the detailed feedback.
>
> I updated the RFC and the implementation to address the concrete API and
> documentation issues you pointed out.
>
> https://wiki.php.net/rfc/opcache_static_cache
>
> The main changes are:
>
> - `persistent_atomic_decrement()` now creates a missing key with -$step,
>   matching `persistent_atomic_increment()`.
> - `volatile_lock()` and `persistent_lock()` now have matching `*_unlock()`
>   functions.
> - The lock APIs also accept an optional lease value, so abandoned builder
>   reservations can expire even in persistent-worker environments.
> - The status APIs now return a read-only `OPcache\StaticCacheInfo` object
>   instead of arrays.
> - The RFC now documents that both static-cache backends are scoped to the
>   lifetime of the current OPcache static-cache shared-memory segment, and
> are
>   not durable storage.
> - The wording around persistent_store() was corrected to describe the void
>   return type rather than "returning void".
> - The RFC now describes when `__serialize()` and `__unserialize()` are
> called, and
>   that userland serialization keeps those object graphs off the fastest
>   direct/shared-graph path.
> - The publication rules for `VolatileStatic immediate` mode,
> `VolatileStatic
>   tracking` mode, and `PersistentStatic` have been expanded.
> - `VolatileStatic tracking` is now documented as publishing at PHP request
>   shutdown, not process shutdown, even under FPM/FrankenPHP/persistent
> workers.
> - Attribute-backed entries can now be deleted either by loaded class name
> or by
>   documented exact static-property/method-static keys, without clearing the
>   whole backend.
> - I also reran the benchmark matrix from clean NTS FPM, NTS/ZTS CLI, and
> ZTS
>   FrankenPHP builds, and updated the RFC tables.
>
> Some of the broader points are open questions or design tradeoffs rather
> than
> direct fixes.
>
> On the default-off setting, I want to be more direct about where I actually
> stand. I would prefer this feature to be default-on. Administrator opt-in
> noticeably limits library adoption, and that significantly reduces the
> value
> of the API as a portable primitive that libraries can rely on being
> present.
>
> What is holding me back is the shared-hosting case. The cache is a single
> shared-memory trust domain, so on a host where multiple tenants share one
> OPcache segment, default-on without an isolation story could expose those
> tenants to each other through the static cache. I do not yet have a design
> I
> am confident makes default-on safe in that environment, and that is the
> only
> reason the RFC currently ships with the feature disabled.
>




> I would genuinely appreciate your input here. If you (or anyone on the
> list)
> see a viable path — per-pool / per-SAPI segments, a trust-domain or
> namespace
> mechanism enforced by the engine, a configuration model where the host opts
> in per vhost rather than per server, or something I have not considered — I
> would be very happy to rework the proposal around it. If we can land on a
> model that is safe for shared hosting, I would gladly flip the default in
> this RFC rather than defer it to a follow-up.
>

There was a post on Reddit recently from someone modifying APCu to be FPM
pool based. Maybe thats a helpful reference?


https://www.reddit.com/r/PHP/comments/1sg9rln/clevel_apcu_key_isolation_based_on_fpm_pool_names/
https://github.com/Samer-Al-iraqi/apcu-fpm-pool-isolation

>
> For disabled backends, the explicit APIs fail rather than silently
> pretending
> to be a miss-only cache. Attribute-backed state falls back to ordinary
> request
> local static behavior when the corresponding backend is unavailable. I can
> add
> a short FAQ entry for this development-mode behavior if that would make
> the RFC
> clearer.
>
> For multi-library key collisions, the explicit cache remains a shared
> namespace
> and applications/libraries still need key prefixes, similar to APCu. The
> new
> attribute deletion and exact-key deletion support should reduce the need
> for
> whole-backend clears, but this RFC does not add a separate namespace
> mechanism.
> I left the broader trust-domain/namespace question as an Open Issue, and
> it is
> closely related to the shared-hosting question above.
>
> The memory-resident SQLite idea is interesting, but I think it is outside
> the
> scope of this RFC. This proposal is intentionally a small key/value and
> static
> state facility first.
>
> Thanks again. The feedback helped make several parts of the API much more
> explicit.
>
> Best Regards,
> Go Kudo
>

Reply via email to