#!/bin/bash

# **********
# test_6_autolisten
#
# 	This test script creates a standalone pgbench database 
#	as slony_test1 and then:
#
#	- initializes a primary node and starts the node daemon
#	- creates a set containing all 4 pgbench tables
#	- creates 5 other databases to contain this set
#       - subscribes #2 and #3 directly to #1, then #4 subscribes
#         to #3, then #5 & #6 subscribe to #4
#	- compares the 6 databases.
# **********

TERMPROG=xterm
WHICHPROG=which

PATH=/usr/local/slony1/bin:$PATH
export PATH
TMPOUT=/tmp/output.$$
ORIGIN=slony_test1
DEBUG_LEVEL=2
PGBENCH_SCALE=1
PGBENCH_CLIENTS=5
PGBENCH_TRANS=`expr 50000 / $PGBENCH_CLIENTS`
declare -a PIDS

trap '
	echo ""
	echo "**** user abort"
	if [ ! -z $pgbench_pid ] ; then
		echo "**** killing pgbench"
		kill -15 $pgbench_pid
	fi
	for node in 1 2 3 4 5 6; do
	    if [ ! -z $PIDS[$node] ] ; then
		echo "**** killing node daemon $node"
		kill -15 $PIDS[$node]
	    fi
	done
	exit -1
' 2 15

function initialize_slave_database () {
  local NODE=$1
  echo "**** creating database for Node $node"
  if ! createdb slony_test$NODE ; then
	kill $pgbench_pid 2>/dev/null
	kill $slon1_pid 2>/dev/null
	exit -1
  fi
  echo "**** creating pgbench tables"
    (
	    cat pgbench_schema.sql
    ) | psql -q slony_test$NODE
}

function extra_listen () {
    local ORIGIN=$1
    local RECEIVER=$2
    local PROVIDER=$3
    slonik <<EOF
$PREAMBLE
try {
    store listen (origin = $ORIGIN, receiver=$RECEIVER, provider=$PROVIDER);
}
EOF
}

function slonik_init () {
    local NODE=$1
    local PARENT=$2
    slonik <<_EOF_
$PREAMBLE
	try {
		store node (id = $NODE, comment = 'Node $NODE', event node=${PARENT});
		store path (server = $PARENT, client = $NODE, conninfo = 'dbname=slony_test$PARENT');
		store path (server = $NODE, client = $PARENT, conninfo = 'dbname=slony_test$NODE');
		store listen (origin = $PARENT, receiver = $NODE, provider=$PARENT );
		store listen (origin = $NODE, receiver = 1, provider=$NODE);
	}
	on error { exit -1; }
	echo 'Database slony_test$NODE added as Node $NODE';
_EOF_
}

function slon_start () {
    local NODE=$1
    echo "**** starting the Slony-I node daemon for slony_test$NODE"
    $TERMPROG -title "Slon node $NODE" -e sh -c "slon -d$DEBUG_LEVEL T1 dbname=slony_test$NODE; echo -n 'Enter>'; read line" &
    PID[$NODE]=$!
    pid=$PID[$NODE]
    echo "slon[$pid] on dbname=slony_test$NODE"
}

function slon_subscribe () {
    local SUBSCRIBER=$1
    local PROVIDER=$2

    ######################################################################
    # And now comes the moment where the big elephant starts to pee
    # and the attendants in the first row climb on their chairs ...
    ######################################################################
    slonik <<_EOF_
$PREAMBLE
	subscribe set ( id = 1, provider = $PROVIDER, receiver = $SUBSCRIBER, forward = yes );
_EOF_

}

function check_pgbench () {
  #####
  # Check that pgbench is still running
  #####
  if ! kill -0 $pgbench_pid 2>/dev/null ; then
    echo "**** pgbench terminated ???"
    kill $slon1_pid 2>/dev/null
    exit -1
  fi
}

######################################################################
# Preparations ... create a standalone pgbench database and
# have the "application" (pgbench) running.
######################################################################

#####
# Make sure the install is up to date
#####
WGM=`$WHICHPROG gmake | egrep '^/'`
if [ -z "$WGM" ] ; then
    MAKE=make
    CGNU=`make -v | grep GNU`
    if [ -z "$CGNU" ]; then
	echo "GNU Make not found - please install GNU Make"
	exit 1
    fi
else
    MAKE=gmake
fi
echo -n "**** running 'make install' in src directory ... "
if ! ${MAKE} -C .. install >$TMPOUT 2>&1 ; then
    echo "failed"; cat $TMPOUT; rm $TMPOUT; exit -1
fi
echo "done"
rm $TMPOUT

#####
# Remove old databases, if they exist
#####
echo "**** remove old test databases"
for db in 1 2 3 4 5 6; do
  dropdb slony_test$db || echo "**** ignored"
  sleep 1
done

#####
# Create the "Primary Node"
#####
echo "**** creating database for Node 1"

createdb $ORIGIN || exit -1
pgbench -i -s $PGBENCH_SCALE $ORIGIN
pg_dump -s $ORIGIN >pgbench_schema.sql

#####
# Start pgbench in the background and give it rampup time
#####
pgbench -n -s $PGBENCH_SCALE -c $PGBENCH_CLIENTS -t $PGBENCH_TRANS $ORIGIN &
pgbench_pid=$!
echo "**** pgbench is running in background with pid $pgbench_pid"
echo -n "**** sleeping 10 seconds to give pgbench time for rampup ... "
sleep 10
echo "done"

echo ""
echo "**********************************************************************"
echo "**** $DB1 is now a standalone database with a running pgbench"
echo "**********************************************************************"
echo ""

######################################################################
# Setup DB1 as the primary cluster T1 node, start the node daemon,
# and create a replication set containing the pgbench tables.
######################################################################

PREAMBLE="
cluster name = T1;
"
for node in 1 2 3 4 5 6; do
  PREAMBLE="$PREAMBLE
  node $node admin conninfo= 'dbname=slony_test$node';"
done


echo "**** initializing $DB1 as Primary Node for Slony-I cluster T1"
slonik <<_EOF_
$PREAMBLE
	init cluster (id = 1, comment = 'Node 1');
	echo 'Database $DB1 initialized as Node 1';
_EOF_
if [ $? -ne 0 ] ; then
	kill $pgbench_pid;
	exit -1
fi

slon_start 1

echo "**** creating a replication set containing the 4 pgbench tables ... "
slonik <<_EOF_
$PREAMBLE
try {
    table add key (node id = 1, fully qualified name = 'public.history');
} on error {
    exit -1;
}

try {
    create set (id = 1, origin = 1, comment = 'Set 1 - pgbench tables');
    set add table (set id = 1, origin = 1,
	id = 1, fully qualified name = 'public.accounts',
	comment = 'Table accounts');
    set add table (set id = 1, origin = 1,
	id = 2, fully qualified name = 'public.branches',
	comment = 'Table branches');
    set add table (set id = 1, origin = 1,
	id = 3, fully qualified name = 'public.tellers',
	comment = 'Table tellers');
    set add table (set id = 1, origin = 1,
	id = 4, fully qualified name = 'public.history',
	key = serial, comment = 'Table accounts');
} on error {
    exit -1;
}
_EOF_
if [ $? -ne 0 ] ; then
	echo "failed"
	kill $pgbench_pid 2>/dev/null
	kill $slon1_pid 2>/dev/null
	cat $TMPOUT
	rm $TMPOUT
	exit -1
fi
echo "**** set created"

#####
# Check that pgbench is still running
#####
check_pgbench

echo ""
echo "**********************************************************************"
echo "**** $DB1 is now the Slony-I origin for set 1"
echo "**********************************************************************"
echo ""

######################################################################
# Setup DB2 and DB3 as subscribers to node 1
######################################################################
for node in 2 3; do
  initialize_slave_database $node
  echo "**** initializing slony_test$node as Node $node of Slony-I cluster T1"
  slonik_init $node 1

  for n2 in 2 3; do
     if [[ $n2 != $node ]]; then
	extra_listen $node $n2 1
     fi
  done

  check_pgbench

  slon_start $node

  check_pgbench
  
  slon_subscribe $node 1

    echo ""
    echo "**********************************************************************"
    echo "**** slony_test$node should now be copying data and attempting to catch up."
    echo "**********************************************************************"
    echo ""
done
echo "done with cluster of [1,2,3]"

######################################################################
# Setup DB4 as subscriber to node 3; it only talks to 3, not 1 or 2
######################################################################
initialize_slave_database 4

echo "**** initializing slony_test4 as Node 4 of Slony-I cluster T1"
slonik <<_EOF_
$PREAMBLE
	try {
		store node (id = 4, comment = 'Node $node', event node=1);
		store path (server = 3, client = 4, conninfo = 'dbname=slony_test3');
		store path (server = 4, client = 3, conninfo = 'dbname=slony_test4');
		store listen (origin = 1, receiver = 4, provider=3);
		store listen (origin = 4, receiver = 1, provider=3);
	}
	on error { exit -1; }
	echo 'Database slony_test4 added as Node 4';
_EOF_

for node in 1 2; do
    extra_listen 4 $node 3
    extra_listen $node 4 3
done

check_pgbench

slon_start 4

check_pgbench

slon_subscribe 4 3

echo ""
echo "**********************************************************************"
echo "**** slony_test4 should now be copying data and attempting to catch up."
echo "**********************************************************************"
echo ""

for node in 5 6; do
    initialize_slave_database $node
    
  echo "**** initializing slony_test4 as Node 4 of Slony-I cluster T1"
  slonik <<_EOF_
$PREAMBLE
	try {
		store node (id = $node, comment = 'Node $node', event node=1);
		store path (server = 4, client = $node, conninfo = 'dbname=slony_test4');
		store path (server = $node, client = 4, conninfo = 'dbname=slony_test$node');
	}
	on error { exit -1; }
	echo 'Database slony_test$node added as Node $node';
_EOF_

    for source in 1 2 3 4 5 6; do
	if [[ $source != $node ]]; then
	    extra_listen $source $node 4
	    extra_listen $node $source 4
	fi
    done

    check_pgbench

    slon_start $node

    check_pgbench

    slon_subscribe $node 4
done

for node in 1 2 3 4 5 6; do
    echo "select  \"_T1\".rebuildlistenentries();" | psql slony_test$node
done

echo -n "**** waiting for pgbench to finish "
while kill -0 $pgbench_pid 2>/dev/null ; do
	echo -n "."
	sleep 10
done
echo "**** pgbench finished"
echo "**** please terminate the replication engines when caught up."

for node in 1 2 3 4 5 6; do
    pid=$PID[$node]
    wait $pid
done

kill $pgbench_pid 2>/dev/null

for node in 1 2 3 4 5 6; do
    pid=$PID[$node]
    kill $pid 2> /dev/null
done

echo -n "**** comparing databases ... "
for node in 1 2 3 4 5 6 ; do
  psql slony_test$node -o dump.tmp.$node.$$ <<_EOF_
	select 'accounts:'::text, aid, bid, abalance, filler
			from accounts order by aid;
	select 'branches:'::text, bid, bbalance, filler
			from branches order by bid;
	select 'tellers:'::text, tid, bid, tbalance, filler
			from tellers order by tid;
	select 'history:'::text, tid, bid, aid, delta, mtime, filler,
			"_Slony-I_T1_rowID" from history order by "_Slony-I_T1_rowID";
_EOF_
done

for node in 2 3 4 5 6; do
    if diff dump.tmp.1.$$ dump.tmp.$node.$$ >test_$node.1-$node.diff ; then
	echo "success - databases 1 and $node are equal."
	rm dump.tmp.$node.$$
	rm test_$node.1-$node.diff
    else
	echo "FAILED - see test_$node.1-$node.diff for database differences"
    fi
done
