rfc:url-opcode-cache

This is an old revision of the document!


PHP RFC: Add support for stream-wrapped URLs in opcode cache

Introduction

This RFC proposes a way to opcode-cache the PHP code generated via a stream wrapper.

It is an extension of the 'is_cacheable' concept developed in a previous RFC.

Proposal

When stream wrappers were introduced in PHP, their relationship with opcode caches was not a problem, as they were mostly used to access remote data. The need for a better interaction arose with package systems, like phar and phk, as these stream wrappers manage a virtual PHP source tree through stream URLs. If the scripts provided by such a system cannot be opcode-cached, the system looses a great part of its value.

Historically, the issue was addressed using different workarounds : either every URL is considered as opcode-cacheable (APC), whatever wrapper it is coming from, or a list of 'cacheable' protocols is declared explicitely in the code. opcache is using the second solution : 'file' and 'phar' are explicitely declared as the only 'cacheable' stream wrappers. It also implies that every 'phar://' and 'file://' paths are cacheable.

I consider that we need a more generic system, which must allow any PHP code handled via a stream wrapper ('core' or 'userspace') to be opcode-cached.

My previous RFC proposed the implementation of an 'is_cacheable' operation, returning a boolean value. This implied that the cache key to use would always be the URL itself. Some users expressed concerns that the stream wrapper should also have the power to determine the cache key. So, this is what I am now proposing :

  • An operation named 'stream_cache_key' is added at the end of the php_stream_wrapper_ops structure. This is an optional pointer to a function able to determine if a given URL is cacheable or not and, if this is the case, to return the (string) key to use to cache this data.
  • A new C function named php_stream_cache_key(zend_string *path, int options, php_stream_context *context) is defined. It determines the right wrapper from the path it receives and forwards the request to the corresponding stream_cache_key() function, if it exists. If the stream_cache_key() element is not defined, NULL is returned.
  • Userspace stream wrappers can define a method named cache_key(path [,options]). This method determines if the input path is cacheable and the key to use. If the path is not cacheable, the function should return null. Any other value is converted to string and considered as the key to use. If the method is not defined, every path for this wrapper are non-cacheable. For security reasons, the returned key must start with the same '<scheme>://' prefix as the input path. If it not the case, an error is raised and the key is ignored.
  • For completeness, a new PHP function named file_cache_key(path [, options [, context]]) is defined. It allows to call the 'cache_key' operation from a PHP script.

Using a (zend_string *) for input and output allows the mechanism to be very fast because, in most cases, when an URL is cacheable, the key to use will be the path itself. Look at the phar example :

/**
 * Called by the opcode cache to get the key to use when caching this URL
 */
static zend_string *phar_wrapper_cache_key(php_stream_wrapper *wrapper
    , zend_string *url, int options, php_stream_context *context) /* {{{ */
{
	/* Phar URLs are always cacheable */
	zend_string_addref(url);
	return url;
}
/* }}} */

In order to ensure key unicity, the returned string must start with a '<wrapper-scheme>://' prefix. For performance reasons, this is checked in debug mode only on C-level wrappers.

The stream wrapper has the responsibility to ensure that the data associated with a given key will always be the same.

Backward Incompatible Changes

None

Proposed PHP Version(s)

7.2

RFC Impact

To SAPIs

None

To Existing Extensions

Phar needs to implement a cache_key() function, always returning the input path.

The same for the plain files wrapper.

Both changes are included in the PR below.

To Opcache

Opcode cache must implement the following logic :

  • If the received path is a 'stream' path, call php_stream_cache_key.
  • If the returned value is null, path is non cacheable.
  • If the returned value is non null, use this value as key to search or register the data.

These changes are NOT included in the PR below because opcache code is a performance-critical part of the core. As the change is not trivial and requires some re-organizations in the way functions call themselves, I would prefer someone more familiar with this code to implement these changes.

New Constants

None

Open Issues

  • Security check on the key returned by userspace wrappers is not implemented yet. I'll add it ASAP.

Unaffected PHP Functionality

Stream wrappers not using the feature are not modified in any way.

Proposed Voting Choices

Required majority: 50%+1

Patches and Tests

Pull Request: https://github.com/php/php-src/pull/1711

This PR includes every modification described in this RFC, except changes to the opcache code. It includes phar and plain files cache_key() handlers and contains a set of tests.

PHP documentation additions (file_cache_key(), streamWrapper::cache_key()) not written yet.

Implementation

References

Rejected Features

rfc/url-opcode-cache.1496413569.txt.gz · Last modified: 2017/09/22 13:28 (external edit)