Newsgroups: php.internals
Path: news.php.net
Xref: news.php.net php.internals:125832
X-Original-To: internals@lists.php.net
Delivered-To: internals@lists.php.net
Received: from php-smtp4.php.net (php-smtp4.php.net [45.112.84.5])
	by qa.php.net (Postfix) with ESMTPS id 598D61A00BD
	for <internals@lists.php.net>; Wed, 23 Oct 2024 09:44:13 +0000 (UTC)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail;
	t=1729676797; bh=J0hMJZ3iZqXl2j0mUK8lyFox2em6xpuDAEXXxODlxUo=;
	h=From:Date:Subject:To:From;
	b=Rhzfjpr+R7E64u4k8L4K4OCuIw7jTQQLJ0lkYoNEanm75Xzl//RU96Bxy8uK4qYtj
	 sD+lGsIVceSWfMz0xIVt5pIoEkjBSnb680amY48HlnCRUMNUsQbhG+QnAd7Ma03/Jc
	 Ms2Yi5rqJA0cUMc3e1FosxISGL8SxDnSmIc5jntg0n/gtzSOt4sEX8HUrHophPqGtH
	 XtijxHfpSfcIOPK//y4OsqBvM21v+Y42D8lFewTtF9uE6aoVyMtodK5NOstFGIL/Hz
	 E0302w7eNPbsfq2edPKKoaRLtv67IUwJj0A/4f7fygS4Lj9a8xg0e+ARUqxZe+AllU
	 PFW8qdWWrebVA==
Received: from php-smtp4.php.net (localhost [127.0.0.1])
	by php-smtp4.php.net (Postfix) with ESMTP id 049F6180037
	for <internals@lists.php.net>; Wed, 23 Oct 2024 09:46:37 +0000 (UTC)
X-Spam-Checker-Version: SpamAssassin 4.0.0 (2022-12-13) on php-smtp4.php.net
X-Spam-Level: 
X-Spam-Status: No, score=0.6 required=5.0 tests=BAYES_50,DKIM_SIGNED,
	DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS,RCVD_IN_DNSWL_NONE,
	RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_PASS autolearn=no
	autolearn_force=no version=4.0.0
X-Spam-Virus: No
X-Envelope-From: <sam@melroseandco.uk>
Received: from mail-ua1-f48.google.com (mail-ua1-f48.google.com [209.85.222.48])
	(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
	 key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256)
	(No client certificate requested)
	by php-smtp4.php.net (Postfix) with ESMTPS
	for <internals@lists.php.net>; Wed, 23 Oct 2024 09:46:36 +0000 (UTC)
Received: by mail-ua1-f48.google.com with SMTP id a1e0cc1a2514c-851d2a36e6dso390071241.0
        for <internals@lists.php.net>; Wed, 23 Oct 2024 02:44:11 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
        d=melroseandco.uk; s=google; t=1729676651; x=1730281451; darn=lists.php.net;
        h=content-transfer-encoding:to:subject:message-id:date:from
         :mime-version:from:to:cc:subject:date:message-id:reply-to;
        bh=Ofg3R7lLc07EKhSSWoOB6F7PpLGDcVcswip9dQIXfqY=;
        b=b1Axw1JQ+6iROm8E3sW4JTuMHLMRphbc8U7UC/YOysSQeSRmkkobb+rZbKBAm28zTE
         W6V8OGF010O3vNR0/AiZX71t5RNTpZvxa89Y7B3A+9BLCwlb/46MbKKa4ToYuWLJHH8b
         7yaMPDBJrRr0nbu8jnYZZmm/EPN88Ecj/5Cq33UU/v7wIvDmdl4yWcOMopTJR8OVcvRm
         o+8dvf54qX4K9mrL3wMcq22drY6rjA6L2aOA6+01G/D0sgcMUGea5LGsmRDRSCG/0jC9
         X8CQBlG4or/9w2hGZbcR4iVVPXsl8qrnbLiCSg8HdSm7JZt1z62Vtui4vOXpeSPqszqI
         hc1g==
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
        d=1e100.net; s=20230601; t=1729676651; x=1730281451;
        h=content-transfer-encoding:to:subject:message-id:date:from
         :mime-version:x-gm-message-state:from:to:cc:subject:date:message-id
         :reply-to;
        bh=Ofg3R7lLc07EKhSSWoOB6F7PpLGDcVcswip9dQIXfqY=;
        b=KQ7xmFAhOqYvZ9UHlsIb7tXhbvLDCye3M6Ug+a9Yrk2dAeUKgSN1eA35oMXe8Thxnb
         7Jv1uF1+ITt0zzg5EnaE7b+UflO3AIAGPt4Yt01q+i3qJROWgWcII0tTchUCqZTsauQu
         TsdiWzR4C8xxKDWi4F7FD+f3/2BwscKpYR8/+PbUtQ0GZV5IuYrF2f/nsroIGIEjB82q
         0//I69eYM/gli5eUSiZgisde0z7918ywRRRHKYipsHtddFnWj7Y6UKTgcxquKHonWRwb
         oeH5rinFZeK3FLo+5wanclv6CdR6HjUr1DlazfUhMHxxEs83x1xx8+8pY4H1s+kqNju3
         b57g==
X-Gm-Message-State: AOJu0YyU6UtATXZxZJcwAS6SmZYYd3OMQmQiZzbQ8lzs6OvxbtDQ5oTx
	+C6KFVm/fe601SM4eC6F6gAHEWQpB69Ga/R4v5ED6CBEFicHFMj96f0Cdy0NwSouBKWfMHlUkH8
	aNP0Y0L33AgrD2bWEizceHY2PKPUgGPlzApXdyK0NgDmM/N7ph8c=
X-Google-Smtp-Source: AGHT+IG0D74UUex2lbPraRHShVHcdbFjWZTOfnK/Bemaje9dnc8/cGUUvoeNovfJzhYbJ/a3XPn8V0a3MZA7BIWAS3o=
X-Received: by 2002:a05:6122:1794:b0:50a:cbdb:b929 with SMTP id
 71dfb90a1353d-50fd0f196cemr1102577e0c.2.1729676650763; Wed, 23 Oct 2024
 02:44:10 -0700 (PDT)
Precedence: bulk
list-help: <mailto:internals+help@lists.php.net
list-unsubscribe: <mailto:internals+unsubscribe@lists.php.net>
list-post: <mailto:internals@lists.php.net>
List-Id: internals.lists.php.net
x-ms-reactions: disallow
MIME-Version: 1.0
Date: Wed, 23 Oct 2024 10:43:59 +0100
Message-ID: <CAMhsXHy=FLtGWEdvtiS9zD3rTP0SOL=A0=i=606W3o8ArLQzkA@mail.gmail.com>
Subject: [PHP-DEV] RFC: Support Read-Only opcache.file_cache for use with Docker
To: internals@lists.php.net
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
From: sam@melroseandco.uk (Samuel Melrose)

Hello,

Following on from a chat with some very helpful contributors on GitHub
(https://github.com/php/php-src/issues/16484), I'd like to request
feedback on a change and potentially put it forward as an RFC please.

Change Description:

The `opcache.file_cache` folder must currently be writable, but I
propose a read-only mode (potentially enabled via
`opcache.file_cache_read_only=3D1`) that allows the folder to be used
for loading files when it isn't writable.

`opcache.file_cache_read_only=3D1` will fail unless
`opcache.validate_timestamps=3D0` and `opcache.revalidate_freq=3D0`,
indicating the code is intended to be read-only.

An example PR to achieve this is here:
https://github.com/php/php-src/pull/16551 (I'd love some feedback
please!).

Why?:

When building containers (i.e. Docker) that contain both the PHP
runtime and application code (that isn=E2=80=99t going to change once built
into the container), a lot of CPU cycles are wasted re-warming the
opcache every time an instance of the container starts, even though
the code will always remain the same.

In a large distributed container based platform like Lambda, App
Runner, App Engine or Cloud Run, there are significant performance
gains & cost savings to be realised by being able to pre-warm the JIT
at container build time (i.e. in the CI/CD pipeline), then load it
from disk at container startup.

It is fairly common on these large container platforms for the entire
file-system to be read-only (e.g. Kubernetes `readOnlyRootFilesystem`)
as a security hardening measure, which makes using the existing
`opcache.file_cache` impossible.

Usage:

The intended usage for this change is to build the opcache via the CLI
in the CI/CD workflow, with something like this:

`@php -dopcache.enable_cli=3Dtrue -dopcache.file_cache=3D$(pwd)/opcache
-dopcache.file_cache_only=3Dtrue prewarm.php`

```php
<?php

/**
 * Composer autoloader...
 */

$filesToLoad =3D require __DIR__ . '/vendor/composer/autoload_files.php';

// Prevent composer autoloading files...
foreach ($filesToLoad as $fileIdentifier =3D> $file) {
    $GLOBALS['__composer_autoload_files'][$fileIdentifier] =3D true;
}

require __DIR__ . '/vendor/autoload.php';

$finder =3D (new Symfony\Component\Finder\Finder())
    ->files()
    ->name('/\.php$/')
    ->ignoreDotFiles(false)
    ->ignoreVCSIgnored(false)
    ->exclude([
        'vendor/composer',
    ])
    ->notPath([
        'fuel/core/bootstrap.php',
        'fuel/core/vendor/htmlawed/htmlawed.php',
    ])
    ->in(__DIR__);

foreach($finder as $file) {
    $filepath =3D $file->getRealPath();
    echo "Compiling file " . $file->getRelativePathname() . " ... " .
        (
            opcache_is_script_cached($filepath) ?
                'EXISTS' : (
                    opcache_compile_file($filepath) ? 'OK' : 'FAIL'
                )
        ) .
        "\n";
}
```

Then including the `opcache` folder, along with the application code,
and the following values in `php.ini` inside the Docker container
build:

```
; Tune opcache
opcache.revalidate_freq=3D0
...other values...
## this below should be true on production
opcache.enable_file_override=3Dtrue
## this below should be false on production
opcache.validate_timestamps=3Dfalse

; Enable pre-warmed opcache
opcache.file_cache=3D/workspace/opcache
opcache.file_cache_read_only=3Dtrue
opcache.file_cache_consistency_checks=3Dfalse
```

Considerations:

Part of the opcache file path when stored on disk is the
`zend_system_id`, which from my testing, only stays the same on the
exact same build of PHP (and as a result this means if your service is
restarting to install a PHP update, the opcache files are no longer
valid anyway, but not necessarily a problem with Docker containers
that stay static until updated as a whole).

Benchmarks:

First request without opcache warm'd:

```
$ curl -w "@curl-format.txt" -o /dev/null -s http://localhost:32768/_ah/war=
mup
     time_namelookup:  0.000032s
        time_connect:  0.000160s
     time_appconnect:  0.000000s
    time_pretransfer:  0.000193s
       time_redirect:  0.000000s
  time_starttransfer:  0.221376s
                     ----------
          time_total:  0.221478s
```

First request with opcache pre-warm'd:

```
$ curl -w "@curl-format.txt" -o /dev/null -s http://localhost:32768/_ah/war=
mup
     time_namelookup:  0.000029s
        time_connect:  0.000164s
     time_appconnect:  0.000000s
    time_pretransfer:  0.000198s
       time_redirect:  0.000000s
  time_starttransfer:  0.053059s
                     ----------
          time_total:  0.053171s
```

The CPU on the machine I'm testing from is a lot more powerful than
the CPU allocation for containers in production (it's lots of small
instances that scale horizontally), so I'm expecting the improvement
to be even more dramatic there.

Struggling to benchmark that properly though, as prod/pre-prod for
serverless all has a read-only root FS.

** What does everyone think? Feedback welcome please. **