#!/bin/bash
#  replication gets stuck on the replica with:
#      'ERROR:  int2vector has too many elements'
#  15 seems immune. 16 it happens with a lot of columns
unset PGSERVICE PGSERVICEFILE PGDATA PGPORT PGDATABASE PG_PROJECT 
# PGPASSFILE is used
PROJECT=HEAD
if   [[ "$1" == "REL_15_STABLE" || "$1" == "15" ]]; then PROJECT=REL_15_STABLE
elif [[ "$1" == "HEAD"          || "$1" == "16" ]]; then PROJECT=HEAD  # master as of 2023.01.15
fi
NUM_INSTANCES=2  num_loop=$(( $NUM_INSTANCES - 1 ))
      BIN_DIR=$HOME/pg_stuff/pg_installations/pgsql.$PROJECT/bin
     POSTGRES=$BIN_DIR/postgres
       INITDB=$BIN_DIR/initdb
         psql=$BIN_DIR/psql
         PATH=$BIN_DIR:$PATH
                            TMP_DIR=${HOME}/tmp/int2vector/${PROJECT}
if [[   -d $TMP_DIR ]]; then rm -rf ${HOME}/tmp/int2vector/${PROJECT}; fi
if [[ ! -d $TMP_DIR ]]; then mkdir -p $TMP_DIR; fi
   devel_file=${TMP_DIR}/.devel
  BASE_PORT=6210  port1=$(( $BASE_PORT + 0 ))
                  port2=$(( $port1 + 1 ))
                  port3=$(( $port1 + 2 ))
scale=1  dbname=postgres  pubname=pub1  subname=sub1
echo "PROJECT $PROJECT   port $port1"
echo 's3kr1t' > $devel_file
                  max_wal_senders=10  # publication side
            max_replication_slots=10  # publication side and subscription side
             max_worker_processes=12  # subscription side
  max_logical_replication_workers=10  # subscription side
max_sync_workers_per_subscription=4   # subscription side
for n in `seq 1 $NUM_INSTANCES`; do
  port=$(( $BASE_PORT + $n -1 ))
    data_dir=$TMP_DIR/pgsql.instance${n}/data
  server_dir=$TMP_DIR/pgsql.instance${n}
  if [[ -d $data_dir ]]; then echo "$data_dir exists - remove [bailing out]"; exit; fi
  $INITDB --pgdata=$data_dir --encoding=UTF8 --auth=scram-sha-256 --pwfile=$devel_file &> initdb.$port.log 
  rc=$?; if [[ $rc -ne 0 ]]; then echo "-- initdb $rc"; fi
 ( $POSTGRES -D $data_dir -p $port -W 1 \
    --wal_level=logical \
    --max_replication_slots=$max_replication_slots \
    --max_worker_processes=$max_worker_processes \
    --max_logical_replication_workers=$max_logical_replication_workers \
    --max_wal_senders=$max_wal_senders \
    --max_sync_workers_per_subscription=$max_sync_workers_per_subscription \
    --logging_collector=on \
    --log_directory=${server_dir} \
    --log_filename=logfile.${port} \
    --log_replication_commands=on \
    --autovacuum=off \
    --client_min_messages=error & )
  pg_isready -q -p $port; rc=$?; while [[ $rc -ne 0 ]]; do sleep 1; pg_isready -q -p $port; rc=$?; done
done

$BIN_DIR/pgbench --port=$BASE_PORT --quiet --initialize --scale=$scale $dbname
echo "alter table pgbench_history add column hid serial primary key" | $psql -d $dbname -p $BASE_PORT -X

# combination of column changes seems necessary to force the int2vector error
(
# ADD_COLUMNS=50   # HEAD: 50x: ok (still small enough?)
# ADD_COLUMNS=600  # HEAD: 600x: int2vector error (if added generated column also)
# ADD_COLUMNS=200  # HEAD: 200x: int2vector error (if added generated column also)
  ADD_COLUMNS=100  # HEAD: 100x: int2vector error (if added generated column also)
# ADD_COLUMNS=1500  # v15: ok  (in 15 never seen this error)
for t in pgbench_accounts pgbench_branches pgbench_tellers pgbench_history; do
for n in `seq 1 ${ADD_COLUMNS}`; do 
  colname=c$n  coltype=text;  echo "alter table $t add column ${colname} ${coltype};"
done 
done 
echo "alter table pgbench_accounts add column aid_gencol numeric generated always
         as (case when aid >0 then log(2, aid) else null end) stored; " 
) | $psql -qX -p $BASE_PORT -d $dbname 

$psql -p $BASE_PORT -d $dbname -qXc "
select current_setting('server_version') \"server\", 'pgbench_accounts' \"table\", count(*) \"#columns\"
                                                  from information_schema.columns where table_name = 'pgbench_accounts'
union all select '', 'pgbench_branches', count(*) from information_schema.columns where table_name = 'pgbench_branches'
union all select '', 'pgbench_history' , count(*) from information_schema.columns where table_name = 'pgbench_history'
union all select '', 'pgbench_tellers' , count(*) from information_schema.columns where table_name = 'pgbench_tellers'
union all select '', 'pgbench_*' , count(*) from information_schema.columns where table_name ~ '^pgbench_' ;"

target_port=$(( $BASE_PORT + 1 ))
pg_dump -Fc -p $BASE_PORT \
  --exclude-table-data=pgbench_history  --exclude-table-data=pgbench_accounts \
  --exclude-table-data=pgbench_branches --exclude-table-data=pgbench_tellers  \
  -t pgbench_history  -t pgbench_accounts \
  -t pgbench_branches -t pgbench_tellers  \
  -d $dbname | pg_restore -1 -p $target_port -d $dbname

for n in `seq 1 $num_loop`; do
  pubport=$(( $BASE_PORT + $n - 1 ))
  subport=$(( $BASE_PORT + $n     ))
  appname='casc:'${subport}'<'${pubport}
  echo "create publication  $pubname for all tables" | $psql -d $dbname -p $pubport -q -X
  echo "create subscription $subname
        connection 'port=${pubport} dbname=${dbname} application_name=${appname}'
        publication $pubname with (enabled=false, slot_name=${subname}_${subport});" | $psql -d $dbname -p $subport -q -X
  echo "alter subscription $subname enable; " | $psql -d $dbname -p $subport -q -a -X
done

# do a pgbench run
echo "-- $BIN_DIR/pgbench -c 32 -T 6 -p $BASE_PORT $dbname -- scale $scale"
         $BIN_DIR/pgbench -c 32 -T 6 -p $BASE_PORT $dbname |& grep -E '^tps'

c_a1=$(echo "select count(*) from pgbench_accounts"|$psql -d$dbname -qtAX -p$port1) 
c_b1=$(echo "select count(*) from pgbench_branches"|$psql -d$dbname -qtAX -p$port1) 
c_t1=$(echo "select count(*) from pgbench_tellers "|$psql -d$dbname -qtAX -p$port1) 
c_h1=$(echo "select count(*) from pgbench_history "|$psql -d$dbname -qtAX -p$port1) 
mda1=$(echo "select aid,bid,abalance,filler            from pgbench_accounts order by aid"|$psql -d $dbname -qtAX -p$port1|md5sum|cut -b 1-5) 
mdb1=$(echo "select bid,bbalance,filler                from pgbench_branches order by bid"|$psql -d $dbname -qtAX -p$port1|md5sum|cut -b 1-5)
mdt1=$(echo "select tid,bid,tbalance,filler            from pgbench_tellers  order by tid"|$psql -d $dbname -qtAX -p$port1|md5sum|cut -b 1-5)
mdh1=$(echo "select hid,bid,aid,delta,mtime,filler,hid from pgbench_history  order by hid"|$psql -d $dbname -qtAX -p$port1|md5sum|cut -b 1-5)
md5_1=$(echo "$mda1  $mdb1  $mdt1  $mdh1" | md5sum | cut -b 1-5)
printf "a,b,t,h  $port1    %6d  %6d  %6d  %6d   $mda1  $mdb1  $mdt1  $mdh1     $md5_1\n" $c_a1  $c_b1  $c_t1  $c_h1
ver1=$( echo "select current_setting('server_version'); -- substring(version(),1,70)" | $psql -d $dbname -qtAXp $port1 )
ver2=$( echo "select current_setting('server_version'); -- substring(version(),1,70)" | $psql -d $dbname -qtAXp $port2 )
echo 
rc=0
while [[ $rc -eq 0 ]]; do
  mda2=$(echo "select aid,bid,abalance,filler            from pgbench_accounts order by aid"|$psql -d$dbname -qtAXp$port2|md5sum|cut -b 1-5) 
  mdb2=$(echo "select bid,bbalance,filler                from pgbench_branches order by bid"|$psql -d$dbname -qtAXp$port2|md5sum|cut -b 1-5)
  mdt2=$(echo "select tid,bid,tbalance,filler            from pgbench_tellers  order by tid"|$psql -d$dbname -qtAXp$port2|md5sum|cut -b 1-5)
  mdh2=$(echo "select hid,bid,aid,delta,mtime,filler,hid from pgbench_history  order by hid"|$psql -d$dbname -qtAXp$port2|md5sum|cut -b 1-5)
  c_a2=$(echo "select count(*) from pgbench_accounts"|$psql -d$dbname -qtAX -p$port2) 
  c_b2=$(echo "select count(*) from pgbench_branches"|$psql -d$dbname -qtAX -p$port2) 
  c_t2=$(echo "select count(*) from pgbench_tellers "|$psql -d$dbname -qtAX -p$port2) 
  c_h2=$(echo "select count(*) from pgbench_history "|$psql -d$dbname -qtAX -p$port2) 
  md5_2=$(echo "$mda2  $mdb2  $mdt2  $mdh2" | md5sum | cut -b 1-5)
  echo "-- $POSTGRES"
  printf "a,b,t,h  $port1    %7d %6d %6d %6d    $mda1  $mdb1  $mdt1  $mdh1   $md5_1   " $c_a1 $c_b1 $c_t1 $c_h1; echo "$ver1"
  printf "a,b,t,h  $port2    %7d %6d %6d %6d    $mda2  $mdb2  $mdt2  $mdh2   $md5_2   " $c_a2 $c_b2 $c_t2 $c_h2; echo "$ver2"
  if [[ "$md5_1" == "$md5_2" ]] ; then echo "OK, primary and replica are the same. Done."; break; fi
  # if that int2vector-error occurred, there is really no point in waiting (I did that a few times)
  if grep -Eqi 'ERROR:  int2vector has too many elements' $TMP_DIR/pgsql.instance2/logfile.*; then
     echo "better bail out - replication is stuck"  
   # break
  fi
  sleep 10
  rc=$?
done
for n in `seq 1 $NUM_INSTANCES`; do 
  port=$(( $BASE_PORT + $n -1 ))
  inst_dir=$TMP_DIR/pgsql.instance${n}
  data_dir=$TMP_DIR/pgsql.instance${n}/data
  grep -Ei 'ERROR:  int2vector has too many elements' $TMP_DIR/pgsql.instance${n}/logf*
  $BIN_DIR/pg_ctl stop -D $data_dir
done

