Skip to content
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

Bring back isapiSAPI? #17838

Draft
wants to merge 13 commits into
base: master
Choose a base branch
from
Draft

Bring back isapiSAPI? #17838

wants to merge 13 commits into from

Conversation

cmb69
Copy link
Member

@cmb69 cmb69 commented Feb 17, 2025

20 years ago, the isapi SAPI was the most performant option to serve PHP with IIS (the only other option was classic CGI). However, ISAPI required ZTS builds, so Microsoft and Zend coorperated to improve the situation, what led to FCGI being the most recommendable way to serve PHP with IIS (and later also NGinX). ISAPI fell from grace, no longer shipped with the Windows binaries as of PHP 5.3.0, and removed from the codebase as of PHP 7.0.0.

During that long time a lot has changed:

  1. ZTS builds became more stable, especially with PHP 7 changing the Linux configuration option from --enable-mainter-zts to --enable-zts.
  2. The thread safe implementation has been greatly improved regarding performance with PHP 7; I couldn't find relevant documentation, but I believe what we have now is quite similar to https://wiki.php.net/rfc/tls (maybe @arnaud-lb can clarify).
  3. Windows introduced ASLR, and while that was optional in early versions, it is pretty much enforced on contemporary Windows versions. Furthermore, it's more aggressive nowadays. So for a long time it is recommended to enable opcache.file_cache_fallback on Windows.
  4. OPcache introduced some optimization which cannot be supported on Windows due to ASLR.

To clarify: ASLR is not the real problem, but rather that Windows has no fork(2), and as such uses spawned processes which try to attach to OPcache shared memory.

Now (3) is the actual problem I'm seeing here. If an FCGI worker process cannot re-attach to SHM, it falls back to using the file cache, which is barely faster than running without OPcache (at least according to some tests I made a couple of years ago). WinCache may alleviate this by caching the filecache in memory, but besides that WinCache has been abandoned by Microsoft (we have a barely maintained fork at https://github.com/php/pecl-caching-wincache), this isn't the real thing anyway (OPcache SHM can apply more optimizations).

The clean solution to the ASLR/spawned processes on Windows would be to introduce something like FPM, but using threads instead of forked children. This would be a major undertaking, though, and since that this is known for years, nobody (to my knowledge) even attempted that.

The next best solution is to use multi-threaded extensions to Webservers. We already have that for Apache (apache2handler) (although that still needs reattaching of the worker process; see #7865). For IIS, we had the isapi SAPI (which might also be supported by Apache on Windows), which can be run inside the IIS worker process (w3wp.exe). Therefore I've made the minimally required fixes (and threw out Zeus Webserver support), and some preliminary tests were satisfactory: it's basically working, and the performance is roughly on par with FCGI, and there is no more re-attaching to OPcache SHM, since there is only one process. One of the strongest points against running PHP inside the Webserver would be that any crash of a PHP thread would crash the Webserver; that appears to be basically solved, though, due to SEH support. Of course, there is still a lot to do to make the isapi SAPI ready for production, e .g.

  • support impersonation
  • support all HTTP status codes
  • proper (stress) testing (I haven't had a look into stresstest/, which may or may not be a good idea to have)
  • check out filter support

Before going on, I would like to gauge opinions from the community whether it's even worth it. Maybe I'm missing some important arguments against isapi API. And of course, I'd be glad to get some help; just helping to test, reporting issues, and maybe doing some relevant benchmarks would be great!

There has been an interesting discussion on the mailing list years ago. Seems like a good read.

cmb69 added 11 commits February 17, 2025 01:01
This reverts commit 4fedc42.
Most notably we need to adapt to the phpng TLS model, but also need to
make some minor changes.

We also disable SEH support for now, since inline asm is no longer
supported by MSVC.
Zeus Web Server is no longer supported for more than ten years[1], so
it does not make sense to keep the Zeus specific code, and its POSIX
config.m4.

[1] <https://web.archive.org/web/20150925055448/https://support.riverbed.com/content/support/eos_eoa/stingray_products.html#WebServerandGlobalLoadBalancer>
This has been done across the code base years ago[1], so we follow
suit.  While we're at it, we also remove the obsolete information about
PHP 7 compatibility.

[1] <http://github.com/php/php-src/commit/0cf7de1c70a459a1207aea9efa780777faa96f2e>
While it may make some sense to have the PHP major version in the names
of the binaries[1], there is no reason to have it in the names of the
source files.

[1] <php#3769>
These definitions are usually used to distinguish between `dllexport`
and `dllimport`, but there is no need for `dllimport`, and we don't
use `dllexport` to specify the visible functions, but rather rely on a
.def file, since the Microsoft ISAPI headers predeclare the functions,
so the `dllexport` name mangling would cause link errors.
That code won't build on x64, because inline assembly is not supported
on that platform, but calling `_resetstkoflw()` is simpler anyway.

After we have fixed this, we also enable SEH again.

This needs to be thoroughly tested, though.
We have .editorconfig for this now.
@arnaud-lb
Copy link
Member

Removing the need for SHM reattachment entirely would be great, as reattachment increases complexity currently. There are special cases in a lot of places around opcache, jit, inheritance cache.

Providing an alternative to SAPIs that rely on reattachment and/or updating them to not require reattachment anymore, so that SHM reattachment can be removed, seems worth it. However, I don't know how likely are PHP users to run IIS, so I don't know if the isapi is the right solution or not.

The thread safe implementation has been greatly improved regarding performance with PHP 7; I couldn't find relevant documentation, but I believe what we have now is quite similar to https://wiki.php.net/rfc/tls (maybe @arnaud-lb can clarify).

This was pursued by @weltling in https://wiki.php.net/rfc/native-tls. Now Zend/bench.php is only 2% slower with ZTS, and Symfony Demo is 7% slower (which is higher than what I expected before running the benchmark, so this should be taken with a grain of salt).

Additionally, there are at least two other use cases that require TLS:

@cmb69
Copy link
Member Author

cmb69 commented Feb 17, 2025

However, I don't know how likely are PHP users to run IIS, so I don't know if the isapi is the right solution or not.

I don't know either. According to https://learn.microsoft.com/en-us/answers/questions/1660821/symfony-iis-10-need-help:

You are not recommended to run PHP on Windows/IIS either

¯\(ツ)

Anyhow, while I would really love to get rid of the re-attachment, I don't think we can do so soonish. At least we would need working alternatives for IIS, Apache and NGinX, I think.

This was pursued by @weltling in https://wiki.php.net/rfc/native-tls.

Thanks!

Now Zend/bench.php is only 2% slower with ZTS, and Symfony Demo is 7% slower (which is higher than what I expected before running the benchmark, so this should be taken with a grain of salt).

A huge grain of salt, I'd say. It certainly depends on many factors. E.g. slow IO might cancel any measurable differences (seems to be the case for me with Symfony demo on IIS/FCGI). On the other hand, I see relevant performance improvements with ISAPI/ZTS vs. FCGI/NTS here (what very much surprises me; need to have a closer look).

@dunglas
Copy link
Contributor

dunglas commented Feb 17, 2025

Symfony relies on reading environment variables. Maybe merging #16565 could help?

@Jan-E
Copy link
Contributor

Jan-E commented Feb 17, 2025

However, I don't know how likely are PHP users to run IIS, so I don't know if the isapi is the right solution or not.

@laurinkeithdavis used to run PHP under IIS. I do not know if some application required him to use IIS. If so, there may be more IIS-users.

@bwoebi
Copy link
Member

bwoebi commented Feb 18, 2025

I can tell you that quite a few users who I have to interact with at work are indeed using IIS with Windows.

@cmb69
Copy link
Member Author

cmb69 commented Feb 19, 2025

I can tell you that quite a few users who I have to interact with at work are indeed using IIS with Windows.

I would think so. The question is rather which other Webservers are used on Windows (besides IIS and Apache).

@Jan-E
Copy link
Contributor

Jan-E commented Feb 19, 2025

Nginx sometimes

@cmb69
Copy link
Member Author

cmb69 commented Feb 19, 2025

I had a closer look at stresstest: it is a tool for integration testing (it tests php8isapi.dll directly instead of going through a webserver) contrary to general stress testing tools such as siege, wrk and ab; and as such is very fast and greatly eases debugging. Currently supported:

  1. regular PHP scripts/applications
  2. PHPTs

Ad (2): support is very limited; no --SKIPIF--, --EXPECTF--, --EXTENSIONS-- and --INI-- (to mention only some). While this could be added, so that we would be able to run CI tests faster (no process creation overhead), this is a lot of work. Bummer: we would have to skip any test that uses INI settings which are not INI_ALL. So it doesn't look sensible to spent further effort on that.

Ad (1): that is also rather limited; you can set up a file, listing the scripts to execute, optionally adding GET parameters (but there is no support for POST parameters), and these scripts will be executed (comparing against some predefined output is not supported). So the only real benefit over external stress testing tools would be ease of debugging, but that alone makes that tool possibly worthwhile to keep (and maintain).

The downside: the tool is written in C++, and uses MFC. I'm not yet sure if it's worth to keep it.

We take the list from lsapi, and basically apply the same linear
search.
@laurinkeithdavis
Copy link

Hello @Jan-E! At my previous employer we did run PHP on IIS for many years but by the time I left we had moved to Apache on Windows.

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.

6 participants