Skip to content

Opcache: Introduce ABI-based versioning for file cache portability #19123

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from

Conversation

iamacarpet
Copy link
Contributor

Hello @dstogov and team,

This PR proposes a new versioning mechanism for the file-based opcache to improve its portability across different patch versions of PHP (e.g., 8.5.x) and minor system library changes.

The Problem

Currently, the file cache is versioned using zend_system_id, which is highly sensitive to minor changes in the environment, such as updates to system libraries or patch-level changes in the PHP version itself.

This fragility makes pre-generated, read-only opcaches difficult to use in modern containerized platforms like Google App Engine and Cloud Run. These platforms often perform automatic base image updates, which can change zend_system_id and invalidate an otherwise perfectly valid opcache, negating the performance benefits.

The Proposed Solution

To address this, this change introduces an "ABI hash" for the file cache. During the build process, it calculates a CRC32 checksum of the header files that define the core data structures used by opcache (e.g., zend_op, zval, zend_string, zend_function, zend_class_entry, etc.).

This hash is then used as part of the cache key instead of the more volatile zend_system_id. The result is a cache key that only changes when the underlying data structures (the ABI) actually change, which typically only happens between minor PHP versions (e.g., 8.5 vs 8.6).

Benefits

  • Improved Portability: Opcache file caches become portable across builds with identical ABIs, even if the PHP patch version or system libraries differ.
  • Enhanced Performance on Managed Platforms: This significantly improves the effectiveness of pre-warmed, read-only caches in environments with automatic base image updates, leading to better cold-start performance and reduced CPU usage.

This change is a continuation of the work started in #16551 and #16979 to optimize opcache for modern, ephemeral environments.

I believe a change of this nature will likely require an RFC. I'm happy to start that process and would appreciate any initial feedback on this approach.

Thank you for your time and consideration.

Regards,

iamacarpet

@iluuu1994
Copy link
Member

Hi @iamacarpet. Sorry for the late response. It's an interesting idea.

Are we solely interested in ABI changes, or are we also interested in behavioral changes? Patches may introduce changes to zend_compile.c or Zend/Optimizer. Such changes would not get reflected until the cache files are cleared. Similarly, inlined constants from executed compile-time functions may become stale. Granted, such fixes are quite rare.

Another issue are function signatures for internal and extension functions. The compiler can emit more specialized opcodes when functions are known at compile-time. If these functions change, which is at least possible for extension versions, which can update independently of PHPs own version (and thus of the ABI headers we're checking), these assumptions may be broken.

@iamacarpet
Copy link
Contributor Author

Hello @iluuu1994 ,

Thank you so much for your time and the incredibly insightful feedback on this proposal. Your comments prompted a much deeper investigation, and I've come to the conclusion that the changes proposed in this PR are not the correct solution to the problem I was trying to solve.

I am closing this PR, but I wanted to share my findings for the benefit of the community, as they clarify the real root cause of the issue.

Summary of Investigation & Findings

My initial motivation was twofold:

  1. To solve an issue where zend_system_id differed between a container build environment and the final run environment, making pre-generated opcache files invalid.
  2. To explore making the file cache portable across PHP patch releases.

1. The zend_system_id Discrepancy

My original assumption that system libraries or minor build configuration differences were causing the ID mismatch was incorrect.

The actual cause was the OpenTelemetry extension being loaded in the runtime environment but not in the build environment. I discovered that through the Observer API, regular extensions like OpenTelemetry can call zend_get_op_array_extension_handle(). This correctly and intentionally adds entropy to the zend_system_id calculation.

The system is behaving exactly as designed. The discrepancy was due to an environmental inconsistency, not a flaw in PHP's portability. The correct solution is for users to ensure their build environment's extension profile (specifically, anything loaded as a zend_extension= or loaded with just extension= && using the Observer API) matches the runtime environment for any extension that interacts with the engine at this level.

2. Portability Across Patch Releases

Your feedback about behavioural changes in zend_compile.c and the Optimizer was spot on. To assess the viability of creating a more stable hash, I analysed the commit history for these core files across stable releases of PHP 8.1, 8.2, and 8.3.

The data showed that these critical files were modified in approximately 70% of all patch releases. The changes were almost exclusively necessary bug fixes (segfaults, memory leaks, incorrect optimisations) that would absolutely require the opcache to be invalidated for safety.

This highlights that ultimately, attempting to maintain compatibility across most patch releases is not feasible and would introduce significant risk for a very small reward. The number of "stable" transitions where a portable cache would have been beneficial was simply too low to justify the change.

Conclusion

This investigation has been a valuable learning experience. The zend_system_id is doing its job correctly, and the problem I was trying to solve is ultimately one of environmental configuration.

Thank you again for your guidance. I'll leave this PR closed.

Regards,
iamacarpet

@iamacarpet iamacarpet closed this Aug 18, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants