Tuesday, June 29, 2010

logrotate: rotating logs in multiple directories

I've got a server with almost one hundred web-sites. Each of the sites is in its own directory and runs its own logs in /usr/local/www/SITENAME/logs/*.log. When the logs grew up, I decided to set up logrotate. Since there were so many web-sites, my first idea was to create one configuration file for every site in logrotate.d and leave just one line in logrotate.conf:

include /usr/local/etc/logrotate.d

Only one hour later I understood that I can just write a path with multiple meta-characters to include ALL logs in just one line:

/usr/local/www/*/logs/*.log {
    daily
    rotate 60
    compress
    notifempty
    postrotate
          [ ! -f /var/run/nginx.pid ] || kill -USR1 `cat /var/run/nginx.pid`
    endscript

}

Thursday, June 24, 2010

PINBA: PHP Is Not a Bottleneck Anymore

Yesterday I installed Pinba on one of my servers. Pinba is a set of tools to monitor performance of PHP scripts. Pinba MySQL database engine runs a list of timers and automatically fills report databases. Pinba PHP extension uses two functions to open and close these timers. Besides, there are default timers, which open when a script is executed and close when it finishes. If you put a timer around some critical piece of code you can get information on how often it runs and how much time it is being executed. Timers can be tagged and the data can be grouped by tags. So, in some pieces of code you can set tags "author" and "task" with corresponding values. Then you'll manage to compare the performance of code written by different developers and identify the most time-consuming parts. The most interesting thing is that when you create database tables following certain rules, these tables become automagically filled with the necessary data. So, if you use the tags "author" and "tags" to group the data, the reports will include all valid combinations of these tags and show summaries on these combinations: how much time Joe's scripts parsed new documents and how often Jackie's front-end scripts were called. Very impressive.

By default, Pinba stores this information for some limited period of time (15 minutes, IIUC), so you need some way to make the data persistent. Since we use Munin to monitor various system indicators, I wrote a couple of plugins (in TCL and Lua, just for fun :)) to display the frequency of execution and the average execution time for each timer. Our developers added a handful of timers in various places of code and here it is:

The last graph looks cluttered and not too informative, so I plan to employ Munin's 'suggest' feature to draw some diagrams using one script. Perhaps, organizing the graphs will be the most difficult part of the deployment. I have to say, though, that the installation was not simple, either. Prerequisites include compiled sources of the installed MySQL (Percona Server 10.2 in my case), Google Protocol Buffers, Judy library, libevent 1.4.1+ (Ubuntu's default one will do) and Hoard memory allocator. And here is the installation process (paths will be different for you, so check carefully):

wget http://pinba.org/files/pinba_engine-0.0.5.tar.gz
tar -xzf pinba_engine-0.0.5.tar.gz
wget http://pinba.org/files/pinba_extension-0.0.5.tgz
tar -xzf pinba_extension-0.0.5.tgz
wget http://protobuf.googlecode.com/files/protobuf-2.3.0.tar.gz
tar -xzf protobuf-2.3.0.tar.gz
wget http://downloads.sourceforge.net/project/judy/judy/\
Judy-1.0.5/Judy-1.0.5.tar.gz?use_mirror=ignum
tar -xzf Judy-1.0.5.tar.gz
wget http://www.cs.umass.edu/%7Eemery/hoard/hoard-3.8/source/hoard-38.tar.gz
tar -xzf hoard-38.tar.gz
sudo aptitude install libevent-1.4-2 libevent-dev
cd protobuf-2.3.0
./configure
make -j
sudo make install
cd ../judy-1.0.5/
./configure
make
sudo make install
cd ../hoard-38/src
make linux-gcc-x86-64
sudo cp libhoard.so /usr/local/lib
sudo cp *.h /usr/local/include
sudo ldconfig
cd pinba_engine-0.0.5/
./configure --with-mysql=/home/minaev/Percona-Server-10.2/ \
--with-judy=/usr/local --with-protobuf=/usr/local \
--with-event=/usr --libdir=/usr/lib/mysql/plugin/ \
--with-hoard=/usr/local
make 
sudo make install
echo "INSTALL PLUGIN pinba SONAME 'libpinba_engine.so'"|mysql
echo "CREATE DATABASE pinba"|mysql
mysql -D pinba <default_tables.sql
cd pinba-0.0.5/
sed -i 's/NOTICE/CHECKING/' config.m4
phpize
./configure --with-pinba=/usr/local
sudo make install

I had to edit config.m4 because my version autoconf was a bit buggy. After this process you'll have to add three lines to your php.ini:

extension=pinba.so
pinba.enabled=1
pinba.server=[MySQL server address]

And here is one of Munin plugins, written in TCL. It collects data on how often certain API parts were called.

#!/usr/bin/tclsh

package require mysqltcl 3.05


proc clean_fieldname arg {
    return [regsub -all {[^A-Za-z]} $arg "_"]
}

set dbuser "pinba"
set db "pinba"

set conn [::mysql::connect -user $dbuser -db $db]

set fields [::mysql::sel $conn 
  "select concat(module_value, '+', action_value) from \
tag_info_module_action" -list]

if {$argc > 0} {
    switch [lindex $argv 0] {
        "config" {
            puts "graph_title PHP Actions per second"
            puts "graph_vlabel reqs per second"
            puts "graph_category Pinba"
            foreach fld $fields {
                set clean [clean_fieldname $fld]
                 puts "$clean.label $fld"
                 puts "$clean.draw LINE3"
            }
        }
        "autoconf" {
            puts "yes"
        }
    }
} else {
    foreach fld $fields {
        set clean [clean_fieldname $fld]
        set data [::mysql::sel $conn 
  "select req_per_sec from tag_info_module_action where \
  concat(module_value, '+', action_value)='$fld'" -list]
        puts "$clean.value $data"
    }
}

::mysql::close $conn

BTW, you may find it interesting that the performance of TCL scripts was almost the same as that of Lua scripts and about 3-4 times higher than for Bash.

Tuesday, June 15, 2010

Dark sides of Python

While reading about Python and playing around with its objects and classes (OOP being a perversions in itself), I witnessed a slightly weird behaviour. Define a class with a class variable.

class Parent:
  variable = "parent 1"

Then define a descendant class that inherits the class variable:

class Child(Parent):
  pass

(That funny single pass stands for empty definition body) Now, let's have a look at the value of variable in Parent and Child:

print Parent.variable
parent 1
print Child.variable
parent 1

Then, change the value of the variable in the parent class and it should also change in the child class:

Parent.variable = "parent 2"
print Parent.variable
parent 2
print Child.variable
parent 2

Sounds good. The variable must be shared between the two classes. Now, let's change the value of this allegedly shared variable in the child class:

Child.variable = "child 1"
print Parent.variable
parent 2
print Child.variable
child 1

Quite of a sudden, the variable turns out to be two separate variables. We have somehow broke the link that connected them and now, even if we change the value of the variable in Parent, this will not affect the variable in Child anymore:

Parent.variable = "parent 3"
print Parent.variable
parent 3
print Child.variable
child 1

And how a language with such non-trivial idiosyncrasies can be promoted as newbie-friendly, "does-what-you-want" language? My interest to Python evaporates so fast that I will probably never get to the famous included "batteries".

Tuesday, May 4, 2010

Errors when installing PHP 5.3 with FPM on Ubuntu

If you try to install PHP 5.3 with FPM on Ubuntu, no matter which installation path you follow, patching sources or downloading FPM from SVN, you will most likely see a lot of error messages similar to the following:

$ ./configure 
cat: confdefs.h: No such file or directory
./configure: 490: ac_fn_c_try_run: not found
./configure: 490: 5: Bad file descriptor    
./configure: 490: :: checking for pthreads_cflags: not found
./configure: 490: 6: Bad file descriptor                    
./configure: 490: checking for pthreads_cflags... : not found
cat: confdefs.h: No such file or directory                   
./configure: 490: ac_fn_c_try_run: not found                 
cat: confdefs.h: No such file or directory                   
./configure: 490: ac_fn_c_try_run: not found                 
cat: confdefs.h: No such file or directory                   
./configure: 490: ac_fn_c_try_run: not found                 
cat: confdefs.h: No such file or directory                   
./configure: 490: ac_fn_c_try_run: not found                 
cat: confdefs.h: No such file or directory                   
./configure: 490: ac_fn_c_try_run: not found                 
cat: confdefs.h: No such file or directory                   
./configure: 490: ac_fn_c_try_run: not found                 
cat: confdefs.h: No such file or directory                   
./configure: 490: ac_fn_c_try_run: not found                 
cat: confdefs.h: No such file or directory                   
./configure: 490: ac_fn_c_try_run: not found                 
./configure: 492: 5: Bad file descriptor                     
./configure: 492: :: result: : not found                     
./configure: 492: 6: Bad file descriptor                     
./configure: 492: : Permission denied                        
./configure: 495: 5: Bad file descriptor                     
./configure: 495: :: checking for pthreads_lib: not found    
./configure: 495: 6: Bad file descriptor                     
./configure: 495: checking for pthreads_lib... : not found   
cat: confdefs.h: No such file or directory                   
./configure: 555: ac_fn_c_try_run: not found                 
cat: confdefs.h: No such file or directory                   
./configure: 555: ac_fn_c_try_run: not found                 
cat: confdefs.h: No such file or directory                   
./configure: 555: ac_fn_c_try_run: not found                 
./configure: 557: 5: Bad file descriptor                     
./configure: 557: :: result: : not found                     
./configure: 557: 6: Bad file descriptor                     
./configure: 557: : Permission denied                        
./configure: 633: 5: Bad file descriptor                     
./configure: 633: :: result: : not found                     
./configure: 633: 6: Bad file descriptor                     
./configure: 633: : Permission denied                        
./configure: 635: 5: Bad file descriptor                     
./configure: 635: :: result: Configuring SAPI modules: not found
./configure: 635: 6: Bad file descriptor                        
./configure: 635: Configuring SAPI modules: not found           
./configure: 666: 5: Bad file descriptor

If so, don't be a fool like me, check what buildconf reported above:

$ ./buildconf --force                                                                                                          
Forcing buildconf                                                                                                                                       
buildconf: checking installation...                                                                                                                     
buildconf: autoconf version 2.64 (ok)                                                                                                                   
buildconf: Your version of autoconf likely contains buggy cache code.                                                                                   
           Running vcsclean for you.                                                                                                                    
           To avoid this, install autoconf-2.13.                                                                                                        
Can't figure out your VCS, not cleaning. 

This is not just a regular warning you can neglect, you have to use autoconf-2.13. You don't have to install it from sources, though. This version is a separate package, so just run sudo aptitude install autoconf-2.13. You will have also to install libevent-1.4 and libevent-dev. Now, you can run buildconf again and then configure.

The full installation procedure would be:

sudo apt-get install autoconf2.13
wget http://ru.php.net/get/php-5.3.2.tar.bz2/from/ru2.php.net/mirror
tar -xjf php-5.3.2.tar.bz2
sudo aptitude install libevent-dev
cd php-5.3.2/
svn co http://svn.php.net/repository/php/php-src/trunk/sapi/fpm sapi/fpm
./buildconf --force
./configure --enable-fpm --with-zlib \
--enable-pdo --with-pdo-mysql --enable-sockets \
--with-mysql --with-config-file-path=/etc \
--enable-calendar --with-iconv --enable-exif\
 --enable-soap --enable-ftp --enable-wddx \
--with-zlib --with-bz2 --with-gettext \
--with-xmlrpc --enable-pcntl --enable-soap \
--enable-bcmath --enable-mbstring --enable-dba \
--with-openssl --with-mhash --with-mcrypt \
--with-xsl --with-curl --with-pcre-regex 
--with-gd --enable-gd-native-ttf --with-ldap \
--enable-pdo --with-pdo-mysql --with-mysql \
--with-sqlite --with-pdo-sqlite --enable-zip \
--enable-sqlite-utf8 --with-pear \
--with-freetype-dir=/usr --with-jpeg-dir=/usr \
--with-mysqli --with-fpm-conf=/etc/php/php-fpm.conf \
--with-fpm-pid=/var/run/php-fpm.pid \
--with-config-file-path=/etc/php/ \
--with-config-file-scan-dir=/etc/php/conf.d/

Of course, your php configure options can be different.

Wednesday, April 28, 2010

Naïve question

Been installing FineReader under Windows today. The question is why does installation process pause when I click'n'hold on the window title bar to move it? No reply...

Wednesday, April 21, 2010

Split screen vertically. You won't find it in `man screen`

Now I know what `serendipity' means. It's when you are not quite awake, sit at the keyboard, your fingers fumble and you suddenly see that your xterm window where `screen' is running, gets split in two, but not horizontally (one above another), as it should according to the documentation, but vertically (one beside another). I didn't even understand what keys I pressed to get this effect. `man screen' includes only familiar `split' command. It took me some time to google out the answer.

It turns out that this feature was included in `screen' quite some time ago, but somehow nobody cared to describe it in man. Pressing `C-a |' splits the screen in left and right regions. To get rid of one of the regions, use the usual `C-a X' or `C-a S'.

Thursday, April 15, 2010

MySQL Proxies

While searching for a MySQL proxy solution, I found the following four products:

At least two of them share the same codebase, MySQL-proxy being the forefather of Spock proxy. Spock proxy tries to increase performance by eliminating the scripting layer. The goal of Spock is to provide efficient sharding, not fault-tolerance. Dormando's proxy positions itself as an alternative to the official MySQL-proxy, and it tries to retain compatibility. It even supports Lua scripting. I'm not sure, though, if the API is the same. And, finally, Proximo is written in Perl, which means its performance is lower than that of the other competitors. But Proximo is in very early stage of development and has a promising architecture. It may have good future.