Posterous theme by Cory Watilo

Filed under: megacomet

More MegaComet testing: Ruling out keepalives

After the last test, which wasn’t much improvement, my suspicion was that the tcp keepalives for all those connections were swamping the interface. So i installed iftop and reran the tests to check:

echo Install iftop
sudo yum -y install libpcap*
sudo yum -y install ncurses*
cd ~
wget http://ex-parrot.com/~pdw/iftop/download/iftop-0.17.tar.gz
tar -xzvf iftop-0.17.tar.gz
cd iftop-0.17
./configure && make && sudo make install
sudo ./iftop

I didn’t get any further, but it did confirm that once the sockets were open that there is no load on the network. Seems like the keepalives aren’t major (or are nonexistent).

Another thing i found to do was ss -s to show how many sockets are open at one time. Very handy for screenshots :)

MegaComet test #4 - This time with more kernel

Summary

This time, I’ll be running the MegaComet tests as per test 3, with kernel logging enabled to see where I’m pushing the TCP stack too far, so that hopefully i can fix it with some configuration.

Setup

As per test 3: start 5 EC2 servers ‘ami-221fec4b’. Out of curiosity, I priced it this time. Since my tests will take less than an hour, it’ll cost $0.34 (ec2 large instance hourly cost) * 5 instances = $1.70 to run this test. I can handle that. It’s also probably worth mentioning that in ec2, i configure the firewall to allow all the MegaComet ports open. In the real world, you’d have the ‘application’ port restricted.

Setup script

echo Configuring TCP stack
sudo bash
echo "# Settings from http://www.metabrew.com/article/a-million-user-comet-application-with-mochiweb-part-3" >> /etc/sysctl.conf
echo "# Config needed to have enough tcp stack memory:" >> /etc/sysctl.conf
echo "net.core.rmem_max = 33554432" >> /etc/sysctl.conf
echo "net.core.wmem_max = 33554432" >> /etc/sysctl.conf
echo "net.ipv4.tcp_rmem = 4096 16384 33554432" >> /etc/sysctl.conf
echo "net.ipv4.tcp_wmem = 4096 16384 33554432" >> /etc/sysctl.conf
echo "net.ipv4.tcp_mem = 786432 1048576 26777216" >> /etc/sysctl.conf
echo "net.ipv4.tcp_max_tw_buckets = 360000" >> /etc/sysctl.conf
echo "net.core.netdev_max_backlog = 2500" >> /etc/sysctl.conf
echo "vm.min_free_kbytes = 65536" >> /etc/sysctl.conf
echo "vm.swappiness = 0" >> /etc/sysctl.conf
echo "# This is for the outgoing connections max:" >> /etc/sysctl.conf
echo "net.ipv4.ip_local_port_range = 1024 65535" >> /etc/sysctl.conf
echo "# I added this to set the system wide file max:" >> /etc/sysctl.conf
echo "fs.file-max = 1100000" >> /etc/sysctl.conf
echo "# Reduce the time sockets stay in time_wait: http://forums.theplanet.com/lofiversion/index.php/t62399.html" >> /etc/sysctl.conf
echo "net.ipv4.tcp_fin_timeout = 12" >> /etc/sysctl.conf
exit
sudo sysctl -p

echo Enlarging user-limits on files
sudo bash
echo "* soft nofile 1048576" >> /etc/security/limits.conf 
echo "* hard nofile 1048576" >> /etc/security/limits.conf
exit

echo Enabling kernel logging
sudo bash
echo "kern.*          /var/log/kern.log" >> /etc/rsyslog.conf
sudo service rsyslog restart
exit

echo Installing build essentials
sudo yum -y install gcc* git* make

echo Installing libev
cd ~
wget http://dist.schmorp.de/libev/libev-4.04.tar.gz
tar -zxvf libev-4.04.tar.gz
cd libev-4.04
./configure && make && sudo make install

echo Adding libev to the library list 
sudo sh -c "echo /usr/local/lib > /etc/ld.so.conf.d/usr-local-lib.conf"
sudo ldconfig

echo Installing MC
cd ~
git clone git://github.com/chrishulbert/MegaComet.git
cd MegaComet
make
cd testing
make

echo Now you have to logout and in again, because you only have a low per-user limit as you can see:
ulimit -n

Viewing kernel logs

Once MC started on the first instance, i run this to view the kernel logs:

sudo tail -f /var/log/kern.log

Starting tests:

On the test instances (2-5):

cd ~/MegaComet/testing
./megatest A 10.40.29.57

Results:

I can only get up to 494k connections. On the server, here is the top output when at maximum. As you can see, there’s plenty of ram free:

top - 11:56:56 up 36 min,  4 users,  load average: 0.00, 0.14, 0.20
Tasks:  84 total,   1 running,  83 sleeping,   0 stopped,   0 zombie
Cpu(s):  0.0%us,  0.1%sy,  0.0%ni, 99.9%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:   7652552k total,  2566036k used,  5086516k free,    20492k buffers
Swap:        0k total,        0k used,        0k free,   798960k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND                                                                   

 3373 ec2-user  20   0  8664  336  264 S  0.0  0.0   0:00.00 megamanager                                                                
 3374 ec2-user  20   0 27592  18m  460 S  0.0  0.2   0:15.90 megacomet                                                                  
 3375 ec2-user  20   0 26640  17m  460 S  0.0  0.2   0:15.40 megacomet                                                                  
 3376 ec2-user  20   0 26660  17m  460 S  0.0  0.2   0:15.29 megacomet                                                                  
 3377 ec2-user  20   0 26680  18m  460 S  0.0  0.2   0:15.27 megacomet                                                                  
 3378 ec2-user  20   0 27420  18m  460 S  0.0  0.2   0:16.59 megacomet                                                                  
 3379 ec2-user  20   0 27304  18m  460 S  0.0  0.2   0:15.81 megacomet                                                                  
 3380 ec2-user  20   0 26828  17m  460 S  0.0  0.2   0:15.52 megacomet                                                                  
 3381 ec2-user  20   0 27188  18m  460 S  0.0  0.2   0:15.60 megacomet

And the slabtop output:

Active / Total Objects (% used)    : 3713138 / 3713562 (100.0%)
 Active / Total Slabs (% used)      : 154792 / 154792 (100.0%)
 Active / Total Caches (% used)     : 58 / 74 (78.4%)
 Active / Total Size (% used)       : 1479546.63K / 1479691.42K (100.0%)
 Minimum / Average / Maximum Object : 0.01K / 0.40K / 8.00K

  OBJS ACTIVE  USE OBJ SIZE  SLABS OBJ/SLAB CACHE SIZE NAME                   
524160 524158  99%    0.19K  24960   21     99840K dentry
496797 496642  99%    0.19K  23657   21     94628K kmalloc-192
496000 496000 100%    0.06K   7750   64     31000K kmalloc-64
495328 495328 100%    0.12K  15479   32     61916K kmalloc-128
495040 495040 100%    0.07K   8840   56     35360K blkdev_ioc
495000 495000 100%    0.62K  41250   12    330000K sock_inode_cache
494950 494950 100%    1.62K  26050   19    833600K TCP
163410 163385  99%    0.10K   4190   39     16760K buffer_head

Nothing appeared in the kernel log. So, for now, i’m not sure what the holdup is: No kernel errors, didn’t hit a memory ceiling, i’m puzzled.

Half way there: Getting MegaComet to 523,000 concurrent HTTP connections

TL;DR

  • With a bit of kernel tuning, i was able to get up to 523k connections opened simultaneously from 4 client boxes to 1 MegaComet server.
  • Memory and CPU usage was minimal (128M across the servers processes, maybe 24% CPU at 4000 connections/sec).
  • I’ll try to improve the kernel tuning to get it to 1M by checking the /var/log/kern.log next time.
  • Libev basically runs on the smell of an oily rag.

Setup

I started 5 EC2 Large 64-bit servers, using the amazon linux image ‘ami-221fec4b’ (aka: amzn-ami-2011.02.1.x86_64). One of these was the server, and the other 4 are the client servers, each trying to open 250k connections. These are vanilla EC2 Large instances, with the following kernel tuning (credits to the metabrew article):

Tuning

The following increases the user limit for number of open file descriptors (TCP connections are file descriptors):

echo "* soft nofile 1048576" >> /etc/security/limits.conf 
echo "* hard nofile 1048576" >> /etc/security/limits.conf

After the above is done, you have to log out and back in again.

To tune the kernel to allow 1M connections, the following was appended to the /etc/sysctl.conf:

# Settings from http://www.metabrew.com/article/a-million-user-comet-application-with-mochiweb-part-3
# Config needed to have enough tcp stack memory:
net.core.rmem_max = 33554432
net.core.wmem_max = 33554432
net.ipv4.tcp_rmem = 4096 16384 33554432
net.ipv4.tcp_wmem = 4096 16384 33554432
net.ipv4.tcp_mem = 786432 1048576 26777216
net.ipv4.tcp_max_tw_buckets = 360000
net.core.netdev_max_backlog = 2500
vm.min_free_kbytes = 65536
vm.swappiness = 0
# This is for the outgoing connections max:
net.ipv4.ip_local_port_range = 1024 65535
# I added this to set the system wide file max:
fs.file-max = 1100000  
# Reduce the time sockets stay in time_wait: http://forums.theplanet.com/lofiversion/index.php/t62399.html
net.ipv4.tcp_fin_timeout = 12

To apply it, you need to do: sudo sysctl -p I believe this tuning still needs work. Next time i run the tests i’ll check the kernel log to see if anything in the TCP stack has maxed out.

Steps

To reproduce my tests, you can follow the steps used to configure the vanilla instances:

# Install compiler / tools
sudo yum -y install gcc* git* make

# Install libev
wget http://dist.schmorp.de/libev/libev-4.04.tar.gz
tar -zxvf libev-4.04.tar.gz
cd libev-4.04
./configure && make && sudo make install
sudo sh -c "echo /usr/local/lib > /etc/ld.so.conf.d/usr-local-lib.conf"
sudo ldconfig

# Install MC
cd ~
git clone git://github.com/chrishulbert/MegaComet.git
cd MegaComet

# Now do the kernel tuning as mentioned above

# To run the server:
cd MegaComet
make
./start

# To run the clients:
cd MegaComet/testing
make
./megatest X Y # (where X is a,b,c,d depending on which testing server this is)
# Also Y is the IP address of the comet server

Results

The clients got up to 142k, 144k, 105k, and 132k connections respectively before trying to open new connections timed out. This is a total of 523k connections, just over half a million! The RAM and CPU usage on the server was minimal throughout the test. Here’s a screenshot of top while the tests were running at approx 4000 new connections/second, to give an idea of CPU and memory usage:

top - 11:03:28 up  1:12,  2 users,  load average: 0.25, 0.58, 0.48
Tasks:  77 total,   2 running,  75 sleeping,   0 stopped,   0 zombie
Cpu(s):  0.8%us,  2.7%sy,  0.0%ni, 95.1%id,  0.0%wa,  0.1%hi,  1.1%si,  0.2%st
Mem:   7652552k total,  1441076k used,  6211476k free,    22144k buffers
Swap:        0k total,        0k used,        0k free,   823848k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND                                                                                                                        
22612 ec2-user  20   0  8664  340  264 S  0.0  0.0   0:00.00 megamanager                                                             
22614 ec2-user  20   0 25552  17m  460 S  4.7  0.2   0:08.22 megacomet                                                               
22615 ec2-user  20   0 25552  17m  460 S  5.0  0.2   0:08.08 megacomet                                                               
22616 ec2-user  20   0 25556  17m  460 S  5.0  0.2   0:08.16 megacomet                                                               
22617 ec2-user  20   0 25556  17m  460 S  5.0  0.2   0:08.28 megacomet                                                               
22618 ec2-user  20   0 25580  17m  460 R  5.0  0.2   0:08.01 megacomet                                                               
22619 ec2-user  20   0 25556  17m  460 S  5.0  0.2   0:08.38 megacomet                                                               
22620 ec2-user  20   0 25556  17m  460 S  4.7  0.2   0:08.23 megacomet                                                               
22621 ec2-user  20   0 25552  17m  460 S  4.7  0.2   0:08.16 megacomet

I forgot to grab a top screenshot when the connections were all opened, but the memory usage was no different, and CPU was zero.

Conclusions

I really can’t believe the CPU and RAM usage are so small when the 1/2M connections are live and idle! At this stage, i’m not really testing for performance when passing messages around. I hope to get to 1M (static) open connections, and then start testing messaging. I’m optimistic: it looks promising. Next time i try this, i’ll keep a close eye on the kernel log (/var/log/kern.log) and see if i can find any bottlenecks.

References

http://www.metabrew.com/article/a-million-user-comet-application-with-mochiwe... http://www.cs.wisc.edu/condor/condorg/linux_scalability.html

MegaComet testing part 2

I've been busy testing megacomet on an EC2 micro instance. Keep in mind this testing is pretty crummy, given the instance only has 600M memory and i'm running the fake-client-flooding app on the same box as the server. Anyway, i was able to get up to 50k connections open before things started getting slow. I'm not sure if i hit a configuration limit, or a memory limit, or what. Testing continues...

Anyway, here's the 'top' results, showing that 50k connections roughly means 16M memory usage:

 

top - 08:52:17 up 58 min,  2 users,  load average: 0.13, 0.16, 0.22
Tasks:  68 total,   1 running,  67 sleeping,   0 stopped,   0 zombie
Cpu(s):  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:    611212k total,   581608k used,    29604k free,     3760k buffers
Swap:        0k total,        0k used,        0k free,    25356k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND          
22542 root      20   0 10392 2020  116 S  0.0  0.3   0:01.47 megacomet         
22548 root      20   0 10400 2016  116 S  0.0  0.3   0:01.48 megacomet         
22544 root      20   0 10452 2004  108 S  0.0  0.3   0:01.49 megacomet         
22546 root      20   0 10392 1968  108 S  0.0  0.3   0:01.49 megacomet         
22543 root      20   0 10388 1908   60 S  0.0  0.3   0:01.48 megacomet         
22549 root      20   0 10396 1860   20 S  0.0  0.3   0:01.47 megacomet         
22545 root      20   0 10396 1840    0 S  0.0  0.3   0:01.47 megacomet         
22547 root      20   0 10392 1840    0 S  0.0  0.3   0:01.50 megacomet

 

And here's where i got the kernel tuning tips from:

http://www.metabrew.com/article/a-million-user-comet-application-with-mochiwe...

http://www.cs.wisc.edu/condor/condorg/linux_scalability.html

Megacomet with 1 million queued messages

TL;DR: Comet server with 1 million queued messages, uses ~5% cpu at 1000msgs/sec and 65m RAM, on the smallest EC2 instance.

I've just started the testing of Megacomet on a 64-bit Amazon EC2 micro instance. My first test is to get it running, and see how it goes when queuing up messages (mainly this is because I haven't yet written a test harness to pull the messages off the queue!). I'm very pleased with the results, so here's the summary:

  • Setup: 1 manager process, 8 worker processes. 64-bit Amazon linux.
  • 1 million messages queued, at 1000 messages per second (!)
  • CPU usage was tiny. Maybe 5%? And most of that was the test harness, which would in a real situation be on a different server from your comet server. Also, keep in mind this is the smallest EC2 server you can get.
  • Total memory usage was: 65m
  • Each worker averaged 8m

I'm really happy with that, both from the perspective of memory usage, and CPU usage. Keep in mind i plan to implement expiry of messages in the queue, so that in reality you'd never get a queue that large, but it's good to know that it'll queue up 1m messages without breaking a sweat.

You can find the code at: https://github.com/chrishulbert/megacomet

Here's the 'top' capture whilst running, to give you an idea of CPU usage. The 'ruby' process was the test harness:

 

top - 08:11:29 up 1 day,  2:15,  3 users,  load average: 0.00, 0.01, 0.05
Tasks:  70 total,   2 running,  68 sleeping,   0 stopped,   0 zombie
Cpu(s):  0.7%us,  2.5%sy,  0.0%ni, 96.1%id,  0.0%wa,  0.0%hi,  0.2%si,  0.5%st
Mem:    611212k total,   603348k used,     7864k free,    23904k buffers
Swap:        0k total,        0k used,        0k free,   433696k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND           
30333 ec2-user  20   0 28028 2736 1424 R  4.7  0.4   0:02.51 ruby               
30202 ec2-user  20   0  8668  340  264 S  3.7  0.1   0:47.10 megamanager        
30219 ec2-user  20   0 34132  25m  484 S  1.0  4.2   0:09.63 megacomet          
30223 ec2-user  20   0 17104 9040  480 S  0.7  1.5   0:03.33 megacomet          
30213 ec2-user  20   0 17104 9044  480 S  0.3  1.5   0:03.39 megacomet          
30217 ec2-user  20   0 17104 9044  480 S  0.3  1.5   0:03.30 megacomet

 

And here's the top capture at the end of the 1 million queued messages, to show the memory usage:

 

top - 06:28:46 up 1 day, 33 min,  3 users,  load average: 0.01, 0.02, 0.05
Tasks:  69 total,   1 running,  68 sleeping,   0 stopped,   0 zombie
Cpu(s):  0.0%us,  0.2%sy,  0.0%ni, 99.8%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:    611212k total,   600784k used,    10428k free,    23840k buffers
Swap:        0k total,        0k used,        0k free,   436568k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND           
30219 ec2-user  20   0 32548  23m  484 S  0.0  4.0   0:09.03 megacomet          
  942 root      39  19  324m  14m 1688 S  0.0  2.5   0:00.00 yum-updatesd       
30213 ec2-user  20   0 16576 8516  480 S  0.0  1.4   0:03.18 megacomet          
30217 ec2-user  20   0 16576 8516  480 S  0.0  1.4   0:03.09 megacomet          
30225 ec2-user  20   0 16576 8516  480 S  0.0  1.4   0:03.07 megacomet          
30227 ec2-user  20   0 16576 8516  480 S  0.0  1.4   0:03.16 megacomet          
30223 ec2-user  20   0 16576 8512  480 S  0.0  1.4   0:03.11 megacomet