Application specific mapping in FastCGI ISAPI 1.5

IIS team recently released FastCGI ISAPI 1.5 for IIS 5.1 and IIS 6.0. One of the major new features added in this release is per application process pools. Per application process pools allow users to specify a unique FastCGI mapping for a particular web application in fcgiext.ini. Using this functionality administrators can now choose to run a web application with PHP version which can be different from what is used for rest of the PHP applications in the same site or the server. This was always possible in IIS 7.0 and 7.5 by overriding PHP handler in web.config but not on WinXP and Win2K3. Version 1.0 of FastCGI ISAPI only allowed specifying a different process pool at a site level granularity. So if a site has both wordpress and gallery installed, it wasn’t possible to use different PHP versions for running them. With FastCGI 1.5 that is possible now. For providing application specific mappings, we extended site specific mapping feature. Let’s learn application specific mapping by comparing it with site specific mapping.
 

 

 
Site specific mapping
Application specific mapping
fcgiext.ini
[Types]
php=PHP_53
php:1234=PHP_52
 
[PHP_53]
ExePath=c:\php530\php-cgi.exe
 
[PHP_52]
ExePath=c:\php520\php-cgi.exe
[Types]
Php=PHP_530
Php:1234=PHP_526
Php:/w3svc/1234/root/wordpress=PHP_530
Php:/w3svc/1234/root/gallery=PHP_520

[PHP_526]
.
.
.

Details
Above configuration will make FastCGI module pick PHP_53 mapping for all sites but use PHP_52 mapping for site with id 1234.
FastCGI module will pick PHP_520 mapping for gallery and PHP_530 for wordpress defined under site 1234. All other php applications under site 1234 will be handled using PHP_526 mapping. Remaining sites will use PHP_530 mapping.

Application path’s format for fcgiext.ini is “/LM/W3SVC/<siteId>/ROOT/<appname>”. In this format “/LM” and “/W3SVC” parts are option. “/Root” can be emitted for applications pointing to root of the site.

fcgiconfig.js
fcgiconfig.js -add -section:php_530 -extension:php -path:c:\php530\php-cgi.exe -site:1234
fcgiconfig.js -add -section:php_530 -extension:php -path:c:\php530\php-cgi.exe -application:/w3svc/1234/root/wordpress
 
 
Picking the right mapping
Among all the formats (including wildcard formats) which are accepted in fcgiext.ini mappings, FastCGI goes through following logic to pick which mapping to use for each request.
 
·         If mapping for current  application is present, it takes highest priority and is used. E.g. php:/w3svc/1234/root/drupal=ApplicationHandler
·         If wildcard mapping for the current application is present, it is used next. E.g. *:/w3svc/1234/root/drupal=ApplicationHandler
·         If mapping for a particular site is present but application specific mapping is not present, than that is used. E.g. php:1234=SiteHandler
·         If site specific and application specific mapping is not present, FastCGI module look for wildcard mapping for a particular site. E.g. *:1234=SiteHandler
·         If none of the above is present, mapping for the file extension takes priority. E.g. php=ExtensionHandler
·         Wildcard mapping comes last. *=WildcardHandler
 
If FastCGI doesn’t find a mapping after going through the logic above, an error is returned.
 
What’s coming next
 
Now that applications can be configured to run using a particular PHP version on IIS 5.1 and above, our next goal is to support multiple PHP versions side-by-side in WebPI. This would require PHP installer to support side-by-side install which is also something we are looking at. Once PHP installer has the required support, we will list different PHP versions in WebPI and let users install multiple versions of PHP. Additionally users will be able to target a particular PHP version when installing a PHP application using WebPI.
 
Hope this helps.
Kanwal

Inconsistencies in wincache statistics page explained

Problem description

 

Many customers running WinCache reported seeing different cache statistics on consecutive requests to wincache.php. Specifically, cache uptime and opcode cache summary returned second time by wincache.php was completely different than the data returned first time. Occasionally data shown in the opcode cache summary table didn’t match the chart displayed on its side. Many customers correctly guessed that this happens because there are multiple cache instances present in different php-cgi processes but thought that none of the PHP processes are sharing cache data and were worried about excessive memory usage by WinCache. I have been replying to many customers personally and posting on forums explaining why they see this behavior but as more and more customers are using WinCache, this question is becoming more common. Ruslan who is PM for WinCache suggested writing a blog post to help customers understand the reason. So here you go.

 

Detailed explanation

 

WinCache creates many memory segments which are shared among all the php-cgi processes launched by a particular IIS worker process. For simplicity sake I will use the term “all php processes” to refer to “all php processes launched by one IIS worker process”. In version 1.0, WinCache creates 5 shared memory segments. Mostly these segments are shared successfully among all the php processes. In fact 4 shared segments are always shared but memory segment for opcode cache is sometimes not shared. Reason for this is that 4 of the 5 memory segments store data structures which are completely defined by us. Anytime we need to store a pointer in the shared segment which points to some other data in the shared segment, we define pointer as simple integer which is then set to offset from start of memory segment. This way other processes can reach the data pointed by this pointer even when absolute address of the data might be different. In contrast, data stored in opcode cache is defined by the PHP engine and include many absolute pointers. These pointer values cannot be changed to offsets without incurring a very heavy performance penalty which will totally defy the purpose of WinCache. For this reason, we try to map the global opcode cache segment at same address in all processes using MapViewOfFileEx which can possibly fail if the address is already occupied. All other caches doesn’t have this restriction and are mapped anywhere which mostly succeed. If we fail to map the opcode cache at a particular address, we create another opcode cache in processs local memory so that requests processed by this process are also very fast. Typically this happens for 5%-10% of php processes. Below figures illustrates state of the caches in a normal php process (figure 1) and in a process which ends up creating a local cache (figure 2). For more information on these caches, read my previous blog.

 

Local Cache Explanation

Now that you understand this design, all the anomalies seen in responses of wincache.php can be explained. If the process which processes request to wincache.php has a local cache, opcode cache data will be different from the one shown when request is handled by a process which is using global opcode cache. Also since cache uptime in wincache.php is picked from opcode cache (ocache_fileinfo), it will be age of either global opcode cache or local opcode cache. Reason why chart sometimes didn’t match opcode cache summary is because request to draw the chart is sent separately. If the chart draw request reaches a process which has different cache structure than the one which returned summary data, there can be a mismatch.

 

Improvements in WinCache 1.0.1

 

In WinCache 1.0.1, we have made few changes to wincache.php which will make all this a little less confusing. Since cache uptime can be different for file cache and opcode cache, we have moved the cache uptime field to opcode cache and file cache summary tables. So you will now see two cache uptimes. File cache uptime will reflect age of global file cache and opcode cache uptime will reflect age of global or local opcode cache. We also added cache scope property in opcode cache summary table which will be either “global” or “local”. This will indicate if the opcode cache data you are looking at is for global cache or a local cache. To fix the chart discrepancy, we now send data required to draw the chart in the request itself so that chart always matches the data in the summary table. All these changes were made based on feedback from you. So keep sending your feature requests and feedback so that we can keep improving WinCache.

 

Hope this helps.

Kanwal

Feature additions and bug fixes coming up in WinCache

We are seeing huge momentum behind adoption of WinCache. In the month of September, WinCache v1 Beta was downloaded more than 13,000 times making it one of the most downloaded IIS extensions in the first month following its release. Many happy customers chose to run it on their production servers despite its beta tag. We got very encouraging feedback from the beta release and we are pushing hard for our next release. Our next release date is approaching fast and I wanted to give an update on new features and bug fixes. If your favorite feature was missing in the beta and is also not in the list below, please email me or post on the forums so that we can consider it for future releases of WinCache.

Bug Fixes

1. We fixed a bug due to which same file was getting included twice even when include_once/require_once was used. Some PHP applications didn’t work due to this bug.
2. Few customers reported crashes when running WinCache on their test servers. This was because file cache component didn’t cache zero-size files properly.
3. One customer didn’t see enough performance improvement when using cakephp. This was because of a bug in opcode cache which caused us to recompile the code every time.
4. One bug caused opcode cache’s total hit count to be negative after first request. Also hit count of different file entries in file cache was one more than expected. Both of these bugs are fixed.
5. WinCache caches compile time errors and warnings generated by PHP engine. We fixed a bug due to which warning/errors caching sometimes caused next request to return 500.

New features

1. We are adding an API “bool wincache_refresh_if_changed()” to force a file change check on a file next time it is used from cache. This is useful if you change a file in PHP code and you want next request to load the file from disk again. You can pass blank or null which tells API to refresh all the files in the cache. An array of absolute/relative paths as argument will make WinCache only refresh these file entries. Please note that file will be reloaded from disk only if a file change is detected. This API enforces a file change check and not necessarily removal of cached entry.
2.
In the next release, users will be able to disable file change checks completely by setting value of wincache.chkinterval to 0. When file change checks are disabled, changed file will be reloaded only when wincache_refresh_if_changed is called or IIS application pool is recycled. Cached files will still get removed by scavenger when the file is not used for a long time causing wincache to load the file again.
3.
Another feature getting added is to enable administrators choose a list of websites for which wincache.ocenabled property is opposite of global setting affecting all the websites. This was requested by a hoster who wanted a safe and quick mechanism to turn-off WinCache for websites which run into some problems.
4.
By default, caching in WinCache will be disabled when PHP is running in CLI (command line) mode. Setting wincache.enablecli to 1 will turn on caching in CLI mode.
5. We will be exposing total_cache_uptime property from wincache_fcache_fileinfo() and wincache_ocache_fileinfo() which will give total cache uptime in seconds.
6.
On popular demand, we are including a PHP script (wincache.php) in WinCache setup which users can use to see cache statistics.

If you haven’t had a chance to test your PHP application with WinCache yet, download and try it out today. We are looking forward to your feature requests, bugs and general feedback to guide us for future releases of WinCache.



Update: WinCache RC release is available now.

Thanks,
Kanwal

Migrating FastCGI configuration from IIS 5.1/6.0 to IIS 7.0/7.5

Problem

As you know FastCGI functionality on IIS 5.1 and IIS 6.0 is provided by FastCGI ISAPI extension which is available as an independent download. On IIS 7.0 and beyond, FastCGI functionality is provided by IIS FastCGI module which comes with the operating system. These components use different configuration stores to store the settings that affect their behavior. FastCGI ISAPI uses an INI file named fcgiext.ini as its configuration store whereas FastCGI module keeps configuration in applicationHost.config in xml format with rest of IIS configuration. Before, migrating from IIS6 to IIS7 involved migrating fcgiext.ini configuration to FastCGI module manually as there was no automated way to do that. MSDeploy only had functionality to migrate metabase configuration but because FastCGI ISAPI configuration is kept separately in an INI file, that wasn’t possible.

Solution

MSDeploy v1 RTW which is released today include a new provider named fcgiextConfig to automate task of migrating FastCGI ISAPI INI configuration to FastCGI module section. FcgiextConfig provider accepts a path which can be “APPHOST” or ”INI”. When path “APPHOST” is specified, configuration from system.webServer/fastCgi section in applicationHost.config is read. Path “APPHOST” should only be used on systems running FastCGI module (i.e. IIS7 and beyond). When path “INI” is specified, this provider reads the configuration from file “%windir%\system32\inetsrv\fcgiext.ini” and produce xml which looks like FastCGI module configuration. Once INI settings are mapped to produce the xml, msdeploy engine can compare configuration of FastCGI ISAPI with FastCGI module and make changes to FastCGI module configuration as required. Again, path “INI” can only be used on a machine which is running FastCGI ISAPI.

Examples

Dump command to dump INI settings looks like following.
        msdeploy –verb:dump –source:fcgiExtConfig=ini –xml
        msdeploy –verb:dump –source:fcgiExtConfig=apphost –xml

Sync command to move INI settings to FastCGI module section is following.
        msdeploy –verb:sync –source:fcgiExtConfig=ini –dest:fcgiExtConfig=apphost -whatif

Details

  Here is how INI settings are mapped to system.webServer/fastCgi settings. For each entry   “<extension>:<optionalsiteid>=<sectionname>” under [Types] section of fcgiext.ini, we read the settings under section [<sectionname>]. If optional site id is present, we look for an entry for the same extension but without site id. If an entry is found, we use configuration under this entry as base set of settings for the site specific entry. So if “php=basephp” and “php:1=site1” entries are present in INI, settings under section [basephp] provide base values and then settings under section [site1] override base settings to generate configuration for “php:1” process pool. If a property is not present in the INI file, we assume the default value as assumed by FastCGI ISAPI extension. A FastCGI process pool entry is created for each INI entry with a valid ExePath. Various INI file settings are mapped to a FastCGI process pool as below.
  

fcgiext.ini configuration setting system.webServer/fastCgi/application property
ExePath fullPath
Arguments arguments
QueueLength queueLength
MaxInstances maxInstances
IdleTimeout idleTimeout
ActivityTimeout activityTimeout
RequestTimeout requestTimeout
InstanceMaxRequests instanceMaxRequests
FlushNamedPipe flushNamedPipe
Protocol protocol
RapidFailsPerMinute rapidFailsPerMinute
EnvironmentVars environmentVariables
ResponseBufferLimit Ignored
IgnoreExistingFiles Ignored
IgnoreDirectories Ignored
UnhealthyOnQueueFull Ignored


If the source version is FastCGI ISAPI 1.5, some additional properties get picked and mapped as below.
 

StderrMode stderrMode
MonitorChangesTo MonitorChangesTo
SignalBeforeTerminateSeconds signalBeforeTerminateSeconds


If FastCGI ISAPI version on source and FastCGI module version on destination are not compatible, fcgiextConfig provider will block the migration. Note that fcgiextConfig provider doesn’t take care of migrating the relevant IIS ScriptMaps. You can migrate ScriptMaps using a separate msdeploy migrate operation.

Hope this helps.
Kanwal

How WinCache make PHP run faster

Why WinCache?

Use of PHP accelerators is very common to make PHP run faster. Most of the existing PHP accelerators which are in use today are designed keeping *nix architecture in mind and mostly doesn’t work well on Windows. For example, APC which is very commonly used on *nix doesn’t work on Windows. Xcache does work in ISAPI mode but crashes when PHP is running in FastCGI mode with multiple php processes alive. Absence of a stable PHP accelerator on Windows made people complain about PHP performance on Windows all the time. Another complaint which we heard consistently from customers running PHP on windows was that file operations on Windows were slower than on *nix. Reason being Windows’s CreateFile system call which is much more expensive than a fopen call on *nix as CreateFile goes through a much more complex security check involving ACLs. Windows programmers typically try to reduce CreateFile calls by caching file contents or keeping the open handles around so that security check happens only once. Because PHP opens the file every time it is required, PHP end up performing slower on Windows than *nix. To solve these problems, IIS team started working on an advanced caching solution few months ago which has resulted in the component known as Windows Cache Extension for PHP (WinCache in short). Below are short descriptions of the caching components which are available in WinCache.

Opcode cache

This is a standard opcode cache similar to other existing opcode caches available for *nix which works by overwriting zend_compile_file function pointer so that all compile file requests go through WinCache. WinCache let PHP core compile the PHP file to generate opcodes first time it is executed. It then caches these opcodes in memory which is shared with other PHP processes. When the same file is executed a second time, WinCache directly points to the cached opcodes in memory and skips compilation altogether. Generating opcodes from PHP code is a very expensive operation and major chunk of request processing time is spent here by PHP core. So having a solution like this saves 50%-70% of request execution time. WinCache creates a separate cache for all child processes under one worker process. So different worker processes keep separate caches and is more secure than current caching solutions.

File content cache

WinCache has a file content cache component which caches content of files in memory to avoid opening files every single time. This component works by replacing zend_stream_open_function function pointer so that file open requests go through WinCache. When a file is opened for the first time, we read the file contents into file cache. When the file is opened again, pointer to file contents in memory is returned instead of opening the file from disk. In our testing we saw that file operations are reduced by approx 80%-90% for each request for most of the popular PHP applications like wordpress, drupal, gallery etc. This is a huge win especially when content is on a UNC share. In our testing we found that PHP’s throughput is significantly impacted when content is moved to UNC share. With WinCache, throughput with content on UNC remains almost same as when content is present on the local machine.

Relative path cache

When we first added the file content cache, we relied on PHP engine to resolve relative path to absolute path as PHP has fairly complex logic to resolve relative paths. Given that most PHP applications use relative paths, this path resolution end up doing a lot of expensive file operations. To reduce these file operations we added a relative path cache component to WinCache which keeps relative path to absolute path mapping in memory. In PHP 5.3, all calls to zend_resolve_path goes through this cache and in PHP 5.2 zend_stream_open_function uses this cache to get the full path. Relative path to absolute path mapping is impacted by lot of parameters like current working directory, current executing file, value of include_path etc. We do take all these parameters into account to ensure that path resolution with WinCache and without WinCache is exactly the same. All you get with this cache is way less file operations but same result.

All the three caches are connected so that if a file is changed, file entry in opcode cache, file cache and relative path cache gets removed in one go so that all three caches stay always in sync. We have tested WinCache with PHP 5.2 and PHP 5.3. Read documentation to learn about properties you can set to control behavior of these caches.

Links

Download link for PHP 5.2, VC6, NTS – http://go.microsoft.com/?linkid=9681612
Download link for PHP 5.3, VC9, NTS – http://go.microsoft.com/?linkid=9681610
Documentation:
http://learn.iis.net/page.aspx/678/using-windows-cache-extension-for-php/
Forums – http://forums.iis.net/t/1160554.aspx

Please try out WinCache beta release and give us feedback.

Thanks,
Kanwal