mirror of https://github.com/citusdata/citus.git
332 lines
11 KiB
Perl
332 lines
11 KiB
Perl
use strict;
|
|
use warnings;
|
|
|
|
my $pg_major_version = int($ENV{'pg_major_version'});
|
|
print("working with PG major version : $pg_major_version\n");
|
|
if ($pg_major_version >= 15) {
|
|
eval "use PostgreSQL::Test::Cluster";
|
|
eval "use PostgreSQL::Test::Utils";
|
|
} else {
|
|
eval "use PostgresNode";
|
|
}
|
|
|
|
|
|
#use PostgresNode;
|
|
use DBI;
|
|
|
|
our $NODE_TYPE_COORDINATOR = 1;
|
|
our $NODE_TYPE_WORKER = 2;
|
|
our $NODE_TYPE_CDC_CLIENT = 3;
|
|
|
|
sub compare_tables_in_different_nodes
|
|
{
|
|
my $result = 1;
|
|
my ($node1, $node2, $dbname, $stmt) = @_;
|
|
|
|
# Connect to the first database node
|
|
my $dbh1 = DBI->connect("dbi:Pg:" . $node1->connstr($dbname));
|
|
|
|
# Connect to the second database node
|
|
my $dbh2 = DBI->connect("dbi:Pg:" . $node2->connstr($dbname));
|
|
|
|
# Define the SQL query for the first database node
|
|
my $sth1 = $dbh1->prepare($stmt);
|
|
$sth1->execute();
|
|
|
|
# Define the SQL query for the second database node
|
|
my $sth2 = $dbh2->prepare($stmt);
|
|
$sth2->execute();
|
|
|
|
# Get the field names for the table
|
|
my @field_names = @{$sth2->{NAME}};
|
|
|
|
#$sth1->dump_results();
|
|
#$sth2->dump_results();
|
|
|
|
our @row1, our @row2;
|
|
|
|
# Use a cursor to iterate over the first database node's data
|
|
while (1) {
|
|
|
|
@row1 = $sth1->fetchrow_array();
|
|
@row2 = $sth2->fetchrow_array();
|
|
#print("row1: @row1\n");
|
|
#print("row2: @row2\n");
|
|
|
|
# Use a cursor to iterate over the second database node's data
|
|
if (@row1 and @row2) {
|
|
#print("row1: @row1\n");
|
|
#print("row2: @row2\n");
|
|
my $field_count_row1 = scalar @row1;
|
|
my $field_count_row2 = scalar @row2;
|
|
if ($field_count_row1 != $field_count_row2) {
|
|
print "Field count mismatch: $field_count_row1 != $field_count_row2 \n";
|
|
print "First row: @row1\n";
|
|
#print "Second row: @row2\n";
|
|
for (my $i = 0; $i < scalar @row2; $i++) {
|
|
print("Field $i, field name: $field_names[$i], value: $row2[$i] \n");
|
|
}
|
|
$result = 0;
|
|
last;
|
|
}
|
|
# Compare the data in each field in each row of the two nodes
|
|
for (my $i = 0; $i < scalar @row1; $i++) {
|
|
if ($row1[$i] ne $row2[$i]) {
|
|
print "Data mismatch in field '$field_names[$i]'\n";
|
|
print "$row1[$i] != $row2[$i]\n";
|
|
print "First row: @row1\n";
|
|
print "Second row: @row2\n";
|
|
$result = 0;
|
|
last;
|
|
}
|
|
}
|
|
} elsif (@row1 and !@row2) {
|
|
print "First node has more rows than the second node\n";
|
|
$result = 0;
|
|
last;
|
|
} elsif (!@row1 and @row2) {
|
|
print "Second node has more rows than the first node\n";
|
|
$result = 0;
|
|
last;
|
|
} else {
|
|
last;
|
|
}
|
|
}
|
|
|
|
$sth1->finish();
|
|
$sth2->finish();
|
|
$dbh1->disconnect();
|
|
$dbh2->disconnect();
|
|
return $result;
|
|
}
|
|
|
|
sub create_node {
|
|
my ($name,$node_type,$host, $port, $config) = @_;
|
|
if (!defined($config)) {
|
|
$config = ""
|
|
}
|
|
|
|
our $node;
|
|
|
|
if ($pg_major_version >= 15) {
|
|
$PostgreSQL::Test::Cluster::use_unix_sockets = 0;
|
|
$PostgreSQL::Test::Cluster::use_tcp = 1;
|
|
$PostgreSQL::Test::Cluster::test_pghost = 'localhost';
|
|
my %params = ( "port" => $port, "host" => "localhost");
|
|
$node = PostgreSQL::Test::Cluster->new($name, %params);
|
|
} else {
|
|
$PostgresNode::use_tcp = 1;
|
|
$PostgresNode::test_pghost = '127.0.0.1';
|
|
my %params = ( "port" => $port, "host" => "localhost");
|
|
$node = get_new_node($name, %params);
|
|
}
|
|
print("node's port:" . $node->port . "\n");
|
|
|
|
$port += 1;
|
|
|
|
my $citus_config_options = "
|
|
max_connections = 100
|
|
max_wal_senders = 100
|
|
max_replication_slots = 100
|
|
citus.enable_change_data_capture = on
|
|
log_statement = 'all'
|
|
citus.override_table_visibility = off";
|
|
|
|
if ($config ne "") {
|
|
$citus_config_options = $citus_config_options . $config
|
|
}
|
|
|
|
my $client_config_options = "
|
|
max_connections = 100
|
|
max_wal_senders = 100
|
|
max_replication_slots = 100
|
|
";
|
|
$node->init(allows_streaming => 'logical');
|
|
if ($node_type == $NODE_TYPE_COORDINATOR || $node_type == $NODE_TYPE_WORKER) {
|
|
$node->append_conf("postgresql.conf",$citus_config_options);
|
|
} else {
|
|
$node->append_conf("postgresql.conf",$citus_config_options);
|
|
}
|
|
|
|
$node->start();
|
|
|
|
if ($node_type == $NODE_TYPE_COORDINATOR || $node_type == $NODE_TYPE_WORKER) {
|
|
$node->safe_psql('postgres', "CREATE EXTENSION citus;");
|
|
my $value = $node->safe_psql('postgres', "SHOW citus.enable_change_data_capture;");
|
|
print("citus.enable_change_data_capture value is $value\n")
|
|
}
|
|
|
|
return $node;
|
|
}
|
|
|
|
# Create a Citus cluster with the given number of workers
|
|
sub create_citus_cluster {
|
|
my ($no_workers,$host,$port,$citus_config) = @_;
|
|
my @workers = ();
|
|
my $node_coordinator;
|
|
print("citus_config :", $citus_config);
|
|
if ($citus_config ne "") {
|
|
$node_coordinator = create_node('coordinator', $NODE_TYPE_COORDINATOR,$host, $port, $citus_config);
|
|
} else {
|
|
$node_coordinator = create_node('coordinator', $NODE_TYPE_COORDINATOR,$host, $port);
|
|
}
|
|
my $coord_host = $node_coordinator->host();
|
|
my $coord_port = $node_coordinator->port();
|
|
$node_coordinator->safe_psql('postgres',"SELECT pg_catalog.citus_set_coordinator_host('$coord_host', $coord_port);");
|
|
for (my $i = 0; $i < $no_workers; $i++) {
|
|
$port = $port + 1;
|
|
my $node_worker;
|
|
if ($citus_config ne "") {
|
|
$node_worker = create_node("worker$i", $NODE_TYPE_WORKER,"localhost", $port, $citus_config);
|
|
} else {
|
|
$node_worker = create_node("worker$i", $NODE_TYPE_WORKER,"localhost", $port);
|
|
}
|
|
my $node_worker_host = $node_worker->host();
|
|
my $node_worker_port = $node_worker->port();
|
|
$node_coordinator->safe_psql('postgres',"SELECT pg_catalog.citus_add_node('$node_worker_host', $node_worker_port);");
|
|
push @workers, $node_worker;
|
|
}
|
|
return $node_coordinator, @workers;
|
|
}
|
|
|
|
sub create_cdc_publication_and_replication_slots_for_citus_cluster {
|
|
my $node_coordinator = $_[0];
|
|
my $workersref = $_[1];
|
|
my $table_names = $_[2];
|
|
|
|
create_cdc_publication_and_slots_for_coordinator($node_coordinator, $table_names);
|
|
create_cdc_slots_for_workers($workersref);
|
|
}
|
|
|
|
sub create_cdc_publication_and_slots_for_coordinator {
|
|
my $node_coordinator = $_[0];
|
|
my $table_names = $_[1];
|
|
print("node node_coordinator connstr: \n" . $node_coordinator->connstr());
|
|
my $pub = $node_coordinator->safe_psql('postgres',"SELECT * FROM pg_publication WHERE pubname = 'cdc_publication';");
|
|
if ($pub ne "") {
|
|
$node_coordinator->safe_psql('postgres',"DROP PUBLICATION IF EXISTS cdc_publication;");
|
|
}
|
|
$node_coordinator->safe_psql('postgres',"CREATE PUBLICATION cdc_publication FOR TABLE $table_names;");
|
|
$node_coordinator->safe_psql('postgres',"SELECT pg_catalog.pg_create_logical_replication_slot('cdc_replication_slot','pgoutput',false)");
|
|
}
|
|
|
|
sub create_cdc_slots_for_workers {
|
|
my $workersref = $_[0];
|
|
for (@$workersref) {
|
|
my $slot = $_->safe_psql('postgres',"select * from pg_replication_slots where slot_name = 'cdc_replication_slot';");
|
|
if ($slot ne "") {
|
|
$_->safe_psql('postgres',"SELECT pg_catalog.pg_drop_replication_slot('cdc_replication_slot');");
|
|
}
|
|
$_->safe_psql('postgres',"SELECT pg_catalog.pg_create_logical_replication_slot('cdc_replication_slot','pgoutput',false)");
|
|
}
|
|
}
|
|
|
|
|
|
sub connect_cdc_client_to_citus_cluster_publications {
|
|
my $node_coordinator = $_[0];
|
|
my $workersref = $_[1];
|
|
my $node_cdc_client = $_[2];
|
|
my $num_args = scalar(@_);
|
|
|
|
|
|
if ($num_args > 3) {
|
|
my $copy_arg = $_[3];
|
|
connect_cdc_client_to_coordinator_publication($node_coordinator,$node_cdc_client, $copy_arg);
|
|
} else {
|
|
connect_cdc_client_to_coordinator_publication($node_coordinator,$node_cdc_client);
|
|
}
|
|
connect_cdc_client_to_workers_publication($workersref, $node_cdc_client);
|
|
}
|
|
|
|
sub connect_cdc_client_to_coordinator_publication {
|
|
my $node_coordinator = $_[0];
|
|
my $node_cdc_client = $_[1];
|
|
my $num_args = scalar(@_);
|
|
my $copy_data = "";
|
|
if ($num_args > 2) {
|
|
my $copy_arg = $_[2];
|
|
$copy_data = 'copy_data='. $copy_arg;
|
|
} else {
|
|
$copy_data = 'copy_data=false';
|
|
}
|
|
|
|
my $conn_str = $node_coordinator->connstr() . " dbname=postgres";
|
|
my $subscription = 'cdc_subscription';
|
|
print "creating subscription $subscription for coordinator: $conn_str\n";
|
|
$node_cdc_client->safe_psql('postgres',"
|
|
CREATE SUBSCRIPTION $subscription
|
|
CONNECTION '$conn_str'
|
|
PUBLICATION cdc_publication
|
|
WITH (
|
|
create_slot=false,
|
|
enabled=true,
|
|
slot_name=cdc_replication_slot,"
|
|
. $copy_data. ");"
|
|
);
|
|
}
|
|
|
|
sub connect_cdc_client_to_workers_publication {
|
|
my $workersref = $_[0];
|
|
my $node_cdc_client = $_[1];
|
|
my $i = 1;
|
|
for (@$workersref) {
|
|
my $conn_str = $_->connstr() . " dbname=postgres";
|
|
my $subscription = 'cdc_subscription_' . $i;
|
|
print "creating subscription $subscription for node$i: $conn_str\n";
|
|
my $subscription_stmt = "CREATE SUBSCRIPTION $subscription
|
|
CONNECTION '$conn_str'
|
|
PUBLICATION cdc_publication
|
|
WITH (
|
|
create_slot=false,
|
|
enabled=true,
|
|
slot_name=cdc_replication_slot,
|
|
copy_data=false);
|
|
";
|
|
|
|
$node_cdc_client->safe_psql('postgres',$subscription_stmt);
|
|
$i++;
|
|
}
|
|
}
|
|
|
|
sub wait_for_cdc_client_to_catch_up_with_citus_cluster {
|
|
my $node_coordinator = $_[0];
|
|
my ($workersref) = $_[1];
|
|
|
|
my $subscription = 'cdc_subscription';
|
|
print "coordinator: waiting for cdc client subscription $subscription to catch up\n";
|
|
$node_coordinator->wait_for_catchup($subscription);
|
|
wait_for_cdc_client_to_catch_up_with_workers($workersref);
|
|
}
|
|
|
|
sub wait_for_cdc_client_to_catch_up_with_coordinator {
|
|
my $node_coordinator = $_[0];
|
|
my $subscription = 'cdc_subscription';
|
|
print "coordinator: waiting for cdc client subscription $subscription to catch up\n";
|
|
$node_coordinator->wait_for_catchup($subscription);
|
|
}
|
|
|
|
sub wait_for_cdc_client_to_catch_up_with_workers {
|
|
my ($workersref) = $_[0];
|
|
my $i = 1;
|
|
for (@$workersref) {
|
|
my $subscription = 'cdc_subscription_' . $i;
|
|
print "node$i: waiting for cdc client subscription $subscription to catch up\n";
|
|
$_->wait_for_catchup($subscription);
|
|
$i++;
|
|
}
|
|
}
|
|
|
|
sub drop_cdc_client_subscriptions {
|
|
my $node = $_[0];
|
|
my ($workersref) = $_[1];
|
|
|
|
$node->safe_psql('postgres',"drop subscription cdc_subscription");
|
|
my $i = 1;
|
|
for (@$workersref) {
|
|
my $subscription = 'cdc_subscription_' . $i;
|
|
$node->safe_psql('postgres',"drop subscription " . $subscription);
|
|
$i++;
|
|
}
|
|
}
|
|
|