-
Notifications
You must be signed in to change notification settings - Fork 452
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Replace sqlite_utils with db_corruption_handling.sqlite_replacement
- Loading branch information
Showing
18 changed files
with
232 additions
and
204 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
from __future__ import annotations | ||
|
||
import logging | ||
import sqlite3 | ||
from contextlib import contextmanager | ||
from pathlib import Path | ||
from typing import Union | ||
|
||
logger = logging.getLogger('db_corruption_handling') | ||
|
||
|
||
class DatabaseIsCorrupted(Exception): | ||
pass | ||
|
||
|
||
@contextmanager | ||
def handling_malformed_db_error(db_filepath: Path): | ||
# Used in all methods of Connection and Cursor classes where the database corruption error can occur | ||
try: | ||
yield | ||
except Exception as e: | ||
if _is_malformed_db_exception(e): | ||
_mark_db_as_corrupted(db_filepath) | ||
raise DatabaseIsCorrupted(str(db_filepath)) from e | ||
raise | ||
|
||
|
||
def handle_db_if_corrupted(db_filename: Union[str, Path]): | ||
# Checks if the database is marked as corrupted and handles it by removing the database file and the marker file | ||
db_path = Path(db_filename) | ||
marker_path = get_corrupted_db_marker_path(db_path) | ||
if marker_path.exists(): | ||
_handle_corrupted_db(db_path) | ||
|
||
|
||
def get_corrupted_db_marker_path(db_filepath: Path) -> Path: | ||
return Path(str(db_filepath) + '.is_corrupted') | ||
|
||
|
||
def _is_malformed_db_exception(exception): | ||
return isinstance(exception, sqlite3.DatabaseError) and 'malformed' in str(exception) | ||
|
||
|
||
def _mark_db_as_corrupted(db_filepath: Path): | ||
# Creates a new `*.is_corrupted` marker file alongside the database file | ||
marker_path = get_corrupted_db_marker_path(db_filepath) | ||
marker_path.touch() | ||
|
||
|
||
def _handle_corrupted_db(db_path: Path): | ||
# Removes the database file and the marker file | ||
if db_path.exists(): | ||
logger.warning(f'Database file was marked as corrupted, removing it: {db_path}') | ||
db_path.unlink() | ||
|
||
marker_path = get_corrupted_db_marker_path(db_path) | ||
if marker_path.exists(): | ||
logger.warning(f'Removing the corrupted database marker: {marker_path}') | ||
marker_path.unlink() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
15 changes: 15 additions & 0 deletions
15
src/tribler/core/utilities/db_corruption_handling/tests/conftest.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import pytest | ||
|
||
from tribler.core.utilities.db_corruption_handling.sqlite_replacement import connect | ||
|
||
|
||
@pytest.fixture(name='db_filepath') | ||
def db_filepath_fixture(tmp_path): | ||
return tmp_path / 'test.db' | ||
|
||
|
||
@pytest.fixture(name='connection') | ||
def connection_fixture(db_filepath): | ||
connection = connect(str(db_filepath)) | ||
yield connection | ||
connection.close() |
57 changes: 57 additions & 0 deletions
57
src/tribler/core/utilities/db_corruption_handling/tests/test_base.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import sqlite3 | ||
from pathlib import Path | ||
from unittest.mock import Mock, patch | ||
|
||
import pytest | ||
|
||
|
||
from tribler.core.utilities.db_corruption_handling.base import DatabaseIsCorrupted, handle_db_if_corrupted, \ | ||
handling_malformed_db_error | ||
|
||
malformed_error = sqlite3.DatabaseError('database disk image is malformed') | ||
|
||
|
||
def test_handling_malformed_db_error__no_error(db_filepath): | ||
# If no error is raised, the database should not be marked as corrupted | ||
with handling_malformed_db_error(db_filepath): | ||
pass | ||
|
||
assert not Path(str(db_filepath) + '.is_corrupted').exists() | ||
|
||
|
||
def test_handling_malformed_db_error__malformed_error(db_filepath): | ||
# Malformed database errors should be handled by marking the database as corrupted | ||
with pytest.raises(DatabaseIsCorrupted): | ||
with handling_malformed_db_error(db_filepath): | ||
raise malformed_error | ||
|
||
assert Path(str(db_filepath) + '.is_corrupted').exists() | ||
|
||
|
||
def test_handling_malformed_db_error__other_error(db_filepath): | ||
# Other errors should not be handled like malformed database errors | ||
class TestError(Exception): | ||
pass | ||
|
||
with pytest.raises(TestError): | ||
with handling_malformed_db_error(db_filepath): | ||
raise TestError() | ||
|
||
assert not Path(str(db_filepath) + '.is_corrupted').exists() | ||
|
||
|
||
def test_handle_db_if_corrupted__corrupted(db_filepath: Path): | ||
# If the corruption marker is found, the corrupted database file is removed | ||
marker_path = Path(str(db_filepath) + '.is_corrupted') | ||
marker_path.touch() | ||
|
||
handle_db_if_corrupted(db_filepath) | ||
assert not db_filepath.exists() | ||
assert not marker_path.exists() | ||
|
||
|
||
@patch('tribler.core.utilities.db_corruption_handling.base._handle_corrupted_db') | ||
def test_handle_db_if_corrupted__not_corrupted(handle_corrupted_db: Mock, db_filepath: Path): | ||
# If the corruption marker is not found, the handling of the database is not performed | ||
handle_db_if_corrupted(db_filepath) | ||
handle_corrupted_db.assert_not_called() |
Oops, something went wrong.