mirror of
https://github.com/FirebirdSQL/firebird-qa.git
synced 2025-01-22 21:43:06 +01:00
Added/Updated tests\functional\replication\test_shutdown_during_applying_segments_leads_to_crash.py: Refactored: make test more robust when it can not remove some files from {repl_journal} and {repl_archive} folders. See notes
This commit is contained in:
parent
ae31248c88
commit
df907f51fc
@ -71,6 +71,14 @@ NOTES:
|
||||
Checked on 5.0.0.1068 on IBSurgeon test server, both for HDD and SSD drives.
|
||||
Checked again crash on 5.0.0.215 (only SS affected).
|
||||
ATTENTION. Further valuable changes/adjustings possible in this test!
|
||||
|
||||
[22.12.2023] pzotov
|
||||
Refactored: make test more robust when it can not remove some files from <repl_journal> and <repl_archive> folders.
|
||||
This can occurs because engine opens <repl_archive>/<DB_GUID> file every 10 seconds and check whether new segments must be applied.
|
||||
Because of this, attempt to drop this file exactly at that moment causes on Windows "PermissionError: [WinError 32]".
|
||||
This error must NOT propagate and interrupt entire test. Rather, we must only to log name of file that can not be dropped.
|
||||
|
||||
Checked on Windows, 6.0.0.193, 5.0.0.1304, 4.0.5.3042 (SS/CS for all).
|
||||
"""
|
||||
import os
|
||||
import shutil
|
||||
@ -119,17 +127,28 @@ def cleanup_folder(p):
|
||||
# Used for cleanup <repl_journal> and <repl_archive> when replication must be reset
|
||||
# in case when any error occurred during test execution.
|
||||
assert os.path.dirname(p) != p, f"@@@ ABEND @@@ CAN NOT operate in the file system root directory. Check your code!"
|
||||
|
||||
for root, dirs, files in os.walk(p):
|
||||
for f in files:
|
||||
os.unlink(os.path.join(root, f))
|
||||
# ::: NB ::: 22.12.2023.
|
||||
# We have to expect that attempt to delete of GUID and (maybe) archived segments can FAIL with
|
||||
# PermissionError: [WinError 32] The process cannot ... used by another process: /path/to/{GUID}
|
||||
# Also, we have to skip exception if file (segment) was just deleted by engine
|
||||
try:
|
||||
Path(root +'/' + f).unlink(missing_ok = True)
|
||||
except PermissionError as x:
|
||||
pass
|
||||
|
||||
for d in dirs:
|
||||
shutil.rmtree(os.path.join(root, d))
|
||||
return len(os.listdir(p))
|
||||
shutil.rmtree(os.path.join(root, d), ignore_errors = True)
|
||||
|
||||
return os.listdir(p)
|
||||
|
||||
#--------------------------------------------
|
||||
|
||||
def reset_replication(act_db_main, act_db_repl, db_main_file, db_repl_file):
|
||||
out_reset = ''
|
||||
failed_shutdown_db_map = {} # K = 'db_main', 'db_repl'; V = error that occurred when we attempted to change DB state to full shutdown (if it occurred)
|
||||
|
||||
with act_db_main.connect_server() as srv:
|
||||
|
||||
@ -149,22 +168,36 @@ def reset_replication(act_db_main, act_db_repl, db_main_file, db_repl_file):
|
||||
#
|
||||
try:
|
||||
srv.database.shutdown(database = f, mode = ShutdownMode.FULL, method = ShutdownMethod.FORCED, timeout = 0)
|
||||
except DatabaseError as e:
|
||||
out_reset += e.__str__()
|
||||
|
||||
# REMOVE db file from disk:
|
||||
###########################
|
||||
os.unlink(f)
|
||||
# REMOVE db file from disk: we can safely assume that this can be done because DB in full shutdown state.
|
||||
###########################
|
||||
os.unlink(f)
|
||||
except DatabaseError as e:
|
||||
failed_shutdown_db_map[ f ] = e.__str__()
|
||||
|
||||
|
||||
# Clean folders repl_journal and repl_archive: remove all files from there.
|
||||
# NOTE: test must NOT raise unrecoverable error if some of files in these folders can not be deleted.
|
||||
# Rather, this must be displayed as diff and test must be considered as just failed.
|
||||
for p in (repl_jrn_sub_dir,repl_arc_sub_dir):
|
||||
if cleanup_folder(repl_root_path / p) > 0:
|
||||
out_reset += f"Directory {str(p)} remains non-empty.\n"
|
||||
|
||||
remained_files = cleanup_folder(repl_root_path/p)
|
||||
|
||||
if out_reset == '':
|
||||
for a in (act_db_main,act_db_repl):
|
||||
d = a.db.db_path
|
||||
if remained_files:
|
||||
out_reset += '\n'.join( (f"Directory '{str(repl_root_path/p)}' remains non-empty. Could not delete file(s):", '\n'.join(remained_files)) )
|
||||
|
||||
# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
# xxx r e c r e a t e d b _ m a i n a n d d b _ r e p l xxx
|
||||
# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
for a in (act_db_main,act_db_repl):
|
||||
d = a.db.db_path
|
||||
failed_shutdown_msg = failed_shutdown_db_map.get( str(d), '' )
|
||||
if failed_shutdown_msg:
|
||||
# we could NOT change state of this database to full shutdown --> we must NOT recreate it.
|
||||
# Accumulate error messages in OUT arg (for displaying as diff):
|
||||
#
|
||||
out_reset += '\n'.join( failed_shutdown_msg )
|
||||
else:
|
||||
try:
|
||||
dbx = create_database(str(d), user = a.db.user)
|
||||
dbx.close()
|
||||
@ -180,8 +213,9 @@ def reset_replication(act_db_main, act_db_repl, db_main_file, db_repl_file):
|
||||
con.commit()
|
||||
except DatabaseError as e:
|
||||
out_reset += e.__str__()
|
||||
|
||||
|
||||
# Must remain EMPTY:
|
||||
####################
|
||||
return out_reset
|
||||
|
||||
#--------------------------------------------
|
||||
@ -499,12 +533,16 @@ def test_1(act_db_main: Action, act_db_repl: Action, capsys):
|
||||
#
|
||||
print('Problem(s) detected:')
|
||||
if out_prep.strip():
|
||||
print('out_prep:\n', out_prep)
|
||||
print('out_prep:')
|
||||
print(out_prep)
|
||||
if out_main.strip():
|
||||
print('out_main:\n', out_main)
|
||||
print('out_main:')
|
||||
print(out_main)
|
||||
if out_drop.strip():
|
||||
print('out_drop:\n', out_drop)
|
||||
print('out_drop:')
|
||||
print(out_drop)
|
||||
if out_reset.strip():
|
||||
print('out_reset:\n', out_reset)
|
||||
print('out_reset:')
|
||||
print(out_reset)
|
||||
|
||||
assert '' == capsys.readouterr().out
|
||||
|
Loading…
Reference in New Issue
Block a user