Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 18 additions & 13 deletions .github/workflows/build-gpdb.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ jobs:
},
{
"name": "test",
"extra_configure_flags": "--enable-debug-extensions"
"extra_configure_flags": "--enable-debug-extensions --enable-tap-tests"
}
]}'
# Output the matrix for GitHub Actions
Expand Down Expand Up @@ -141,6 +141,7 @@ jobs:
"contrib/postgres_fdw:installcheck",
"contrib/sslinfo:installcheck",
"contrib/tablefunc:installcheck",
"contrib/test_decoding:installcheck",
"contrib/test_parser:installcheck",
"contrib/test_shm_mq:installcheck",
"contrib/try_convert:generate-tests,installcheck",
Expand Down Expand Up @@ -168,6 +169,10 @@ jobs:
},
{"test":"ic-mirrorless",
"make_configs":["src/test/isolation2:installcheck-mirrorless"]
},
{"test":"ic-recovery",
"install_target":true,
"make_configs":["src/test/recovery:installcheck"]
}
]
}'
Expand Down Expand Up @@ -302,7 +307,7 @@ jobs:
ref: main
path: gpdb-devops
fetch-depth: 1

- name: Move gpdb-devops directory
run: |
set -ex pipefail
Expand Down Expand Up @@ -362,7 +367,7 @@ jobs:
# set env for debian build
export BUILD_DESTINATION=${SRC_DIR}/debian/build

if ! su - gpadmin -c "cd ${SRC_DIR} && SRC_DIR=${SRC_DIR} ENABLE_DEBUG=${{ env.ENABLE_DEBUG }} CONFIGURE_EXTRA_OPTS=${{ matrix.extra_configure_flags }} BUILD_DESTINATION=${BUILD_DESTINATION} ${SRC_DIR}/../gpdb-devops/build_automation/gpdb/scripts/configure-gpdb.sh"; then
if ! su - gpadmin -c "cd ${SRC_DIR} && SRC_DIR=${SRC_DIR} ENABLE_DEBUG=${{ env.ENABLE_DEBUG }} CONFIGURE_EXTRA_OPTS='${{ matrix.extra_configure_flags }}' BUILD_DESTINATION=${BUILD_DESTINATION} ${SRC_DIR}/../gpdb-devops/build_automation/gpdb/scripts/configure-gpdb.sh"; then
echo "::error::Configure script failed"
exit 1
fi
Expand All @@ -375,7 +380,7 @@ jobs:

# set env for debian build
export BUILD_DESTINATION=${SRC_DIR}/debian/build

if ! su - gpadmin -c "cd ${SRC_DIR} && SRC_DIR=${SRC_DIR} BUILD_DESTINATION=${BUILD_DESTINATION} ${SRC_DIR}/../gpdb-devops/build_automation/gpdb/scripts/build-gpdb.sh"; then
echo "::error::Build script failed"
exit 1
Expand All @@ -385,7 +390,7 @@ jobs:
if ! cp /usr/local/lib/libsigar.so ${BUILD_DESTINATION}/lib; then
echo "::error::Copy libsigar.so failed"
exit 1
fi
fi

if ! cp /usr/local/lib/libxerces* ${BUILD_DESTINATION}/lib; then
echo "::error::Copy libxerces failed"
Expand Down Expand Up @@ -464,7 +469,7 @@ jobs:
if ! echo "$GPDB_VERSION" | grep -qE '^[0-9]'; then
GPDB_VERSION="6.$GPDB_VERSION"
fi

echo "We will built ${GPDB_VERSION}"
export BUILD_DESTINATION=${SRC_DIR}/debian/build
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${BUILD_DESTINATION}/lib
Expand Down Expand Up @@ -745,7 +750,7 @@ jobs:
ref: main
path: gpdb-devops
fetch-depth: 1

- name: Move gpdb-devops directory
run: |
set -ex pipefail
Expand Down Expand Up @@ -1016,7 +1021,7 @@ jobs:
# set env for debian build
export BUILD_DESTINATION=/opt/greenplum-db-6

if ! su - gpadmin -c "cd ${SRC_DIR} && ./configure --with-perl --with-python --with-libxml --enable-mapreduce --with-gssapi --prefix=${BUILD_DESTINATION} --with-ldap --enable-gpperfmon --with-pam --with-openssl --disable-pxf --enable-ic-proxy --enable-orafce --without-mdblocales --with-zstd"; then
if ! su - gpadmin -c "cd ${SRC_DIR} && ./configure --with-perl --with-python --with-libxml --enable-mapreduce --with-gssapi --prefix=${BUILD_DESTINATION} --with-ldap --enable-gpperfmon --with-pam --with-openssl --disable-pxf --enable-ic-proxy --enable-orafce --without-mdblocales --with-zstd --enable-tap-tests"; then
echo "::error::Configure script failed"
exit 1
fi
Expand All @@ -1027,7 +1032,7 @@ jobs:
run: |
set -ex pipefail

{
{
if ! su - gpadmin -c "cd ${SRC_DIR} && NUM_PRIMARY_MIRROR_PAIRS='${{ matrix.num_primary_mirror_pairs }}' SRC_DIR=${SRC_DIR} ${SRC_DIR}/../gpdb-devops/build_automation/gpdb/scripts/create-gpdb-demo-cluster.sh"; then
echo "::error::Demo cluster creation failed"
exit 1
Expand Down Expand Up @@ -1066,7 +1071,7 @@ jobs:
# 1. Add a new check below for the setting
# 2. Follow the same pattern as optimizer
# 3. Update matrix entries to include the new setting

# Set PostgreSQL options if defined
PG_OPTS=""
if [[ "${{ matrix.pg_settings.optimizer != '' }}" == "true" ]]; then
Expand Down Expand Up @@ -1117,7 +1122,7 @@ jobs:
echo "Make command: make -C $dir $target"
echo "Environment:"
echo "- PGOPTIONS: ${PG_OPTS}"

# Create unique log file for this configuration
config_log="build-logs/details/make-${{ matrix.test }}-config$i.log"

Expand All @@ -1135,7 +1140,7 @@ jobs:
echo "::warning::Test execution failed for configuration $((i+1)): make -C $dir $target"
overall_status=1
fi

# Check for results directory
results_dir="${dir}/results"

Expand Down Expand Up @@ -1470,7 +1475,7 @@ jobs:
echo "Check test logs and regression files for details"
fi
fi

} >> "$GITHUB_STEP_SUMMARY"
- name: Notify on failure
if: |
Expand Down
10 changes: 10 additions & 0 deletions src/backend/access/transam/xlog.c
Original file line number Diff line number Diff line change
Expand Up @@ -5581,6 +5581,16 @@ readRecoveryCommandFile(void)
item->name)));
}

/*
* recovery.conf does not have restore_command,
* fallback to GUC restore_command_hint (already loaded)
*/
if (recoveryRestoreCommand == NULL && XLogRestoreCommandDummy != NULL) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the bracket should be on the next line according to PostgreSQL code style

recoveryRestoreCommand = XLogRestoreCommandDummy;
ereport(DEBUG2,
(errmsg_internal("restore_command = '%s'", recoveryRestoreCommand)));
}

/*
* Check for compulsory parameters
*/
Expand Down
7 changes: 7 additions & 0 deletions src/bin/pg_rewind/sql/run_test.sh
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ max_connections = 50
listen_addresses = '$LISTEN_ADDRESSES'
port = $PORT_MASTER
wal_keep_segments=5
restore_command_hint='touch $TEST_MASTER/restore_command_hint_flag'
EOF

# Accept replication connections on master
Expand Down Expand Up @@ -181,6 +182,12 @@ echo "Old master restarted after rewind."
wait_until_standby_streaming_state
PGOPTIONS=${PGOPTIONS_UTILITY} $STANDBY_PSQL -c "SELECT state FROM pg_stat_replication;"

# Make sure restore_command is taken from postgres.conf (restore_command_hint) when missing from recovery.conf
if [ ! -f "$TEST_MASTER/restore_command_hint_flag" ]; then
echo "Restore command was not found"
exit 1
fi

# Now promote master and run validation queries
pg_ctl -w -D $TEST_MASTER promote >>$log_path 2>&1
wait_until_master_is_promoted >>$log_path 2>&1
Expand Down
138 changes: 138 additions & 0 deletions src/test/recovery/t/139_restore_command_hint_timeline_collision.pl
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# Test for fixing timeline collision when restore_command is not written into
# the generated recovery.conf during recovery, resulting in the promoting node not seeing
# a newer timeline ID in the archive. Instead, restore_command is read as a
# restore_command_hint from postgresql.conf to resolve archive timeline conflicts.

use strict;
use warnings;
use PostgresNode;
use TestLib;
use Test::More tests => 6;
use File::Copy;
use File::Path qw(rmtree);

# Initialize master with archiving and streaming
my $node_master = get_new_node('master');
$node_master->init(has_archiving => 1, allows_streaming => 1);
$node_master->start;

# Create test data
$node_master->safe_psql('postgres', "CREATE TABLE test_tbl (id int)");
$node_master->safe_psql('postgres', "INSERT INTO test_tbl VALUES (1)");
Comment on lines +20 to +21
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about CTAS?


# Take backup for standby
my $backup_name = 'my_backup';
$node_master->backup($backup_name);

# Initialize standby from backup
my $node_standby = get_new_node('standby');
$node_standby->init_from_backup($node_master, $backup_name,
has_streaming => 1,
has_restoring => 1);

# Enable archiving on standby for timeline history archival
$node_standby->enable_archiving();

# Disable hot standby as it is not supported
$node_standby->append_conf('postgresql.conf', "hot_standby = off\n");

# Start standby and wait for it to catch up
$node_standby->start;
my $target_lsn = $node_master->lsn('insert');
$node_master->wait_for_catchup($node_standby, 'replay', $target_lsn);

# First promotion: standby moves to timeline 2
$node_standby->promote;
$node_standby->poll_query_until('postgres', "SELECT NOT pg_is_in_recovery()")
or die "Timed out waiting for first promotion";

# Force checkpoint and wait for timeline history file to be archived
$node_standby->safe_psql('postgres', "CHECKPOINT");

# Verify 00000002.history is archived
my $standby_archive = $node_standby->archive_dir;
$node_standby->poll_query_until('postgres',
"SELECT size IS NOT NULL FROM pg_stat_file('pg_xlog/archive_status/00000002.history.done', true);")
or die "Timed out waiting for 00000002.history to be archived";

ok(-f "$standby_archive/00000002.history",
'timeline 2 history file archived after first promotion');

# Crash standby
$node_standby->stop('immediate');

# Rebuild standby from master using pg_basebackup
my $standby_datadir = $node_standby->data_dir;
File::Path::rmtree($standby_datadir);

command_ok(['pg_basebackup',
'-D', $standby_datadir,
'-h', $node_master->host,
'-p', $node_master->port,
'--target-gp-dbid', '123',
'-x',
'--write-recovery-conf'],
'pg_basebackup standby from master');

# Verify recovery.conf does not contain restore_command
my $recovery_conf = "$standby_datadir/recovery.conf";
ok(-f $recovery_conf, 'recovery.conf exists after pg_basebackup');

my $recovery_conf_content = slurp_file($recovery_conf);
unlike($recovery_conf_content, qr/restore_command\s*=/,
'recovery.conf does not contain restore_command');

# Restore configuration
$node_standby->append_conf('postgresql.conf', "hot_standby = off\n");
$node_standby->enable_archiving();

# Append restore_command_hint to postgres.conf
my $path = TestLib::perl2host($standby_archive);
$path =~ s{\\}{\\\\}g if ($TestLib::windows_os);
my $restore_cmd = $TestLib::windows_os
? qq{copy "$path\\\\%f" "%p"}
: qq{cp "$path/%f" "%p"};

$node_standby->append_conf('postgresql.conf',
"restore_command_hint = '$restore_cmd'\n");

# Start standby (now back on timeline 1)
$node_standby->start;
sleep(2);

# Kill master and promote standby again
$node_master->stop('immediate');

# Promote using pg_ctl directly since PostgresNode->promote() verification fails in mirror mode
# (after pg_basebackup we have gp_contentid=0 and it affects connection acceptance).
# Verify via logs.
my $pgdata = $node_standby->data_dir;
my $logfile = $node_standby->logfile;
print "### Promoting node \"" . $node_standby->name . "\"\n";
TestLib::system_or_bail('pg_ctl', '-D', $pgdata, '-l', $logfile, 'promote');

# Wait for second promotion to complete
my $max_wait = 60;
my $waited = 0;
my $promotions = 0;

while ($waited < $max_wait) {
my $log_content = slurp_file($logfile);
my @promotion_matches = $log_content =~ /database system is ready to accept connections/g;
$promotions = scalar @promotion_matches;

last if $promotions >= 2;

sleep(1);
$waited++;
}

die "Second promotion did not complete in time (found $promotions ready messages)" if $promotions < 2;

# Verify timeline 2 was selected only once and currently active timeline is 3
my $log_content = slurp_file($logfile);
my @timeline_2_matches = $log_content =~ /selected new timeline ID: 2/g;
is(scalar @timeline_2_matches, 1, 'timeline 2 selected once');

my @timeline_3_matches = $log_content =~ /selected new timeline ID: 3/g;
is(scalar @timeline_3_matches, 1, 'timeline 3 selected once');
Loading