Skip to content

php7.0 doesn't report accurate socket state in PHP_RINIT #2

@vdloo

Description

@vdloo

On the 7.0 node:

# apt-get install php7.0-dev php-hypernode
root@2a1186-php7-magweb-vgr /vagrant/php-hypernode/php-7.0/hypernode-20160309.1 
# phpize

Configuring for:
PHP Api Version:         20151012
Zend Module Api No:      20151012
Zend Extension Api No:   320151012

On the 5.5 node:

# apt-get install php5-dev php5-hypernode

root@cd03d0-php5-magweb-vgr /vagrant/php-hypernode/php-5/hypernode-20160309.1 # phpize
Configuring for:
PHP Api Version:         20121113
Zend Module Api No:      20121212
Zend Extension Api No:   220121212

On both nodes:

In hypernode.c, above

        if (tcp_info.tcpi_state == TCP_CLOSE_WAIT) {

add

        char msg[100];
        sprintf(msg, "tcp_info.tcp_state is %d, TCP_CLOSE_WAIT_STATE is %d\n", tcp_info.tcpi_state, TCP_CLOSE_WAIT);
        php_syslog(LOG_NOTICE, msg);

For reference, the TCP state mapping looks like:

enum {
  TCPF_ESTABLISHED = (1 << 1),
  TCPF_SYN_SENT  = (1 << 2),
  TCPF_SYN_RECV  = (1 << 3),
  TCPF_FIN_WAIT1 = (1 << 4),
  TCPF_FIN_WAIT2 = (1 << 5),
  TCPF_TIME_WAIT = (1 << 6),
  TCPF_CLOSE     = (1 << 7),
  TCPF_CLOSE_WAIT = (1 << 8),
  TCPF_LAST_ACK  = (1 << 9),
  TCPF_LISTEN    = (1 << 10),
  TCPF_CLOSING   = (1 << 11)
};

https://github.com/mirror/reactos/blob/master/reactos/drivers/network/tcpip/include/tcpdef.h#L77

Install build tools

# apt-get install build-essential autoconf

Run autoconf

# autoconf

Build the things

# ./configure && make && make install

On the 7.0 node:

in /etc/php/7.0/fpm/pool.d/www.conf
 pm.max_children = 3

# cp modules/hypernode.so /usr/lib/php/20151012/hypernode.so 
# service php7.0-fpm restart

On the 5.5 node:

in /etc/php5/fpm/pool.d/www.conf
 pm.max_children = 3
# cp modules/hypernode.so /usr/lib/php5/20121212/hypernode.so
# service php5-fpm restart

On both nodes:

in /data/web/public/index.php:

<?php
    sleep(2000000);

disable varnish

# echo -e 'vcl 4.0;\nbackend default {\n  .host = "127.0.0.1";\n  .port = "8080";\n}\nsub vcl_recv {\n  return(pass);\n}' > /data/web/disabled_caching.vcl && varnishadm vcl.load nocache /data/web/disabled_caching.vcl && varnishadm vcl.use nocache

tail syslog

# tail -f /var/log/syslog 

on the host spam some requests:

for i in 1 2 3 4 5 6 7 8 9; do curl -k --max-time 5 https://php5.hypernode.local & done
for i in 1 2 3 4 5 6 7 8 9; do curl -k --max-time 5 https://php7.hypernode.local & done

php7:

20614 BUSY 3.2169 10.0.3.1        GET  php7.hypernode.local/   (curl/7.47.0)
20615 BUSY 3.2192 10.0.3.1        GET  php7.hypernode.local/   (curl/7.47.0)
21687 BUSY 2.3646 10.0.3.1        GET  php7.hypernode.local/   (curl/7.47.0)

important

## tcp_state 10 is TCP_LISTEN
Apr  4 12:24:36 xxxxx-dummytag-vagrant ool www: tcp_info.tcp_state is 10, TCP_CLOSE_WAIT_STATE is 8

php5:

21746 BUSY 42.2295 10.0.3.1        GET  php5.hypernode.local/   (curl/7.47.0)
21747 BUSY 42.2294 10.0.3.1        GET  php5.hypernode.local/   (curl/7.47.0)
21855 BUSY 41.7218 10.0.3.1        GET  php5.hypernode.local/   (curl/7.47.0)

important

## tcp_state 1 is TCP_ESTABLISHED
Apr  4 12:24:04 xxxxx-dummytag-vagrant ool www: tcp_info.tcp_state is 1, TCP_CLOSE_WAIT_STATE is 8

Stop nginx so the client to FPM is gone:

# service nginx stop
20614 GONE 99.0227 10.0.3.1        GET  php7.hypernode.local/   (curl/7.47.0)
20615 GONE 99.0255 10.0.3.1        GET  php7.hypernode.local/   (curl/7.47.0)
21687 GONE 98.1713 10.0.3.1        GET  php7.hypernode.local/   (curl/7.47.0)
21746 GONE 127.7270 10.0.3.1        GET  php5.hypernode.local/   (curl/7.47.0)
21747 GONE 127.7275 10.0.3.1        GET  php5.hypernode.local/   (curl/7.47.0)
21855 GONE 127.2204 10.0.3.1        GET  php5.hypernode.local/   (curl/7.47.0)

Now, if we kill one of the FPM children we expect the tcp_info.tcp_state of the newly spawned child to be WAIT_CLOSE because the requests from nginx left queued in the kernel will already be disconnected.

important

On the PHP5 node:

# kill 21746
Apr  4 12:27:34 xxxxx-dummytag-vagrant ool www: tcp_info.tcp_state is 8, TCP_CLOSE_WAIT_STATE is 8
Apr  4 12:27:34 xxxxx-dummytag-vagrant ool www: Terminated request GET php5.hypernode.local/index.php because client at 10.0.3.1 is already gone
Apr  4 12:27:34 xxxxx-dummytag-vagrant ool www: tcp_info.tcp_state is 8, TCP_CLOSE_WAIT_STATE is 8
Apr  4 12:27:34 xxxxx-dummytag-vagrant ool www: Terminated request GET php5.hypernode.local/index.php because client at 10.0.3.1 is already gone
Apr  4 12:27:34 xxxxx-dummytag-vagrant ool www: tcp_info.tcp_state is 8, TCP_CLOSE_WAIT_STATE is 8
Apr  4 12:27:34 xxxxx-dummytag-vagrant ool www: Terminated request GET php5.hypernode.local/index.php because client at 10.0.3.1 is already gone
Apr  4 12:27:34 xxxxx-dummytag-vagrant ool www: tcp_info.tcp_state is 8, TCP_CLOSE_WAIT_STATE is 8
Apr  4 12:27:34 xxxxx-dummytag-vagrant ool www: Terminated request GET php5.hypernode.local/index.php because client at 10.0.3.1 is already gone
etc.. until the queue is empty

21747 GONE 226.7354 10.0.3.1        GET  php5.hypernode.local/   (curl/7.47.0)
21855 GONE 226.2278 10.0.3.1        GET  php5.hypernode.local/   (curl/7.47.0)
27027 IDLE                                ()

important

On the PHP7 node:

# kill 21687
## tcp_state 10 is TCP_LISTEN
Apr  4 12:28:34 xxxxx-dummytag-vagrant ool www: tcp_info.tcp_state is 10, TCP_CLOSE_WAIT_STATE is 8

20614 GONE 251.6493 10.0.3.1        GET  php7.hypernode.local/   (curl/7.47.0)
20615 GONE 251.6518 10.0.3.1        GET  php7.hypernode.local/   (curl/7.47.0)
27620 GONE 0.4032 10.0.3.1        GET  php7.hypernode.local/   (curl/7.47.0) <- this one should have been dropped immediately, instead the state is TCP_LISTEN in the RINIT context.

Now if immediately if we run netsat on the proc we do get a CLOSE_WAIT:

# netstat -nap | grep 27620
tcp        9      0 127.0.0.1:9000          127.0.0.1:57038         CLOSE_WAIT  28844/php-fpm: pool

So it looks like the inner workings of fpm has changed and it deals with the request->fd differently

I tried compiling fpm with the socket code from 5.5 but that made no difference

cd ~/code/projects/php; git checkout hypernode-7.0; git checkout -b get-listening-socket-again
cd sapi/fpm/
wget https://raw.githubusercontent.com/ByteInternet/php/master-5.5/sapi/fpm/fpm/fpm_sockets.c
wget https://raw.githubusercontent.com/ByteInternet/php/master-5.5/sapi/fpm/fpm/fpm_sockets.h
dpkg-source --commit
gbp buildpackage --git-pbuilder --git-dist=precise --git-arch=amd64 --git-debian-branch=get-listening-socket-again -us -sa

still:

Apr  4 13:17:09 2a1186-php7-magweb-vgr ool www: tcp_info.tcp_state is 10, TCP_CLOSE_WAIT_STATE is 8

I also tried using a UNIX socket instead of a network socket, but then even in php55 the state was always TCP_LISTEN

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions