laravel / octane

Supercharge your Laravel application's performance.
https://laravel.com/docs/octane
MIT License
3.71k stars 284 forks source link

May throw BindingResolutionException when high concurrently requests in OpenSwoole #906

Closed evan361425 closed 1 month ago

evan361425 commented 1 month ago

Octane Version

2.4.0

Laravel Version

11.10.0

PHP Version

8.2.20

What server type are you using?

Open Swoole

Server Version

22.0.0

Database Driver & Version

php8.2-sqlite3

Description

When sending 10 concurrent requests into Octane server with OpenSwoole coroutine enabled, it may throw BindingResolutionException:

$php artisan octane:start --workers=2

   INFO  Server running…

  Local: http://127.0.0.1:8000 

  Press Ctrl+C to stop the server

  200    GET /google ........................................................................ 19.36 mb 189.63 ms
  200    GET /google ........................................................................ 19.37 mb 194.08 ms
  200    GET /google ........................................................................ 19.40 mb 206.60 ms
  200    GET /google ........................................................................ 19.35 mb 214.35 ms
  200    GET /google ........................................................................ 19.31 mb 221.36 ms
  200    GET /google ........................................................................ 19.26 mb 227.83 ms

   Illuminate\Contracts\Container\BindingResolutionException 

  Target [Illuminate\Contracts\Encryption\Encrypter] is not instantiable while building [Illuminate\Cookie\Middleware\EncryptCookies].

  at vendor/laravel/framework/src/Illuminate/Container/Container.php:1118
    1114▕         } else {
    1115▕             $message = "Target [$concrete] is not instantiable.";
    1116▕         }
    1117▕ 
  ➜ 1118▕         throw new BindingResolutionException($message);
    1119▕     }
    1120▕ 
    1121▕     /**
    1122▕      * Throw an exception for an unresolvable primitive.

  200    GET /google ........................................................................ 19.32 mb 240.42 ms
  200    GET /google ........................................................................ 19.29 mb 253.11 ms
  200    GET /google ........................................................................ 19.24 mb 267.67 ms
  200    GET /google ........................................................................ 19.20 mb 281.84 ms

   Illuminate\Contracts\Container\BindingResolutionException 

  Target [Illuminate\Contracts\Encryption\Encrypter] is not instantiable while building [Illuminate\Cookie\Middleware\EncryptCookies].

  at vendor/laravel/framework/src/Illuminate/Container/Container.php:1118
    1114▕         } else {
    1115▕             $message = "Target [$concrete] is not instantiable.";
    1116▕         }
    1117▕ 
  ➜ 1118▕         throw new BindingResolutionException($message);
    1119▕     }
    1120▕ 
    1121▕     /**
    1122▕      * Throw an exception for an unresolvable primitive.

  200    GET /google ........................................................................ 19.78 mb 158.28 ms
  200    GET /google ........................................................................ 19.73 mb 165.39 ms
  200    GET /google ........................................................................ 19.59 mb 172.71 ms
  200    GET /google ........................................................................ 19.51 mb 179.75 ms
  200    GET /google ........................................................................ 19.46 mb 188.33 ms

   Illuminate\Contracts\Container\BindingResolutionException 

  Target [Illuminate\Contracts\Encryption\Encrypter] is not instantiable while building [Illuminate\Cookie\Middleware\EncryptCookies].

  at vendor/laravel/framework/src/Illuminate/Container/Container.php:1118
    1114▕         } else {
    1115▕             $message = "Target [$concrete] is not instantiable.";
    1116▕         }
    1117▕ 
  ➜ 1118▕         throw new BindingResolutionException($message);
    1119▕     }
    1120▕ 
    1121▕     /**
    1122▕      * Throw an exception for an unresolvable primitive.

  200    GET /google ........................................................................ 19.78 mb 184.72 ms
  200    GET /google ........................................................................ 19.73 mb 192.32 ms

   Illuminate\Contracts\Container\BindingResolutionException 

  Target class [events] does not exist.

  at vendor/laravel/framework/src/Illuminate/Container/Container.php:906
    902▕ 
    903▕         try {
    904▕             $reflector = new ReflectionClass($concrete);
    905▕         } catch (ReflectionException $e) {
  ➜ 906▕             throw new BindingResolutionException("Target class [$concrete] does not exist.", 0, $e);
    907▕         }
    908▕ 
    909▕         // If the type is not instantiable, the developer is attempting to resolve
    910▕         // an abstract type such as an Interface or Abstract Class and there is

   Illuminate\Contracts\Container\BindingResolutionException 

  Target class [events] does not exist.

  at vendor/laravel/framework/src/Illuminate/Container/Container.php:906
    902▕ 
    903▕         try {
    904▕             $reflector = new ReflectionClass($concrete);
    905▕         } catch (ReflectionException $e) {
  ➜ 906▕             throw new BindingResolutionException("Target class [$concrete] does not exist.", 0, $e);
    907▕         }
    908▕ 
    909▕         // If the type is not instantiable, the developer is attempting to resolve
    910▕         // an abstract type such as an Interface or Abstract Class and there is

  200    GET /google ........................................................................ 19.77 mb 149.22 ms
  200    GET /google ........................................................................ 19.74 mb 159.78 ms
  200    GET /google ........................................................................ 19.70 mb 167.36 ms
  200    GET /google ........................................................................ 19.65 mb 174.66 ms
  200    GET /google ........................................................................ 19.46 mb 182.03 ms
  200    GET /google ........................................................................ 19.77 mb 218.69 ms
  200    GET /google ........................................................................ 19.74 mb 227.10 ms
  200    GET /google ........................................................................ 19.70 mb 234.80 ms
  200    GET /google ........................................................................ 19.65 mb 242.13 ms
  200    GET /google ........................................................................ 19.46 mb 249.64 ms
  200    GET /google ........................................................................ 19.77 mb 147.72 ms
  200    GET /google ........................................................................ 19.77 mb 161.08 ms
  200    GET /google ........................................................................ 19.74 mb 167.77 ms
  200    GET /google ........................................................................ 19.70 mb 174.74 ms
  200    GET /google ........................................................................ 19.65 mb 182.64 ms
  200    GET /google ........................................................................ 19.61 mb 191.55 ms

There are two main exception:

I believe these two errors are not the only ones; there should be similar objects that encountered errors during the binding process.

Although some requests encountered these errors, the response still returned 200.

Steps To Reproduce

Router:

Route::get('/google', function () {
    $curl = curl_init();

    curl_setopt_array($curl, [
        CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
        CURLOPT_RETURNTRANSFER => 1,
        CURLOPT_FOLLOWLOCATION => 0,
        CURLOPT_URL => "https://google.com",
    ]);

    $response = curl_exec($curl);

    if ($errorNo = curl_errno($curl)) {
        $errorMsg = curl_error($curl);
        throw new Exception("cURL error($errorNo): $errorMsg");
    }

    curl_close($curl);

    return 'ok';
});

Configuration (all remains default values except WorkerStarting listeners and swoole.options):

return [
    'server' => env('OCTANE_SERVER', 'swoole'),
    'https' => env('OCTANE_HTTPS', false),
    'listeners' => [
        WorkerStarting::class => [
            EnsureUploadedFilesAreValid::class,
            EnsureUploadedFilesCanBeMoved::class,
            // ================= Changed =============
            \App\EnableCoroutine::class,
        ],
        RequestReceived::class => [
            ...Octane::prepareApplicationForNextOperation(),
            ...Octane::prepareApplicationForNextRequest(),
        ],
        RequestHandled::class => [
        ],
        RequestTerminated::class => [
        ],
        TaskReceived::class => [
            ...Octane::prepareApplicationForNextOperation(),
        ],
        TaskTerminated::class => [
        ],
        TickReceived::class => [
            ...Octane::prepareApplicationForNextOperation(),
        ],
        TickTerminated::class => [
        ],
        OperationTerminated::class => [
            FlushOnce::class,
            FlushTemporaryContainerInstances::class,
        ],
        WorkerErrorOccurred::class => [
            ReportException::class,
            StopWorkerIfNecessary::class,
        ],
        WorkerStopping::class => [
            CloseMonologHandlers::class,
        ],
    ],
    'warm' => [
        ...Octane::defaultServicesToWarm(),
    ],
    'flush' => [
    ],
    'tables' => [
        'example:1000' => [
            'name' => 'string:1000',
            'votes' => 'int',
        ],
    ],
    'cache' => [
        'rows' => 1000,
        'bytes' => 10000,
    ],
    'watch' => [
        'app',
        'bootstrap',
        'config/**/*.php',
        'database/**/*.php',
        'public/**/*.php',
        'resources/**/*.php',
        'routes',
        'composer.lock',
        '.env',
    ],
    'garbage' => 50,
    'max_execution_time' => 30,
    'swoole' => [
        'options' => [
            // ================= Changed =============
            'enable_coroutine' => true,
        ],
    ],
];

\App\EnableCoroutine.php:

namespace App;

class EnableCoroutine
{
    public function handle(): void
    {
        \OpenSwoole\Runtime::enableCoroutine();
    }
}

Dockerfile for listing all system libraries:

FROM ubuntu:20.04

ARG PHP_VERSION=8.2

# Install PHP and Swoole
RUN apt-get update && \
    apt-get install -yq --no-install-recommends curl apt-transport-https gnupg2 software-properties-common ca-certificates lsb-release && \
    add-apt-repository ppa:ondrej/php && \
    apt-get update && \
    apt-get install -yq --no-install-recommends -yq pkg-config autoconf dpkg-dev file g++ gcc libc-dev make re2c netbase openssl gzip zip unzip && \
    # local test
    apt-get install -yq sqlite3 php${PHP_VERSION}-sqlite3 && \
    # for swoole
    apt-get install -yq libc-ares-dev libssl-dev libcurl4-openssl-dev libpcre3-dev build-essential && \
    # PHP
    apt-get install --no-install-recommends -yq php${PHP_VERSION}-dev php-pear php${PHP_VERSION}-bcmath php${PHP_VERSION}-curl php${PHP_VERSION}-mbstring php${PHP_VERSION}-mysql \
        php${PHP_VERSION}-opcache php${PHP_VERSION}-readline php${PHP_VERSION}-soap php${PHP_VERSION}-xml \
        php${PHP_VERSION}-xmlrpc php${PHP_VERSION}-zip php${PHP_VERSION}-sockets php${PHP_VERSION}-cli && \
    pecl channel-update pecl.php.net && \
    pecl install --configureoptions 'enable-sockets="no" enable-openssl="yes" enable-http2="no" enable-mysqlnd="no" enable-hook-curl="yes" enable-cares="yes" with-postgres="no"' openswoole-22.0.0 && \
    echo "extension = openswoole.so" >> /etc/php/${PHP_VERSION}/cli/conf.d/openswoole.ini

Start the server:

php artisan octane:start --workers=2

Concurrent requests by fortio:

docker run --network host --rm fortio/fortio load -qps 10 -c 10 -t '2s' -connection-reuse 0:0 http://127.0.0.1:8000/google
crynobone commented 1 month ago

Swoole Coroutine is not supported by Laravel Octane as mentioned in #765

evan361425 commented 1 month ago

OK, but why not open the issue #765 ?