From c78d1af7827e48835e4c827cbbae4d04dab54c1d Mon Sep 17 00:00:00 2001 From: Artem Gavrilov Date: Wed, 5 Jun 2024 19:40:59 +0200 Subject: [PATCH] Add stats_since and minmax_stats_since fields --- hash_query.c | 2 + pg_stat_monitor--2.0--2.1.sql | 10 +++- pg_stat_monitor.c | 18 +++++-- pg_stat_monitor.h | 12 +++-- t/018_column_names.pl | 8 ++-- t/033_stats_since.pl | 89 +++++++++++++++++++++++++++++++++++ 6 files changed, 123 insertions(+), 16 deletions(-) create mode 100644 t/033_stats_since.pl diff --git a/hash_query.c b/hash_query.c index ff34a4c..7383de3 100644 --- a/hash_query.c +++ b/hash_query.c @@ -303,6 +303,8 @@ hash_entry_alloc(pgsmSharedState * pgsm, pgsmHashKey * key, int encoding) memset(&entry->counters, 0, sizeof(Counters)); entry->query_text.query_pos = InvalidDsaPointer; entry->counters.info.parent_query = InvalidDsaPointer; + entry->stats_since = GetCurrentTimestamp(); + entry->minmax_stats_since = entry->stats_since; /* set the appropriate initial usage count */ /* re-initialize the mutex each time ... we assume no one using it */ diff --git a/pg_stat_monitor--2.0--2.1.sql b/pg_stat_monitor--2.0--2.1.sql index a28adac..0ee2a46 100644 --- a/pg_stat_monitor--2.0--2.1.sql +++ b/pg_stat_monitor--2.0--2.1.sql @@ -89,7 +89,10 @@ CREATE FUNCTION pg_stat_monitor_internal( OUT jit_deform_count int8, OUT jit_deform_time float8, - OUT toplevel BOOLEAN, --66 + OUT stats_since timestamp with time zone, -- 66 + OUT minmax_stats_since timestamp with time zone, + + OUT toplevel BOOLEAN, -- 68 OUT bucket_done BOOLEAN ) RETURNS SETOF record @@ -439,7 +442,10 @@ CREATE VIEW pg_stat_monitor AS SELECT jit_emission_count, jit_emission_time, jit_deform_count, - jit_deform_time + jit_deform_time, + + stats_since, + minmax_stats_since FROM pg_stat_monitor_internal(TRUE) ORDER BY bucket_start_time; diff --git a/pg_stat_monitor.c b/pg_stat_monitor.c index 616fa2b..993d963 100644 --- a/pg_stat_monitor.c +++ b/pg_stat_monitor.c @@ -42,7 +42,7 @@ PG_MODULE_MAGIC; /* Number of output arguments (columns) for various API versions */ #define PG_STAT_MONITOR_COLS_V1_0 52 #define PG_STAT_MONITOR_COLS_V2_0 64 -#define PG_STAT_MONITOR_COLS_V2_1 68 //TODO !!!!!!! +#define PG_STAT_MONITOR_COLS_V2_1 70 #define PG_STAT_MONITOR_COLS PG_STAT_MONITOR_COLS_V2_0 /* maximum of above */ #define PGSM_TEXT_FILE PGSTAT_STAT_PERMANENT_DIRECTORY "pg_stat_monitor_query" @@ -1361,9 +1361,12 @@ pgsm_update_entry(pgsmEntry * entry, int sqlcode_len = error_info ? strlen(error_info->sqlcode) : 0; int plan_text_len = plan_info ? plan_info->plan_len : 0; - /* Start collecting data for next bucket and reset all counters */ - if (reset) + /* Start collecting data for next bucket and reset all counters and timestamps */ + if (reset) { memset(&entry->counters, 0, sizeof(Counters)); + entry->stats_since = GetCurrentTimestamp(); + entry->minmax_stats_since = entry->stats_since; + } /* volatile block */ { @@ -2475,10 +2478,15 @@ pg_stat_monitor_internal(FunctionCallInfo fcinfo, values[i++] = Int64GetDatumFast(tmp.jitinfo.jit_deform_count); values[i++] = Float8GetDatumFast(tmp.jitinfo.jit_deform_time); } - /* toplevel at column number 64 */ + + /* at column number 64 */ + values[i++] = TimestampTzGetDatum(entry->stats_since); + values[i++] = TimestampTzGetDatum(entry->minmax_stats_since); + + /* toplevel at column number 66 */ values[i++] = BoolGetDatum(toplevel); - /* bucket_done at column number 65 */ + /* bucket_done at column number 67 */ values[i++] = BoolGetDatum(pg_atomic_read_u64(&pgsm->current_wbucket) != bucketid); /* clean up and return the tuplestore */ diff --git a/pg_stat_monitor.h b/pg_stat_monitor.h index 69e7af5..1658852 100644 --- a/pg_stat_monitor.h +++ b/pg_stat_monitor.h @@ -370,13 +370,15 @@ typedef struct Counters */ typedef struct pgsmEntry { - pgsmHashKey key; /* hash key of entry - MUST BE FIRST */ - uint64 pgsm_query_id; /* pgsm generate normalized query hash */ + pgsmHashKey key; /* hash key of entry - MUST BE FIRST */ + uint64 pgsm_query_id; /* pgsm generate normalized query hash */ char datname[NAMEDATALEN]; /* database name */ char username[NAMEDATALEN]; /* user name */ - Counters counters; /* the statistics for this query */ - int encoding; /* query text encoding */ - slock_t mutex; /* protects the counters only */ + Counters counters; /* the statistics for this query */ + int encoding; /* query text encoding */ + TimestampTz stats_since; /* timestamp of entry allocation */ + TimestampTz minmax_stats_since; /* timestamp of last min/max values reset */ + slock_t mutex; /* protects the counters only */ union { dsa_pointer query_pos; /* query location within query buffer */ diff --git a/t/018_column_names.pl b/t/018_column_names.pl index 70360d6..80eb037 100644 --- a/t/018_column_names.pl +++ b/t/018_column_names.pl @@ -30,11 +30,11 @@ my %pg_versions_pgsm_columns = ( 17 => "application_name,". "jit_inlining_count,jit_inlining_time,jit_optimization_count,jit_optimization_time," . "local_blk_read_time,local_blk_write_time,local_blks_dirtied,local_blks_hit,". "local_blks_read,local_blks_written,max_exec_time,max_plan_time,mean_exec_time," . - "mean_plan_time,message,min_exec_time,min_plan_time,pgsm_query_id,planid," . - "plans,query,query_plan,queryid,relations,resp_calls,rows," . + "mean_plan_time,message,min_exec_time,min_plan_time,minmax_stats_since," . + "pgsm_query_id,planid,plans,query,query_plan,queryid,relations,resp_calls,rows," . "shared_blk_read_time,shared_blk_write_time,shared_blks_dirtied," . - "shared_blks_hit,shared_blks_read,shared_blks_written," . - "sqlcode,stddev_exec_time,stddev_plan_time,temp_blk_read_time,temp_blk_write_time," . + "shared_blks_hit,shared_blks_read,shared_blks_written,sqlcode,stats_since," . + "stddev_exec_time,stddev_plan_time,temp_blk_read_time,temp_blk_write_time," . "temp_blks_read,temp_blks_written,top_query,top_queryid,toplevel," . "total_exec_time,total_plan_time,userid,username,wal_bytes,wal_fpi,wal_records", 16 => "application_name,blk_read_time," . diff --git a/t/033_stats_since.pl b/t/033_stats_since.pl new file mode 100644 index 0000000..e151886 --- /dev/null +++ b/t/033_stats_since.pl @@ -0,0 +1,89 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use File::Basename; +use File::Compare; +use File::Copy; +use Text::Trim qw(trim); +use Test::More; +use lib 't'; +use pgsm; + +# Get filename and create out file name and dirs where requried +PGSM::setup_files_dir(basename($0)); + +if ($PGSM::PG_MAJOR_VERSION <= 16) +{ + plan skip_all => "pg_stat_monitor test cases for versions 16 and below."; +} + +# Create new PostgreSQL node and do initdb +my $node = PGSM->pgsm_init_pg(); +my $pgdata = $node->data_dir; + +# Update postgresql.conf to include/load pg_stat_monitor library +$node->append_conf('postgresql.conf', "shared_preload_libraries = 'pg_stat_monitor'"); + +# Set change postgresql.conf for this test case. +$node->append_conf('postgresql.conf', "pg_stat_monitor.pgsm_bucket_time = 1"); +$node->append_conf('postgresql.conf', "pg_stat_monitor.pgsm_max_buckets = 3"); +$node->append_conf('postgresql.conf', "pg_stat_monitor.pgsm_normalized_query = yes"); +$node->append_conf('postgresql.conf', "pg_stat_monitor.pgsm_track = 'all'"); + +# Start server +my $rt_value = $node->start; +ok($rt_value == 1, "Start Server"); + +# Create EXTENSION and change out file permissions +my ($cmdret, $stdout, $stderr) = $node->psql('postgres', 'CREATE EXTENSION pg_stat_monitor;', extra_params => ['-a']); +ok($cmdret == 0, "Create PGSM EXTENSION"); +PGSM::append_to_debug_file($stdout); + +# Run 'SELECT pg_stat_monitor settings' and dump output to out file +($cmdret, $stdout, $stderr) = $node->psql('postgres', "SELECT name, setting, unit, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, pending_restart FROM pg_settings WHERE name LIKE '%pg_stat_monitor%';", extra_params => ['-a', '-Pformat=aligned','-Ptuples_only=off']); +ok($cmdret == 0, "Print PGSM EXTENSION Settings"); +PGSM::append_to_debug_file($stdout); + +# Run required commands/queries and dump output to out file. +($cmdret, $stdout, $stderr) = $node->psql('postgres', 'SELECT pg_stat_monitor_reset();', extra_params => ['-a', '-Pformat=aligned','-Ptuples_only=off']); +ok($cmdret == 0, "Reset PGSM EXTENSION"); +PGSM::append_to_debug_file($stdout); + +($cmdret, $stdout, $stderr) = $node->psql('postgres', 'SELECT pg_sleep(1);', extra_params => ['-a', '-Pformat=aligned','-Ptuples_only=off']); +ok($cmdret == 0, "1 - Run pg_sleep(1)"); +PGSM::append_to_debug_file($stdout); + +($cmdret, $stdout, $stderr) = $node->psql('postgres', 'SELECT pg_sleep(1);', extra_params => ['-a', '-Pformat=aligned','-Ptuples_only=off']); +ok($cmdret == 0, "2 - Run pg_sleep(1)"); +PGSM::append_to_debug_file($stdout); + +($cmdret, $stdout, $stderr) = $node->psql('postgres', 'SELECT pg_sleep(1);', extra_params => ['-a', '-Pformat=aligned','-Ptuples_only=off']); +ok($cmdret == 0, "3 - Run pg_sleep(1)"); +PGSM::append_to_debug_file($stdout); + +# Check that we have more than one bucket +($cmdret, $stdout, $stderr) = $node->psql('postgres', "SELECT COUNT(bucket) != 0 AS PGSM FROM pg_stat_monitor WHERE query LIKE '%sleep%';", extra_params => ['-t', '-Pformat=unaligned','-Ptuples_only=on']); +trim($stdout); +is($stdout,'t',"Check: we have more that one bucket"); + +# Check that stats timestamps are different for each query/bucket +($cmdret, $stdout, $stderr) = $node->psql('postgres', "SELECT COUNT(DISTINCT stats_since) = COUNT(stats_since) AS PGSM FROM pg_stat_monitor WHERE query LIKE '%sleep%';", extra_params => ['-t', '-Pformat=unaligned','-Ptuples_only=on']); +trim($stdout); +is($stdout,'t',"Check: for every bucket stats_since should be unique"); + +# Check that minmax_stats_since always match stats_since +($cmdret, $stdout, $stderr) = $node->psql('postgres', "SELECT COUNT(*) AS ST FROM pg_stat_monitor WHERE query LIKE '%sleep%' AND stats_since != minmax_stats_since;", extra_params => ['-a', '-Pformat=aligned','-Ptuples_only=off']); +ok($stdout == 0, "Compare: Calls count is 1"); +PGSM::append_to_debug_file($stdout); + +# DROP EXTENSION +$stdout = $node->safe_psql('postgres', 'DROP EXTENSION pg_stat_monitor;', extra_params => ['-a']); +ok($cmdret == 0, "DROP PGSM EXTENSION"); +PGSM::append_to_debug_file($stdout); + +# Stop the server +$node->stop; + +# Done testing for this testcase file. +done_testing();