#!/bin/bash

WD=$(readlink -f ./test)
PG92_BIN=~/inst/pg9.2/bin
PG94_BIN=~/inst/pg9.4/bin

export LC_ALL=C
export PGDATABASE=test

[[ "$WD" ]] || exit 1
cd "$WD" || exit 1

try_run ()
{
    exec 3>&1 4>&2
    exec > logfile 2>&1

    echo "init" >&4

    mkdir m s w

    "$PG92_BIN"/initdb -A trust -E UTF-8 --locale=C m/9.2 || exit 1
    "$PG94_BIN"/initdb -A trust -E UTF-8 --locale=C m/9.4 || exit 1

    cat <<EOF > m/9.2/postgresql.conf
port = 5430
shared_buffers = 32MB
wal_level = hot_standby
# shared_buffers = 512MB
# checkpoint_segments = 30
# checkpoint_timeout = 15min
# checkpoint_completion_target = 1.0
archive_mode = on
archive_command = 'test ! -f "$WD/w/%f" && cp %p "$WD/w/%f"'
max_wal_senders = 5
hot_standby = on
log_line_prefix = '%t '
log_checkpoints = on
lc_messages = C
autovacuum = off
EOF
    cat <<EOF >> m/9.2/pg_hba.conf
local   replication     all                                trust
host    replication     all        127.0.0.1/32            trust
host    replication     all        ::1/128                 trust
EOF

    cp m/9.2/postgresql.conf m/9.4/postgresql.conf
    cp m/9.2/pg_hba.conf m/9.4/pg_hba.conf

    echo "start master 9.2" >&4

    "$PG92_BIN"/pg_ctl -w -D m/9.2 -l logfile.m.92 start || exit 1

    echo "base backup" >&4

    "$PG92_BIN"/pg_basebackup -p 5430 -c fast -X stream -D s/9.2/ || exit 1
    cat <<EOF >> s/9.2/postgresql.conf
port = 5431
EOF
    cat <<EOF > recovery.conf
restore_command = 'cp "$WD/w/%f" %p'
standby_mode = on
EOF
    cp s/9.2/postgresql.conf sb_postgresql.conf
    cp recovery.conf s/9.2/recovery.conf

    echo "start standby 9.2" >&4

    "$PG92_BIN"/pg_ctl -w -D s/9.2 -l logfile.s.92 start || exit 1

    #
    "$PG92_BIN"/psql -X -q -p 5430 -d postgres -c 'create database test'
    "$PG92_BIN"/psql -X -q -p 5430 -f- <<EOF
create table test (id int primary key, v text);
insert into test select n, 'this is long line for fill table, line number ' || n from generate_series(1, 1000000) n;
-- vacuum analyze test;
select pg_current_xlog_insert_location();
EOF

    echo "run DELETE" >&4

    # some transactions
    for (( i = 300; i <= 300000; i += 300 )); do
        "$PG92_BIN"/psql -X -q -p 5430 -c "delete from test where id <= $i"
    done
    "$PG92_BIN"/psql -X -q -p 5430 -c 'select pg_current_xlog_insert_location()'

    lsn=$("$PG92_BIN"/psql -X -At -q -p 5430 -c 'select pg_current_xlog_insert_location()')

    echo "stop master 9.2" >&4

    "$PG92_BIN"/pg_ctl -w -D m/9.2 stop || exit 1

    echo "wait for standby catch up master: $lsn" >&4
    diff=-1
    while [[ "$diff" -lt 0 ]]; do
        sleep 1;
        diff=$("$PG92_BIN"/psql -X -At -q -p 5431 -c "select pg_xlog_location_diff(pg_last_xlog_replay_location(), '$lsn')")
        echo "diff: $diff" >&4
    done

    #copy_last_wal

    echo "stop standby 9.2" >&4

    "$PG92_BIN"/pg_ctl -w -D s/9.2 stop || exit 1

    # check restartpoint, it is not equal to master shutdown
    grep 'recovery restart point at\|restored log file' logfile.s.92

    # compare checkpoint location
    pr -m -t -w 140 <("$PG92_BIN"/pg_controldata m/9.2/) <("$PG92_BIN"/pg_controldata s/9.2/) > controldata.before_upgrade
    less -x8 controldata.before_upgrade | grep checkpoint\\\|Minimum

    # tar -C .. -cf - --exclude=before.tar.bz2 test | pbzip2 > before.tar.bz2

    # upgrade

    echo "run upgrade" >&4

    "$PG94_BIN"/pg_upgrade -b "$PG92_BIN" -B "$PG94_BIN" -d m/9.2 -D m/9.4 -k -c || exit 1

    "$PG94_BIN"/pg_upgrade -b "$PG92_BIN" -B "$PG94_BIN" -d m/9.2 -D m/9.4 -k || exit 1

    echo "reset wal_level" >&4
    "$PG94_BIN"/pg_ctl -w -D m/9.4 -l logfile.m.94 start || exit 1
    "$PG94_BIN"/pg_ctl -w -D m/9.4 stop || exit 1

    ps axu | grep postgres

    rsync -q -v -i --archive --delete --hard-links --size-only m/9.2 m/9.4 s --log-file rsync.log

    # restore recovery.conf and postgresql.conf
    cp recovery.conf s/9.4/recovery.conf
    cp sb_postgresql.conf s/9.4/postgresql.conf

    echo "start standby 9.4" >&4

    "$PG94_BIN"/pg_ctl -w -D s/9.4 -l logfile.s.94 start || exit 1

    # count

    "$PG94_BIN"/psql -X -e -p 5431 -f- <<EOF
explain select count(1) from test;
select pg_last_xlog_replay_location();
EOF

    cnt=$("$PG94_BIN"/psql -X -At -q -p 5431 -c 'select count(1) from test')
    echo "$cnt" >&3
    echo "count: $cnt"

    echo "stop standby 9.4" >&4

    "$PG94_BIN"/pg_ctl -w -D s/9.4 stop || exit 1

    exec 1>&3 2>&4
}

copy_last_wal ()
{
    echo "fixing (send last WAL to archive) ..." >&4

    local r_lsn=$("$PG92_BIN"/pg_controldata m/9.2/ | grep 'Latest checkpoint location:' | rev | awk '{ print $1 }')
    local lsn=$(rev <<< "$r_lsn")

    local tl=00000001
    local off=$(IFS=/ read -r r_off tmp <<< "$r_lsn"; rev <<< "${r_off:6}")
    local seg=$(IFS=/ read -r tmp r_seg <<< "$r_lsn"; rev <<< "$r_seg")
    local wal="$(printf "%8s%8s%8s" $tl $seg $off)"
    wal=${wal// /0}

    echo "need WAL: '$wal'" >&4
    # do not start old master after that
    cp -n m/9.2/pg_xlog/"$wal" "$WD"/w/

    echo "wait it replay ..." >&4

    local diff=-1
    while [[ "$diff" -lt 0 ]]; do
        sleep 1;
        >&4 "$PG92_BIN"/psql -X -At -q -p 5431 -c \
            "select pg_last_xlog_replay_location(), '$lsn', pg_xlog_location_diff(pg_last_xlog_replay_location(), '$lsn')"
        diff=$("$PG92_BIN"/psql -X -At -q -p 5431 -c "select pg_xlog_location_diff(pg_last_xlog_replay_location(), '$lsn')")
        echo "diff: $diff" >&4
    done    
}

loops=0
while true; do
    rm -r m s w logfile.* controldata.* analyze_new_cluster.sh delete_old_cluster.sh recovery.conf rsync.log logfile sb_postgresql.conf

    let loops++
    cnt=$(try_run) || exit 1
    echo "=== run $loops, cnt: $cnt"
    if [[ $cnt = '700000' ]]; then
        :;
    else
        break
    fi
done
