# # Copyright (c) 2012 Red Hat. # Copyright (c) 2008 Aconex. All Rights Reserved. # # This program is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation; either version 2 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # # The memcached PMDA # NOTE: Not all metrics are exported at the moment, in particular, # the per-slab and per-item statistics are not. It may be better to # manage instances differently as these values are dynamic - perhaps # have the monitored memcaches (the current indom) in the namespace, # like the old DBMS PMDAs. # use strict; use warnings; use PCP::PMDA; use vars qw( $pmda $id $n %caches ); my $memcache_delay = 5; # refresh rate in seconds my @memcache_instances = ( 0 => '127.0.0.1:11211', # 1 => '127.0.0.1:11212', # 2 => '192.168.5.76:11211', ); # Configuration files for overriding the above settings for my $file ( pmda_config('PCP_PMDAS_DIR') . '/memcache/memcache.conf', './memcache.conf' ) { eval `cat $file` unless ! -f $file; } my $memcache_indom = 0; # one for each memcached my $query = "stats\r\nstats slabs\r\nstats items\r\n"; # sent to memcached sub memcache_stats_callback { ( $id, $_ ) = @_; # $pmda->log("memcache_stats_callback: id $id"); if (/^STAT items:(\d+):(\w+) (\d+)/) { # stats items # $caches{$id}{"item$1"}{$2} = $3; } elsif (/^STAT (\d+):(\w+) (\d+)/) { # stats slabs # $caches{$id}{"slab$1"}{$2} = $3; } elsif (/^STAT (\w+) (\d+)/) { # stats $caches{$id}{$1} = $2; } elsif (!(/^END/)) { # unknown $pmda->log("Eh?: $_"); } } sub memcache_connect { # $pmda->log("memcache_connect: $#memcache_instances"); for ($id = 0; $id < $#memcache_instances; $id += 2) { my ($host, $port) = split(/:/, $memcache_instances[$id+1]); $pmda->add_sock($host, $port, \&memcache_stats_callback, $id); } } sub memcache_timer_callback { # $pmda->log("memcache_timer_callback"); for ($id = 0; $id < $#memcache_instances; $id += 2) { $pmda->put_sock($id, $query); } } sub memcache_fetch_callback { my ($cluster, $item, $inst) = @_; # $pmda->log("memcache_fetch_callback: $cluster:$item ($inst)"); return (PM_ERR_INST, 0) unless ($inst != PM_IN_NULL); return (PM_ERR_INST, 0) unless ($inst < $#memcache_instances); $id = $memcache_instances[$inst]; return (PM_ERR_AGAIN, 0) unless defined($caches{$id}); if ($cluster == 0) { if ($item == 0) { return ($caches{$id}{'pid'}, 1); } elsif ($item == 1) { return ($caches{$id}{'uptime'}, 1); } elsif ($item == 2) { return ($caches{$id}{'curr_items'}, 1); } elsif ($item == 3) { return ($caches{$id}{'total_items'}, 1); } elsif ($item == 4) { return ($caches{$id}{'bytes'}, 1); } elsif ($item == 5) { return ($caches{$id}{'curr_connections'}, 1); } elsif ($item == 6) { return ($caches{$id}{'total_connections'}, 1); } elsif ($item == 7) { return ($caches{$id}{'connection_structures'}, 1); } elsif ($item == 8) { return ($caches{$id}{'cmd_get'}, 1); } elsif ($item == 9) { return ($caches{$id}{'cmd_set'}, 1); } elsif ($item == 10) { return ($caches{$id}{'get_hits'}, 1); } elsif ($item == 11) { return ($caches{$id}{'get_misses'}, 1); } elsif ($item == 12) { return ($caches{$id}{'bytes_read'}, 1); } elsif ($item == 13) { return ($caches{$id}{'bytes_written'}, 1); } elsif ($item == 14) { return ($caches{$id}{'limit_maxbytes'}, 1); } } # elsif ($cluster == 1) { # # many different slabs (X..Y), and 7 metrics in this cluster # if ($item > 7 * 11) { return (PM_ERR_PMID, 0); } # $id = int($item / 7) + 6; # $item %= 7; # my $slab = "slab$id"; # # return (PM_ERR_AGAIN, 0) unless defined($caches{$id}{$slab}); # if ($item == 0) { return ($caches{$id}{$slab}{'chunk_size'}, 1); } # elsif ($item == 1) { return ($caches{$id}{$slab}{'chunks_per_page'}, 1); } # elsif ($item == 2) { return ($caches{$id}{$slab}{'total_pages'}, 1); } # elsif ($item == 3) { return ($caches{$id}{$slab}{'total_chunks'}, 1); } # elsif ($item == 4) { return ($caches{$id}{$slab}{'used_chunks'}, 1); } # elsif ($item == 5) { return ($caches{$id}{$slab}{'free_chunks'}, 1); } # elsif ($item == 6) { return ($caches{$id}{$slab}{'free_chunks_end'}, 1); } # } elsif ($cluster == 2) { if ($item == 0) { return ($caches{$id}{'active_slabs'}, 1); } elsif ($item == 1) { return ($caches{$id}{'total_malloced'}, 1); } } # elsif ($cluster == 3) { # # many different slabs (X..Y), and 2 metrics in this cluster # if ($item > 2 * [Y]) { return (PM_ERR_PMID, 0); } # $id = int($item / 2) + [X]; # $item %= 2; # my $itemid = "item$id"; # # return (PM_ERR_AGAIN, 0) unless defined($caches{$id}{$itemid}); # if ($item == 0) { return ($caches{$id}{$itemid}{'count'}, 1); } # elsif ($item == 1) { return ($caches{$id}{$itemid}{'age'}, 1); } # } return (PM_ERR_PMID, 0); } $pmda = PCP::PMDA->new('memcache', 89); $pmda->add_metric(pmda_pmid(0,0), PM_TYPE_U32, $memcache_indom, PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), 'memcache.pid', '', ''); $pmda->add_metric(pmda_pmid(0,1), PM_TYPE_U32, $memcache_indom, PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), 'memcache.uptime', '', ''); $pmda->add_metric(pmda_pmid(0,2), PM_TYPE_U32, $memcache_indom, PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), 'memcache.current_items', '', ''); $pmda->add_metric(pmda_pmid(0,3), PM_TYPE_U32, $memcache_indom, PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), 'memcache.total_items', '', ''); $pmda->add_metric(pmda_pmid(0,4), PM_TYPE_U64, $memcache_indom, PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), 'memcache.bytes', '', ''); $pmda->add_metric(pmda_pmid(0,5), PM_TYPE_U32, $memcache_indom, PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), 'memcache.current_connections', '', ''); $pmda->add_metric(pmda_pmid(0,6), PM_TYPE_U32, $memcache_indom, PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'memcache.total_connections', '', ''); $pmda->add_metric(pmda_pmid(0,7), PM_TYPE_U32, $memcache_indom, PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), 'memcache.connection_structures', '', ''); $pmda->add_metric(pmda_pmid(0,8), PM_TYPE_U32, $memcache_indom, PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'memcache.gets', '', ''); $pmda->add_metric(pmda_pmid(0,9), PM_TYPE_U32, $memcache_indom, PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'memcache.sets', '', ''); $pmda->add_metric(pmda_pmid(0,10), PM_TYPE_U32, $memcache_indom, PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'memcache.hits', '', ''); $pmda->add_metric(pmda_pmid(0,11), PM_TYPE_U32, $memcache_indom, PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'memcache.misses', '', ''); $pmda->add_metric(pmda_pmid(0,12), PM_TYPE_U64, $memcache_indom, PM_SEM_COUNTER, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), 'memcache.bytes_read', '', ''); $pmda->add_metric(pmda_pmid(0,13), PM_TYPE_U64, $memcache_indom, PM_SEM_COUNTER, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), 'memcache.bytes_written', '', ''); $pmda->add_metric(pmda_pmid(0,14), PM_TYPE_U32, $memcache_indom, PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), 'memcache.limit_maxbytes', '', ''); # $id = 0; # foreach $n (6 .. 17) { # stats slabs (N=6-17) # $pmda->add_metric(pmda_pmid(1,$id++), PM_TYPE_U32, $memcache_indom, # PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), # "memcache.slabs.slab$n.chunk_size", '', ''); # $pmda->add_metric(pmda_pmid(1,$id++), PM_TYPE_U32, $memcache_indom, # PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), # "memcache.slabs.slab$n.chunks_per_page", '', ''); # $pmda->add_metric(pmda_pmid(1,$id++), PM_TYPE_U32, $memcache_indom, # PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), # "memcache.slabs.slab$n.total_pages", '', ''); # $pmda->add_metric(pmda_pmid(1,$id++), PM_TYPE_U32, $memcache_indom, # PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), # "memcache.slabs.slab$n.total_chunks", '', ''); # $pmda->add_metric(pmda_pmid(1,$id++), PM_TYPE_U32, $memcache_indom, # PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), # "memcache.slabs.slab$n.used_chunks", '', ''); # $pmda->add_metric(pmda_pmid(1,$id++), PM_TYPE_U32, $memcache_indom, # PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), # "memcache.slabs.slab$n.free_chunks", '', ''); # $pmda->add_metric(pmda_pmid(1,$id++), PM_TYPE_U32, $memcache_indom, # PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), # "memcache.slabs.slab$n.free_chunks_end", '', ''); # } $pmda->add_metric(pmda_pmid(2,0), PM_TYPE_U32, $memcache_indom, PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), 'memcache.active_slabs', '', ''); $pmda->add_metric(pmda_pmid(2,1), PM_TYPE_U32, $memcache_indom, PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), 'memcache.total_malloced', '', ''); # $id = 0; # foreach $n (6 .. 17) { # stats items (N=6-17) # $pmda->add_metric(pmda_pmid(3,$id++), PM_TYPE_U32, $memcache_indom, # PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), # "memcache.items.item$n.count", '', ''); # $pmda->add_metric(pmda_pmid(3,$id++), PM_TYPE_U32, $memcache_indom, # PM_SEM_INSTANT, pmda_units(0,1,0,0,PM_TIME_SEC,0), # "memcache.items.item$n.age", '', ''); # } $pmda->add_indom($memcache_indom, \@memcache_instances, 'Instance domain exporting each memcache daemon', ''); $pmda->add_timer($memcache_delay, \&memcache_timer_callback, 0); $pmda->set_fetch_callback(\&memcache_fetch_callback); &memcache_connect; &memcache_timer_callback; $pmda->set_user('pcp'); $pmda->run; =pod =head1 NAME pmdamemcache - memcache performance metrics domain agent (PMDA) =head1 DESCRIPTION This PMDA extracts performance data from memcached, a distributed memory caching daemon commonly used to improve web serving performance. A farm of memcached processes over multiple servers can be utilised by a single web application, increasing the total available object cache size, and decreasing the database load associated with smaller cache sizes. This system is described in detail at http://www.danga.com/memcached. =head1 INSTALLATION Configure B to extract the values from set of hosts used in the memcache farm. These hosts can be listed in the $PCP_PMDAS_DIR/memcache/memcache.conf file, in the format (i.e. Perl array) described at the top of pmdamemcache.pl. A custom refresh rate can also be configured using this mechanism. # cd $PCP_PMDAS_DIR/memcache # [ edit memcache.conf ] Once this is setup, you can access the names and values for the memcache performance metrics by doing the following as root: # cd $PCP_PMDAS_DIR/memcache # ./Install If you want to undo the installation, do the following as root: # cd $PCP_PMDAS_DIR/memcache # ./Remove B is launched by pmcd(1) and should never be executed directly. The Install and Remove scripts notify pmcd(1) when the agent is installed or removed. =head1 FILES =over =item $PCP_PMDAS_DIR/memcache/memcache.conf configuration file listing monitored memcache instances =item $PCP_PMDAS_DIR/memcache/Install installation script for the B agent =item $PCP_PMDAS_DIR/memcache/Remove undo installation script for the B agent =item $PCP_LOG_DIR/pmcd/memcache.log default log file for error messages from B =back =head1 SEE ALSO pmcd(1) and memcached(1).