提交 e6400930 编写于 作者: A Alvaro Herrera

Add POD docs to PostgresNode

Also, the dump_info method got split into another method that returns
the stuff as a string instead of just printing it to stdout.

Add a new README in src/test/perl too.

Author: Craig Ringer
Reviewed by: Michaël Paquier
上级 bda0b081
# PostgresNode, class representing a data directory and postmaster.
#
# This contains a basic set of routines able to work on a PostgreSQL node,
# allowing to start, stop, backup and initialize it with various options.
# The set of nodes managed by a given test is also managed by this module.
=pod
=head1 NAME
PostgresNode - class representing PostgreSQL server instance
=head1 SYNOPSIS
use PostgresNode;
my $node = get_new_node('mynode');
# Create a data directory with initdb
$node->init();
# Start the PostgreSQL server
$node->start();
# Change a setting and restart
$node->append_conf('postgresql.conf', 'hot_standby = on');
$node->restart('fast');
# run a query with psql
# like: psql -qAXt postgres -c 'SELECT 1;'
$psql_stdout = $node->psql('postgres', 'SELECT 1');
# run query every second until it returns 't'
# or times out
$node->poll_query_until('postgres', q|SELECT random() < 0.1;|')
or print "timed out";
# Do an online pg_basebackup
my $ret = $node->backup('testbackup');
# Restore it to create a new independent node (not a replica)
my $replica = get_new_node('replica');
$replica->init_from_backup($node, 'testbackup');
$replica->start;
# Stop the server
$node->stop('fast');
=head1 DESCRIPTION
PostgresNode contains a set of routines able to work on a PostgreSQL node,
allowing to start, stop, backup and initialize it with various options.
The set of nodes managed by a given test is also managed by this module.
In addition to node management, PostgresNode instances have some wrappers
around Test::More functions to run commands with an envronment set up to
point to the instance.
The IPC::Run module is required.
=cut
package PostgresNode;
......@@ -40,6 +90,21 @@ INIT
$last_port_assigned = int(rand() * 16384) + 49152;
}
=pod
=head1 METHODS
=over
=item PostgresNode::new($class, $name, $pghost, $pgport)
Create a new PostgresNode instance. Does not initdb or start it.
You should generally prefer to use get_new_node() instead since it takes care
of finding port numbers, registering instances for cleanup, etc.
=cut
sub new
{
my $class = shift;
......@@ -61,36 +126,91 @@ sub new
return $self;
}
=pod
=item $node->port()
Get the port number assigned to the host. This won't necessarily be a TCP port
open on the local host since we prefer to use unix sockets if possible.
Use $node->connstr() if you want a connection string.
=cut
sub port
{
my ($self) = @_;
return $self->{_port};
}
=pod
=item $node->host()
Return the host (like PGHOST) for this instance. May be a UNIX socket path.
Use $node->connstr() if you want a connection string.
=cut
sub host
{
my ($self) = @_;
return $self->{_host};
}
=pod
=item $node->basedir()
The directory all the node's files will be within - datadir, archive directory,
backups, etc.
=cut
sub basedir
{
my ($self) = @_;
return $self->{_basedir};
}
=pod
=item $node->name()
The name assigned to the node at creation time.
=cut
sub name
{
my ($self) = @_;
return $self->{_name};
}
=pod
=item $node->logfile()
Path to the PostgreSQL log file for this instance.
=cut
sub logfile
{
my ($self) = @_;
return $self->{_logfile};
}
=pod
=item $node->connstr()
Get a libpq connection string that will establish a connection to
this node. Suitable for passing to psql, DBD::Pg, etc.
=cut
sub connstr
{
my ($self, $dbname) = @_;
......@@ -103,6 +223,15 @@ sub connstr
return "port=$pgport host=$pghost dbname=$dbname";
}
=pod
=item $node->data_dir()
Returns the path to the data directory. postgresql.conf and pg_hba.conf are
always here.
=cut
sub data_dir
{
my ($self) = @_;
......@@ -110,6 +239,14 @@ sub data_dir
return "$res/pgdata";
}
=pod
=item $node->archive_dir()
If archiving is enabled, WAL files go here.
=cut
sub archive_dir
{
my ($self) = @_;
......@@ -117,6 +254,14 @@ sub archive_dir
return "$basedir/archives";
}
=pod
=item $node->backup_dir()
The output path for backups taken with $node->backup()
=cut
sub backup_dir
{
my ($self) = @_;
......@@ -124,23 +269,55 @@ sub backup_dir
return "$basedir/backup";
}
# Dump node information
=pod
=item $node->info()
Return a string containing human-readable diagnostic information (paths, etc)
about this node.
=cut
sub info
{
my ($self) = @_;
my $_info = '';
open my $fh, '>', \$_info or die;
print $fh "Name: " . $self->name . "\n";
print $fh "Data directory: " . $self->data_dir . "\n";
print $fh "Backup directory: " . $self->backup_dir . "\n";
print $fh "Archive directory: " . $self->archive_dir . "\n";
print $fh "Connection string: " . $self->connstr . "\n";
print $fh "Log file: " . $self->logfile . "\n";
close $fh or die;
return $_info;
}
=pod
=item $node->dump_info()
Print $node->info()
=cut
sub dump_info
{
my ($self) = @_;
print "Name: " . $self->name . "\n";
print "Data directory: " . $self->data_dir . "\n";
print "Backup directory: " . $self->backup_dir . "\n";
print "Archive directory: " . $self->archive_dir . "\n";
print "Connection string: " . $self->connstr . "\n";
print "Log file: " . $self->logfile . "\n";
print $self->info;
}
# Internal method to set up trusted pg_hba.conf for replication. Not
# documented because you shouldn't use it, it's called automatically if needed.
sub set_replication_conf
{
my ($self) = @_;
my $pgdata = $self->data_dir;
$self->host eq $test_pghost
or die "set_replication_conf only works with the default host";
open my $hba, ">>$pgdata/pg_hba.conf";
print $hba "\n# Allow replication (set up by PostgresNode.pm)\n";
if (!$TestLib::windows_os)
......@@ -155,13 +332,26 @@ sub set_replication_conf
close $hba;
}
# Initialize a new cluster for testing.
#
# Authentication is set up so that only the current OS user can access the
# cluster. On Unix, we use Unix domain socket connections, with the socket in
# a directory that's only accessible to the current user to ensure that.
# On Windows, we use SSPI authentication to ensure the same (by pg_regress
# --config-auth).
=pod
=item $node->init(...)
Initialize a new cluster for testing.
Authentication is set up so that only the current OS user can access the
cluster. On Unix, we use Unix domain socket connections, with the socket in
a directory that's only accessible to the current user to ensure that.
On Windows, we use SSPI authentication to ensure the same (by pg_regress
--config-auth).
pg_hba.conf is configured to allow replication connections. Pass the keyword
parameter hba_permit_replication => 0 to disable this.
The new node is set up in a fast but unsafe configuration where fsync is
disabled.
=cut
sub init
{
my ($self, %params) = @_;
......@@ -197,6 +387,19 @@ sub init
$self->set_replication_conf if ($params{hba_permit_replication});
}
=pod
=item $node->append_conf(filename, str)
A shortcut method to append to files like pg_hba.conf and postgresql.conf.
Does no validation or sanity checking. Does not reload the configuration
after writing.
A newline is NOT automatically appended to the string.
=cut
sub append_conf
{
my ($self, $filename, $str) = @_;
......@@ -206,6 +409,19 @@ sub append_conf
TestLib::append_to_file($conffile, $str);
}
=pod
=item $node->backup(backup_name)
Create a hot backup with pg_basebackup in $node->backup_dir,
including the transaction logs. xlogs are fetched at the
end of the backup, not streamed.
You'll have to configure a suitable max_wal_senders on the
target server since it isn't done by default.
=cut
sub backup
{
my ($self, $backup_name) = @_;
......@@ -218,6 +434,23 @@ sub backup
print "# Backup finished\n";
}
=pod
=item $node->init_from_backup(root_node, backup_name)
Initialize a node from a backup, which may come from this node or a different
node. root_node must be a PostgresNode reference, backup_name the string name
of a backup previously created on that node with $node->backup.
Does not start the node after init.
A recovery.conf is not created.
The backup is copied, leaving the original unmodified. pg_hba.conf is
unconditionally set to enable replication connections.
=cut
sub init_from_backup
{
my ($self, $root_node, $backup_name) = @_;
......@@ -248,6 +481,16 @@ port = $port
$self->set_replication_conf;
}
=pod
=item $node->start()
Wrapper for pg_ctl -w start
Start the node and wait until it is ready to accept connections.
=cut
sub start
{
my ($self) = @_;
......@@ -268,6 +511,14 @@ sub start
$self->_update_pid;
}
=pod
=item $node->stop(mode)
Stop the node using pg_ctl -m $mode and wait for it to stop.
=cut
sub stop
{
my ($self, $mode) = @_;
......@@ -281,6 +532,14 @@ sub stop
$self->_update_pid;
}
=pod
=item $node->restart()
wrapper for pg_ctl -w restart
=cut
sub restart
{
my ($self) = @_;
......@@ -294,6 +553,8 @@ sub restart
$self->_update_pid;
}
# Internal method
sub _update_pid
{
my $self = shift;
......@@ -314,14 +575,20 @@ sub _update_pid
print "# No postmaster PID\n";
}
#
# Cluster management functions
#
=pod
=item get_new_node(node_name)
Build a new PostgresNode object, assigning a free port number. Standalone
function that's automatically imported.
We also register the node, to avoid the port number from being reused
for another node even when this one is not active.
You should generally use this instead of PostgresNode::new(...).
=cut
# Build a new PostgresNode object, assigning a free port number.
#
# We also register the node, to avoid the port number from being reused
# for another node even when this one is not active.
sub get_new_node
{
my $name = shift;
......@@ -360,6 +627,7 @@ sub get_new_node
return $node;
}
# Attempt automatic cleanup
sub DESTROY
{
my $self = shift;
......@@ -369,6 +637,14 @@ sub DESTROY
TestLib::system_log('pg_ctl', 'kill', 'QUIT', $self->{_pid});
}
=pod
=item $node->teardown_node()
Do an immediate stop of the node
=cut
sub teardown_node
{
my $self = shift;
......@@ -376,6 +652,18 @@ sub teardown_node
$self->stop('immediate');
}
=pod
=item $node->psql(dbname, sql)
Run a query with psql and return stdout, or on error print stderr.
Executes a query/script with psql and returns psql's standard output. psql is
run in unaligned tuples-only quiet mode with psqlrc disabled so simple queries
will just return the result row(s) with fields separated by commas.
=cut
sub psql
{
my ($self, $dbname, $sql) = @_;
......@@ -399,7 +687,15 @@ sub psql
return $stdout;
}
# Run a query once a second, until it returns 't' (i.e. SQL boolean true).
=pod
=item $node->poll_query_until(dbname, query)
Run a query once a second, until it returns 't' (i.e. SQL boolean true).
Continues polling if psql returns an error result. Times out after 90 seconds.
=cut
sub poll_query_until
{
my ($self, $dbname, $query) = @_;
......@@ -432,6 +728,16 @@ sub poll_query_until
return 0;
}
=pod
=item $node->command_ok(...)
Runs a shell command like TestLib::command_ok, but with PGPORT
set so that the command will default to connecting to this
PostgresNode.
=cut
sub command_ok
{
my $self = shift;
......@@ -441,6 +747,14 @@ sub command_ok
TestLib::command_ok(@_);
}
=pod
=item $node->command_fails(...) - TestLib::command_fails with our PGPORT
See command_ok(...)
=cut
sub command_fails
{
my $self = shift;
......@@ -450,6 +764,14 @@ sub command_fails
TestLib::command_fails(@_);
}
=pod
=item $node->command_like(...)
TestLib::command_like with our PGPORT. See command_ok(...)
=cut
sub command_like
{
my $self = shift;
......@@ -459,8 +781,17 @@ sub command_like
TestLib::command_like(@_);
}
# Run a command on the node, then verify that $expected_sql appears in the
# server log file.
=pod
=item $node->issues_sql_like(cmd, expected_sql, test_name)
Run a command on the node, then verify that $expected_sql appears in the
server log file.
Reads the whole log file so be careful when working with large log outputs.
=cut
sub issues_sql_like
{
my ($self, $cmd, $expected_sql, $test_name) = @_;
......@@ -474,4 +805,10 @@ sub issues_sql_like
like($log, $expected_sql, "$test_name: SQL found in server log");
}
=pod
=back
=cut
1;
Perl-based TAP tests
====================
src/test/perl/ contains shared infrastructure that's used by Perl-based tests
across the source tree, particularly tests in src/bin and src/test. It's used
to drive tests for backup and restore, replication, etc - anything that can't
really be expressed using pg_regress or the isolation test framework.
You should prefer to write tests using pg_regress in src/test/regress, or
isolation tester specs in src/test/isolation, if possible. If not, check to
see if your new tests make sense under an existing tree in src/test, like
src/test/ssl, or should be added to one of the suites for an existing utility.
Writing tests
-------------
Tests are written using Perl's Test::More with some PostgreSQL-specific
infrastructure from src/test/perl providing node management, support for
invoking 'psql' to run queries and get results, etc. You should read the
documentation for Test::More before trying to write tests.
Test scripts in the t/ subdirectory of a suite are executed in alphabetical
order.
Each test script should begin with:
use strict;
use warnings;
use PostgresNode;
use TestLib;
# Replace with the number of tests to execute:
use Test::More tests => 1;
then it will generally need to set up one or more nodes, run commands
against them and evaluate the results. For example:
my $node = get_new_node('master');
$node->init;
$node->start;
my $ret = $node->psql('postgres', 'SELECT 1');
is($ret, '1', 'SELECT 1 returns 1');
$node->stop('fast');
Read the Test::More documentation for more on how to write tests:
perldoc Test::More
For available PostgreSQL-specific test methods and some example tests read the
perldoc for the test modules, e.g.:
perldoc src/test/perl/PostgresNode.pm
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册