-
Notifications
You must be signed in to change notification settings - Fork 2k
feat: FrankenPHP Worker Mode #9889
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
base: 4.7
Are you sure you want to change the base?
Conversation
paulbalandan
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to read up on this, but sharing initial comments.
neznaika0
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't fully appreciate PR - I've never tried frankenphp. I'll probably come back to this later.
After accepting the PR, you can contact the developers and add CI4 to the list of available frameworks in the Native support for section. It would be a bit of a boost and a reminder of CI4.
| /** | ||
| * Checks if a service instance has been created. | ||
| * | ||
| * @param string $key Identifier of the entry to check. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are these important comments? Or rename the $key > $serviceName would be clearer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The $key is used throughout this class, including in other methods. The comments are applied for consistency with the other methods, so I think we're fine.
|
I apply PR as patch for v4.7. phpstan have errors: [ErrorException]
Class CodeIgniter\Cache\ReconnectCacheDecorator contains 1 abstract method and must therefore be declared abstract or implement the remaining method
(CodeIgniter\Cache\CacheInterface::remember)
at SYSTEMPATH/Cache/ReconnectCacheDecorator.php:24 |
I think something may have gone wrong when you applied the patch, because the final code no longer contains |
Co-authored-by: John Paul E. Balandan, CPA <paulbalandan@gmail.com>
apply code suggestion Co-authored-by: neznaika0 <ozornick.ks@gmail.com>
paulbalandan
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a great feature! I have read on the FrankenPHP's docs. Adding here some comments and I think this is good to go.
Co-authored-by: John Paul E. Balandan, CPA <paulbalandan@gmail.com>
|
Caddyfile uses tabs instead of spaces. I didn't find it in the documentation, the built-in formatter replaced them automatically. |
File created: FCPATH/frankenphp-worker.php
File created: ROOTPATH/CaddyfileThe paths in the terminal are clickable. |
2. Test your application:
curl http://localhost:8080/I understand that Caddyfile is configurable. But by default, the user can have a non-standard It's not necessary, just my opinion. |
|
I checked on the main page: speed 0.007 > 0.002 memory 0.951 > 2.890 |
Caddy doesn't care whether you use tabs, spaces, or even no indentation at all.
We use this approach pretty much everywhere in the CLI (including generators), so I'm not sure if I want to change it just for this case. Keeping paths relative also makes the output more consistent and portable across different environments.
By default, Caddy will listen on For the initial setup, we assume the default local Caddyfile is used. That's why the example uses |
|
Can we have two default Caddyfiles for Development and Production in the installer? I hope we can have ready-deploy to Docker too maybe with option Specifically, the Since Caddy supports HTTP/3, we can use a production-ready Caddyfile as the default for production environments. Thanks for the hard work on this. Caddyfile {
frankenphp {
num_threads {$NUM_THREADS:4}
worker {
# Worker running
file ${WORKDIR:/app}/public/index.php
# Amount of Worker
num {$NUM_WORKER:2} # Sets the number of PHP threads to start, defaults to 2x the number of available CPUs.
# Watcher (you can reload automatically when file already changes files in project)
watch
}
}
}
:{$HTTP_PORT:80}, :{$HTTPS_PORT:443} {
# Enable compression (optional)
encode gzip br zstd
# Webroot
root * ${WORKDIR:/app}/public
# Route requests to index.php if not a file or directory
@phpFiles {
not file
not path /favicon.ico
}
rewrite @phpFiles /index.php{uri}
# Serve PHP files through FrankenPHP
php_server {
index index.php
}
# Must set value off for deploy production
php_server off
}Dockerfile ARG PHP
FROM dunglas/frankenphp:php${PHP}
# Tentukan environment variables
ENV WORKDIR=${WORKDIR:-/app} \
WORKER_MODE=${WORKER_MODE:-on} \
SERVER_NAME=${SERVER_NAME:-localhost} \
CI_ENVIRONMENT=${CI_ENVIRONMENT:-development} \
NUM_THREADS=${NUM_THREADS:-4} \
NUM_WORKER=${NUM_WORKER:-2}
COPY . ${WORKDIR}
WORKDIR ${WORKDIR}
RUN apt update && apt install libnss3-tools -y
# add additional extensions here:
RUN install-php-extensions \
intl \
bz2 \
gd \
gmagick \
redis \
zip \
opcache
# Enable PHP production settings
RUN if [ "$CI_ENVIRONMENT" = "production" ]; then \
mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"; \
else \
mv "$PHP_INI_DIR/php.ini-development" "$PHP_INI_DIR/php.ini"; \
fi
# https://github.com/composer/docker/pull/250
COPY --from=composer/composer:2-bin /composer /usr/local/bin/composer
# Install vendor
RUN if [ "$CI_ENVIRONMENT" = "production" ]; then \
composer install --no-dev --prefer-dist --no-interaction --no-scripts --optimize-autoloader; \
else \
composer install --prefer-dist --no-interaction --no-scripts --optimize-autoloader; \
fi
# Remove Cache Manually
RUN rm -rf ${WORKDIR}/writable/cache/FactoriesCache*
RUN rm -rf ${WORKDIR}/writable/cache/FileLocatorCache*
RUN chmod -R 777 ${WORKDIR}/public/assets/uploads && chmod -R 777 ${WORKDIR}/writable
COPY --link Caddyfile /Caddyfile
CMD [ "frankenphp", "run", "--config", "/Caddyfile" ]docker-compose.yml services:
php:
container_name: myproject
# uncomment the following line if you want to use a custom Dockerfile
build:
context: .
dockerfile: Dockerfile
args:
PHP: ${PHP:-8.4}
# uncomment the following line if you want to run this in a production environment
restart: always
environment:
WORKDIR: ${WORKDIR:-/app}
WORKER_MODE: ${WORKER_MODE:-on}
SERVER_NAME: ${SERVER_NAME:-localhost}
CI_ENVIRONMENT: ${CI_ENVIRONMENT:-development}
NUM_THREADS: ${NUM_THREADS:-4}
NUM_WORKER: ${NUM_WORKER:-2}
ports:
# HTTP
- target: 80
published: ${HTTP_PORT:-80}
protocol: tcp
# HTTPS
- target: 443
published: ${HTTPS_PORT:-443}
protocol: tcp
# HTTP/3
- target: 443
published: ${HTTP3_PORT:-443}
protocol: udp
volumes:
- ./:${WORKDIR:-/app}
- caddy_data:/data
- caddy_config:/config
depends_on:
- redis
# comment the following line in production, it allows to have nice human-readable logs in dev
tty: true
redis:
image: redis:alpine
container_name: redis-askred
ports:
- "6379:6379"
# Volumes needed for Caddy certificates and configuration
volumes:
caddy_data:
caddy_config: |
|
@ddevsr Thanks for sharing the detailed setup and examples. At the moment, we don't provide production-ready configs or Dockerfile setups for Nginx or Apache either, so I don't think it makes sense to treat FrankenPHP differently. Production environments and container setups can vary a lot, and shipping official "production" configs or Dockerfiles could be misleading. I did consider adding a For now, I think we should stick with the current setup and potentially add more guidance and examples in the docs later. |
ddevsr
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For me, this is pretty clear.
Description
This PR adds experimental support for FrankenPHP Worker Mode, allowing to handle multiple HTTP requests within the same PHP process. Instead of bootstrapping the framework for every request (traditional PHP-FPM model), the worker boots once and reuses resources across requests, resulting in 2-3x performance improvements for typical database-driven applications.
How it works
New features
app/Config/WorkerMode.php- Configuration for persistent services and garbage collectionphp spark worker:install- Generates Caddyfile and worker entry pointphp spark worker:uninstall- Removes worker mode filesOptimizations
Database:
BaseConnection::ping()method for connection health checkspg_ping(), other drivers useSELECT 1DatabaseConfig::validateForWorkerMode()- validates connections at request startDatabaseConfig::cleanupForWorkerMode()- detects uncommitted transactions and rolls backSession handlers:
PersistsConnectiontrait for Redis and Memcached handlersServices and state management:
Boot::bootWorker()- one-time initialization for worker modeCodeIgniter::resetForWorkerMode()- resets request-specific stateServices::resetForWorkerMode()- resets non-persistent services between requestsServices::validateForWorkerMode()- validates cache connectionsEvents::cleanupForWorkerMode()- clears event performance dataDebug Toolbar:
Toolbar::reset()- resets collectors between requests (development only)No impact on traditional PHP-FPM
All worker mode methods are only called from the worker entry point (
public/frankenphp-worker.php). When running in traditional PHP-FPM mode viapublic/index.php, none of this code is executed. The changes introduce zero performance overhead for applications not using worker mode.Benchmarks
I also prepared a set of simple benchmarks to evaluate worker mode performance: https://github.com/michalsn/benchmark-codeigniter-frankenphp
Based on existing benchmarks, nginx with PHP-FPM should perform similarly to FrankenPHP running in classic mode. When I attempted to verify this, the results showed roughly 60% of classic mode performance. However, these tests were run in a local development environment that is not properly tuned, which likely skewed the results.
For this reason, I decided not to include the benchmark numbers for nginx with PHP-FPM. In a real-world, properly configured environment, nginx is expected to perform similarly to FrankenPHP in classic mode.
Checklist: