Skip to content

Commit 629b732

Browse files
committed
more checks and tests
1 parent 1811eb7 commit 629b732

File tree

4 files changed

+211
-13
lines changed

4 files changed

+211
-13
lines changed

src/catchup.c

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,27 @@ catchup_preflight_checks(PGNodeInfo *source_node_info, PGconn *source_conn,
174174
}
175175
}
176176

177+
/* check backup_label absence in dest */
178+
if (current.backup_mode != BACKUP_MODE_FULL)
179+
{
180+
char backup_label_filename[MAXPGPATH];
181+
182+
join_path_components(backup_label_filename, dest_pgdata, PG_BACKUP_LABEL_FILE);
183+
if (fio_access(backup_label_filename, F_OK, FIO_LOCAL_HOST) == 0)
184+
elog(ERROR, "Destination directory contains \"" PG_BACKUP_LABEL_FILE "\" file");
185+
}
186+
187+
/* check that destination database is shutdowned cleanly */
188+
if (current.backup_mode != BACKUP_MODE_FULL)
189+
{
190+
DBState state;
191+
state = get_system_dbstate(dest_pgdata, FIO_LOCAL_HOST);
192+
/* see states in postgres sources (src/include/catalog/pg_control.h) */
193+
if (state != DB_SHUTDOWNED && state != DB_SHUTDOWNED_IN_RECOVERY)
194+
elog(ERROR, "Postmaster in destination directory \"%s\" must be stopped cleanly",
195+
dest_pgdata);
196+
}
197+
177198
/* Check that connected PG instance, source and destination PGDATA are the same */
178199
{
179200
uint64 source_conn_id, source_id, dest_id;
@@ -206,16 +227,6 @@ catchup_preflight_checks(PGNodeInfo *source_node_info, PGconn *source_conn,
206227
elog(ERROR, "Ptrack is disabled");
207228
}
208229

209-
/* check backup_label absence in dest */
210-
if (current.backup_mode != BACKUP_MODE_FULL)
211-
{
212-
char backup_label_filename[MAXPGPATH];
213-
214-
join_path_components(backup_label_filename, dest_pgdata, PG_BACKUP_LABEL_FILE);
215-
if (fio_access(backup_label_filename, F_OK, FIO_LOCAL_HOST) == 0)
216-
elog(ERROR, "Destination directory contains \"" PG_BACKUP_LABEL_FILE "\" file");
217-
}
218-
219230
if (current.from_replica && exclusive_backup)
220231
elog(ERROR, "Catchup from standby is only available for PostgreSQL >= 9.6");
221232

src/pg_probackup.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#include "access/xlog_internal.h"
1919
#include "utils/pg_crc.h"
20+
#include "catalog/pg_control.h"
2021

2122
#if PG_VERSION_NUM >= 120000
2223
#include "common/logging.h"
@@ -1176,6 +1177,7 @@ extern uint64 get_system_identifier(const char *pgdata_path, fio_location locati
11761177
extern uint64 get_remote_system_identifier(PGconn *conn);
11771178
extern uint32 get_data_checksum_version(bool safe);
11781179
extern pg_crc32c get_pgcontrol_checksum(const char *pgdata_path);
1180+
extern DBState get_system_dbstate(const char *pgdata_path, fio_location location);
11791181
extern uint32 get_xlog_seg_size(const char *pgdata_path);
11801182
extern void get_redo(const char *pgdata_path, fio_location pgdata_location, RedoParams *redo);
11811183
extern void set_min_recovery_point(pgFile *file, const char *backup_path,

src/util.c

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@
1010

1111
#include "pg_probackup.h"
1212

13-
#include "catalog/pg_control.h"
14-
1513
#include <time.h>
1614

1715
#include <unistd.h>
@@ -351,6 +349,22 @@ get_pgcontrol_checksum(const char *pgdata_path)
351349
return ControlFile.crc;
352350
}
353351

352+
DBState
353+
get_system_dbstate(const char *pgdata_path, fio_location location)
354+
{
355+
ControlFileData ControlFile;
356+
char *buffer;
357+
size_t size;
358+
359+
buffer = slurpFile(pgdata_path, XLOG_CONTROL_FILE, &size, false, location);
360+
if (buffer == NULL)
361+
return 0;
362+
digestControlFile(&ControlFile, buffer, size);
363+
pg_free(buffer);
364+
365+
return ControlFile.state;
366+
}
367+
354368
void
355369
get_redo(const char *pgdata_path, fio_location pgdata_location, RedoParams *redo)
356370
{

tests/catchup.py

Lines changed: 172 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import os
2+
import signal
23
import unittest
34
from .helpers.ptrack_helpers import ProbackupTest, ProbackupException
45

@@ -525,6 +526,176 @@ def test_local_tablespace_without_mapping(self):
525526
e.message,
526527
'\n Unexpected Error Message: {0}\n CMD: {1}'.format(repr(e.message), self.cmd))
527528

529+
# Cleanup
530+
src_pg.stop()
531+
self.del_test_dir(module_name, self.fname)
532+
533+
def test_running_dest_postmaster(self):
534+
"""
535+
Test that we detect running postmaster in destination
536+
"""
537+
# preparation 1: source
538+
src_pg = self.make_simple_node(
539+
base_dir = os.path.join(module_name, self.fname, 'src'),
540+
set_replication = True,
541+
pg_options = { 'wal_log_hints': 'on' }
542+
)
543+
src_pg.slow_start()
544+
545+
# preparation 2: destination
546+
dst_pg = self.make_empty_node(os.path.join(module_name, self.fname, 'dst'))
547+
self.catchup_node(
548+
backup_mode = 'FULL',
549+
source_pgdata = src_pg.data_dir,
550+
destination_node = dst_pg,
551+
options = ['-d', 'postgres', '-p', str(src_pg.port), '--stream']
552+
)
553+
dst_options = {}
554+
dst_options['port'] = str(dst_pg.port)
555+
self.set_auto_conf(dst_pg, dst_options)
556+
dst_pg.slow_start()
557+
# leave running destination postmaster
558+
#dst_pg.stop()
559+
560+
# try delta catchup
561+
try:
562+
self.catchup_node(
563+
backup_mode = 'DELTA',
564+
source_pgdata = src_pg.data_dir,
565+
destination_node = dst_pg,
566+
options = ['-d', 'postgres', '-p', str(src_pg.port), '--stream']
567+
)
568+
self.assertEqual(1, 0, "Expecting Error because postmaster in destination is running.\n Output: {0} \n CMD: {1}".format(
569+
repr(self.output), self.cmd))
570+
except ProbackupException as e:
571+
self.assertIn(
572+
'ERROR: Postmaster with pid ',
573+
e.message,
574+
'\n Unexpected Error Message: {0}\n CMD: {1}'.format(repr(e.message), self.cmd))
575+
576+
# Cleanup
577+
src_pg.stop()
578+
self.del_test_dir(module_name, self.fname)
579+
580+
def test_same_db_id(self):
581+
"""
582+
Test that we detect different id's of source and destination
583+
"""
584+
# preparation:
585+
# source
586+
src_pg = self.make_simple_node(
587+
base_dir = os.path.join(module_name, self.fname, 'src'),
588+
set_replication = True
589+
)
590+
src_pg.slow_start()
591+
# destination
592+
dst_pg = self.make_empty_node(os.path.join(module_name, self.fname, 'dst'))
593+
self.catchup_node(
594+
backup_mode = 'FULL',
595+
source_pgdata = src_pg.data_dir,
596+
destination_node = dst_pg,
597+
options = ['-d', 'postgres', '-p', str(src_pg.port), '--stream']
598+
)
599+
# fake destination
600+
fake_dst_pg = self.make_simple_node(base_dir = os.path.join(module_name, self.fname, 'fake_dst'))
601+
# fake source
602+
fake_src_pg = self.make_simple_node(base_dir = os.path.join(module_name, self.fname, 'fake_src'))
603+
604+
# try delta catchup (src (with correct src conn), fake_dst)
605+
try:
606+
self.catchup_node(
607+
backup_mode = 'DELTA',
608+
source_pgdata = src_pg.data_dir,
609+
destination_node = fake_dst_pg,
610+
options = ['-d', 'postgres', '-p', str(src_pg.port), '--stream']
611+
)
612+
self.assertEqual(1, 0, "Expecting Error because database identifiers mismatch.\n Output: {0} \n CMD: {1}".format(
613+
repr(self.output), self.cmd))
614+
except ProbackupException as e:
615+
self.assertIn(
616+
'ERROR: Database identifiers mismatch: ',
617+
e.message,
618+
'\n Unexpected Error Message: {0}\n CMD: {1}'.format(repr(e.message), self.cmd))
619+
620+
# try delta catchup (fake_src (with wrong src conn), dst)
621+
try:
622+
self.catchup_node(
623+
backup_mode = 'DELTA',
624+
source_pgdata = fake_src_pg.data_dir,
625+
destination_node = dst_pg,
626+
options = ['-d', 'postgres', '-p', str(src_pg.port), '--stream']
627+
)
628+
self.assertEqual(1, 0, "Expecting Error because database identifiers mismatch.\n Output: {0} \n CMD: {1}".format(
629+
repr(self.output), self.cmd))
630+
except ProbackupException as e:
631+
self.assertIn(
632+
'ERROR: Database identifiers mismatch: ',
633+
e.message,
634+
'\n Unexpected Error Message: {0}\n CMD: {1}'.format(repr(e.message), self.cmd))
635+
636+
# Cleanup
637+
src_pg.stop()
638+
self.del_test_dir(module_name, self.fname)
639+
640+
def test_destination_dbstate(self):
641+
"""
642+
Test that we detect that destination pg is not cleanly shutdowned
643+
"""
644+
# preparation 1: source
645+
src_pg = self.make_simple_node(
646+
base_dir = os.path.join(module_name, self.fname, 'src'),
647+
set_replication = True,
648+
pg_options = { 'wal_log_hints': 'on' }
649+
)
650+
src_pg.slow_start()
651+
652+
# preparation 2: destination
653+
dst_pg = self.make_empty_node(os.path.join(module_name, self.fname, 'dst'))
654+
self.catchup_node(
655+
backup_mode = 'FULL',
656+
source_pgdata = src_pg.data_dir,
657+
destination_node = dst_pg,
658+
options = ['-d', 'postgres', '-p', str(src_pg.port), '--stream']
659+
)
660+
661+
# try #1
662+
try:
663+
self.catchup_node(
664+
backup_mode = 'DELTA',
665+
source_pgdata = src_pg.data_dir,
666+
destination_node = dst_pg,
667+
options = ['-d', 'postgres', '-p', str(src_pg.port), '--stream']
668+
)
669+
self.assertEqual(1, 0, "Expecting Error because destination pg is not cleanly shutdowned.\n Output: {0} \n CMD: {1}".format(
670+
repr(self.output), self.cmd))
671+
except ProbackupException as e:
672+
self.assertIn(
673+
'ERROR: Destination directory contains "backup_label" file',
674+
e.message,
675+
'\n Unexpected Error Message: {0}\n CMD: {1}'.format(repr(e.message), self.cmd))
676+
677+
# try #2
678+
dst_options = {}
679+
dst_options['port'] = str(dst_pg.port)
680+
self.set_auto_conf(dst_pg, dst_options)
681+
dst_pg.slow_start()
682+
self.assertNotEqual(dst_pg.pid, 0, "Cannot detect pid of running postgres")
683+
os.kill(dst_pg.pid, signal.SIGKILL)
684+
try:
685+
self.catchup_node(
686+
backup_mode = 'DELTA',
687+
source_pgdata = src_pg.data_dir,
688+
destination_node = dst_pg,
689+
options = ['-d', 'postgres', '-p', str(src_pg.port), '--stream']
690+
)
691+
self.assertEqual(1, 0, "Expecting Error because destination pg is not cleanly shutdowned.\n Output: {0} \n CMD: {1}".format(
692+
repr(self.output), self.cmd))
693+
except ProbackupException as e:
694+
self.assertIn(
695+
'must be stopped cleanly',
696+
e.message,
697+
'\n Unexpected Error Message: {0}\n CMD: {1}'.format(repr(e.message), self.cmd))
698+
699+
# Cleanup
528700
src_pg.stop()
529-
# Clean after yourself
530701
self.del_test_dir(module_name, self.fname)

0 commit comments

Comments
 (0)