Enhance database connection handling in BotDB class with error checking for file existence and access permissions. Implement methods for database integrity checks and WAL file cleanup. Update database initialization to use absolute project path for improved reliability.
This commit is contained in:
@@ -23,11 +23,38 @@ class BotDB:
|
||||
|
||||
def connect(self):
|
||||
"""Создание соединения и курсора."""
|
||||
try:
|
||||
# Проверяем существование файла базы данных
|
||||
if not os.path.exists(self.db_file):
|
||||
self.logger.error(f"Файл базы данных не найден: {self.db_file}")
|
||||
raise FileNotFoundError(f"Файл базы данных не найден: {self.db_file}")
|
||||
|
||||
# Проверяем права доступа к файлу
|
||||
if not os.access(self.db_file, os.R_OK | os.W_OK):
|
||||
self.logger.error(f"Нет прав доступа к файлу базы данных: {self.db_file}")
|
||||
raise PermissionError(f"Нет прав доступа к файлу базы данных: {self.db_file}")
|
||||
|
||||
# Добавляем таймаут для предотвращения зависаний
|
||||
self.conn = sqlite3.connect(self.db_file, timeout=10.0)
|
||||
|
||||
# Включаем WAL режим для лучшей производительности
|
||||
self.conn.execute("PRAGMA journal_mode=WAL")
|
||||
self.conn.execute("PRAGMA synchronous=NORMAL")
|
||||
self.conn.execute("PRAGMA cache_size=10000")
|
||||
self.conn.execute("PRAGMA temp_store=MEMORY")
|
||||
|
||||
self.cursor = self.conn.cursor()
|
||||
self.logger.info(f"Успешное подключение к базе данных: {self.db_file}")
|
||||
|
||||
except sqlite3.Error as e:
|
||||
self.logger.error(f"Ошибка SQLite при подключении к базе данных: {e}")
|
||||
raise
|
||||
except (FileNotFoundError, PermissionError) as e:
|
||||
self.logger.error(f"Ошибка файловой системы при подключении к базе данных: {e}")
|
||||
raise
|
||||
except Exception as e:
|
||||
self.logger.error(f"Неожиданная ошибка при подключении к базе данных: {e}")
|
||||
raise
|
||||
|
||||
def create_table(self, sql_script):
|
||||
"""
|
||||
@@ -1379,10 +1406,19 @@ class BotDB:
|
||||
|
||||
def close(self):
|
||||
"""Закрытие соединения и курсора."""
|
||||
try:
|
||||
if self.cursor:
|
||||
self.cursor.close()
|
||||
self.cursor = None
|
||||
except Exception as e:
|
||||
self.logger.error(f"Ошибка при закрытии курсора: {e}")
|
||||
|
||||
try:
|
||||
if self.conn:
|
||||
self.conn.close()
|
||||
self.conn = None
|
||||
except Exception as e:
|
||||
self.logger.error(f"Ошибка при закрытии соединения: {e}")
|
||||
|
||||
async def check_user_in_blacklist_async(self, user_id: int):
|
||||
"""
|
||||
@@ -1397,3 +1433,43 @@ class BotDB:
|
||||
"""
|
||||
loop = asyncio.get_event_loop()
|
||||
return await loop.run_in_executor(self.executor, self.get_blacklist_users_by_id, user_id)
|
||||
|
||||
def check_database_integrity(self):
|
||||
"""
|
||||
Проверяет целостность базы данных и очищает WAL файлы.
|
||||
"""
|
||||
try:
|
||||
self.connect()
|
||||
# Проверяем целостность базы данных
|
||||
self.cursor.execute("PRAGMA integrity_check")
|
||||
integrity_result = self.cursor.fetchone()
|
||||
|
||||
if integrity_result and integrity_result[0] == "ok":
|
||||
self.logger.info("Проверка целостности базы данных прошла успешно")
|
||||
# Очищаем WAL файлы
|
||||
self.cursor.execute("PRAGMA wal_checkpoint(TRUNCATE)")
|
||||
self.logger.info("WAL файлы очищены")
|
||||
else:
|
||||
self.logger.warning(f"Проблемы с целостностью базы данных: {integrity_result}")
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Ошибка при проверке целостности базы данных: {e}")
|
||||
raise
|
||||
finally:
|
||||
self.close()
|
||||
|
||||
def cleanup_wal_files(self):
|
||||
"""
|
||||
Очищает WAL файлы и переключает на DELETE режим для предотвращения проблем с I/O.
|
||||
"""
|
||||
try:
|
||||
self.connect()
|
||||
# Переключаем на DELETE режим для очистки WAL файлов
|
||||
self.cursor.execute("PRAGMA journal_mode=DELETE")
|
||||
self.cursor.execute("PRAGMA journal_mode=WAL")
|
||||
self.logger.info("WAL файлы очищены и режим восстановлен")
|
||||
except Exception as e:
|
||||
self.logger.error(f"Ошибка при очистке WAL файлов: {e}")
|
||||
raise
|
||||
finally:
|
||||
self.close()
|
||||
|
||||
152
database/fix_database.py
Normal file
152
database/fix_database.py
Normal file
@@ -0,0 +1,152 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Скрипт для диагностики и исправления проблем с базой данных Telegram бота.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import sqlite3
|
||||
from pathlib import Path
|
||||
|
||||
def check_database_file(db_path):
|
||||
"""Проверяет состояние файла базы данных."""
|
||||
print(f"Проверка файла: {db_path}")
|
||||
|
||||
if not os.path.exists(db_path):
|
||||
print(f"❌ Файл базы данных не найден: {db_path}")
|
||||
return False
|
||||
|
||||
# Проверяем права доступа
|
||||
if not os.access(db_path, os.R_OK | os.W_OK):
|
||||
print(f"❌ Нет прав доступа к файлу: {db_path}")
|
||||
return False
|
||||
|
||||
# Проверяем размер файла
|
||||
file_size = os.path.getsize(db_path)
|
||||
print(f"✅ Размер файла: {file_size} байт")
|
||||
|
||||
return True
|
||||
|
||||
def check_wal_files(db_path):
|
||||
"""Проверяет WAL файлы."""
|
||||
db_dir = os.path.dirname(db_path)
|
||||
db_name = os.path.basename(db_path)
|
||||
base_name = os.path.splitext(db_name)[0]
|
||||
|
||||
wal_file = os.path.join(db_dir, f"{base_name}.db-wal")
|
||||
shm_file = os.path.join(db_dir, f"{base_name}.db-shm")
|
||||
|
||||
print(f"\nПроверка WAL файлов:")
|
||||
|
||||
if os.path.exists(wal_file):
|
||||
wal_size = os.path.getsize(wal_file)
|
||||
print(f"✅ WAL файл найден: {wal_file} ({wal_size} байт)")
|
||||
else:
|
||||
print(f"ℹ️ WAL файл не найден: {wal_file}")
|
||||
|
||||
if os.path.exists(shm_file):
|
||||
shm_size = os.path.getsize(shm_file)
|
||||
print(f"✅ SHM файл найден: {shm_file} ({shm_size} байт)")
|
||||
else:
|
||||
print(f"ℹ️ SHM файл не найден: {shm_file}")
|
||||
|
||||
return wal_file, shm_file
|
||||
|
||||
def test_database_connection(db_path):
|
||||
"""Тестирует подключение к базе данных."""
|
||||
print(f"\nТестирование подключения к базе данных...")
|
||||
|
||||
try:
|
||||
conn = sqlite3.connect(db_path, timeout=10.0)
|
||||
cursor = conn.cursor()
|
||||
|
||||
# Проверяем версию SQLite
|
||||
cursor.execute("SELECT sqlite_version()")
|
||||
version = cursor.fetchone()[0]
|
||||
print(f"✅ SQLite версия: {version}")
|
||||
|
||||
# Проверяем таблицы
|
||||
cursor.execute("SELECT name FROM sqlite_master WHERE type='table'")
|
||||
tables = cursor.fetchall()
|
||||
print(f"✅ Найдено таблиц: {len(tables)}")
|
||||
|
||||
# Проверяем целостность
|
||||
cursor.execute("PRAGMA integrity_check")
|
||||
integrity = cursor.fetchone()[0]
|
||||
if integrity == "ok":
|
||||
print("✅ Целостность базы данных: OK")
|
||||
else:
|
||||
print(f"⚠️ Проблемы с целостностью: {integrity}")
|
||||
|
||||
conn.close()
|
||||
return True
|
||||
|
||||
except sqlite3.Error as e:
|
||||
print(f"❌ Ошибка SQLite: {e}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"❌ Неожиданная ошибка: {e}")
|
||||
return False
|
||||
|
||||
def cleanup_wal_files(db_path):
|
||||
"""Очищает WAL файлы."""
|
||||
print(f"\nОчистка WAL файлов...")
|
||||
|
||||
try:
|
||||
conn = sqlite3.connect(db_path, timeout=10.0)
|
||||
cursor = conn.cursor()
|
||||
|
||||
# Переключаем на DELETE режим для очистки WAL
|
||||
cursor.execute("PRAGMA journal_mode=DELETE")
|
||||
cursor.execute("PRAGMA journal_mode=WAL")
|
||||
|
||||
# Принудительно создаем checkpoint
|
||||
cursor.execute("PRAGMA wal_checkpoint(TRUNCATE)")
|
||||
|
||||
conn.close()
|
||||
print("✅ WAL файлы очищены")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Ошибка при очистке WAL файлов: {e}")
|
||||
return False
|
||||
|
||||
def main():
|
||||
"""Основная функция."""
|
||||
print("🔧 Диагностика базы данных Telegram бота")
|
||||
print("=" * 50)
|
||||
|
||||
# Определяем путь к базе данных
|
||||
current_dir = os.getcwd()
|
||||
db_path = os.path.join(current_dir, 'database', 'tg-bot-database.db')
|
||||
|
||||
print(f"Текущая директория: {current_dir}")
|
||||
print(f"Путь к базе данных: {db_path}")
|
||||
|
||||
# Проверяем файл базы данных
|
||||
if not check_database_file(db_path):
|
||||
print("\n❌ Файл базы данных недоступен. Проверьте права доступа и существование файла.")
|
||||
return
|
||||
|
||||
# Проверяем WAL файлы
|
||||
wal_file, shm_file = check_wal_files(db_path)
|
||||
|
||||
# Тестируем подключение
|
||||
if not test_database_connection(db_path):
|
||||
print("\n❌ Не удалось подключиться к базе данных.")
|
||||
return
|
||||
|
||||
# Очищаем WAL файлы
|
||||
if cleanup_wal_files(db_path):
|
||||
print("\n✅ База данных проверена и исправлена.")
|
||||
else:
|
||||
print("\n⚠️ База данных проверена, но не удалось очистить WAL файлы.")
|
||||
|
||||
print("\n📋 Рекомендации:")
|
||||
print("1. Убедитесь, что у процесса есть права на запись в директорию database/")
|
||||
print("2. Проверьте свободное место на диске")
|
||||
print("3. Если проблемы продолжаются, попробуйте перезапустить бота")
|
||||
print("4. В крайнем случае, создайте резервную копию и пересоздайте базу данных")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -14,7 +14,9 @@ class BaseDependencyFactory:
|
||||
self.config = configparser.ConfigParser()
|
||||
self.config.read(config_path)
|
||||
self.settings = {}
|
||||
self.database = BotDB(current_dir, 'tg-bot-database.db')
|
||||
# Используем абсолютный путь к директории проекта
|
||||
project_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
self.database = BotDB(project_dir, 'tg-bot-database.db')
|
||||
|
||||
for section in self.config.sections():
|
||||
self.settings[section] = {}
|
||||
|
||||
Reference in New Issue
Block a user