Thu Sep 30 18:37:34 PDT 2004
- Previous message: [Slony1-commit] By wieck: Change the numeric values of message levels so that the debug
- Next message: [Slony1-commit] By cbbrowne: 1.
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Log Message:
-----------
Add the "altperl" tools into stable branch
Tags:
----
REL_1_0_STABLE
Added Files:
-----------
slony1-engine/tools/altperl:
Makefile (r1.1.2.1)
README (r1.6.2.1)
ToDo (r1.2.2.1)
build_env.pl (r1.5.2.1)
create_set.pl (r1.6.2.1)
drop_node.pl (r1.4.2.1)
drop_set.pl (r1.4.2.1)
failover.pl (r1.4.2.1)
init_cluster.pl (r1.4.2.1)
merge_sets.pl (r1.4.2.1)
move_set.pl (r1.3.2.1)
replication_test.pl (r1.1.2.1)
reset_cluster.pl (r1.4.2.1)
restart_node.pl (r1.3.2.1)
restart_nodes.pl (r1.2.2.1)
show_configuration.pl (r1.1.2.1)
show_nodes.pl (r1.1.2.1)
slon-tools.pm (r1.11.2.1)
slon.env (r1.5.2.1)
slon_kill.pl (r1.4.2.1)
slon_pushsql.pl (r1.5.2.1)
slon_start.pl (r1.5.2.1)
slon_watchdog.pl (r1.3.2.1)
slon_watchdog2.pl (r1.2.2.1)
subscribe_set.pl (r1.4.2.1)
uninstall_nodes.pl (r1.2.2.1)
unsubscribe_set.pl (r1.3.2.1)
update_nodes.pl (r1.2.2.1)
-------------- next part --------------
--- /dev/null
+++ tools/altperl/merge_sets.pl
@@ -0,0 +1,45 @@
+#!perl # -*- perl -*-
+# $Id: merge_sets.pl,v 1.4.2.1 2004/09/30 17:37:28 cbbrowne Exp $
+# Author: Christopher Browne
+# Copyright 2004 Afilias Canada
+
+require 'slon-tools.pm';
+require 'slon.env';
+
+my ($node, $set1, $set2) = @ARGV;
+if ($node =~ /^node(\d+)$/) {
+ # Set name is in proper form
+ $node = $1;
+} else {
+ print "Valid node names are node1, node2, ...\n\n";
+ die "Usage: ./merge_sets.pl nodeN setOLD setNEW\n";
+}
+
+if ($set1 =~ /^set(\d+)$/) {
+ $set1 = $1;
+} else {
+ print "Valid set names are set1, set2, ...\n\n";
+ die "Usage: ./merge_sets.pl nodeN setOLD setNEW\n";
+}
+if ($set2 =~ /^set(\d+)$/) {
+ $set2 = $1;
+} else {
+ print "Valid set names are set1, set2, ...\n\n";
+ die "Usage: ./merge_sets.pl nodeN setOLD setNEW\n";
+}
+
+open(SLONIK, ">/tmp/slonik.$$");
+print SLONIK genheader();
+my ($dbname, $dbhost)=($DBNAME[1], $HOST[1]);
+print SLONIK qq[
+try {
+ merge set (id = $set1, add id = $set2, origin = $node);
+} on error {
+ echo 'Failure to merge sets $set1 and $set2 with origin $node';
+ exit 1;
+}
+echo 'Replication set $set2 merged in with $set1 on origin $node';
+];
+
+close SLONIK;
+run_slonik_script("/tmp/slonik.$$");
--- /dev/null
+++ tools/altperl/create_set.pl
@@ -0,0 +1,123 @@
+#!/usr/bin/perl
+# $Id: create_set.pl,v 1.6.2.1 2004/09/30 17:37:28 cbbrowne Exp $
+# Author: Christopher Browne
+# Copyright 2004 Afilias Canada
+
+require 'slon-tools.pm';
+require 'slon.env';
+my ($set) = @ARGV;
+if ($set =~ /^set(\d+)$/) {
+ $set = $1;
+} else {
+ print "Need set identifier\n";
+ die "create_set.pl setN\n";
+}
+
+$OUTPUTFILE="/tmp/add_tables.$$";
+
+open (OUTFILE, ">$OUTPUTFILE");
+print OUTFILE genheader();
+
+foreach my $table (@SERIALTABLES) {
+ $table = ensure_namespace($table);
+ print OUTFILE "
+ echo ' Adding unique key to table $table...';
+ table add key (
+ node id=1,
+ full qualified name='$table'
+ );
+";
+}
+close OUTFILE;
+run_slonik_script($OUTPUTFILE);
+
+open (OUTFILE, ">$OUTPUTFILE");
+print OUTFILE genheader();
+
+print OUTFILE "
+try {
+ create set (id = $set, origin = 1, comment = 'Set $set for $SETNAME');
+} on error {
+ echo 'Could not create subscription set $set for $SETNAME!';
+ exit -1;
+}
+";
+
+close OUTFILE;
+run_slonik_script($OUTPUTFILE);
+
+open (OUTFILE, ">$OUTPUTFILE");
+print OUTFILE genheader();
+print OUTFILE "
+ echo 'Subscription set $set created';
+ echo 'Adding tables to the subscription set';
+
+";
+
+if ($TABLE_ID < 1) {
+ $TABLE_ID = 1;
+}
+foreach my $table (@SERIALTABLES) {
+ $table = ensure_namespace($table);
+ print OUTFILE "
+ set add table (set id = $set, origin = 1, id = $TABLE_ID, full qualified name = '$table', comment = 'Table $table without primary key', key=serial);
+ echo 'Add unkeyed table $table';
+";
+ $TABLE_ID++;
+}
+
+foreach my $table (@PKEYEDTABLES) {
+ $table = ensure_namespace($table);
+ print OUTFILE "
+ set add table (set id = $set, origin = 1, id = $TABLE_ID, full qualified name = '$table', comment = 'Table $table with primary key');
+ echo 'Add primary keyed table $table';
+";
+ $TABLE_ID++;
+}
+
+foreach my $table (keys %KEYEDTABLES) {
+ my $key = $KEYEDTABLES{$table};
+ $table = ensure_namespace($table);
+ print OUTFILE "
+ set add table (set id = $set, origin = 1, id = $TABLE_ID, full qualified name = '$table', key='$key', comment = 'Table $table with candidate primary key $key');
+ echo 'Add candidate primary keyed table $table';
+";
+ $TABLE_ID++;
+}
+
+close OUTFILE;
+run_slonik_script($OUTPUTFILE);
+
+open (OUTFILE, ">$OUTPUTFILE");
+print OUTFILE genheader();
+# Finish subscription set...
+print OUTFILE "
+ echo 'Adding sequences to the subscription set';
+";
+
+$SEQID=1;
+foreach my $seq (@SEQUENCES) {
+ $seq = ensure_namespace($seq);
+ print OUTFILE "
+ set add sequence (set id = $set, origin = 1, id = $SEQID, full qualified name = '$seq', comment = 'Sequence $seq');
+ echo 'Add sequence $seq';
+";
+ $SEQID++;
+}
+print OUTFILE "
+ echo 'All tables added';
+";
+
+run_slonik_script($OUTPUTFILE);
+
+### If object hasn't a namespace specified, assume it's in "public", and make it so...
+sub ensure_namespace {
+ my ($object) = @_;
+ if ($object =~ /^(.*\..*)$/) {
+ # Table has a namespace specified
+ } else {
+ $object = "public.$object";
+ }
+ return $object;
+}
+
--- /dev/null
+++ tools/altperl/subscribe_set.pl
@@ -0,0 +1,53 @@
+#!perl # -*- perl -*-
+# $Id: subscribe_set.pl,v 1.4.2.1 2004/09/30 17:37:28 cbbrowne Exp $
+# Author: Christopher Browne
+# Copyright 2004 Afilias Canada
+
+require 'slon-tools.pm';
+require 'slon.env';
+my ($set, $node) = @ARGV;
+if ($node =~ /^node(\d+)$/) {
+ $node = $1;
+} else {
+ print "Need to specify node!\n";
+ die "subscribe_set setM nodeN\n";
+}
+
+if ($set =~ /^set(\d+)$/) {
+ $set = $1;
+} else {
+ print "Need to specify set!\n";
+ die "subscribe_set setM nodeN\n";
+}
+
+$FILE="/tmp/slonik-subscribe.$$";
+open(SLONIK, ">$FILE");
+print SLONIK genheader();
+print SLONIK "try {\n";
+
+if ($DSN[$node]) {
+ my $parent = 1;
+ my $forward;
+ if ($PARENT[$node]) {
+ $parent = $PARENT[$node];
+ }
+ if ($NOFORWARD[$node] eq "yes") {
+ $forward = "no";
+ } else {
+ $forward = "yes";
+ }
+ print SLONIK " subscribe set (id = $set, provider = $parent, receiver = $node, forward = $forward);\n";
+} else {
+ die "Node $node not found\n";
+}
+
+print SLONIK "}\n";
+print SLONIK qq{
+ on error {
+ exit 1;
+ }
+ echo 'Subscribed nodes to set $set';
+};
+
+close SLONIK;
+run_slonik_script($FILE);
--- /dev/null
+++ tools/altperl/init_cluster.pl
@@ -0,0 +1,210 @@
+#!perl # -*- perl -*-
+# $Id: init_cluster.pl,v 1.4.2.1 2004/09/30 17:37:28 cbbrowne Exp $
+# Author: Christopher Browne
+# Copyright 2004 Afilias Canada
+my @COST;
+my @PATH;
+
+require 'slon-tools.pm';
+require 'slon.env';
+my $FILE="/tmp/init-cluster.$$";
+open(SLONIK, ">$FILE");
+
+print SLONIK genheader();
+
+my ($dbname, $dbhost)=($DBNAME[1], $HOST[1]);
+print SLONIK "
+ init cluster (id = 1, comment = 'Node $node - $dbname\@$dbhost');
+";
+close SLONIK;
+run_slonik_script($FILE);
+
+open(SLONIK, ">$FILE");
+print SLONIK genheader();
+
+foreach my $node (@NODES) {
+ if ($node > 1) { # skip the first one; it's already initialized!
+ my ($dbname, $dbhost) = ($DBNAME[$node], $HOST[$node]);
+ print SLONIK " store node (id = $node, comment = 'Node $node - $dbname\@$dbhost');\n";
+ }
+}
+
+print SLONIK "echo 'Set up replication nodes';
+";
+close SLONIK;
+run_slonik_script($FILE);
+
+open(SLONIK, ">$FILE");
+print SLONIK genheader();
+
+my @VIA ;
+generate_listen_paths();
+report_on_paths();
+print SLONIK qq[
+echo 'Next: configure paths for each node/origin';
+];
+foreach my $nodea (@NODES) {
+ my $dsna = $DSN[$nodea];
+ foreach my $nodeb (@NODES) {
+ if ($nodea != $nodeb) {
+ my $dsnb = $DSN[$nodeb];
+ my $providerba = $VIA[$nodea][$nodeb];
+ my $providerab = $VIA[$nodeb][$nodea];
+ if (!$printed[$nodea][$nodeb]) {
+ print SLONIK " store path (server = $nodea, client = $nodeb, conninfo = '$dsna');\n";
+ $printed[$nodea][$nodeb] = "done";
+ }
+ if (!$printed[$nodeb][$nodea]) {
+ print SLONIK " store path (server = $nodeb, client = $nodea, conninfo = '$dsnb');\n";
+ $printed[$nodeb][$nodea] = "done";
+ }
+ print SLONIK "echo 'configured path between $nodea and $nodeb';\n";
+ }
+ }
+}
+
+close SLONIK;
+
+run_slonik_script($FILE);
+
+open(SLONIK, ">$FILE");
+print SLONIK genheader();
+
+foreach my $origin (@NODES) {
+ my $dsna = $DSN[$origin];
+ foreach my $receiver (@NODES) {
+ if ($origin != $receiver) {
+ my $provider = $VIA[$origin][$receiver];
+ print SLONIK " store listen (origin = $origin, receiver = $receiver, provider = $provider);\n";
+ }
+ }
+}
+
+print SLONIK qq[
+ echo 'Replication nodes prepared';
+ echo 'Please start a slon replication daemon for each node';
+];
+
+close SLONIK;
+run_slonik_script($FILE);
+
+sub generate_listen_paths {
+ my @COST;
+ my @PATH;
+
+ my $infinity = 10000000; # Initial costs are all infinite
+ foreach my $node1 (@NODES) {
+ foreach my $node2 (@NODES) {
+ $COST[$node1][$node2] = $infinity;
+ }
+ }
+
+ # Initialize paths between parents and children, and based on them,
+ # generate initial seeding of listener paths, @VIA
+
+ foreach my $node1 (@NODES) {
+ $COST[$node1][$node1] = 0;
+ $VIA[$node1][$node1] = 0;
+ foreach my $node2 (@NODES) {
+ if ($node2 != $node1) {
+ if ($PARENT[$node1] == $node2) {
+ $PATH[$node1][$node2] = 1;
+ $PATH[$node2][$node1] = 1;
+ # Set up a cost 1 path between them
+ # Parent to child
+ $COST[$node1][$node2] = 1;
+ $VIA[$node1][$node2] = $node1;
+
+ # Child to parent
+ $COST[$node2][$node1] = 1;
+ $VIA[$node2][$node1] = $node2;
+ }
+ }
+ }
+ }
+
+ # Now, update the listener paths...
+ # 4 level nested iteration:
+ # 1 while not done, do
+ # 2 for each node, node1
+ # 3 for each node, node2, where node2 <> node1, where we don't
+ # yet have a listener path
+ # 4 for each node node3 (<> node1 or node2),
+ # consider introducing the listener path:
+ # node1 to node2 then node2 to node3
+ # In concept, it's an O(n^4) algorithm; since the number of nodes, n,
+ # is not likely to get particularly large, it's not worth tuning
+ # further.
+ $didwork = "yes";
+ while ($didwork eq "yes") {
+ $didwork = "no";
+ foreach my $node1 (@NODES) {
+ foreach my $node3 (@NODES) {
+ if (($VIA[$node3][$node1] == 0) && ($node3 != $node1)) {
+ foreach my $node2 (@NODES) {
+ if ($PATH[$node1][$node2] && ($VIA[$node2][$node3] != 0) && ($node2 != $node3) && ($node2 != $node1)) {
+ # Consider introducing a path from n1 to n2 then n2 to n3
+ # as a cheaper alternative to going direct from n1 to n3
+ my $oldcost = $COST[$node3][$node1];
+ my $newcost = $COST[$node1][$node2] + $COST[$node2][$node3];
+ if ($newcost < $oldcost) {
+ $didwork = "yes";
+ # So we go via node 2
+ $VIA[$node3][$node1] = $node2;
+ $COST[$node3][$node1] = $newcost;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+sub report_on_paths {
+ print "cost\n";
+ print " ";
+ foreach my $node2 (@NODES) {
+ printf "%4d|", $node2;
+ }
+ print "\n--------------------------------------------\n";
+ foreach my $node1 (@NODES) {
+ printf "%4d|", $node1;
+ foreach my $node2 (@NODES) {
+ if ($COST[$node2][$node1] == $infinity) {
+ printf "inf ";
+ } else {
+ printf "%4d ", $COST[$node2][$node1];
+ }
+ print "\n";
+ }
+ }
+ print "\n\n";
+ print "VIA\n";
+ print " ";
+ foreach my $node2 (@NODES) {
+ printf "%4d|", $node2;
+ }
+ print "\n--------------------------------------------\n";
+ foreach my $node1 (@NODES) {
+ printf "%4d", $node1;
+ foreach my $node2 (@NODES) {
+ printf "%4d ", $VIA[$node2][$node1];
+ }
+ print "\n";
+ }
+
+ print "PATHS\n";
+ print " ";
+ foreach my $node2 (@NODES) {
+ printf "%4d|", $node2;
+ }
+ print "\n--------------------------------------------\n";
+ foreach my $node1 (@NODES) {
+ printf "%4d", $node1;
+ foreach my $node2 (@NODES) {
+ printf "%4d ", $PATH[$node2][$node1];
+ }
+ print "\n";
+ }
+}
--- /dev/null
+++ tools/altperl/slon.env
@@ -0,0 +1,68 @@
+#!perl # -*- perl -*-
+# $Id: slon.env,v 1.5.2.1 2004/09/30 17:37:28 cbbrowne Exp $
+# Author: Christopher Browne
+# Copyright 2004 Afilias Canada
+
+if ($ENV{"SLONYNODES"}) {
+ require $ENV{"SLONYNODES"};
+} else {
+ # Define environment locally...
+ $SETNAME=flex2test;
+ $LOGDIR='/opt/logs/slon';
+ $SLON_BIN_PATH='/opt/OXRS/dbs/pgsql74/bin';
+ #$APACHE_ROTATOR="/opt/OXRS/apache/rotatelogs"; # optional path to Apache rotatelog tool
+
+ add_node(host => 'marge', dbname=>'transtest', port=>5532,
+ user=>'postgres', password=>'postgres', node=>1);
+
+ add_node(host => 'marge', dbname=>'transreplica', port=>5533,
+ user=>'postgres', password=>'postgres', node=>2, parent=>1);
+
+ add_node(host => 'marge', dbname=>'transreplica', port=>5534, user=>'postgres',
+ password=>'postgres', node=>3, parent=>1);
+
+ # add_node(host => 'marge', dbname=>'flexnodeb', port=>5532,user=>'postgres',
+ # password=>'postgres', node=>4, parent=>3);
+
+ # add_node(host => 'marge', dbname=>'flexnodec', port=>5532,user=>'postgres',
+ # password=>'postgres', node=>5, parent=>4);
+
+ # add_node(host => 'marge', dbname=>'flexnoded', port=>5532,user=>'postgres',
+ # password=>'postgres', node=>6, parent=>3);
+ # add_node(host => 'marge', dbname=>'flexnodee', port=>5532,user=>'postgres',
+ # password=>'postgres', node=>7, parent=>6, noforward=>'no');
+}
+
+if ($ENV{"SLONYSET"}) {
+ require $ENV{"SLONYSET"};
+} else {
+
+ # Table Numbering - controlled here...
+ $TABLE_ID=1;
+ # These are the tables that already have primary keys, that therefore do
+ # not need for Slony-I to add sequences/indices
+ @PKEYEDTABLES=(
+ );
+
+ # These are tables with candidate primary keys; we assume Slony
+ # isn't smart enough (yet) to discover the key.
+
+ %KEYEDTABLES=(
+ table1 => 'index_on_table1',
+ table2 => 'index_on_table2'
+ );
+
+ # Here are the tables to be replicated that do NOT have unique
+ # keys, to which Slony-I will have to add a key field
+ @SERIALTABLES=(
+ );
+
+ # These are the applications' sequences that are to be
+ # replicated
+ @SEQUENCES=(
+ "seq1",
+ "seq2",
+ "seq3"
+ );
+
+}
--- /dev/null
+++ tools/altperl/drop_set.pl
@@ -0,0 +1,29 @@
+#!perl # -*- perl -*-
+# $Id: drop_set.pl,v 1.4.2.1 2004/09/30 17:37:28 cbbrowne Exp $
+# Author: Christopher Browne
+# Copyright 2004 Afilias Canada
+
+require 'slon-tools.pm';
+require 'slon.env';
+my ($set) = @ARGV;
+if ($set =~ /^set(\d+)$/) {
+ $set = $1;
+} else {
+ print "Need set identifier\n";
+ die "drop_set.pl setN\n";
+}
+$OUTFILE="/tmp/dropset.$$";
+open(SLONIK, ">>$OUTFILE");
+
+print SLONIK genheader();
+
+print SLONIK qq{
+try {
+ drop set (id = $set, origin=1);
+} on error {
+ exit 1;
+}
+echo 'Dropped set $set';
+};
+close SLONIK;
+run_slonik_script($OUTFILE);
--- /dev/null
+++ tools/altperl/slon_start.pl
@@ -0,0 +1,44 @@
+#!perl # -*- perl -*-
+# $Id: slon_start.pl,v 1.5.2.1 2004/09/30 17:37:28 cbbrowne Exp $
+# Author: Christopher Browne
+# Copyright 2004 Afilias Canada
+
+#start the slon daemon
+require 'slon-tools.pm';
+require 'slon.env';
+$SLEEPTIME=30; # number of seconds for watchdog to sleep
+
+$node =$ARGV[0];
+
+if ( scalar(@ARGV) < 1 ) {
+ die "Usage: ./slon_start [node]\n";
+}
+
+if ($node =~ /^node(\d+)$/) {
+ # Node name is in proper form
+ $nodenum = $1;
+} else {
+ print "Valid node names are node1, node2, ...\n\n";
+ die "Usage: ./slon_start [node]\n";
+}
+
+$pid = get_pid($node);
+
+if ($pid) {
+ die "Slon is already running for set $SETNAME!\n";
+}
+
+my $dsn = $DSN[$nodenum];
+my $dbname=$DBNAME[$nodenum];
+start_slon($nodenum);
+
+$pid = get_pid($node);
+
+if (!($pid)) {
+ print "Slon failed to start for cluster $SETNAME, node $node\n";
+} else {
+ print "Slon successfully started for cluster $SETNAME, node $node\n";
+ print "PID [$pid]\n";
+ print "Start the watchdog process as well...\n";
+ system "perl slon_watchdog.pl $node $SLEEPTIME &";
+}
--- /dev/null
+++ tools/altperl/reset_cluster.pl
@@ -0,0 +1,48 @@
+#!perl # -*- perl -*-
+# $Id: reset_cluster.pl,v 1.4.2.1 2004/09/30 17:37:28 cbbrowne Exp $
+# Author: Christopher Browne
+# Copyright 2004 Afilias Canada
+
+require 'slon-tools.pm';
+require 'slon.env';
+
+open(SLONIK, ">/tmp/slonik.$$");
+
+print SLONIK genheader();
+
+my ($dbname, $dbhost)=($DBNAME[1], $HOST[1]);
+print SLONIK "
+try {
+";
+
+foreach my $node (@NODES) {
+ if ($node > 1) {
+ my ($dbname, $dbhost) = ($DBNAME[$node], $HOST[$node]);
+ print SLONIK " store node (id = $node, comment = 'Node $dbname@$dbhost');\n";
+ }
+}
+
+foreach my $nodea (@NODES) {
+ my $dsna = $DSN[$nodea];
+ foreach my $nodeb (@NODES) {
+ if ($nodea != $nodeb) {
+ my $dsnb = $DSN[$nodeb];
+ print SLONIK " store path (server = $nodea, client = $nodeb, conninfo = '$dsna');\n";
+ print SLONIK " store path (server = $nodeb, client = $nodea, conninfo = '$dsnb');\n";
+ print SLONIK " store listen (origin = $nodea, receiver = $nodeb);\n";
+ print SLONIK " store listen (origin = $nodeb, receiver = $nodea);\n";
+ }
+ }
+}
+
+print SLONIK qq[
+} on error {
+ echo 'Remapping of cluster failed...';
+ exit 1;
+}
+echo 'Replication nodes prepared';
+echo 'Please start a slon replication daemon for each node';
+];
+
+close SLONIK;
+run_slonik_script("/tmp/slonik.$$");
--- /dev/null
+++ tools/altperl/unsubscribe_set.pl
@@ -0,0 +1,37 @@
+#!perl # -*- perl -*-
+# $Id: unsubscribe_set.pl,v 1.3.2.1 2004/09/30 17:37:28 cbbrowne Exp $
+# Author: Christopher Browne
+# Copyright 2004 Afilias Canada
+
+require 'slon-tools.pm';
+require 'slon.env';
+
+my ($set, $node) = @ARGV;
+if ($node =~ /^node(\d+)$/) {
+ $node = $1;
+} else {
+ print "Need to specify node!\n";
+ die "unsubscribe_set setM nodeN\n";
+}
+
+if ($set =~ /^set(\d+)$/) {
+ $set = $1;
+} else {
+ print "Need to specify set!\n";
+ die "unsubscribe_set setM nodeN\n";
+}
+
+open(SLONIK, ">/tmp/slonik-unsubscribe.$$");
+print SLONIK genheader();
+print SLONIK qq{
+ try {
+ unsubscribe set (id = $set, receiver = $node);
+ }
+ on error {
+ echo 'Failed to unsubscribe node $node from set $set';
+ exit 1;
+ }
+ echo 'unsubscribed node $node from set $set';
+};
+close SLONIK;
+run_slonik_script("/tmp/slonik-unsubscribe.$$");
--- /dev/null
+++ tools/altperl/show_nodes.pl
@@ -0,0 +1,212 @@
+#!perl # -*- perl -*-
+# $Id: show_nodes.pl,v 1.1.2.1 2004/09/30 17:37:28 cbbrowne Exp $
+# Author: Christopher Browne
+# Copyright 2004 Afilias Canada
+my @COST;
+my @PATH;
+
+use Getopt::Long;
+
+require 'slon-tools.pm';
+require 'slon.env';
+my $FILE="/tmp/init-cluster.$$";
+open(SLONIK, ">$FILE");
+
+print SLONIK genheader();
+
+my ($dbname, $dbhost)=($DBNAME[1], $HOST[1]);
+print SLONIK "
+ init cluster (id = 1, comment = 'Node $dbname\@$dbhost');
+";
+close SLONIK;
+run_slonik_script($FILE);
+
+open(SLONIK, ">$FILE");
+print SLONIK genheader();
+
+foreach my $node (@NODES) {
+ if ($node > 1) { # skip the first one; it's already initialized!
+ my ($dbname, $dbhost) = ($DBNAME[$node], $HOST[$node]);
+ print SLONIK " store node (id = $node, comment = 'Node $node - $dbname\@$dbhost');\n";
+ }
+}
+
+print SLONIK "echo 'Set up replication nodes';
+";
+close SLONIK;
+run_slonik_script($FILE);
+
+open(SLONIK, ">$FILE");
+print SLONIK genheader();
+
+my @VIA ;
+generate_listen_paths();
+report_on_paths();
+print SLONIK qq[
+echo 'Next: configure paths for each node/origin';
+];
+foreach my $nodea (@NODES) {
+ my $dsna = $DSN[$nodea];
+ foreach my $nodeb (@NODES) {
+ if ($nodea != $nodeb) {
+ my $dsnb = $DSN[$nodeb];
+ my $providerba = $VIA[$nodea][$nodeb];
+ my $providerab = $VIA[$nodeb][$nodea];
+ if (!$printed[$nodea][$nodeb]) {
+ print SLONIK " store path (server = $nodea, client = $nodeb, conninfo = '$dsna');\n";
+ $printed[$nodea][$nodeb] = "done";
+ }
+ if (!$printed[$nodeb][$nodea]) {
+ print SLONIK " store path (server = $nodeb, client = $nodea, conninfo = '$dsnb');\n";
+ $printed[$nodeb][$nodea] = "done";
+ }
+ print SLONIK "echo 'configured path between $nodea and $nodeb';\n";
+ }
+ }
+}
+
+close SLONIK;
+
+run_slonik_script($FILE);
+
+open(SLONIK, ">$FILE");
+print SLONIK genheader();
+
+foreach my $origin (@NODES) {
+ my $dsna = $DSN[$origin];
+ foreach my $receiver (@NODES) {
+ if ($origin != $receiver) {
+ my $provider = $VIA[$origin][$receiver];
+ print SLONIK " store listen (origin = $origin, receiver = $receiver, provider = $provider);\n";
+ }
+ }
+}
+
+print SLONIK qq[
+ echo 'Replication nodes prepared';
+ echo 'Please start the replication daemon on both systems';
+];
+
+close SLONIK;
+run_slonik_script($FILE);
+
+sub generate_listen_paths {
+ my @COST;
+ my @PATH;
+
+ my $infinity = 10000000; # Initial costs are all infinite
+ foreach my $node1 (@NODES) {
+ foreach my $node2 (@NODES) {
+ $COST[$node1][$node2] = $infinity;
+ }
+ }
+
+ # Initialize paths between parents and children, and based on them,
+ # generate initial seeding of listener paths, @VIA
+
+ foreach my $node1 (@NODES) {
+ $COST[$node1][$node1] = 0;
+ $VIA[$node1][$node1] = 0;
+ foreach my $node2 (@NODES) {
+ if ($node2 != $node1) {
+ if ($PARENT[$node1] == $node2) {
+ $PATH[$node1][$node2] = 1;
+ $PATH[$node2][$node1] = 1;
+ # Set up a cost 1 path between them
+ # Parent to child
+ $COST[$node1][$node2] = 1;
+ $VIA[$node1][$node2] = $node1;
+
+ # Child to parent
+ $COST[$node2][$node1] = 1;
+ $VIA[$node2][$node1] = $node2;
+ }
+ }
+ }
+ }
+
+ # Now, update the listener paths...
+ # 4 level nested iteration:
+ # 1 while not done, do
+ # 2 for each node, node1
+ # 3 for each node, node2, where node2 <> node1, where we don't
+ # yet have a listener path
+ # 4 for each node node3 (<> node1 or node2),
+ # consider introducing the listener path:
+ # node1 to node2 then node2 to node3
+ # In concept, it's an O(n^4) algorithm; since the number of nodes, n,
+ # is not likely to get particularly large, it's not worth tuning
+ # further.
+ $didwork = "yes";
+ while ($didwork eq "yes") {
+ $didwork = "no";
+ foreach my $node1 (@NODES) {
+ foreach my $node3 (@NODES) {
+ if (($VIA[$node3][$node1] == 0) && ($node3 != $node1)) {
+ foreach my $node2 (@NODES) {
+ if ($PATH[$node1][$node2] && ($VIA[$node2][$node3] != 0) && ($node2 != $node3) && ($node2 != $node1)) {
+ # Consider introducing a path from n1 to n2 then n2 to n3
+ # as a cheaper alternative to going direct from n1 to n3
+ my $oldcost = $COST[$node3][$node1];
+ my $newcost = $COST[$node1][$node2] + $COST[$node2][$node3];
+ if ($newcost < $oldcost) {
+ $didwork = "yes";
+ # So we go via node 2
+ $VIA[$node3][$node1] = $node2;
+ $COST[$node3][$node1] = $newcost;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+sub report_on_paths {
+ print "cost\n";
+ print " ";
+ foreach my $node2 (@NODES) {
+ printf "%4d|", $node2;
+ }
+ print "\n--------------------------------------------\n";
+ foreach my $node1 (@NODES) {
+ printf "%4d|", $node1;
+ foreach my $node2 (@NODES) {
+ if ($COST[$node2][$node1] == $infinity) {
+ printf "inf ";
+ } else {
+ printf "%4d ", $COST[$node2][$node1];
+ }
+ print "\n";
+ }
+ }
+ print "\n\n";
+ print "VIA\n";
+ print " ";
+ foreach my $node2 (@NODES) {
+ printf "%4d|", $node2;
+ }
+ print "\n--------------------------------------------\n";
+ foreach my $node1 (@NODES) {
+ printf "%4d", $node1;
+ foreach my $node2 (@NODES) {
+ printf "%4d ", $VIA[$node2][$node1];
+ }
+ print "\n";
+ }
+
+ print "PATHS\n";
+ print " ";
+ foreach my $node2 (@NODES) {
+ printf "%4d|", $node2;
+ }
+ print "\n--------------------------------------------\n";
+ foreach my $node1 (@NODES) {
+ printf "%4d", $node1;
+ foreach my $node2 (@NODES) {
+ printf "%4d ", $PATH[$node2][$node1];
+ }
+ print "\n";
+ }
+}
--- /dev/null
+++ tools/altperl/restart_node.pl
@@ -0,0 +1,21 @@
+#!perl # -*- perl -*-
+# $Id: restart_node.pl,v 1.3.2.1 2004/09/30 17:37:28 cbbrowne Exp $
+# Author: Christopher Browne
+# Copyright 2004 Afilias Canada
+
+require 'slon-tools.pm';
+require 'slon.env';
+
+my ($node) = @_;
+if ($node =~ /^node(\d+)$/) {
+ $nodenum = $node
+} else {
+ die "./restart_node nodeN\n";
+}
+my $FILE="/tmp/restart.$$";
+
+open(SLONIK, ">$FILE");
+print SLONIK genheader();
+print SLONIK "restart node $node;\n";
+close SLONIK;
+run_slonik_script($FILE);
--- /dev/null
+++ tools/altperl/failover.pl
@@ -0,0 +1,43 @@
+#!perl # -*- perl -*-
+# $Id: failover.pl,v 1.4.2.1 2004/09/30 17:37:28 cbbrowne Exp $
+# Author: Christopher Browne
+# Copyright 2004 Afilias Canada
+
+require 'slon-tools.pm';
+require 'slon.env';
+
+my ($node1, $set1, $node2) = @ARGV;
+if ($node1 =~ /^node(\d+)$/) {
+ $node1 = $1;
+} else {
+ print "Valid node names are node1, node2, ...\n\n";
+ die "Usage: ./failover.pl nodeN setOLD nodeNEW\n";
+}
+if ($set1 =~ /^set(\d+)$/) {
+ $set1 = $1;
+} else {
+ print "Valid set names are set1, set2, ...\n\n";
+ die "Usage: ./failover.pl nodeN setOLD nodeNEW\n";
+}
+if ($node2 =~ /^node(\d+)$/) {
+ $node2 = $1;
+} else {
+ print "Valid node names are node1, node2, ...\n\n";
+ die "Usage: ./failover.pl nodeN setOLD nodeNEW\n";
+}
+
+open(SLONIK, ">/tmp/slonik.$$");
+print SLONIK genheader();
+my ($dbname, $dbhost)=($DBNAME[1], $HOST[1]);
+print SLONIK qq[
+try {
+ failover (id = $node1, backup node = $node2);
+} on error {
+ echo 'Failure to fail node $node1 over to $node2';
+ exit 1;
+}
+ echo 'Replication sets originating on $node1 failed over to $node2';
+];
+
+close SLONIK;
+run_slonik_script("/tmp/slonik.$$");
--- /dev/null
+++ tools/altperl/Makefile
@@ -0,0 +1,23 @@
+# ----------
+# Makefile for the HOWTOs
+#
+# Copyright (c) 2003-2004, PostgreSQL Global Development Group
+# $Id: Makefile,v 1.1.2.1 2004/09/30 17:37:28 cbbrowne Exp $
+#
+# ----------
+
+slony_subdir = tools/altperl
+slony_top_builddir = ../..
+include $(slony_top_builddir)/Makefile.global
+
+DISTFILES = Makefile ToDo README $(wildcard *.pl*) $(wildcard *.pm*) $(wildcard *.env*)
+
+distdir: $(DISTFILES)
+ mkdir $(distdir)/$(subdir)
+ -chmod 777 $(distdir)/$(subdir)
+ for file in $(DISTFILES) ; do \
+ cp $$file $(distdir)/$(subdir)/$$file ; \
+ done
+
+clean distclean maintainer-clean:
+
--- /dev/null
+++ tools/altperl/slon_watchdog2.pl
@@ -0,0 +1,82 @@
+#!perl # -*- perl -*-
+# $Id: slon_watchdog2.pl,v 1.2.2.1 2004/09/30 17:37:28 cbbrowne Exp $
+# Author: Christopher Browne
+# Copyright 2004 Afilias Canada
+
+require 'slon-tools.pm';
+require 'slon.env';
+
+$node =$ARGV[0];
+$sleep =$ARGV[1];
+
+if ( scalar(@ARGV) < 2 ) {
+ die "Usage: ./slon_watchdog node sleep-time\n";
+}
+
+if ($node =~/^node(\d+)$/) {
+ $nodenum = $1;
+}
+
+log_to_watchdog_log("Invoking watchdog for $SETNAME node $nodenum");
+while (1) {
+ my $res = query_slony_status($nodenum); # See where the node stands
+ my $eventsOK;
+ if ($res =~ /^\s*f\s*\|/) {
+ $eventsOK = "YES";
+ } else {
+ $eventsOK = "NO";
+ }
+ my $pid = get_pid($node); # See if the slon process is alive
+ my ($restart, $kick);
+ $kick = "NO"; # Initially, assume we don't need to submit a "restart node" command
+ if ($pid) { # PID is alive...
+ if ($eventsOK eq "YES") {
+ # All is well - do nothing!
+ $restart = "NO";
+ } else {
+ $restart = "YES";
+ }
+ } else {
+ $restart = "YES";
+ # See if the slon log ends with "FATAL localListenThread: Another slon daemon is serving this node already"
+ my $lastlog=`/bin/ls -t $LOGDIR/slony1/node$nodenum/$dbname*log | head -1`;
+ my $lastline=`tail -1 $lastlog`;
+ if ($lastline =~ /Another slon daemon is serving this node already/) {
+ $kick = "YES"; # Yup, need to tell slonik to reset this node
+ }
+ }
+
+ # If the node needs a swift kick in the "RESTART", then submit that to slonik
+ if ($kick eq "YES") {
+ log_to_watchdog_log("submit slonik to restart $SETNAME node $nodenum");
+ open(SLONIK, "|$SLON_BIN_PATH/slonik");
+ print SLONIK genheader();
+ print SLONIK "restart node $node\n";
+ close SLONIK;
+ }
+ if ($restart eq "YES") {
+ if ($pid) {
+ log_to_watchdog_log("terminate slon daemon for $SETNAME node $nodenum");
+ # Kill slon until dead...
+ kill 2, $pid;
+ sleep 3;
+ kill 15, $pid;
+ sleep 3;
+ kill 9, $pid;
+ }
+ log_to_watchdog_log("restart slon for $nodenum");
+ start_slon($nodenum);
+ }
+ sleep $sleep;
+}
+
+
+sub log_to_watchdog_log {
+ my ($message) = @_;
+ chomp $message;
+ my $date = `date`;
+ chomp $date;
+ open (SLONLOG, ">>$LOGDIR/slony-watchdog.log");
+ print SLONLOG $date, "|", $message, "\n";
+ close SLONLOG;
+}
--- /dev/null
+++ tools/altperl/slon-tools.pm
@@ -0,0 +1,165 @@
+#!perl # -*- perl -*-
+# $Id: slon-tools.pm,v 1.11.2.1 2004/09/30 17:37:28 cbbrowne Exp $
+# Author: Christopher Browne
+# Copyright 2004 Afilias Canada
+
+sub add_node {
+ my %PARAMS = (host=> undef,
+ dbname => 'template1',
+ port => 5432,
+ user => 'postgres',
+ node => undef,
+ password => undef,
+ parent => 1,
+ noforward => undef
+ );
+ my $K;
+ while ($K= shift) {
+ $PARAMS{$K} = shift;
+ }
+ die ("I need a node number") unless $PARAMS{'node'};
+ my $node = $PARAMS{'node'};
+ push @NODES, $node;
+ my $loginstr;
+ my $host = $PARAMS{'host'};
+ if ($host) {
+ $loginstr .= "host=$host";
+ $HOST[$node] = $host;
+ } else {
+ die("I need a host name") unless $PARAMS{'host'};
+ }
+ my $dbname = $PARAMS{'dbname'};
+ if ($dbname) {
+ $loginstr .= " dbname=$dbname";
+ $DBNAME[$node] = $dbname;
+ }
+ my $user=$PARAMS{'user'};
+ $loginstr .= " user=$user";
+ $USER[$node]= $user;
+
+ my $port = $PARAMS{'port'};
+ if ($port) {
+ $loginstr .= " port=$port";
+ $PORT[$node] = $port;
+ } else {
+ die ("I need a port number");
+ }
+ my $password = $PARAMS{'password'};
+ if ($password) {
+ $loginstr .= " password=$password";
+ $PASSWORD[$node] = $password;
+ }
+ $DSN[$node] = $loginstr;
+ my $parent = $PARAMS{'parent'};
+ if ($parent) {
+ $PARENT[$node] = $parent;
+ }
+ my $noforward = $PARAMS{'noforward'};
+ if ($noforward) {
+ $NOFORWARD[$node] = $noforward;
+ }
+}
+
+# This is the usual header to a slonik invocation that declares the
+# cluster name and the set of nodes and how to connect to them.
+sub genheader {
+ my $header = "cluster name = $SETNAME;\n";
+ foreach my $node (@NODES) {
+ if ($DSN[$node]) {
+ my $dsn = $DSN[$node];
+ $header .= " node $node admin conninfo='$dsn';\n";
+ }
+ }
+ return $header;
+}
+
+# Stores copy of slonik script in log file in $LOGDIR
+# then invokes it and deletes it
+sub run_slonik_script {
+ my ($script) = @_;
+ chomp $script;
+ open(OUT, ">>$LOGDIR/slonik_scripts.log");
+ my $now = `date`;
+ chomp $now;
+ print OUT "# -------------------------------------------------------------\n";
+ print OUT "# Script: $script submitted at $now \n";
+ print OUT "# -------------------------------------------------------------\n";
+ close OUT;
+ `cat $script >> $LOGDIR/slonik_scripts.log`;
+ #print `slonik < $script`;
+ print `cat $script`;
+ unlink($script);
+}
+
+sub ps_args {
+ my $sys=`uname`;
+ chomp $sys; # strip off edges
+ if ($sys eq "Linux") {
+ return "/bin/ps -auxww";
+ } elsif ($sys eq "FreeBSD") {
+ return "/bin/ps -auxww";
+ } elsif ($sys eq "SunOS") {
+ return "/usr/ucb/ps -auxww";
+ } elsif ($sys eq "AIX") {
+ return "/usr/bin/ps auxww";
+ }
+ return "/usr/bin/ps -auxww"; # This may be questionable for other systems; extend as needed!
+}
+
+sub get_pid {
+ my ($node) = @_;
+ $node =~ /node(\d*)$/;
+ my $nodenum = $1;
+ my $pid;
+ my $tpid;
+ my ($dbname, $dbport, $dbhost) = ($DBNAME[$nodenum], $PORT[$nodenum], $HOST[$nodenum]);
+ # print "Searching for PID for $dbname on port $dbport\n";
+ my $command = ps_args() . "| egrep \"[s]lon .*$SETNAME\" | egrep \"host=$dbhost dbname=$dbname.*port=$dbport\" | sort -n | awk '{print \$2}'";
+ #print "Command:\n$command\n";
+ open(PSOUT, "$command|");
+ while ($tpid = <PSOUT>) {
+ chomp $tpid;
+ $pid = $tpid;
+ }
+ close(PSOUT);
+ return $pid;
+}
+
+sub start_slon {
+ my ($nodenum) = @_;
+ my ($dsn, $dbname) = ($DSN[$nodenum], $DBNAME[$nodenum]);
+ my $cmd;
+ `mkdir -p $LOGDIR/slony1/node$nodenum`;
+ if ($APACHE_ROTATOR) {
+ $cmd = "$SLON_BIN_PATH/slon -s 1000 -d2 $SETNAME '$dsn' 2>&1 | $APACHE_ROTATOR \"$LOGDIR/slony1/node$nodenum/" . $dbname . "_%Y-%m-%d_%H:%M:%S.log\" 10M&";
+ } else {
+ $cmd = "$SLON_BIN_PATH/slon -s 1000 -d2 $SETNAME '$dsn' 2>&1 > $LOGDIR/slony1/node$nodenum/$dbname.log &";
+ }
+ print "Invoke slon for node $nodenum - $cmd\n";
+ system $cmd;
+}
+
+sub query_slony_status {
+ my ($nodenum) = @_;
+ my $query = qq{
+ select now() - ev_timestamp > '00:40:00'::interval as event_old, now() - ev_timestamp as age,
+ ev_timestamp, ev_seqno, ev_origin as origin
+from _$SETNAME.sl_event events, _$SETNAME.sl_subscribe slony_master
+ where
+ events.ev_origin = slony_master.sub_provider and
+ not exists (select * from _$SETNAME.sl_subscribe providers
+ where providers.sub_receiver = slony_master.sub_provider and
+ providers.sub_set = slony_master.sub_set and
+ slony_master.sub_active = 't' and
+ providers.sub_active = 't')
+order by ev_origin desc, ev_seqno desc limit 1;
+};
+ my ($port, $host, $dbname)= ($PORT[$nodenum], $HOST[$nodenum], $DBNAME[$nodenum]);
+ my $result=`$SLON_BIN_PATH/psql -p $port -h $host -c "$query" --tuples-only $dbname`;
+ chomp $result;
+ #print "Query was: $query\n";
+ #print "Result was: $result\n";
+ return $result;
+}
+
+1;
--- /dev/null
+++ tools/altperl/drop_node.pl
@@ -0,0 +1,30 @@
+#!perl # -*- perl -*-
+# $Id: drop_node.pl,v 1.4.2.1 2004/09/30 17:37:28 cbbrowne Exp $
+# Author: Christopher Browne
+# Copyright 2004 Afilias Canada
+
+require 'slon-tools.pm';
+require 'slon.env';
+
+my ($node) = @ARGV;
+if ($node =~ /^node(\d+)$/) {
+ $node = $1;
+} else {
+ print "Need to specify node!\n";
+ die "drop_node nodeN\n";
+}
+
+my $OUTPUTFILE="/tmp/slonik-drop.$$";
+open(SLONIK, ">$OUTPUTFILE");
+print SLONIK genheader();
+print SLONIK qq{
+try {
+ drop node (id = $node);
+} on error {
+ echo 'Failed to drop node $node from cluster';
+ exit 1;
+}
+echo 'dropped node $node cluster';
+};
+close SLONIK;
+run_slonik_script($OUTPUTFILE);
--- /dev/null
+++ tools/altperl/ToDo
@@ -0,0 +1,82 @@
+- Need to write a "repair_cluster.pl" script that
+ modifies configuration to reflect new configuration,
+ dropping and adding paths and listeners.
+
+ This would compare the configuration computed (as in init_cluster)
+ with the configuration actually found on some node, add "additional"
+ bits, and drop obsolete bits.
+
+- It would seem likely that the function "generate_listen_paths()" in
+ init_cluster.pl would be beneficial to port to pl/pgsql, as
+ there presently isn't any capability to rebuild the listener
+ paths by automatically dropping the old ones.
+
+- At present, the configuration generated by this set of tools is
+ fairly fragile. If just about any sort of error is made, it is
+ commonly needful to drop all of the Slony schemas, thereby cleaning
+ _everything_ out, and restarting the configuration process from
+ scratch.
+
+ That certainly isn't ideal.
+
+
+-------------------------------------------------------------------------------------------------
+
+More about the "generating SET LISTEN" calculation
+------------------------------------------------------------------------
+
+I have been mulling over the notion of setting up the Slonik STORE
+LISTEN(ORIGIN=a, RECEIVER=b, PROVIDER=c) configuration via a stored
+procedure, using in-the-DBMS data.
+
+The "features" of this idea:
+
+This involves having a table (view?) containing the intended parentage
+for each node, that is, which nodes point to which parents.
+
+This would allow the following good things:
+
+- Can't drop a node that has children, probably adding in other
+possible data checks
+
+- Can calculate the full "listener matrix" within pl/pgsql instead of
+doing it in Perl (take a look at init_cluster.pl, subroutine
+generate_listen_paths()).
+
+My preliminary thinking about it was pointing to there being a pretty
+elegant way to do this using SQL queries that might be more readable
+than the dynamic programming formulation embedded in that subroutine.
+(I didn't write out the Bellman equations, but took a look back at my
+old optimization texts ;-).)
+
+The primary problem that this solves is to create those STORE LISTEN()
+definitions, which get pretty involved to generate by hand if you get
+more than three nodes.
+
+In doing some further thinking, I noticed a couple of conspicuous
+challenges:
+
+1. There can be no fixed association with sets, as the sl_listen table
+does not contain set fields, and different sets can use differently
+shaped subscription trees.
+
+(I think users would be doing something pretty stupid to have _wildly_
+different arrangements for different sets, but I still have to support
+it...)
+
+2. The tree cannot be based on subscriptions because it needs to exist
+before any subscriptions are established
+
+In effect, I have no fixed place where I can get the information at
+the point at which I most need it.
+
+Once all nodes are subscribed, I could use subscription information to
+weight the cost functions, but I need the data BEFORE we do anything.
+
+This isn't pointing yet to a good approach to "seeding" it; if someone
+has some inspiration, let me know...
+--
+Christopher Browne
+<cbbrowne at acm.org>
+
+-------------------------------------------------------------------------------------------------
--- /dev/null
+++ tools/altperl/restart_nodes.pl
@@ -0,0 +1,20 @@
+#!perl # -*- perl -*-
+# $Id: restart_nodes.pl,v 1.2.2.1 2004/09/30 17:37:28 cbbrowne Exp $
+# Author: Christopher Browne
+# Copyright 2004 Afilias Canada
+
+require 'slon-tools.pm';
+require 'slon.env';
+
+my $FILE="/tmp/restart.$$";
+foreach my $node (@NODES) {
+ my $dsn = $DSN[$node];
+ open(SLONIK, ">$FILE");
+ print SLONIK qq{
+ cluster name = $SETNAME ;
+ node $node admin conninfo = '$dsn';
+ restart node $node;
+ };
+ close SLONIK;
+ run_slonik_script($FILE);
+}
--- /dev/null
+++ tools/altperl/slon_watchdog.pl
@@ -0,0 +1,48 @@
+#!perl # -*- perl -*-
+# $Id: slon_watchdog.pl,v 1.3.2.1 2004/09/30 17:37:28 cbbrowne Exp $
+# Author: Christopher Browne
+# Copyright 2004 Afilias Canada
+
+require 'slon-tools.pm';
+require 'slon.env';
+
+$node =$ARGV[0];
+$sleep =$ARGV[1];
+
+if ( scalar(@ARGV) < 2 ) {
+ die "Usage: ./slon_watchdog node sleep-time\n";
+}
+
+if ($node =~/^node(\d+)$/) {
+ $nodenum = $1;
+}
+
+slon_watchdog($node, $nodenum);
+
+sub slon_watchdog {
+ my ($node, $nodenum) = @_;
+ $pid = get_pid($node);
+ if (!($pid)) {
+ my ($dsn, $dbname) = ($DSN[$nodenum], $DBNAME[$nodenum]);
+ open (SLONLOG, ">>$LOGDIR/slon-$dbname-$node.err");
+ print SLONLOG "WATCHDOG: No Slon is running for node $node!\n";
+ print SLONLOG "WATCHDOG: You ought to check the postmaster and slon for evidence of a crash!\n";
+ print SLONLOG "WATCHDOG: I'm going to restart slon for $node...\n";
+ # First, restart the node using slonik
+ system "./restart_node.sh $node";
+ # Next, restart the slon process to service the node
+ start_slon($nodenum);
+ $pid = get_pid($node);
+ print SLONLOG "WATCHDOG: Restarted slon for set $SETNAME, PID $pid\n";
+ } else {
+ open(LOG, ">>$LOGDIR/slon_watchdog.log");
+ print LOG "\n";
+ system "date >> $LOGDIR/slon_watchdog.log";
+ print LOG "Found slon daemon running for set $SETNAME, PID $pid\n";
+ print LOG "Looks Ok\n";
+ print LOG "Sleeping for $sleep seconds\n";
+ }
+ close(PSOUT);
+ sleep $sleep;
+ slon_watchdog();
+}
--- /dev/null
+++ tools/altperl/uninstall_nodes.pl
@@ -0,0 +1,28 @@
+#!perl # -*- perl -*-
+# $Id: uninstall_nodes.pl,v 1.2.2.1 2004/09/30 17:37:28 cbbrowne Exp $
+# Author: Christopher Browne
+# Copyright 2004 Afilias Canada
+
+require 'slon-tools.pm';
+require 'slon.env';
+
+$FILE="/tmp/slonik.$$";
+open(SLONIK, ">$FILE");
+print SLONIK genheader();
+print SLONIK qq{
+ uninstall node (id=1);
+};
+close SLONIK;
+run_slonik_script($FILE);
+
+foreach my $node (@NODES) {
+ foreach my $command ("drop schema _$SETNAME cascade;") {
+ print $command, "\n";
+ print `echo "$command" | psql -h $HOST[$node] -U $USER[$node] -d $DBNAME[$node] -p $PORT[$node]`;
+ }
+ foreach my $t (@SERIALTABLES) {
+ my $command = "alter table $t drop column \\\"_Slony-I_" . $SETNAME . "_rowID\\\";";
+ print $command, "\n";
+ print `echo "$command" | psql -h $HOST[$node] -U $USER[$node] -d $DBNAME[$node] -p $PORT[$node]`;
+ }
+}
--- /dev/null
+++ tools/altperl/move_set.pl
@@ -0,0 +1,51 @@
+#!perl # -*- perl -*-
+# $Id: move_set.pl,v 1.3.2.1 2004/09/30 17:37:28 cbbrowne Exp $
+# Author: Christopher Browne
+# Copyright 2004 Afilias Canada
+
+require 'slon-tools.pm';
+require 'slon.env';
+
+my ($set, $node1, $node2) = @ARGV;
+if ($set =~ /^set(\d+)$/) {
+ # Node name is in proper form
+ $set = $1;
+} else {
+ print "Valid set names are set1, set2, ...\n\n";
+ die "Usage: ./move_set.pl setN nodeOLD nodeNEW\n";
+}
+
+if ($node1 =~ /^node(\d+)$/) {
+ $node1 = $1;
+} else {
+ print "Valid node names are node1, node2, ...\n\n";
+ die "Usage: ./move_set.pl setN nodeOLD nodeNEW\n";
+}
+if ($node2 =~ /^node(\d+)$/) {
+ $node2 = $1;
+} else {
+ print "Valid node names are node1, node2, ...\n\n";
+ die "Usage: ./move_set.pl setN nodeOLD nodeNEW\n";
+}
+
+open(SLONIK, ">/tmp/slonik.$$");
+print SLONIK genheader();
+my ($dbname, $dbhost)=($DBNAME[1], $HOST[1]);
+print SLONIK qq[
+ try {
+ echo 'Locking down set $set on node $node1';
+ lock set (id = $set, origin = $node1);
+ echo 'Locked down - moving it';
+ move set (id = $set, old origin = $node1, new origin = $node2);
+ unlock set (id = $set, origin = $node2);
+ }
+ on error {
+ echo 'Failure to move set $set from $node1 to $node2';
+ unlock set (id = $set, origin = $node1);
+ exit 1;
+ }
+ echo 'Replication set $set moved from node $node1 to $node2';
+];
+
+close SLONIK;
+run_slonik_script("/tmp/slonik.$$");
--- /dev/null
+++ tools/altperl/show_configuration.pl
@@ -0,0 +1,38 @@
+#!perl #-*- perl -*-
+# $Id: show_configuration.pl,v 1.1.2.1 2004/09/30 17:37:28 cbbrowne Exp $
+# Author: Christopher Browne
+# Copyright 2004 Afilias Canada
+
+# This script simply displays an overview of node configuration
+# for a given SLONY node set
+
+require 'slon-tools.pm';
+require 'slon.env';
+
+print "Slony Configuration\n-------------------------------------\n";
+if ($ENV{"SLONYNODES"}) {
+ print "With node configuration from ", $ENV{"SLONYNODES"}, "\n";
+}
+if ($ENV{"SLONYSET"}) {
+ print "With set configuration from ", $ENV{"SLONYSET"}, "\n";
+}
+
+print qq{
+Slony-I Cluster: $SETNAME
+Logs stored under $LOGDIR
+Slony Binaries in: $SLON_BIN_PATH
+};
+if ($APACHE_ROTATOR) {
+ print "Rotating logs using Apache Rotator: $APACHE_ROTATOR\n";
+}
+print qq{
+Node information
+--------------------------------
+};
+foreach $node (0..100) {
+ if ($DSN[$node]) {
+ printf("Node: %2d Host: %15s User: %8s Port: %4d Forwarding? %4s Parent: %2d Database: %10s\n DSN: %s\n",
+ $node, $HOST[$node], $USER[$node], $PORT[$node], $NOFORWARD[$node],
+ $PARENT[$node], $DBNAME[$node], $DSN[$node]);
+ }
+}
--- /dev/null
+++ tools/altperl/build_env.pl
@@ -0,0 +1,108 @@
+#!perl # -*- perl -*-
+# $Id: build_env.pl,v 1.5.2.1 2004/09/30 17:37:28 cbbrowne Exp $
+# Contributed by:
+# Joe Kalash
+# kalash at savicom.net
+
+# This script, given parameters concerning the database nodes,
+# generates output for "slon.env" consisting of:
+# - A set of add_node() calls to configure the cluster
+# - The arrays @KEYEDTABLES, @SERIALTABLES, and @SEQUENCES
+
+use DBI;
+use Getopt::Long;
+use strict;
+
+my $dataBase;
+my $host;
+my $dataBaseUser;
+my $dataBasePassword;
+my $dataBasePort;
+my @nodes;
+my $usage = "$0 -node host:database:user[:password:port] [-node ...]
+First node is assumed to be the master.\n";
+
+&usage if(!GetOptions('node=s@'=>\@nodes));
+
+die "At least one node is required" if ( scalar(@nodes) < 1 );
+
+my $nodeNumber = 1;
+my $parentString;
+foreach my $node (@nodes)
+{
+ my($tmpHost,$tmpDataBase,$tmpDataBaseUser,$tmpDataBasePassword,$tmpPort) =
+ split(/:/,$node);
+ die "Host is required" if ( !$tmpHost );
+ die "database is required" if ( !$tmpDataBase );
+ die "user is required" if ( !$tmpDataBaseUser );
+ $tmpPort = 5432 if ( !$tmpPort );
+ $host = $tmpHost if ( !$host );
+ $dataBase = $tmpDataBase if ( !$dataBase );
+ if ( !$dataBaseUser ) {
+ $dataBaseUser = $tmpDataBaseUser;
+ $dataBasePassword = $tmpDataBasePassword if ( $tmpDataBasePassword );
+ $dataBasePort = $tmpPort if ( $tmpPort );
+ }
+ print "&add_node(host => '$tmpHost', dbname => '$tmpDataBase', port =>$tmpPort,
+ user=>'$tmpDataBaseUser', password=>'$tmpDataBasePassword', node=>$nodeNumber $parentString);\n";
+ $parentString = ', parent=>1';
+ $nodeNumber++;
+
+}
+my $connectString = "dbi:Pg:dbname=$dataBase;host=$host;port=$dataBasePort";
+my $dbh = DBI->connect($connectString,$dataBaseUser,$dataBasePassword,
+ {RaiseError => 0, PrintError => 0, AutoCommit => 1});
+die "connect: $DBI::errstr" if ( !defined($dbh) || $DBI::err );
+# Read in all the user 'normal' tables in public.
+my $tableQuery = $dbh->prepare("
+SELECT pg_namespace.nspname || '.' || pg_class.relname,pg_class.relkind,pg_class.relhaspkey
+FROM pg_namespace,pg_class
+WHERE pg_class.reltype > 0
+AND pg_class.relnamespace = pg_catalog.pg_namespace.oid
+AND (pg_class.relkind = 'r' OR pg_class.relkind = 'S')
+AND pg_namespace.nspname = 'public' AND pg_namespace.oid = pg_class.relnamespace");
+
+die "prepare(tableQuery): $DBI::errstr" if ( !defined($tableQuery) || $DBI::err );
+die "execute(tableQuery): $DBI::errstr" if ( !$tableQuery->execute() );
+
+my @tablesWithIndexes;
+my @tablesWithoutIndexes;
+my @sequences;
+while ( my $row = $tableQuery->fetchrow_arrayref() ) {
+ my $relname = @$row[0];
+ my $relkind = @$row[1];
+ my $relhaspkey = @$row[2];
+ push(@sequences,$relname) if ( $relkind eq 'S' );
+ push(@tablesWithIndexes,$relname) if ( $relkind eq 'r' && $relhaspkey == 1 );
+ push(@tablesWithoutIndexes,$relname) if ( $relkind eq 'r' && $relhaspkey == 0 );
+}
+$tableQuery->finish();
+$dbh->disconnect();
+
+if ( scalar(@tablesWithIndexes) > 1 ) {
+ print '@KEYEDTABLES=(' . "\n";
+ foreach my $table (sort @tablesWithIndexes) {
+ print "\t\"$table\",\n";
+ }
+ print ");\n";
+}
+if ( scalar(@tablesWithoutIndexes) > 1 ) {
+ print '@SERIALTABLES=(' . "\n";
+ foreach my $table (sort @tablesWithoutIndexes) {
+ print "\t\"$table\",\n";
+ }
+ print ");\n";
+}
+if ( scalar(@sequences) > 1 ) {
+ print '@SEQUENCES=(' . "\n";
+ foreach my $table (sort @sequences) {
+ print "\t\"$table\",\n";
+ }
+ print ");\n";
+}
+exit 0;
+
+sub usage {
+ print "$usage";
+ exit 0;
+}
--- /dev/null
+++ tools/altperl/slon_pushsql.pl
@@ -0,0 +1,40 @@
+#!perl # -*- perl -*-
+# $Id: slon_pushsql.pl,v 1.5.2.1 2004/09/30 17:37:28 cbbrowne Exp $
+# Author: Christopher Browne
+# Copyright 2004 Afilias Canada
+
+require 'slon-tools.pm';
+require 'slon.env';
+my ($set, $node, $file) = @ARGV;
+if ($set =~ /^set(\d+)$/) {
+ $set = $1;
+} else {
+ print "Invalid set identifier";
+ die "Usage: ./slon_pushsql.pl set[N] node[N] full_path_to_sql_script_file\n";
+}
+if ($node =~ /^node(\d+)$/) {
+ $node = $1;
+} else {
+ print "Invalid node identifier";
+ die "Usage: ./slon_pushsql.pl set[N] node[N] full_path_to_sql_script_file\n";
+}
+
+if ($file =~ /^\//) {
+} else {
+ print "SQL script path needs to be a full path, i.e. /tmp/my_script.sql\n";
+ die "Usage: ./slon_pushsql.pl set[N] node[N] full_path_to_sql_script_file\n";
+}
+
+my $FILE="/tmp/gensql.$$";
+open(SLONIK, ">$FILE");
+print SLONIK genheader();
+
+print SLONIK qq{
+ execute script (
+ set id=$set,
+ filename='$file',
+ event node = $node
+ );
+};
+close SLONIK;
+run_slonik_script($FILE);
--- /dev/null
+++ tools/altperl/slon_kill.pl
@@ -0,0 +1,47 @@
+#!perl # -*- perl -*-
+# $Id: slon_kill.pl,v 1.4.2.1 2004/09/30 17:37:28 cbbrowne Exp $
+# Kill all slon instances for the current setname
+# Author: Christopher Browne
+# Copyright 2004 Afilias Canada
+
+require 'slon-tools.pm';
+require 'slon.env';
+
+print "slon_kill.pl... Killing all slon and slon_watchdog instances for setname $SETNAME\n";
+print "1. Kill slon watchdogs\n";
+#kill the watchdog
+
+open(PSOUT, ps_args() . " | egrep '[s]lon_watchdog' | sort -n | awk '{print \$2}'|");
+$found="n";
+while ($pid = <PSOUT>) {
+ chomp $pid;
+ if (!($pid)) {
+ print "No slon_watchdog is running for set $SETNAME!\n";
+ } else {
+ $found="y";
+ kill 9, $pid;
+ print "slon_watchdog for set $SETNAME killed - PID [$pid]\n";
+ }
+}
+close(PSOUT);
+if ($found eq 'n') {
+ print "No watchdogs found\n";
+}
+print "\n2. Kill slon processes\n";
+#kill the slon daemon
+$found="n";
+open(PSOUT, ps_args() . " | egrep \"[s]lon .*$SETNAME\" | sort -n | awk '{print \$2}'|");
+while ($pid = <PSOUT>) {
+ chomp $pid;
+ if (!($pid)) {
+ print "No Slon is running for set $SETNAME!\n";
+ } else {
+ kill 9, $pid;
+ print "Slon for set $SETNAME killed - PID [$pid]\n";
+ $found="y";
+ }
+}
+close(PSOUT);
+if ($found eq 'n') {
+ print "No slon processes found\n";
+}
--- /dev/null
+++ tools/altperl/replication_test.pl
@@ -0,0 +1,187 @@
+#!perl # -*- perl -*-
+# $Id: replication_test.pl,v 1.1.2.1 2004/09/30 17:37:28 cbbrowne Exp $
+# Christopher Browne
+# Copyright 2004
+# Afilias Canada
+
+# This script, given DSN parameters to access a Slony-I cluster,
+# submits insert, update, and delete requests and sees how they
+# propagate through the system.
+
+# It requires setting up the following table and propagating it via
+# (surprise!) Slony-I
+
+#create table slony_test (
+# description text,
+# mod_date timestamp with time zone
+#);
+
+use Pg;
+use Getopt::Long;
+use strict;
+
+$LOGDIR="/opt/OXRS/logs/general";
+`mkdir -p $LOGDIR`;
+$HOST = `hostname`;
+$IDENT = "-";
+$USER = `whoami`;
+$SIZE = "-";
+chomp($HOST, $IDENT, $USER, $SIZE);
+my $sleep_seconds = 80;
+
+$goodopts = GetOptions("help", "database=s", "host=s", "user=s", "cluster=s", "password=s", "port=s", "set=s");
+if (defined($opt_help)) {
+ show_usage();
+}
+my ($database,$user, $port, $cluster, $host, $password, $set);
+
+$database = $opt_database if (defined($opt_database));
+$port = 5432;
+$port = $opt_port if (defined($opt_port));
+$user = $opt_user if (defined($opt_user));
+$password = $opt_password if (defined($opt_password));
+$host = $opt_host if (defined($opt_host));
+$cluster = $opt_cluster if (defined($opt_cluster));
+$set = $opt_set if (defined($opt_set));
+
+#DBI: my $initialDSN = "dbi:Pg:dbname=$database;host=$host;port=$port";
+my $initialDSN = "dbname=$database host=$host port=$port";
+$initialDSN = $initialDSN . " password=$password" if defined($opt_password);
+
+# DBI: my $dbh = DBI->connect($initialDSN, $user, $password,
+# {RaiseError => 0, PrintError => 0, AutoCommit => 1});
+# die "connect: $DBI::errstr" if ( !defined($dbh) || $DBI::err );
+my $dbh = Pg::connectdb($initialDSN);
+
+# Query to find the "master" node
+my $masterquery = qq{
+ select sub_provider
+ from _oxrslive.sl_subscribe s1
+ where not exists (select * from _oxrslive.sl_subscribe s2
+ where s2.sub_receiver = s1.sub_provider and
+ s1.sub_set = $set and s2.sub_set = $set and
+ s1.sub_active = 't' and s2.sub_active = 't')
+ group by sub_provider;
+};
+
+# my $tq = $dbh->prepare($masterquery);
+# die "prepare(masterquery): $DBI::errstr" if (!defined($tq) || $DBI::err);
+# die "execute(masterquery): $DBI::errstr" if (!$tq->execute());
+my $tq = $dbh->exec($masterquery);
+
+my $masternode;
+#while (my $row = $tq->fetchrow_arrayref()) {
+while (my @row = $tq->fetchrow) {
+ ($masternode) = @row;
+}
+#$tq->finish();
+
+# Query to find live DSNs
+my $dsnsquery =
+qq {
+ select p.pa_server, p.pa_conninfo
+ from _$cluster.sl_path p
+ where exists (select * from _$cluster.subscribe s where
+ s.sub_set = $set and
+ (s.sub_provider = p.pa_server or s.sub_receiver = p.pa_server)
+ sub_active = 't')
+ group by pa_server, pa_conninfo;
+};
+
+# $tq = $dbh->prepare($dsnsquery);
+# die "prepare(dsnsquery): $DBI::errstr" if (!defined($tq) || $DBI::err);
+# die "execute(dsnsquery): $DBI::errstr" if (!$tq->execute());
+$tq = $dbh->exec($dsnsquery);
+my @DSN;
+#while (my $row = $tq->fetchrow_arrayref()) {
+while (my @row = $tq->fetchrow) {
+ ($node, $dsn) = @row;
+ $DSN{$node} = $dsn;
+}
+#$tq->finish();
+
+($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
+my $time_date = sprintf ("%d-%.2d-%.2d %.2d:%.2d:%.2d", $year+1900, $mon+1, $mday, $hour, $min, $sec);
+
+my $insert_text = "INSERT Replication Test";
+my $insert_query = "insert into slony_test (description, mod_date) values ('$insert_text', '$time_date');";
+foreach my $source (sort keys %SOURCES) {
+ submit_to_master($masternode, $insert_query, 1);
+}
+
+sleep $sleep_seconds;
+
+sub submit_to_master {
+ my ($node, $query, $expected_count) = @_;
+ my $dsn = $DSN{$node};
+ my $master = Pg::connectdb($conn_info);
+ my $status_conn = $master->status;
+ if ($status_conn ne PGRES_CONNECTION_OK) {
+ alog($source, "", "Master connection Query failed - " . $master->errorMessage, $status_conn);
+ report_failed_conn($conn_info);
+ return -1;
+ }
+ my $result = $master->exec($query);
+ if ($result->ntuples != $expect_count) {
+ alog($source, "", "Master $query failed - unexpected tuple count", $result->cmdTuples);
+ return -2;
+ } else {
+ alog($source, "", "Master $query succeeded", 0);
+ return 0;
+ }
+}
+
+sub submit_to_slave {
+ my ($source, $dest, $conn_info, $query, $expect_count)=@_;
+ my $slave = Pg::connectdb($conn_info);
+ my $status_conn = $slave->status;
+ if ($status_conn ne PGRES_CONNECTION_OK) {
+ alog($source, $dest, "Connection Query failed!", -1);
+ return "Connection Failed";
+ }
+ my $result = $slave->exec($query);
+ if ($result->resultStatus != 2) {
+ alog($source, $dest, "Slave query $query failed", $result->resultStatus);
+ report_failed_conn($conn_info);
+ return "Query Failed";
+ } elsif ($result->ntuples != $expect_count) {
+ alog($source, $dest, "Slave query failed - $query - slave is behind master", -3);
+ # This indicates that the slave is behind - issue message!
+ return "Slave Behind";
+ } else {
+ alog($source, $dest, "Query $query succeeded", 0);
+ return "OK";
+ }
+}
+
+sub report_failed_conn {
+ my ($ci) = @_;
+ $ci =~ s/password=.*$//g;
+ print "Failure - connection to $ci\n";
+}
+
+sub alog {
+ my ($source, $dest, $message, $rc) = @_;
+ chomp ($source, $dest, $message, $rc);
+ #print"Master DB: $source Slave DB: $dest Message: [$message] RC=$rc\n";
+ apache_log("replication.log", "Master DB: $source Slave DB: $dest Message: [$message]", $rc);
+}
+
+sub apache_log {
+ my ($logfile, $request, $status) = @_;
+ my $date = `date`;
+ chomp($request, $status, $date);
+ my $LOGENTRY ="$HOST $IDENT $USER [$date] \"$request\" $status $SIZE";
+ open(OUTPUT, ">>$LOGDIR/$logfile");
+ print OUTPUT $LOGENTRY, "\n";
+ close OUTPUT;
+}
+
+sub show_usage {
+ my ($inerr) = @_;
+ if ($inerr) {
+ chomp $inerr;
+ print $inerr, "\n";
+ }
+ die "$0 --host --database --user --cluster --port --password";
+}
--- /dev/null
+++ tools/altperl/update_nodes.pl
@@ -0,0 +1,16 @@
+#!perl # -*- perl -*-
+# $Id: update_nodes.pl,v 1.2.2.1 2004/09/30 17:37:28 cbbrowne Exp $
+# Author: Christopher Browne
+# Copyright 2004 Afilias Canada
+
+require 'slon-tools.pm';
+require 'slon.env';
+
+open(SLONIK, ">/tmp/update_nodes.$$");
+print SLONIK genheader();
+
+foreach my $node (@NODES) {
+ print SLONIK "update functions (id = $node);\n";
+};
+close SLONIK;
+run_slonik_script("/tmp/update_nodes.$$");
--- /dev/null
+++ tools/altperl/README
@@ -0,0 +1,129 @@
+README
+$Id: README,v 1.6.2.1 2004/09/30 17:37:28 cbbrowne Exp $
+
+Christopher Browne
+Database Administrator
+Afilias Canada
+
+This is a "second system" set of scripts for managing a set of Slony-I
+instances.
+
+Unlike the shell scripts that have previously been used, these scripts
+support having an arbitrary number of Slony-I nodes. They are
+configured in the [cluster].nodes file (e.g. - environment variable
+SLONYNODES) by calling add_node() once indicating the configuration
+for each node that is needed.
+
+The following configuration is set up:
+
+ Host Level Configuration
+--------------------------------------
+
+This configuration will normally apply to all clusters being managed
+on a particular host, so it would probably make sense to modify it
+directly in slon.env.
+
+ $SLON_BIN_PATH is the path to use to find the slon and slonik
+ binaries.
+
+ $APACHE_ROTATOR is an optional reference to the location of the
+ Apache log rotator; if you set it to a path to an Apache "rotatelog"
+ program, that will be used to keep log file size down to a "dull
+ roar".
+ <http://httpd.apache.org/docs-2.0/programs/rotatelogs.html>
+
+ $LOGDIR is the directory in which to put log files. The script will
+ generate a subdirectory for each node.
+
+ Node Level Configuration
+--------------------------------------
+
+This configuration should be set up in the file represented in the
+environment variable SLONYNODES.
+
+ $SETNAME represents the name of the cluster. In each database
+ involved in the replication set, you will find the namespace
+ "_$SETNAME" that contains Slony-I's configuration tables
+
+
+ Set Level Configuration
+-----------------------------------
+
+The configuration of the tables, sequences and such are stored in the
+file pointed to by the environment variable SLONYSET, in the following
+Perl variables:
+
+ $TABLE_ID - where to start numbering table IDs
+
+ The table IDs are required to be unique across all sets in a
+ Slony-I cluster, so if you add extra sets, you need to set
+ $TABLE_ID to a value that won't conflict, typically something
+ higher than largest value used in earlier sets.
+
+ @PKEYEDTABLES contains all of the tables that have primary keys
+
+ %KEYEDTABLES contains tables with candidate primary keys associated
+ with the index you _want_.
+
+ @SERIALTABLES contains tables that do not have a unique key
+ to which Slony-I will need to add and populate
+ a unique key
+
+ @SEQUENCES lists all of the application sequences that are to be
+ replicated.
+
+The values in slon.env are "hardcoded" as far as the tools are
+concerned.
+
+To make this more flexible, slon.env also looks at the environment
+variables SLONYNODES and SLONYSET as alternative sources for
+configuration.
+
+That way, you may do something like:
+
+ for i in `seq 10`; do
+ SLONYNODES="./node$i.config" ./init_cluster.pl
+ done
+
+- Such an "alternative cluster.nodes" might import Pg, and do queries
+against a database to be replicated in order to populate the sets of
+tables and such.
+
+- The "alternative cluster.nodes" might search some sort of 'registry' for
+the set of nodes to be replicated.
+
+Parallel to SLONYNODES is the environment variable SLONYSET, which
+controls the contents of replication sets. It looks as though it
+should be usual for there to be just one "intentionally active"
+subscription set at any given time, with other sets being set up in
+order to be merged with the "main" set.
+
+Steps to start up replication
+-------------------------------
+
+0. Dump from source system to destination
+ pg_dump -s -c flex1 | psql flex2
+
+1. Initializes the Slony cluster
+ ./init_cluster.pl
+
+ This sets up a FULL cross-join set of paths and listeners, doing
+ something of a shortest-path evaluation of which "store listens" to
+ set up.
+
+2. Start up slon servers for both DB instances
+ ./slon_start.pl node1
+ ./slon_start.pl node2
+
+3. Sets up all the tables for "set 1" for FlexReg 2.0
+ ./create_set.pl set1
+
+4. Subscribe Node #2 to Set #1
+ ./subscribe_set.pl set1 node2
+ This is the Big One...
+
+That SHOULD be it, although "should" is probably too strong a word :-)
+
+There are numerous other tools for adding/dropping Slony-I
+configuration, and scripts that might manage simple forms of
+switchover/failover.
- Previous message: [Slony1-commit] By wieck: Change the numeric values of message levels so that the debug
- Next message: [Slony1-commit] By cbbrowne: 1.
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
More information about the Slony1-commit mailing list