Skip to content

Commit 1167c8d

Browse files
committed
Merge branch 'release_2_5' into release_2_5-issue313
2 parents 0ef2339 + 97b23c4 commit 1167c8d

File tree

3 files changed

+107
-43
lines changed

3 files changed

+107
-43
lines changed

src/utils/file.c

+39-42
Original file line numberDiff line numberDiff line change
@@ -777,6 +777,34 @@ fio_fwrite(FILE* f, void const* buf, size_t size)
777777
return fwrite(buf, 1, size, f);
778778
}
779779

780+
/*
781+
* Write buffer to descriptor by calling write(),
782+
* If size of written data is less than buffer size,
783+
* then try to write what is left.
784+
* We do this to get honest errno if there are some problems
785+
* with filesystem, since writing less than buffer size
786+
* is not considered an error.
787+
*/
788+
static ssize_t
789+
durable_write(int fd, const char* buf, size_t size)
790+
{
791+
off_t current_pos = 0;
792+
size_t bytes_left = size;
793+
794+
while (bytes_left > 0)
795+
{
796+
int rc = write(fd, buf + current_pos, bytes_left);
797+
798+
if (rc <= 0)
799+
return rc;
800+
801+
bytes_left -= rc;
802+
current_pos += rc;
803+
}
804+
805+
return size;
806+
}
807+
780808
/* Write data to the file synchronously */
781809
ssize_t
782810
fio_write(int fd, void const* buf, size_t size)
@@ -806,7 +834,7 @@ fio_write(int fd, void const* buf, size_t size)
806834
}
807835
else
808836
{
809-
return write(fd, buf, size);
837+
return durable_write(fd, buf, size);
810838
}
811839
}
812840

@@ -816,7 +844,7 @@ fio_write_impl(int fd, void const* buf, size_t size, int out)
816844
int rc;
817845
fio_header hdr;
818846

819-
rc = write(fd, buf, size);
847+
rc = durable_write(fd, buf, size);
820848

821849
hdr.arg = 0;
822850
hdr.size = 0;
@@ -838,34 +866,6 @@ fio_fwrite_async(FILE* f, void const* buf, size_t size)
838866
: fwrite(buf, 1, size, f);
839867
}
840868

841-
/*
842-
* Write buffer to descriptor by calling write(),
843-
* If size of written data is less than buffer size,
844-
* then try to write what is left.
845-
* We do this to get honest errno if there are some problems
846-
* with filesystem, since writing less than buffer size
847-
* is not considered an error.
848-
*/
849-
static ssize_t
850-
durable_write(int fd, const char* buf, size_t size)
851-
{
852-
off_t current_pos = 0;
853-
size_t bytes_left = size;
854-
855-
while (bytes_left > 0)
856-
{
857-
int rc = write(fd, buf + current_pos, bytes_left);
858-
859-
if (rc <= 0)
860-
return rc;
861-
862-
bytes_left -= rc;
863-
current_pos += rc;
864-
}
865-
866-
return size;
867-
}
868-
869869
/* Write data to the file */
870870
/* TODO: support async report error */
871871
ssize_t
@@ -950,23 +950,22 @@ fio_fwrite_async_compressed(FILE* f, void const* buf, size_t size, int compress_
950950
}
951951
else
952952
{
953-
char uncompressed_buf[BLCKSZ];
954953
char *errormsg = NULL;
955-
int32 uncompressed_size = fio_decompress(uncompressed_buf, buf, size, compress_alg, &errormsg);
954+
char decompressed_buf[BLCKSZ];
955+
int32 decompressed_size = fio_decompress(decompressed_buf, buf, size, compress_alg, &errormsg);
956956

957-
if (uncompressed_size < 0)
957+
if (decompressed_size < 0)
958958
elog(ERROR, "%s", errormsg);
959959

960-
return fwrite(uncompressed_buf, 1, uncompressed_size, f);
960+
return fwrite(decompressed_buf, 1, decompressed_size, f);
961961
}
962962
}
963963

964964
static void
965965
fio_write_compressed_impl(int fd, void const* buf, size_t size, int compress_alg)
966966
{
967-
int rc;
968-
int32 uncompressed_size;
969-
char uncompressed_buf[BLCKSZ];
967+
int32 decompressed_size;
968+
char decompressed_buf[BLCKSZ];
970969

971970
/* If the previous command already have failed,
972971
* then there is no point in bashing a head against the wall
@@ -975,14 +974,12 @@ fio_write_compressed_impl(int fd, void const* buf, size_t size, int compress_alg
975974
return;
976975

977976
/* decompress chunk */
978-
uncompressed_size = fio_decompress(uncompressed_buf, buf, size, compress_alg, &async_errormsg);
977+
decompressed_size = fio_decompress(decompressed_buf, buf, size, compress_alg, &async_errormsg);
979978

980-
if (uncompressed_size < 0)
979+
if (decompressed_size < 0)
981980
return;
982981

983-
rc = write(fd, uncompressed_buf, uncompressed_size);
984-
985-
if (rc <= 0)
982+
if (durable_write(fd, decompressed_buf, decompressed_size) <= 0)
986983
{
987984
async_errormsg = pgut_malloc(ERRMSG_MAX_LEN);
988985
snprintf(async_errormsg, ERRMSG_MAX_LEN, "%s", strerror(errno));

tests/helpers/ptrack_helpers.py

+3
Original file line numberDiff line numberDiff line change
@@ -2003,6 +2003,9 @@ def stopped_in_breakpoint(self):
20032003
return True
20042004
return False
20052005

2006+
def quit(self):
2007+
self.proc.terminate()
2008+
20062009
# use for breakpoint, run, continue
20072010
def _execute(self, cmd, running=True):
20082011
output = []

tests/restore.py

+65-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
import shutil
1010
import json
1111
from shutil import copyfile
12-
from testgres import QueryException
12+
from testgres import QueryException, StartNodeException
13+
from stat import S_ISDIR
1314

1415

1516
module_name = 'restore'
@@ -3856,3 +3857,66 @@ def test_concurrent_restore(self):
38563857

38573858
# Clean after yourself
38583859
self.del_test_dir(module_name, fname)
3860+
3861+
# @unittest.skip("skip")
3862+
def test_restore_issue_313(self):
3863+
"""
3864+
Check that partially restored PostgreSQL instance cannot be started
3865+
"""
3866+
fname = self.id().split('.')[3]
3867+
backup_dir = os.path.join(self.tmp_path, module_name, fname, 'backup')
3868+
node = self.make_simple_node(
3869+
base_dir=os.path.join(module_name, fname, 'node'),
3870+
set_replication=True,
3871+
initdb_params=['--data-checksums'])
3872+
3873+
self.init_pb(backup_dir)
3874+
self.add_instance(backup_dir, 'node', node)
3875+
self.set_archiving(backup_dir, 'node', node)
3876+
node.slow_start()
3877+
3878+
# FULL backup
3879+
backup_id = self.backup_node(backup_dir, 'node', node)
3880+
node.cleanup()
3881+
3882+
count = 0
3883+
filelist = self.get_backup_filelist(backup_dir, 'node', backup_id)
3884+
for file in filelist:
3885+
# count only nondata files
3886+
if int(filelist[file]['is_datafile']) == 0 and int(filelist[file]['size']) > 0:
3887+
count += 1
3888+
3889+
node_restored = self.make_simple_node(
3890+
base_dir=os.path.join(module_name, fname, 'node_restored'))
3891+
node_restored.cleanup()
3892+
self.restore_node(backup_dir, 'node', node_restored)
3893+
3894+
gdb = self.restore_node(backup_dir, 'node', node, gdb=True, options=['--progress'])
3895+
gdb.verbose = False
3896+
gdb.set_breakpoint('restore_non_data_file')
3897+
gdb.run_until_break()
3898+
gdb.continue_execution_until_break(count - 2)
3899+
gdb.quit()
3900+
3901+
# emulate the user or HA taking care of PG configuration
3902+
for fname in os.listdir(node_restored.data_dir):
3903+
if fname.endswith('.conf'):
3904+
os.rename(
3905+
os.path.join(node_restored.data_dir, fname),
3906+
os.path.join(node.data_dir, fname))
3907+
3908+
try:
3909+
node.slow_start()
3910+
# we should die here because exception is what we expect to happen
3911+
self.assertEqual(
3912+
1, 0,
3913+
"Expecting Error because backup is not fully restored")
3914+
except StartNodeException as e:
3915+
self.assertIn(
3916+
'Cannot start node',
3917+
e.message,
3918+
'\n Unexpected Error Message: {0}\n CMD: {1}'.format(
3919+
repr(e.message), self.cmd))
3920+
3921+
# Clean after yourself
3922+
self.del_test_dir(module_name, fname)

0 commit comments

Comments
 (0)