dunglas / frankenphp

🧟 The modern PHP app server
https://frankenphp.dev
MIT License
6.69k stars 220 forks source link

PHP Fatal error: Maximum call stack size of 83360 bytes reached during compilation. Try splitting expression #380

Closed cdaguerre closed 6 months ago

cdaguerre commented 9 months ago

Hi, I tried FrankenPHP on 2 different Symfony 6.3 projects and get the error:

Maximum call stack size of 83360 bytes reached during compilation. Try splitting expression in /app/vendor/symfony/framework-bundle/DependencyInjection/Configuration.php on line 351

The symfony/framework-bundle version is 6.3.4. Using PHP 8.3 alpine version.

I'm not sure what compilation refers to in this context and Google didn't help ;)

cdaguerre commented 9 months ago

I'm also seeing the following line on startup:

2023-12-13 15:39:24 2023/12/13 14:39:24.413     INFO    using provided configuration    {"config_file": "/etc/caddy/Caddyfile", "config_adapter": "caddyfile"}
2023-12-13 15:39:24 2023/12/13 14:39:24.414     WARN    Import file is empty    {"file": "/etc/caddy/conf.d/logging/dev.caddy"}
2023-12-13 15:39:24 2023/12/13 14:39:24.418     WARN    admin   admin endpoint disabled
2023-12-13 15:39:24 2023/12/13 14:39:24.442     INFO    FrankenPHP started 🐘    {"php_version": "8.3.0"}
2023-12-13 15:39:24 2023/12/13 14:39:24.463     INFO    autosaved config (load with --resume flag)      {"file": "/config/caddy/autosave.json"}
2023-12-13 15:39:24 2023/12/13 14:39:24.464     INFO    serving initial configuration
2023-12-13 15:39:24 2023/12/13 14:39:24.471     WARN    PHP Warning:  Failed to set memory limit to 0 bytes (Current memory usage is 2097152 bytes) in Unknown on line 0
withinboredom commented 9 months ago

Might be related to #378?

cdaguerre commented 9 months ago

Well it's the same error but I'm not compiling for a standalone binary. I'm trying it out in our local development setup (docker-compose), where we favor developer experience and have no production optimizations. For instance, we have memory_limit set to -1 there. Any idea how I can allow a higher limit for this setting ? I've never seen it before and can't find anything online. Is this even a PHP error?

withinboredom commented 9 months ago

I meant specifically:

Have you warmed the cache

I do know there is something to be done here; I use Symfony at work and write compiler passes from time to time, but I'm not usually doing devops on it, so I don't know how to perform a compile pass on the container. (maybe composer dump?)

Sadly, this seems to be a Symfony-specific thing and I won't be much help here. Maybe @dunglas has some insight?

For instance, we have memory_limit set to -1 there.

Hmmm, usually, I see the other way around; lower limits in dev than in prod (since you don't want to end up having everything working in dev, but crashing in prod due to hitting limits).

konrado commented 9 months ago

I have seen this issue a few times, but manually removing the cache directory fixes it. IMO it seems to be related to cache warming. So you can easily "fix" it locally, but it's annoying.

cdaguerre commented 9 months ago

Actually probably more related to https://github.com/php/php-src/issues/12234 and https://github.com/dunglas/frankenphp/pull/223... I'm not to sure what should be done here... Maybe this should indeed be improved at symfony/framework-bundle level. Any thoughts @dunglas ?

dunglas commented 9 months ago

Unfortunately it's a tradeoff.

There are 3 options:

  1. Using Debian variants in dev (it's what I recommend to do)
  2. Creating a custom Alpine build with an increased stack size for dev (in prod, it's better to keep with the defaults)
  3. Increasing the default size in our image, but it will affect "negatively" all apps in production for some that need this only in dev

It's unlikely that the "problem" is in Symfony itself (and it's not really a problem actually, there are just many files to "compile" and it's too much for the default stack limit). It's more likely in the bundles used or the custom code the app. It can probably be optimized in your app itself... but does it worth it as it's unlikely that this affect the production environment (where the cache is already populated)?

darkweak commented 9 months ago

Actually with a simple API Platform it doesn't work using the dunglas/frankenphp:latest-builder-alpine combined to dunglas/frankenphp:latest-alpine so we can't build any FrankenPHP instances with additional modules.

It can probably be optimized in your app itself

That doesn't seem to be an application issue (or maybe API Platform itself should be optimized too).

Using Debian variants in dev (it's what I recommend to do)

That means, we have to rewrite the Dockerfile to use the Debian distribution instead of the default one to use apt instead of apk.

cdaguerre commented 9 months ago

It seems it always occurs at the exact same point during symfony container compilation, notably here: https://github.com/symfony/framework-bundle/blob/4a4e93d93f2081c8e90a289644198fc2ec6258e0/DependencyInjection/Configuration.php#L337 I tried 2 pretty different applications and it was the same on the issue raised by @dunglas here: https://github.com/php/php-src/issues/12234 Did it fail at the same line for you @darkweak ?

That's why I was wondering if it can not be optimized at symfony/framework-bundle level. My guess is that even the symfony demo doesn't work out of the box with cold cache.

For the record, I increased the stack size manually like here and other problems started popping up (redis connection timeouts !?) on stuff that works without fiddling the stack size...

dunglas commented 9 months ago

@darkweak can you post your Dockerfile? Are you sure that you're increasing the stack size as we do in the official images in your custom build including extra modules? I'm not able to reproduce the problem with our official Alpine images (no custom build) and API Platform.

@cdaguerre Did you try to increase the stack size? It can be done by changing the value here: https://github.com/dunglas/frankenphp/blob/main/alpine.Dockerfile#L88 (the stack-size flag)?

cdaguerre commented 9 months ago

arg sorry @dunglas I updated my comment above ;)

dunglas commented 9 months ago

@cdaguerre are you using the worker mode? Redis timeouts are likely unrelated.

cdaguerre commented 9 months ago

I tried both, both the same.

dunglas commented 9 months ago

And do you have the same issue with the Debian image?

darkweak commented 9 months ago

@dunglas I'm following the FrankenPHP documentation

FROM dunglas/frankenphp:latest-builder-alpine AS builder
COPY --from=caddy:builder /usr/bin/xcaddy /usr/bin/xcaddy

COPY ./middleware ./middleware

ENV CGO_ENABLED=1 XCADDY_SETCAP=1
RUN xcaddy build \
    --output /usr/local/bin/frankenphp \
    --with github.com/dunglas/frankenphp=./ \
    --with github.com/dunglas/frankenphp/caddy=./caddy/ \
    --with github.com/dunglas/mercure/caddy \
    --with github.com/dunglas/vulcain/caddy \
    --with connector=./middleware

# Versions
# hadolint ignore=DL3007
FROM dunglas/frankenphp:latest-alpine AS frankenphp_upstream
FROM composer/composer:2-bin AS composer_upstream

# The different stages of this Dockerfile are meant to be built into separate images
# https://docs.docker.com/develop/develop-images/multistage-build/#stop-at-a-specific-build-stage
# https://docs.docker.com/compose/compose-file/#target

# Base FrankenPHP image
FROM frankenphp_upstream AS frankenphp_base
COPY --from=builder --link /usr/local/bin/frankenphp /usr/local/bin/frankenphp

WORKDIR /app

# persistent / runtime deps
# hadolint ignore=DL3018
RUN apk add --no-cache \
        acl \
        file \
        gettext \
        git \
    ;

RUN set -eux; \
    install-php-extensions \
        apcu \
        intl \
        opcache \
        gd \
        zip \
    ;

###> recipes ###
###> doctrine/doctrine-bundle ###
RUN set -eux; \
    install-php-extensions pdo_pgsql
###< doctrine/doctrine-bundle ###
###< recipes ###

COPY --link api/frankenphp/conf.d/app.ini $PHP_INI_DIR/conf.d/
COPY --link --chmod=755 api/frankenphp/docker-entrypoint.sh /usr/local/bin/docker-entrypoint
COPY --link api/frankenphp/Caddyfile /etc/caddy/Caddyfile

ENTRYPOINT ["docker-entrypoint"]

# https://getcomposer.org/doc/03-cli.md#composer-allow-superuser
ENV COMPOSER_ALLOW_SUPERUSER=1
ENV PATH="${PATH}:/root/.composer/vendor/bin"

COPY --from=composer_upstream --link /composer /usr/bin/composer

HEALTHCHECK --start-period=60s CMD curl -f http://localhost:2019/metrics || exit 1
CMD [ "frankenphp", "run", "--config", "/etc/caddy/Caddyfile" ]

# Dev FrankenPHP image
FROM frankenphp_base AS frankenphp_dev

ENV APP_ENV=dev XDEBUG_MODE=off
VOLUME /app/var/

RUN mv "$PHP_INI_DIR/php.ini-development" "$PHP_INI_DIR/php.ini"

RUN set -eux; \
    install-php-extensions \
        xdebug \
    ;

COPY --link api/frankenphp/conf.d/app.dev.ini $PHP_INI_DIR/conf.d/

CMD [ "frankenphp", "run", "--config", "/etc/caddy/Caddyfile", "--watch" ]

# Prod FrankenPHP image
FROM frankenphp_base AS frankenphp_prod

ENV APP_ENV=prod
ENV FRANKENPHP_CONFIG="import worker.Caddyfile"

RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"

COPY --link api/frankenphp/conf.d/app.prod.ini $PHP_INI_DIR/conf.d/
COPY --link api/frankenphp/worker.Caddyfile /etc/caddy/worker.Caddyfile

# prevent the reinstallation of vendors at every changes in the source code
COPY --link api/composer.* api/symfony.* ./
RUN set -eux; \
    composer install --no-cache --prefer-dist --no-dev --no-autoloader --no-scripts --no-progress

# copy sources
COPY --link ./api ./
RUN rm -Rf frankenphp/

RUN set -eux; \
    mkdir -p var/cache var/log; \
    composer dump-autoload --classmap-authoritative --no-dev; \
    composer dump-env prod; \
    composer run-script --no-dev post-install-cmd; \
    chmod +x bin/console; sync;
dunglas commented 9 months ago

@darkweak the documentation assumes that you're using the GNU libc and not musl: FROM dunglas/frankenphp:latest-builder AS builder ;)

I added a note about musl in https://github.com/dunglas/frankenphp/pull/385. That should fix your issue.

darkweak commented 9 months ago

But if you provide a latest-builder-alpine image, I assume that you support alpine 😂 Thank you for the documentation update.

dunglas commented 9 months ago

We do support Alpine ;) This option is only necessary for people using Alpine + a custom build + Symfony in dev mode. Quite an edge case.

dunglas commented 9 months ago

That bein said, and even if it's not related to this issue. I'm considering switching from Alpine to Debian in API Platform and Symfony Docker, to prevent this kind of weird edge cases. PHP has many known issues with Alpine.