roadrunner-php/laravel-bridge

Performance degradation with sessions. Laravel 8. Docker

apoldev opened this issue · 8 comments

Hello.
I have a docker container with RoadrRunner and Laravel 8.16.1 by default. (clean install laravel new www)

I try it many times:
ab -n 1000 -c 100 "http://serverhost:8080/"

I notice a degradation of app performance:

3113.96 [#/sec]
....
1024.88 [#/sec]
539.06 [#/sec]
439.41 [#/sec]

If i remove in Http/Kernel.php in $middlewareGroups
these lines:

\Illuminate\Session\Middleware\StartSession::class, 
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class, 

then performance not degrade:

Requests per second:    2844.97 [#/sec] (mean)
Time per request:       35.150 [ms] (mean)
Time per request:       0.351 [ms] (mean, across all concurrent requests)

I suppose I need to reset the sessions? Only with the default config, roadrunner-laravel does not work.
How can I do it?

Dockerfile

FROM php:7.4-cli

#COPY opcache.ini /usr/local/etc/php/conf.d/opcache.ini

RUN apt-get update && apt-get install -y \
        curl \
  		vim \
  		libfreetype6-dev \
  		libjpeg62-turbo-dev \
  		libmcrypt-dev \
  		libpng-dev \
  		zlib1g-dev \
  		libxml2-dev \
  		libzip-dev \
  		libonig-dev \
  		graphviz \
  		libcurl4-openssl-dev \
  		pkg-config \
  		libpq-dev \
  		iputils-ping \
  		wget \
  		git

# Install PHP Extensions
RUN docker-php-ext-install -j$(nproc) iconv mbstring mysqli pdo_mysql zip \
    && docker-php-ext-install opcache \
    && docker-php-ext-enable opcache

RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer

# Download RoadRunner
ENV RR_VERSION 1.8.4
RUN mkdir /tmp/rr \
  && cd /tmp/rr \
  && echo "{\"require\":{\"spiral/roadrunner\":\"${RR_VERSION}\"}}" >> composer.json \
  && composer install --ignore-platform-reqs \
  && vendor/bin/rr get-binary -l /usr/local/bin \
  && rm -rf /tmp/rr

## Copy RoadRunner config
COPY config /etc/roadrunner

WORKDIR /var/www

CMD ["/usr/local/bin/rr", "serve", "-c", "/etc/roadrunner/.rr.yaml", "-w", "/var/www"]

docker-compose.yml

version: "3.7"
services:
  roadrunner:
    build: ./images/roadrunner
    container_name: "roadrunner"
    ports:
      - 8080:8080
    volumes:
      - ./www:/var/www:cached
      - ./configs/roadrunner:/etc/roadrunner:cached
    network_mode: "host"

.rr.yaml

env:
#APP_REFRESH: true

http:
  address: 0.0.0.0:8080
  workers:
    command: 'php /var/www/vendor/bin/rr-worker' # for windows: `php vendor/spiral/roadrunner-laravel/bin/rr-worker`
static:
  dir: 'public'

reload:
 interval: 1s
 patterns: [".php"]
 services:
   http:
     dirs: [""]
     recursive: true

Hi @apoldev! What about memory usage during your ab tests?

Hello!

If i remove these three lines from $middlewareGroups:

Start. MEMORY USAGE
197MiB

After `ab` tests:
ab -c 100 -t 120  "http://remote_server:8080"

Time taken for tests:   17.783 seconds
Requests per second:    2811.70 [#/sec] (mean)
MEMORY USAGE  287.7MiB

WITH SESSION IN $middlewareGroups.

Start. MEMORY USAGE
197MiB

After ab tests:
ab -c 100 -t 120  "http://remote_server:8080"

Complete requests:      13926
Time taken for tests:   120.201 seconds
Requests per second:    115.86 [#/sec] (mean)
MEMORY USAGE:  398.9MiB

Performance degradation confirmed.

My dockerfile:

FROM php:7.4.13-alpine

# Install PHP Extensions
RUN set -x \
    && docker-php-ext-install opcache \
    && docker-php-ext-enable opcache

# Download RoadRunner
COPY --from=spiralscout/roadrunner:1.9.0 /usr/bin/rr /usr/bin/rr

# Download Composer
COPY --from=composer:2.0.7 /usr/bin/composer /usr/bin/composer

# Install "clean" laravel
RUN composer create-project --prefer-dist --no-progress "laravel/laravel" /src "^8.4"

# Change working diretory to the laravel installation
WORKDIR /src

# Install RR bridge
RUN composer require --no-progress spiral/roadrunner-laravel "^3.4"

# Create simple RR config file
RUN echo -e \
"rpc:\n"\
"  enable: false\n"\
"http:\n"\
"  address: 0.0.0.0:8080\n"\
"  workers:\n"\
"    command: 'php ./vendor/bin/rr-worker'\n"\
"    pool:\n"\
"      numWorkers: 4\n"\
"      maxJobs: 0\n"\
"      allocateTimeout: 60\n"\
"      destroyTimeout: 60\n"\
"static:\n"\
"  dir: 'public'" > ./.rr.yaml

CMD rr -c /src/.rr.yaml serve -d

Build & run:

$ docker build --tag laravel-rr:local . && docker run --rm -p "8080:8080/tcp" laravel-rr:local

In separate terminal:

$ for i in {0..20}; do ab -n 1000 -c 100 "http://127.0.0.1:8080/" 2>&1 | grep 'per second:'; done
Requests per second:    1102.43 [#/sec] (mean)
Requests per second:    918.61 [#/sec] (mean)
Requests per second:    746.16 [#/sec] (mean)
Requests per second:    637.29 [#/sec] (mean)
Requests per second:    614.52 [#/sec] (mean)
Requests per second:    512.78 [#/sec] (mean)
Requests per second:    540.68 [#/sec] (mean)
Requests per second:    445.41 [#/sec] (mean)
Requests per second:    479.77 [#/sec] (mean)
Requests per second:    388.88 [#/sec] (mean)
Requests per second:    445.12 [#/sec] (mean)
Requests per second:    381.85 [#/sec] (mean)
Requests per second:    328.83 [#/sec] (mean)
Requests per second:    324.74 [#/sec] (mean)
Requests per second:    409.21 [#/sec] (mean)
Requests per second:    351.63 [#/sec] (mean)
Requests per second:    304.58 [#/sec] (mean)
Requests per second:    347.62 [#/sec] (mean)
Requests per second:    262.54 [#/sec] (mean)
Requests per second:    281.68 [#/sec] (mean)
Requests per second:    221.30 [#/sec] (mean)

screenshot

I need in some time for additional local tests. Thank for your report!

With next config RR works better:

http:
  workers:
    pool:
      numWorkers: 16
      maxJobs: 64
      allocateTimeout: 60
      destroyTimeout: 60

screenshot

$ for i in {0..10}; do ab -n 1000 -c 100 "http://127.0.0.1:8080/" 2>&1 | grep 'per second:'; done
Requests per second:    1053.86 [#/sec] (mean)
Requests per second:    578.81 [#/sec] (mean)
Requests per second:    613.15 [#/sec] (mean)
Requests per second:    517.34 [#/sec] (mean)
Requests per second:    552.03 [#/sec] (mean)
Requests per second:    492.22 [#/sec] (mean)
Requests per second:    491.06 [#/sec] (mean)
Requests per second:    475.94 [#/sec] (mean)
Requests per second:    455.25 [#/sec] (mean)
Requests per second:    359.54 [#/sec] (mean)
Requests per second:    401.63 [#/sec] (mean)

@apoldev

Hello, I will take part in this discussion.

Are you using the RoadRunner config (If you publish a vendor config, it is placed in ./config/roadrunner.php)?

Please, show it.

Hello.

Default laravel session driver is file (.env.example, config).

Each time session starts, laravel calls garbage-collector (StartSession middleware). This operation is I/O bound for FileSessionHandler and degrade on large session list.

For tests i use "Apache jMeter" and "Apache ab", dockerfile from @tarampampam reply with configs:

http:
  workers:
    pool:
      numWorkers: 4 # I have only 4 threads on testing machine
      maxJobs: 64
      allocateTimeout: 60
      destroyTimeout: 60

jMeter tests was run in 50 parallel threads. Each thread does 5000 calls.

All graphics contains 90th percentile.

Default run:

$ docker run --rm -p "8080:8080/tcp" laravel-rr:local

изображение

$ for i in {0..20}; do ab -n 1000 -c 100 "http://127.0.0.1:8080/" 2>&1 | grep 'per second:'; done 
Requests per second:    714.26 [#/sec] (mean)
Requests per second:    676.30 [#/sec] (mean)
Requests per second:    650.93 [#/sec] (mean)
Requests per second:    619.68 [#/sec] (mean)
Requests per second:    581.30 [#/sec] (mean)
Requests per second:    587.99 [#/sec] (mean)
Requests per second:    493.58 [#/sec] (mean)
Requests per second:    577.38 [#/sec] (mean)
Requests per second:    480.70 [#/sec] (mean)
Requests per second:    486.99 [#/sec] (mean)
Requests per second:    506.49 [#/sec] (mean)
Requests per second:    422.18 [#/sec] (mean)
Requests per second:    419.97 [#/sec] (mean)
Requests per second:    410.45 [#/sec] (mean)
Requests per second:    400.14 [#/sec] (mean)
Requests per second:    458.22 [#/sec] (mean)
Requests per second:    412.70 [#/sec] (mean)
Requests per second:    419.72 [#/sec] (mean)
Requests per second:    518.80 [#/sec] (mean)
Requests per second:    388.01 [#/sec] (mean)
Requests per second:    347.34 [#/sec] (mean)

With array session driver:

$ docker run --rm -p "8080:8080/tcp" -e "SESSION_DRIVER=array"  laravel-rr:local

изображение

$ for i in {0..20}; do ab -n 1000 -c 100 "http://127.0.0.1:8080/" 2>&1 | grep 'per second:'; done 
Requests per second:    780.17 [#/sec] (mean)
Requests per second:    725.75 [#/sec] (mean)
Requests per second:    742.22 [#/sec] (mean)
Requests per second:    699.86 [#/sec] (mean)
Requests per second:    730.22 [#/sec] (mean)
Requests per second:    734.53 [#/sec] (mean)
Requests per second:    711.45 [#/sec] (mean)
Requests per second:    705.01 [#/sec] (mean)
Requests per second:    715.21 [#/sec] (mean)
Requests per second:    718.66 [#/sec] (mean)
Requests per second:    703.98 [#/sec] (mean)
Requests per second:    747.79 [#/sec] (mean)
Requests per second:    684.66 [#/sec] (mean)
Requests per second:    718.03 [#/sec] (mean)
Requests per second:    724.57 [#/sec] (mean)
Requests per second:    723.42 [#/sec] (mean)
Requests per second:    720.13 [#/sec] (mean)
Requests per second:    695.69 [#/sec] (mean)
Requests per second:    711.15 [#/sec] (mean)
Requests per second:    724.82 [#/sec] (mean)
Requests per second:    687.52 [#/sec] (mean)

Remove gc call from startSession middleware, using session.lottery config:

изображение

$ for i in {0..20}; do ab -n 1000 -c 100 "http://127.0.0.1:8080/" 2>&1 | grep 'per second:'; done                    
Requests per second:    699.82 [#/sec] (mean)
Requests per second:    661.41 [#/sec] (mean)
Requests per second:    603.76 [#/sec] (mean)
Requests per second:    597.45 [#/sec] (mean)
Requests per second:    702.27 [#/sec] (mean)
Requests per second:    658.57 [#/sec] (mean)
Requests per second:    659.56 [#/sec] (mean)
Requests per second:    726.27 [#/sec] (mean)
Requests per second:    738.47 [#/sec] (mean)
Requests per second:    724.43 [#/sec] (mean)
Requests per second:    669.35 [#/sec] (mean)
Requests per second:    705.24 [#/sec] (mean)
Requests per second:    704.70 [#/sec] (mean)
Requests per second:    695.04 [#/sec] (mean)
Requests per second:    710.15 [#/sec] (mean)
Requests per second:    683.25 [#/sec] (mean)
Requests per second:    695.19 [#/sec] (mean)
Requests per second:    704.88 [#/sec] (mean)
Requests per second:    679.48 [#/sec] (mean)
Requests per second:    680.28 [#/sec] (mean)
Requests per second:    734.98 [#/sec] (mean)

@jetexe - great thanks for your research!

@apoldev - in a two words - laravel uses file driver for sessions by default, and file implementation is very expensive for I/O. Also you should remember about RR http.workers.pool.maxJobs option - do not use 0. Can you confirm problem resolving?

@tarampampam
Yes. Thanks for your research!
There is no more problem. Thanks