Skip to content

TCP resets on high concurrency tests #354

@acasademont

Description

@acasademont

While trying to debug php-pm/php-pm#456 I noticed that during high concurrency tests (-c 100) some of the TCP streams where reset and failed. I ended up testing the simplest reactphp http server to reproduce it and compare it with the simplest nodejs server. For the tests, I used the libuv event loop, but it also happens with the StreamSelect loop.

reactphp

<?php

include __DIR__.'/vendor/autoload.php';

$loop = React\EventLoop\Factory::create();

$server = new React\Http\Server(function (Psr\Http\Message\ServerRequestInterface $request) {
    return new React\Http\Response(
        200,
        array('Content-Type' => 'text/plain'),
        "Hello World!\n"
    );
});

$socket = new React\Socket\Server(8080, $loop);
$server->listen($socket);

echo "Server running at http://127.0.0.1:8080\n";

$loop->run();

nodejs

const http = require('http')

const requestHandler = (request, response) => {
  response.end('Hello World!')
}

const server = http.createServer(requestHandler)

server.listen(8080, (err) => {
  if (err) {
    return console.log('something bad happened', err)
  }

  console.log('Server running at http://127.0.0.1:8080')
})

Benchmarked using https://github.com/rakyll/hey, but the resets also occur using ab

hey -n 100 -c 100 http://127.0.0.1:8080/

reactphp

Summary:
  Total:	0.2094 secs
  Slowest:	0.2055 secs
  Fastest:	0.0207 secs
  Average:	0.0767 secs
  Requests/sec:	477.4997

  Total data:	988 bytes
  Size/request:	13 bytes

Response time histogram:
  0.021 [1]	|■
  0.039 [32]	|■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
  0.058 [0]	|
  0.076 [0]	|
  0.095 [0]	|
  0.113 [37]	|■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
  0.132 [2]	|■■
  0.150 [0]	|
  0.169 [0]	|
  0.187 [0]	|
  0.205 [4]	|■■■■


Latency distribution:
  10% in 0.0227 secs
  25% in 0.0239 secs
  50% in 0.1051 secs
  75% in 0.1099 secs
  90% in 0.1122 secs
  95% in 0.2047 secs
  0% in 0.0000 secs

Details (average, fastest, slowest):
  DNS+dialup:	0.0637 secs, 0.0207 secs, 0.2055 secs
  DNS-lookup:	0.0000 secs, 0.0000 secs, 0.0000 secs
  req write:	0.0002 secs, 0.0000 secs, 0.0006 secs
  resp wait:	0.0127 secs, 0.0004 secs, 0.0252 secs
  resp read:	0.0000 secs, 0.0000 secs, 0.0002 secs

Status code distribution:
  [200]	76 responses

Error distribution:
  [17]	Get http://127.0.0.1:8080/: dial tcp 127.0.0.1:8080: connect: connection reset by peer
  [1]	Get http://127.0.0.1:8080/: read tcp 127.0.0.1:57270->127.0.0.1:8080: read: connection reset by peer
  [1]	Get http://127.0.0.1:8080/: read tcp 127.0.0.1:57303->127.0.0.1:8080: read: connection reset by peer
  [1]	Get http://127.0.0.1:8080/: read tcp 127.0.0.1:57307->127.0.0.1:8080: read: connection reset by peer
  [1]	Get http://127.0.0.1:8080/: read tcp 127.0.0.1:57308->127.0.0.1:8080: read: connection reset by peer
  [1]	Get http://127.0.0.1:8080/: read tcp 127.0.0.1:57309->127.0.0.1:8080: read: connection reset by peer
  [1]	Get http://127.0.0.1:8080/: read tcp 127.0.0.1:57323->127.0.0.1:8080: read: connection reset by peer
  [1]	Get http://127.0.0.1:8080/: read tcp 127.0.0.1:57325->127.0.0.1:8080: read: connection reset by peer

nodejs

Summary:
  Total:	0.0574 secs
  Slowest:	0.0393 secs
  Fastest:	0.0176 secs
  Average:	0.0303 secs
  Requests/sec:	1740.8161

  Total data:	1200 bytes
  Size/request:	12 bytes

Response time histogram:
  0.018 [1]	|■
  0.020 [3]	|■■■■
  0.022 [0]	|
  0.024 [6]	|■■■■■■■■
  0.026 [19]	|■■■■■■■■■■■■■■■■■■■■■■■■■
  0.028 [29]	|■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
  0.031 [1]	|■
  0.033 [2]	|■■■
  0.035 [2]	|■■■
  0.037 [6]	|■■■■■■■■
  0.039 [31]	|■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■


Latency distribution:
  10% in 0.0244 secs
  25% in 0.0257 secs
  50% in 0.0276 secs
  75% in 0.0376 secs
  90% in 0.0381 secs
  95% in 0.0384 secs
  99% in 0.0393 secs

Details (average, fastest, slowest):
  DNS+dialup:	0.0032 secs, 0.0176 secs, 0.0393 secs
  DNS-lookup:	0.0000 secs, 0.0000 secs, 0.0000 secs
  req write:	0.0001 secs, 0.0000 secs, 0.0033 secs
  resp wait:	0.0262 secs, 0.0076 secs, 0.0389 secs
  resp read:	0.0001 secs, 0.0000 secs, 0.0013 secs

Status code distribution:
  [200]	100 responses

Not sure what could be the culprit here, is this a php streams issue? Or is reactphp somehow inadvertently causing those TCP resets?

Here are the TCP dumps

tcpdump.zip

PS: Besides the TCP resets, the reactphp histogram is also pretty weird, with some requests being very fast, but others being considerably slower. nodejs performance is much more consistent

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions