diff options
Diffstat (limited to 'src/pmdas/postgresql')
-rw-r--r-- | src/pmdas/postgresql/GNUmakefile | 51 | ||||
-rwxr-xr-x | src/pmdas/postgresql/Install | 40 | ||||
-rwxr-xr-x | src/pmdas/postgresql/Remove | 25 | ||||
-rw-r--r-- | src/pmdas/postgresql/pmdapostgresql.pl | 1546 | ||||
-rw-r--r-- | src/pmdas/postgresql/pmlogconf.summary | 8 |
5 files changed, 1670 insertions, 0 deletions
diff --git a/src/pmdas/postgresql/GNUmakefile b/src/pmdas/postgresql/GNUmakefile new file mode 100644 index 0000000..114c0cf --- /dev/null +++ b/src/pmdas/postgresql/GNUmakefile @@ -0,0 +1,51 @@ +#!gmake +# +# Copyright (c) 2011 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. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = postgresql +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +LSRCFILES = Install Remove pmda$(IAM).pl pmlogconf.summary + +ifneq ($(POD2MAN),) +MAN_SECTION = 1 +MAN_PAGES = pmda$(IAM).$(MAN_SECTION) +MAN_DEST = $(PCP_MAN_DIR)/man$(MAN_SECTION) +endif + +LDIRT = domain.h root pmns *.log $(MAN_PAGES) + +default: check_domain $(MAN_PAGES) + +pmda$(IAM).1: pmda$(IAM).pl + $(POD_MAKERULE) + +include $(BUILDRULES) + +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 Install Remove $(PMDADIR) + $(INSTALL) -m 644 pmda$(IAM).pl $(PMDADIR)/pmda$(IAM).pl + @$(INSTALL_MAN) + $(INSTALL) -m 755 -d $(PCP_VAR_DIR)/config/pmlogconf/$(IAM) + $(INSTALL) -m 644 pmlogconf.summary $(PCP_VAR_DIR)/config/pmlogconf/$(IAM)/summary + +default_pcp : default + +install_pcp : install + +check_domain: ../../pmns/stdpmid + $(DOMAIN_PERLRULE) diff --git a/src/pmdas/postgresql/Install b/src/pmdas/postgresql/Install new file mode 100755 index 0000000..3cf31be --- /dev/null +++ b/src/pmdas/postgresql/Install @@ -0,0 +1,40 @@ +#! /bin/sh +# +# Copyright (c) 2011 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. +# +# Install the PostgreSQL PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=postgresql +perl_opt=true +daemon_opt=false +forced_restart=true + +perl -e "use DBI" 2>/dev/null +if test $? -ne 0; then + echo "Perl database interface (DBI) is not installed" + exit 1 +fi + +perl -e "use DBD::Pg" 2>/dev/null +if test $? -ne 0; then + echo "Postgres database driver (DBD::Pg) is not installed" + exit 1 +fi + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/postgresql/Remove b/src/pmdas/postgresql/Remove new file mode 100755 index 0000000..db3f251 --- /dev/null +++ b/src/pmdas/postgresql/Remove @@ -0,0 +1,25 @@ +#! /bin/sh +# +# Copyright (c) 2011 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. +# +# Remove the PostgreSQL PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=postgresql + +pmdaSetup +pmdaRemove +exit 0 diff --git a/src/pmdas/postgresql/pmdapostgresql.pl b/src/pmdas/postgresql/pmdapostgresql.pl new file mode 100644 index 0000000..9290981 --- /dev/null +++ b/src/pmdas/postgresql/pmdapostgresql.pl @@ -0,0 +1,1546 @@ +# +# Copyright (c) 2011 Nathan Scott. 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. +# + +use strict; +use warnings; +use PCP::PMDA; +use DBI; + +my $database = 'dbi:Pg:dbname=postgres'; +my $username = 'postgres'; +my $password = ''; # DBI parameter, typically unused for postgres + +# Configuration files for overriding the above settings +for my $file ( '/etc/pcpdbi.conf', # system defaults (lowest priority) + pmda_config('PCP_PMDAS_DIR') . '/postgresql/postgresql.conf', + './postgresql.conf' ) { # current directory (high priority) + eval `cat $file` unless ! -f $file; +} + +use vars qw( $pmda $dbh ); +my $pm_in_null = PM_IN_NULL; +my $all_rel_indom = 0; my @all_rel_instances; +my $sys_rel_indom = 1; my @sys_rel_instances; +my $user_rel_indom = 2; my @user_rel_instances; +my $process_indom = 3; my @process_instances; +my $function_indom = 4; my @function_instances; +my $database_indom = 5; my @database_instances; +my $all_index_indom = 6; my @all_index_instances; +my $sys_index_indom = 7; my @sys_index_instances; +my $user_index_indom = 8; my @user_index_instances; +my $all_seq_indom = 9; my @all_seq_instances; +my $sys_seq_indom = 10; my @sys_seq_instances; +my $user_seq_indom = 11; my @user_seq_instances; +my $replicant_indom = 12; my @replicant_instances; + +# hash of hashes holding DB handle, last values, and min version indexed by table name +my %tables_by_name = ( + pg_stat_activity => { handle => undef, values => {}, version => undef }, + pg_stat_bgwriter => { handle => undef, values => {}, version => undef }, + pg_stat_database => { handle => undef, values => {}, version => undef }, + pg_stat_database_conflicts => { handle => undef, values => {}, version => 9.1 }, + pg_stat_replication => { handle => undef, values => {}, version => 9.1 }, + pg_stat_all_tables => { handle => undef, values => {}, version => undef }, + pg_stat_sys_tables => { handle => undef, values => {}, version => undef }, + pg_stat_user_tables => { handle => undef, values => {}, version => undef }, + pg_stat_all_indexes => { handle => undef, values => {}, version => undef }, + pg_stat_sys_indexes => { handle => undef, values => {}, version => undef }, + pg_stat_user_indexes => { handle => undef, values => {}, version => undef }, + pg_statio_all_tables => { handle => undef, values => {}, version => undef }, + pg_statio_sys_tables => { handle => undef, values => {}, version => undef }, + pg_statio_user_tables => { handle => undef, values => {}, version => undef }, + pg_statio_all_indexes => { handle => undef, values => {}, version => undef }, + pg_statio_sys_indexes => { handle => undef, values => {}, version => undef }, + pg_statio_user_indexes => { handle => undef, values => {}, version => undef }, + pg_statio_all_sequences => { handle => undef, values => {}, version => undef }, + pg_statio_sys_sequences => { handle => undef, values => {}, version => undef }, + pg_statio_user_sequences => { handle => undef, values => {}, version => undef }, + pg_stat_user_functions => { handle => undef, values => {}, version => undef }, + pg_stat_xact_user_functions => { handle => undef, values => {}, version => 9.1 }, + pg_stat_xact_all_tables => { handle => undef, values => {}, version => 9.1 }, + pg_stat_xact_sys_tables => { handle => undef, values => {}, version => 9.1 }, + pg_stat_xact_user_tables => { handle => undef, values => {}, version => 9.1 }, + pg_active => { handle => undef, values => {}, version => undef, sql => 'select pg_is_in_recovery(), pg_current_xlog_location()' }, + pg_recovery => { handle => undef, values => {}, version => undef, sql => 'select pg_is_in_recovery(), pg_last_xlog_receive_location(), pg_last_xlog_replay_location()' }, +); + +# hash of hashes holding setup and refresh function, indexed by PMID cluster +my %tables_by_cluster = ( + '0' => { + name => 'pg_stat_activity', + setup => \&setup_activity, + indom => $process_indom, + refresh => \&refresh_activity }, + '1' => { + name => 'pg_stat_bgwriter', + setup => \&setup_bgwriter, + indom => PM_INDOM_NULL, + refresh => \&refresh_bgwriter }, + '2' => { + name => 'pg_stat_database', + setup => \&setup_database, + indom => $database_indom, + refresh => \&refresh_database }, + '3' => { + name => 'pg_stat_user_functions', + setup => \&setup_user_functions, + indom => $function_indom, + refresh => \&refresh_user_functions }, + '4' => { + name => 'pg_stat_xact_user_functions', + setup => \&setup_xact_user_functions, + indom => $function_indom, + refresh => \&refresh_user_functions }, # identical refresh routine + '5' => { + name => 'pg_stat_database_conflicts', + setup => \&setup_database_conflicts, + indom => $database_indom, + refresh => \&refresh_database }, # identical refresh routine + '6' => { + name => 'pg_stat_replication', + setup => \&setup_replication, + indom => $replicant_indom, + refresh => \&refresh_replication }, + '7' => { + name => 'pg_active', + setup => \&setup_active_functions, + indom => PM_INDOM_NULL, + refresh => \&refresh_active_functions }, + '8' => { + name => 'pg_recovery', + setup => \&setup_recovery_functions, + indom => PM_INDOM_NULL, + refresh => \&refresh_recovery_functions }, + '10' => { + name => 'pg_stat_all_tables', + setup => \&setup_stat_tables, + indom => $all_rel_indom, + params => 'all_tables', + refresh => \&refresh_all_tables }, + '11' => { + name => 'pg_stat_sys_tables', + setup => \&setup_stat_tables, + indom => $sys_rel_indom, + params => 'sys_tables', + refresh => \&refresh_sys_tables }, + '12' => { + name => 'pg_stat_user_tables', + setup => \&setup_stat_tables, + indom => $user_rel_indom, + params => 'user_tables', + refresh => \&refresh_user_tables }, + '13' => { + name => 'pg_stat_all_indexes', + setup => \&setup_stat_indexes, + indom => $all_index_indom, + params => 'all_indexes', + refresh => \&refresh_all_indexes }, + '14' => { + name => 'pg_stat_sys_indexes', + setup => \&setup_stat_indexes, + params => 'sys_indexes', + indom => $sys_index_indom, + refresh => \&refresh_sys_indexes }, + '15' => { + name => 'pg_stat_user_indexes', + setup => \&setup_stat_indexes, + indom => $user_index_indom, + params => 'user_indexes', + refresh => \&refresh_user_indexes }, + '16' => { + name => 'pg_stat_xact_all_tables', + setup => \&setup_stat_xact_tables, + indom => $all_rel_indom, + params => 'all_tables', + refresh => \&refresh_xact_all_tables }, + '17' => { + name => 'pg_stat_xact_sys_tables', + setup => \&setup_stat_xact_tables, + indom => $sys_rel_indom, + params => 'sys_tables', + refresh => \&refresh_xact_sys_tables }, + '18' => { + name => 'pg_stat_xact_user_tables', + setup => \&setup_stat_xact_tables, + indom => $user_rel_indom, + params => 'user_tables', + refresh => \&refresh_xact_user_tables }, + '30' => { + name => 'pg_statio_all_tables', + setup => \&setup_statio_tables, + indom => $all_rel_indom, + params => 'all_tables', + refresh => \&refresh_io_all_tables }, + '31' => { + name => 'pg_statio_sys_tables', + setup => \&setup_statio_tables, + indom => $sys_rel_indom, + params => 'sys_tables', + refresh => \&refresh_io_sys_tables }, + '32' => { + name => 'pg_statio_user_tables', + setup => \&setup_statio_tables, + indom => $user_rel_indom, + params => 'user_tables', + refresh => \&refresh_io_user_tables }, + '33' => { + name => 'pg_statio_all_indexes', + setup => \&setup_statio_indexes, + indom => $all_index_indom, + params => 'all_indexes', + refresh => \&refresh_io_all_indexes }, + '34' => { + name => 'pg_statio_sys_indexes', + setup => \&setup_statio_indexes, + indom => $sys_index_indom, + params => 'sys_indexes', + refresh => \&refresh_io_all_indexes }, + '35' => { + name => 'pg_statio_user_indexes', + setup => \&setup_statio_indexes, + indom => $user_index_indom, + params => 'user_indexes', + refresh => \&refresh_io_all_indexes }, + '36' => { + name => 'pg_statio_all_sequences', + setup => \&setup_statio_sequences, + indom => $all_seq_indom, + params => 'all_sequences', + refresh => \&refresh_io_all_sequences }, + '37' => { + name => 'pg_statio_sys_sequences', + setup => \&setup_statio_sequences, + indom => $sys_seq_indom, + params => 'sys_sequences', + refresh => \&refresh_io_sys_sequences }, + '38' => { + name => 'pg_statio_user_sequences', + setup => \&setup_statio_sequences, + params => 'user_sequences', + indom => $user_seq_indom, + refresh => \&refresh_io_user_sequences }, +); + +sub postgresql_version_query +{ + my $handle = $dbh->prepare("select VERSION()"); + + if (defined($handle->execute())) { + my $result = $handle->fetchall_arrayref(); + return 0 unless defined($result); + my $version = $result->[0][0]; + $version =~ s/^PostgreSQL (\d+\.\d+)\.\d+ .*/$1/g; + return $version; + } + return 0; +} + +sub postgresql_connection_setup +{ + if (!defined($dbh)) { + $dbh = DBI->connect($database, $username, $password, + {AutoCommit => 1, pg_bool_tf => 0}); + if (defined($dbh)) { + $pmda->log("PostgreSQL connection established"); + my $version = postgresql_version_query(); + + foreach my $key (keys %tables_by_name) { + my $minversion = $tables_by_name{$key}{version}; + my $sqlquery = $tables_by_name{$key}{sql}; + my $query; + + $minversion = 0 unless defined($minversion); + if ($minversion > $version) { + $pmda->log("Skipping table $key, not supported on $version"); + } elsif (defined($sqlquery)) { + $query = $dbh->prepare($sqlquery); + } else { + $query = $dbh->prepare("select * from $key"); + } + $tables_by_name{$key}{handle} = $query unless !defined($query); + } + } + } +} + +sub postgresql_indoms_setup +{ + $all_rel_indom = $pmda->add_indom($all_rel_indom, \@all_rel_instances, + 'Instance domain for PostgreSQL relations, all tables', ''); + $sys_rel_indom = $pmda->add_indom($sys_rel_indom, \@sys_rel_instances, + 'Instance domain for PostgreSQL relations, system tables', ''); + $user_rel_indom = $pmda->add_indom($user_rel_indom, \@user_rel_instances, + 'Instance domain for PostgreSQL relations, user tables', ''); + + $function_indom = $pmda->add_indom($function_indom, \@function_instances, + 'Instance domain exporting PostgreSQL user functions', ''); + + $process_indom = $pmda->add_indom($process_indom, \@process_instances, + 'Instance domain exporting each PostgreSQL client process', ''); + + $database_indom = $pmda->add_indom($database_indom, \@database_instances, + 'Instance domain exporting each PostgreSQL database', ''); + + $replicant_indom = $pmda->add_indom($replicant_indom, \@replicant_instances, + 'Instance domain exporting PostgreSQL replication processes', ''); + + $all_index_indom = $pmda->add_indom($all_index_indom, \@all_index_instances, + 'Instance domain for PostgreSQL indexes, all tables', ''); + $sys_index_indom = $pmda->add_indom($sys_index_indom, \@sys_index_instances, + 'Instance domain for PostgreSQL indexes, system tables', ''); + $user_index_indom = $pmda->add_indom($user_index_indom, \@user_index_instances, + 'Instance domain for PostgreSQL indexes, user tables', ''); + + $all_seq_indom = $pmda->add_indom($all_seq_indom, \@all_seq_instances, + 'Instance domain for PostgreSQL sequences, all tables', ''); + $sys_seq_indom = $pmda->add_indom($sys_seq_indom, \@sys_seq_instances, + 'Instance domain for PostgreSQL sequences, system tables', ''); + $user_seq_indom = $pmda->add_indom($user_seq_indom, \@user_seq_instances, + 'Instance domain for PostgreSQL sequences, user tables', ''); +} + +sub postgresql_metrics_setup +{ + foreach my $cluster (sort (keys %tables_by_cluster)) { + my $setup = $tables_by_cluster{"$cluster"}{setup}; + my $indom = $tables_by_cluster{"$cluster"}{indom}; + &$setup($cluster, $indom, $tables_by_cluster{"$cluster"}{params}); + } +} + +sub postgresql_refresh +{ + my ($cluster) = @_; + + my $name = $tables_by_cluster{"$cluster"}{name}; + my $refresh = $tables_by_cluster{"$cluster"}{refresh}; + &$refresh($tables_by_name{$name}); +} + +sub postgresql_fetch_callback +{ + my ($cluster, $item, $inst) = @_; + my $metric_name = pmda_pmid_name($cluster, $item); + my ($key, $value, $valueref, @columns ); + + return (PM_ERR_PMID, 0) unless defined($metric_name); + $key = $metric_name; + $key =~ s/^postgresql\./pg_/; + $key =~ s/\.[a-zA-Z0-9_]*$//; + $key =~ s/\./_/g; + + # $pmda->log("fetch_cb $metric_name $cluster:$item ($inst) - $key"); + + $valueref = $tables_by_name{$key}{values}{"$inst"}; + if (!defined($valueref)) { + return (PM_ERR_INST, 0) unless ($inst == PM_IN_NULL); + return (PM_ERR_VALUE, 0); + } + + @columns = @$valueref; + $value = $columns[$item]; + return (PM_ERR_APPVERSION, 0) unless defined($value); + + return ($value, 1); +} + +# +# Refresh routines - one per table (cluster) - format database query +# result set for later use by the generic fetch callback routine. +# +sub refresh_results +{ + my $tableref = shift; + my %table = %$tableref; + my ( $handle, $valuesref ) = ( $table{handle}, $table{values} ); + my %values = %$valuesref; + + %values = (); # clear any previous values + + if (defined($dbh) && defined($handle)) { + if (defined($handle->execute())) { + return $handle->fetchall_arrayref(); + } + $table{handle} = undef; + } + return undef; +} + +sub refresh_activity +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @process_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $process_instances[($i*2)] = "$result->[$i][2]"; + $tableref = $result->[$i]; + $process_instances[($i*2)+1] = "$tableref->[2] $tableref->[5]"; + # 9.0 does not have client_hostname, deal with that first + splice @$tableref, 7, 0, (undef) unless (@$tableref > 13); + # special case needed for 'client_*' columns (6 -> 8), may be null + for my $j (6 .. 8) { # for each special case column + $tableref->[$j] = '' unless (defined($tableref->[$j])); + } + $table{values}{$instid} = $tableref; + } + } + $pmda->replace_indom($process_indom, \@process_instances); +} + +sub refresh_replication +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @replicant_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $replicant_instances[($i*2)] = "$result->[$i][2]"; + $replicant_instances[($i*2)+1] = "$result->[$i][2] $result->[$i][5]"; + # special case needed for 'client_*' columns (4 -> 5) + for my $j (4 .. 5) { # for each special case column + $result->[$i][$j] = '' unless (defined($result->[$i][$j])); + } + $table{values}{$instid} = $result->[$i]; + } + } + $pmda->replace_indom($replicant_indom, \@replicant_instances); +} + +sub refresh_bgwriter +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + $table{values}{"$pm_in_null"} = \@{$result->[0]} unless (!defined($result)); +} + +sub refresh_active_functions +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + # Results from SQL (2 values; 2nd may well contain undef): + # select pg_is_in_recovery(), pg_current_xlog_location(), + # We need to split out the log identifier and log record offset in the + # final value, and save separately into the cached result table. + # + if (defined($result)) { + my $arrayref = $result->[0]; + my @values = @$arrayref; + my @massaged = ( $values[0], undef,undef ); + my $moffset = 1; # index for @massaged array + + if (defined($values[1])) { + my @pieces = split /\//, $values[1]; + $massaged[$moffset++] = hex($pieces[0]); + $massaged[$moffset++] = hex($pieces[1]); + } + $table{values}{"$pm_in_null"} = \@massaged; + } +} + +sub refresh_recovery_functions +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + # Results from SQL (3 values; 3rd,4th may well contain undef): + # select pg_is_in_recovery(), + # pg_last_xlog_receive_location(), pg_last_xlog_replay_location() + # We need to split out the log identifier and log record offset in the + # final values, and save separately into the cached result table. + # + if (defined($result)) { + my $arrayref = $result->[0]; + my @values = @$arrayref; + my @massaged = ( $values[0], undef,undef, undef,undef ); + my $moffset = 1; # index for @massaged array + + foreach my $voffset (1..2) { # index for @values array + if (defined($values[$voffset])) { + my @pieces = split /\//, $values[$voffset]; + $massaged[$moffset++] = hex($pieces[0]); + $massaged[$moffset++] = hex($pieces[1]); + } + } + $table{values}{"$pm_in_null"} = \@massaged; + } +} + +sub refresh_database +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @database_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $database_instances[($i*2)] = $result->[$i][0]; + $database_instances[($i*2)+1] = $result->[$i][1]; + $table{values}{$instid} = $result->[$i]; + } + } + $pmda->replace_indom($database_indom, \@database_instances); +} + +sub refresh_user_functions +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @function_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $function_instances[($i*2)] = $result->[$i][0]; + $function_instances[($i*2)+1] = $result->[$i][2]; + $table{values}{$instid} = $result->[$i]; + } + } + $pmda->replace_indom($function_indom, \@function_instances); +} + +sub refresh_all_tables +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @all_rel_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $all_rel_instances[($i*2)] = $result->[$i][0]; + $all_rel_instances[($i*2)+1] = $result->[$i][2]; + # special case needed for 'last_*' columns (13 -> 16) + for my $j (13 .. 16) { # for each special case column + $result->[$i][$j] = '' unless (defined($result->[$i][$j])); + } + $table{values}{$instid} = $result->[$i]; + } + } + $pmda->replace_indom($all_rel_indom, \@all_rel_instances); +} + +sub refresh_sys_tables +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @sys_rel_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $sys_rel_instances[($i*2)] = $result->[$i][0]; + $sys_rel_instances[($i*2)+1] = $result->[$i][2]; + # special case needed for 'last_*' columns (13 -> 16) + for my $j (13 .. 16) { # for each special case column + $result->[$i][$j] = '' unless (defined($result->[$i][$j])); + } + $table{values}{$instid} = $result->[$i]; + } + } + $pmda->replace_indom($sys_rel_indom, \@sys_rel_instances); +} + +sub refresh_user_tables +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @user_rel_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $user_rel_instances[($i*2)] = $result->[$i][0]; + $user_rel_instances[($i*2)+1] = $result->[$i][2]; + # special case needed for 'last_*' columns (13 -> 16) + for my $j (13 .. 16) { # for each special case column + $result->[$i][$j] = '' unless (defined($result->[$i][$j])); + } + $table{values}{$instid} = $result->[$i]; + } + } + $pmda->replace_indom($user_rel_indom, \@user_rel_instances); +} + +sub refresh_xact_all_tables +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @all_rel_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $all_rel_instances[($i*2)] = $result->[$i][0]; + $all_rel_instances[($i*2)+1] = $result->[$i][2]; + $table{values}{$instid} = $result->[$i]; + } + } + $pmda->replace_indom($all_rel_indom, \@all_rel_instances); +} + +sub refresh_xact_sys_tables +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @sys_rel_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $sys_rel_instances[($i*2)] = $result->[$i][0]; + $sys_rel_instances[($i*2)+1] = $result->[$i][2]; + $table{values}{$instid} = $result->[$i]; + } + } + $pmda->replace_indom($sys_rel_indom, \@sys_rel_instances); +} + +sub refresh_xact_user_tables +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @user_rel_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $user_rel_instances[($i*2)] = $result->[$i][0]; + $user_rel_instances[($i*2)+1] = $result->[$i][2]; + $table{values}{$instid} = $result->[$i]; + } + } + $pmda->replace_indom($user_rel_indom, \@user_rel_instances); +} + +sub refresh_all_indexes +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @all_index_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $all_index_instances[($i*2)] = $result->[$i][1]; + $all_index_instances[($i*2)+1] = $result->[$i][4]; + $table{values}{$instid} = $result->[$i]; + } + } + $pmda->replace_indom($all_index_indom, \@all_index_instances); +} + +sub refresh_sys_indexes +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @sys_index_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $sys_index_instances[($i*2)] = $result->[$i][1]; + $sys_index_instances[($i*2)+1] = $result->[$i][4]; + $table{values}{$instid} = $result->[$i]; + } + } + $pmda->replace_indom($sys_index_indom, \@sys_index_instances); +} + +sub refresh_user_indexes +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @user_index_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $user_index_instances[($i*2)] = $result->[$i][1]; + $user_index_instances[($i*2)+1] = $result->[$i][4]; + $table{values}{$instid} = $result->[$i]; + } + } + $pmda->replace_indom($user_index_indom, \@user_index_instances); +} + +sub refresh_io_all_tables +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @all_rel_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $all_rel_instances[($i*2)] = $result->[$i][0]; + $all_rel_instances[($i*2)+1] = $result->[$i][2]; + $table{values}{$instid} = $result->[$i]; + } + } + $pmda->replace_indom($all_rel_indom, \@all_rel_instances); +} + +sub refresh_io_sys_tables +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @sys_rel_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $sys_rel_instances[($i*2)] = $result->[$i][0]; + $sys_rel_instances[($i*2)+1] = $result->[$i][2]; + $table{values}{$instid} = $result->[$i]; + } + } + $pmda->replace_indom($sys_rel_indom, \@sys_rel_instances); +} + +sub refresh_io_user_tables +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @user_rel_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $user_rel_instances[($i*2)] = $result->[$i][0]; + $user_rel_instances[($i*2)+1] = $result->[$i][2]; + $table{values}{$instid} = $result->[$i]; + } + } + $pmda->replace_indom($user_rel_indom, \@user_rel_instances); +} + +sub refresh_io_all_indexes +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @all_index_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $all_index_instances[($i*2)] = $result->[$i][1]; + $all_index_instances[($i*2)+1] = $result->[$i][4]; + $table{values}{$instid} = $result->[$i]; + } + } + $pmda->replace_indom($all_index_indom, \@all_index_instances); +} + +sub refresh_io_sys_indexes +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @sys_index_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $sys_index_instances[($i*2)] = $result->[$i][1]; + $sys_index_instances[($i*2)+1] = $result->[$i][4]; + $table{values}{$instid} = $result->[$i]; + } + } + $pmda->replace_indom($sys_index_indom, \@sys_index_instances); +} + +sub refresh_io_user_indexes +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @user_index_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $user_index_instances[($i*2)] = $result->[$i][1]; + $user_index_instances[($i*2)+1] = $result->[$i][4]; + $table{values}{$instid} = $result->[$i]; + } + } + $pmda->replace_indom($user_index_indom, \@user_index_instances); +} + +sub refresh_io_all_sequences +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @all_seq_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $all_seq_instances[($i*2)] = $result->[$i][0]; + $all_seq_instances[($i*2)+1] = $result->[$i][2]; + $table{values}{$instid} = $result->[$i]; + } + } + $pmda->replace_indom($all_seq_indom, \@all_seq_instances); +} + +sub refresh_io_sys_sequences +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @sys_seq_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $sys_seq_instances[($i*2)] = $result->[$i][0]; + $sys_seq_instances[($i*2)+1] = $result->[$i][2]; + $table{values}{$instid} = $result->[$i]; + } + } + $pmda->replace_indom($sys_seq_indom, \@sys_seq_instances); +} + +sub refresh_io_user_sequences +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @user_seq_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $user_seq_instances[($i*2)] = $result->[$i][0]; + $user_seq_instances[($i*2)+1] = $result->[$i][2]; + $table{values}{$instid} = $result->[$i]; + } + } + $pmda->replace_indom($user_seq_indom, \@user_seq_instances); +} + +# +# Setup routines - one per cluster, add metrics to PMDA +# +# For help text, see +# http://www.postgresql.org/docs/9.2/static/monitoring-stats.html +# +# and the Postgres Licence: +# Portions Copyright (c) 1996-2012, The PostgreSQL Global Development Group +# +# Portions Copyright (c) 1994, The Regents of the University of California +# +# Permission to use, copy, modify, and distribute this software +# and its documentation for any purpose, without fee, and without a +# written agreement is hereby granted, provided that the above copyright +# notice and this paragraph and the following two paragraphs appear in +# all copies. +# +# IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY +# FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, +# INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND +# ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY +# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE +# PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY +# OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, +# UPDATES, ENHANCEMENTS, OR MODIFICATIONS +# + +sub setup_activity +{ + my ($cluster, $indom) = @_; + + # indom: procpid + application_name + $pmda->add_metric(pmda_pmid($cluster,0), PM_TYPE_U32, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.activity.datid', + 'OID of the database this backend is connected to', ''); + $pmda->add_metric(pmda_pmid($cluster,1), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.activity.datname', + 'Name of the database this backend is connected to', ''); + $pmda->add_metric(pmda_pmid($cluster,3), PM_TYPE_U32, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.activity.usesysid', + 'OID of the user logged into this backend', ''); + $pmda->add_metric(pmda_pmid($cluster,4), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.activity.usename', + 'Name of the user logged into this backend', ''); + $pmda->add_metric(pmda_pmid($cluster,5), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.activity.application_name', + 'Name of the application that is connected to this backend', ''); + $pmda->add_metric(pmda_pmid($cluster,6), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.activity.client_addr', + 'Client IP address', +'IP address of the client connected to this backend. If this field is null, +it indicates either that the client is connected via a Unix socket on the +server machine or that this is an internal process such as autovacuum.'); + $pmda->add_metric(pmda_pmid($cluster,7), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.activity.client_hostname', + 'Host name of the connected client', +'Client host name is derived from reverse DNS lookup of +postgresql.stat.activity.client_addr. This field will only be non-null +for IP connections, and only when log_hostname is enabled.'); + $pmda->add_metric(pmda_pmid($cluster,8), PM_TYPE_U32, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.activity.client_port', + 'Client TCP port number', +'TCP port number that the client is using for communication with this +backend. Value will be -1 if a Unix socket is used.'); + $pmda->add_metric(pmda_pmid($cluster,9), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.activity.backend_start', + 'Time when this process was started', +'Time when this client process connected to the server.'); + $pmda->add_metric(pmda_pmid($cluster,10), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.activity.xact_start', + 'Transaction start time', +'Time when this process' . "'" . ' current transaction was started. +The value will be null if no transaction is active. If the +current query is the first of its transaction, value is equal to +postgresql.stat.activity.query_start.'); + $pmda->add_metric(pmda_pmid($cluster,11), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.activity.query_start', + 'Query start time', +'Time when the currently active query was started, or if state is not +active, when the last query was started'); + $pmda->add_metric(pmda_pmid($cluster,12), PM_TYPE_32, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.activity.waiting', + 'True if this backend is currently waiting on a lock', ''); + $pmda->add_metric(pmda_pmid($cluster,13), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.activity.current_query', + 'Most recent query', +'Text of this backend' . "'" . 's most recent query. If state is active this field +shows the currently executing query. In all other states, it shows the +last query that was executed.'); +} + +sub setup_replication +{ + my ($cluster, $indom) = @_; + + # indom: procpid + application_name + $pmda->add_metric(pmda_pmid($cluster,1), PM_TYPE_U32, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.replication.usesysid', + 'OID of the user logged into this WAL sender process', ''); + $pmda->add_metric(pmda_pmid($cluster,2), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.replication.usename', + 'Name of the user logged into this WAL sender process', ''); + $pmda->add_metric(pmda_pmid($cluster,3), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.replication.application_name', + 'Name of the application that is connected to this WAL sender', ''); + $pmda->add_metric(pmda_pmid($cluster,4), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.replication.client_addr', + 'WAL client IP address', +'IP address of the client connected to this WAL sender. If this field is +null, it indicates that the client is connected via a Unix socket on the +server machine.'); + $pmda->add_metric(pmda_pmid($cluster,5), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.replication.client_hostname', + 'WAL client host name', +'Host name of the connected client, as reported by a reverse DNS lookup of +postgresql.stat.replication.client_addr. This field will only be non-null +for IP connections, and only when log_hostname is enabled.'); + $pmda->add_metric(pmda_pmid($cluster,6), PM_TYPE_U32, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.replication.client_port', + 'WAL client TCP port', +'TCP port number that the client is using for communication with this WAL +sender, or -1 if a Unix socket is used.'); + $pmda->add_metric(pmda_pmid($cluster,7), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.replication.backend_start', + 'Time when when the client connected to this WAL sender', ''); + $pmda->add_metric(pmda_pmid($cluster,8), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.replication.state', + 'Current WAL sender state', ''); + $pmda->add_metric(pmda_pmid($cluster,9), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.replication.sent_location', + 'Last transaction log position sent on this connection', ''); + $pmda->add_metric(pmda_pmid($cluster,10), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.replication.write_location', + 'Last transaction log position written to disk by this standby server', ''); + $pmda->add_metric(pmda_pmid($cluster,11), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.replication.flush_location', + 'Last transaction log position flushed to disk by this standby server', ''); + $pmda->add_metric(pmda_pmid($cluster,12), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.replication.replay_location', + 'Last transaction log position replayed into the database on this standby server', ''); + $pmda->add_metric(pmda_pmid($cluster,13), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.replication.sync_priority', + 'Priority of this standby server for being chosen as the synchronous standby', ''); + $pmda->add_metric(pmda_pmid($cluster,14), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.replication.sync_state', + 'Synchronous state of this standby server', ''); +} + +sub setup_stat_xact_tables +{ + my ($cluster, $indom, $tables) = @_; + + # indom: relid + relname + $pmda->add_metric(pmda_pmid($cluster,1), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + "postgresql.stat.xact.$tables.schemaname", + 'Name of the schema that this table is in', +''); + $pmda->add_metric(pmda_pmid($cluster,3), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + "postgresql.stat.xact.$tables.seq_scan", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,4), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + "postgresql.stat.xact.$tables.seq_tup_read", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,5), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + "postgresql.stat.xact.$tables.idx_scan", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,6), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + "postgresql.stat.xact.$tables.idx_tup_fetch", + 'Number of rows fetched by queries in this database', ''); + $pmda->add_metric(pmda_pmid($cluster,7), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + "postgresql.stat.xact.$tables.n_tup_ins", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,8), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + "postgresql.stat.xact.$tables.n_tup_upd", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,9), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + "postgresql.stat.xact.$tables.n_tup_del", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,10), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + "postgresql.stat.xact.$tables.n_tup_hot_upd", + '', ''); +} + +sub setup_stat_tables +{ + my ($cluster, $indom, $tables) = @_; + + # indom: relid + relname + $pmda->add_metric(pmda_pmid($cluster,1), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + "postgresql.stat.$tables.schemaname", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,3), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + "postgresql.stat.$tables.seq_scan", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,4), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + "postgresql.stat.$tables.seq_tup_read", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,5), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + "postgresql.stat.$tables.idx_scan", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,6), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + "postgresql.stat.$tables.idx_tup_fetch", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,7), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + "postgresql.stat.$tables.n_tup_ins", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,8), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + "postgresql.stat.$tables.n_tup_upd", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,9), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + "postgresql.stat.$tables.n_tup_del", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,10), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + "postgresql.stat.$tables.n_tup_hot_upd", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,11), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + "postgresql.stat.$tables.n_live_tup", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,12), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + "postgresql.stat.$tables.n_dead_tup", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,13), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + "postgresql.stat.$tables.last_vacuum", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,14), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + "postgresql.stat.$tables.last_autovacuum", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,15), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + "postgresql.stat.$tables.last_analyze", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,16), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + "postgresql.stat.$tables.last_autoanalyze", + '', ''); +} + +sub setup_bgwriter +{ + my ($cluster, $indom) = @_; + + $pmda->add_metric(pmda_pmid($cluster,0), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.bgwriter.checkpoints_timed', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,1), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.bgwriter.checkpoints_req', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,2), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.bgwriter.buffers_checkpoints', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,3), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.bgwriter.buffers_clean', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,4), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.bgwriter.maxwritten_clean', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,5), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.bgwriter.buffers_backend', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,6), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.bgwriter.buffers_alloc', + '', ''); +} + +sub setup_active_functions +{ + my ($cluster, $indom) = @_; + + $pmda->add_metric(pmda_pmid($cluster,0), PM_TYPE_U32, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.active.is_in_recovery', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,1), PM_TYPE_U64, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.active.xlog_current_location_log_id', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,2), PM_TYPE_U64, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.active.xlog_current_location_offset', + '', ''); +} + +sub setup_recovery_functions +{ + my ($cluster, $indom) = @_; + + $pmda->add_metric(pmda_pmid($cluster,0), PM_TYPE_U32, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.recovery.is_in_recovery', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,1), PM_TYPE_U64, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.recovery.xlog_receive_location_log_id', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,2), PM_TYPE_U64, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.recovery.xlog_receive_location_offset', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,3), PM_TYPE_U64, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.recovery.xlog_replay_location_log_id', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,4), PM_TYPE_U64, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.recovery.xlog_replay_location_offset', + '', ''); +} + +sub setup_database_conflicts +{ + my ($cluster, $indom) = @_; + + # indom: datid + datname + $pmda->add_metric(pmda_pmid($cluster,2), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.database_conflicts.tablespace', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,3), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.database_conflicts.lock', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,4), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.database_conflicts.snapshot', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,5), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.database_conflicts.bufferpin', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,6), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.database_conflicts.deadlock', + '', ''); + +} +sub setup_database +{ + my ($cluster, $indom) = @_; + + # indom: datid + datname + $pmda->add_metric(pmda_pmid($cluster,2), PM_TYPE_U32, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.database.numbackends', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,3), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.database.xact_commit', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,4), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.database.xact_rollback', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,5), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.database.blks_read', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,6), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.database.blks_hit', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,7), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.database.tup_returned', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,8), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.database.tup_fetched', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,9), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.database.tup_inserted', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,10), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.database.tup_updated', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,11), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.database.tup_deleted', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,12), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.database.conflicts', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,13), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.database.stats_reset', + '', ''); +} + +sub setup_stat_indexes +{ + my ($cluster, $indom, $indexes) = @_; + + $pmda->add_metric(pmda_pmid($cluster,0), PM_TYPE_32, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + "postgresql.stat.$indexes.relid", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,2), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + "postgresql.stat.$indexes.schemaname", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,3), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + "postgresql.stat.$indexes.relname", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,5), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "postgresql.stat.$indexes.idx_scan", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,6), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "postgresql.stat.$indexes.idx_tup_read", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,7), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "postgresql.stat.$indexes.idx_tup_fetch", + '', ''); +} + +sub setup_statio_tables +{ + my ($cluster, $indom, $tables) = @_; + + $pmda->add_metric(pmda_pmid($cluster,1), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + "postgresql.statio.$tables.schemaname", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,3), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "postgresql.statio.$tables.heap_blks_read", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,4), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "postgresql.statio.$tables.heap_blks_hit", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,5), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "postgresql.statio.$tables.idx_blks_read", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,6), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "postgresql.statio.$tables.idx_blks_hit", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,7), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "postgresql.statio.$tables.toast_blks_read", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,8), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "postgresql.statio.$tables.toast_blks_hit", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,9), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "postgresql.statio.$tables.tidx_blks_read", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,10), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "postgresql.statio.$tables.tidx_blks_hit", + '', ''); +} + +sub setup_statio_indexes +{ + my ($cluster, $indom, $indexes) = @_; + + # indom: indexrelid + indexrelname + $pmda->add_metric(pmda_pmid($cluster,0), PM_TYPE_32, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + "postgresql.statio.$indexes.relid", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,2), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + "postgresql.statio.$indexes.schemaname", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,4), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + "postgresql.statio.$indexes.relname", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,5), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "postgresql.statio.$indexes.idx_blks_read", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,6), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "postgresql.statio.$indexes.idx_blks_hit", + '', ''); +} + +sub setup_statio_sequences +{ + my ($cluster, $indom, $sequences) = @_; + + # indom: relid + relname + $pmda->add_metric(pmda_pmid($cluster,1), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + "postgresql.statio.$sequences.schemaname", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,3), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "postgresql.statio.$sequences.blks_read", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,4), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "postgresql.statio.$sequences.blks_hit", + '', ''); +} + +sub setup_xact_user_functions +{ + my ($cluster, $indom) = @_; + + # indom: funcid + funcname + $pmda->add_metric(pmda_pmid($cluster,1), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.xact.user_functions.schemaname', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,3), PM_TYPE_U64, $indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.xact.user_functions.calls', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,4), PM_TYPE_U64, $indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'postgresql.stat.xact.user_functions.total_time', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,5), PM_TYPE_U64, $indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'postgresql.stat.xact.user_functions.self_time', + '', ''); +} + +sub setup_user_functions +{ + my ($cluster, $indom) = @_; + + # indom: funcid + funcname + $pmda->add_metric(pmda_pmid($cluster,1), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.user_functions.schemaname', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,3), PM_TYPE_U64, $indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.user_functions.calls', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,4), PM_TYPE_U64, $indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'postgresql.stat.user_functions.total_time', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,5), PM_TYPE_U64, $indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'postgresql.stat.user_functions.self_time', + '', ''); +} + +# +# Main PMDA thread of execution starts here - setup metrics and callbacks +# drop root priveleges, then call PMDA 'run' routine to talk to pmcd. +# +$pmda = PCP::PMDA->new('postgresql', 110); +postgresql_metrics_setup(); +postgresql_indoms_setup(); + +$pmda->set_fetch_callback(\&postgresql_fetch_callback); +$pmda->set_fetch(\&postgresql_connection_setup); +$pmda->set_refresh(\&postgresql_refresh); +$pmda->set_user('postgres'); +$pmda->run; + +=pod + +=head1 NAME + +pmdapostgresql - PostgreSQL database PMDA + +=head1 DESCRIPTION + +B<pmdapostgresql> is a Performance Co-Pilot PMDA which extracts +live performance data from a running PostgreSQL database. + +=head1 INSTALLATION + +B<pmdapostgresql> uses a configuration file from (in this order): + +=over + +=item * /etc/pcpdbi.conf + +=item * $PCP_PMDAS_DIR/postgresql/postgresql.conf + +=back + +This file can contain overridden values (Perl code) for the settings +listed at the start of pmdapostgresql.pl, namely: + +=over + +=item * database name (see DBI(3) for details) + +=item * database username + +=back + +Once this is setup, you can access the names and values for the +postgresql performance metrics by doing the following as root: + + # cd $PCP_PMDAS_DIR/postgresql + # ./Install + +If you want to undo the installation, do the following as root: + + # cd $PCP_PMDAS_DIR/postgresql + # ./Remove + +B<pmdapostgresql> 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 /etc/pcpdbi.conf + +configuration file for all PCP database monitors + +=item $PCP_PMDAS_DIR/postgresql/postgresql.conf + +configuration file for B<pmdapostgresql> + +=item $PCP_PMDAS_DIR/postgresql/Install + +installation script for the B<pmdapostgresql> agent + +=item $PCP_PMDAS_DIR/postgresql/Remove + +undo installation script for the B<pmdapostgresql> agent + +=item $PCP_LOG_DIR/pmcd/postgresql.log + +default log file for error messages from B<pmdapostgresql> + +=back + +=head1 SEE ALSO + +pmcd(1), pmdadbping.pl(1) and DBI(3). diff --git a/src/pmdas/postgresql/pmlogconf.summary b/src/pmdas/postgresql/pmlogconf.summary new file mode 100644 index 0000000..d278918 --- /dev/null +++ b/src/pmdas/postgresql/pmlogconf.summary @@ -0,0 +1,8 @@ +#pmlogconf-setup 2.0 +ident postgresql summary information +probe postgresql.stat.all_tables.seq_scan ? include : exclude + postgresql.stat.database + postgresql.stat.all_tables + postgresql.stat.all_indexes + postgresql.statio.all_tables + postgresql.statio.all_indexes |