feat(database-migrations): implement database migration manager and related components
- Add DatabaseMigrationManager for orchestrating database migrations, including planning and executing migration steps. - Introduce models for migration state, execution context, and migration steps. - Implement MigrationPlanner to generate migration plans based on current and target versions. - Create MigrationRegistry for registering and managing migration steps. - Develop SchemaVersionResolver to determine the current database schema version. - Add SQLiteSchemaInspector for inspecting SQLite database structures. - Implement progress reporting tools using rich for visualizing migration progress. - Introduce SQLiteUserVersionStore for managing schema version storage in SQLite.
This commit is contained in:
171
src/common/database/migrations/bootstrap.py
Normal file
171
src/common/database/migrations/bootstrap.py
Normal file
@@ -0,0 +1,171 @@
|
||||
"""数据库迁移启动桥接层。"""
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from sqlalchemy.engine import Engine
|
||||
|
||||
from src.common.logger import get_logger
|
||||
|
||||
from .builtin import (
|
||||
LATEST_SCHEMA_VERSION,
|
||||
build_default_migration_registry,
|
||||
build_default_schema_version_resolver,
|
||||
)
|
||||
from .exceptions import DatabaseMigrationExecutionError
|
||||
from .manager import DatabaseMigrationManager
|
||||
from .models import DatabaseMigrationState, MigrationPlan, ResolvedSchemaVersion, SchemaVersionSource
|
||||
from .registry import MigrationRegistry
|
||||
from .resolver import SchemaVersionResolver
|
||||
from .version_store import SQLiteUserVersionStore
|
||||
|
||||
logger = get_logger("database_migration")
|
||||
|
||||
|
||||
class DatabaseMigrationBootstrapper:
|
||||
"""数据库迁移启动桥接器。
|
||||
|
||||
该桥接器负责把数据库迁移基础设施接入现有启动流程,同时保持如下约束:
|
||||
1. 若数据库为空,则直接交给当前模型定义建出最新结构;
|
||||
2. 若数据库版本高于当前代码支持的最新版本,则立即终止启动;
|
||||
3. 若存在待执行迁移步骤,则在正常建表流程之前先执行迁移;
|
||||
4. 若数据库已是最新结构但尚未写入 ``user_version``,则在建表后补写版本号。
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
manager: DatabaseMigrationManager,
|
||||
latest_schema_version: int = LATEST_SCHEMA_VERSION,
|
||||
) -> None:
|
||||
"""初始化数据库迁移启动桥接器。
|
||||
|
||||
Args:
|
||||
manager: 数据库迁移编排器。
|
||||
latest_schema_version: 当前代码支持的最新 schema 版本号。
|
||||
"""
|
||||
self.manager = manager
|
||||
self.latest_schema_version = latest_schema_version
|
||||
|
||||
def prepare_database(self) -> DatabaseMigrationState:
|
||||
"""为数据库初始化阶段准备迁移状态。
|
||||
|
||||
Returns:
|
||||
DatabaseMigrationState: 迁移准备完成后的数据库状态。
|
||||
|
||||
Raises:
|
||||
DatabaseMigrationExecutionError: 当数据库版本高于当前代码支持版本时抛出。
|
||||
"""
|
||||
with self.manager.engine.connect() as connection:
|
||||
resolved_version = self.manager.resolver.resolve(connection)
|
||||
|
||||
if resolved_version.version > self.latest_schema_version:
|
||||
raise DatabaseMigrationExecutionError(
|
||||
"当前数据库版本高于代码内注册的最新迁移版本,已拒绝继续启动。"
|
||||
f" 数据库版本={resolved_version.version},代码支持版本={self.latest_schema_version}"
|
||||
)
|
||||
|
||||
if resolved_version.source == SchemaVersionSource.EMPTY_DATABASE:
|
||||
logger.info(
|
||||
"检测到空数据库,将直接根据当前模型创建最新结构。"
|
||||
f" 目标版本={self.latest_schema_version}"
|
||||
)
|
||||
return self._build_noop_state(
|
||||
current_version=resolved_version.version,
|
||||
target_version=self.latest_schema_version,
|
||||
resolved_state=resolved_version,
|
||||
)
|
||||
|
||||
migration_state = self.manager.describe_state(target_version=self.latest_schema_version)
|
||||
if not migration_state.requires_migration():
|
||||
logger.info(
|
||||
f"数据库 schema 已是目标版本,无需迁移。当前版本={migration_state.resolved_version.version}"
|
||||
)
|
||||
return migration_state
|
||||
|
||||
logger.info(
|
||||
"检测到数据库需要迁移,"
|
||||
f" 当前版本={migration_state.resolved_version.version},目标版本={migration_state.target_version}"
|
||||
)
|
||||
self.manager.migrate(target_version=self.latest_schema_version)
|
||||
return self.manager.describe_state(target_version=self.latest_schema_version)
|
||||
|
||||
def finalize_database(self, migration_state: DatabaseMigrationState) -> None:
|
||||
"""在数据库初始化末尾补写最终 schema 版本号。
|
||||
|
||||
该方法主要负责两类场景:
|
||||
1. 空库首次建表完成后,将 ``user_version`` 写入为最新版本;
|
||||
2. 已是最新结构但此前未写入 ``user_version`` 的数据库,补写版本号。
|
||||
|
||||
Args:
|
||||
migration_state: 初始化前解析得到的迁移状态。
|
||||
"""
|
||||
if migration_state.requires_migration():
|
||||
return
|
||||
if migration_state.target_version <= 0:
|
||||
return
|
||||
if migration_state.resolved_version.source == SchemaVersionSource.PRAGMA:
|
||||
return
|
||||
|
||||
with self.manager.engine.begin() as connection:
|
||||
self.manager.version_store.write_version(connection, migration_state.target_version)
|
||||
|
||||
logger.info(
|
||||
"数据库 schema 版本写入完成。"
|
||||
f" 来源={migration_state.resolved_version.source.value},"
|
||||
f" 写入版本={migration_state.target_version}"
|
||||
)
|
||||
|
||||
def _build_noop_state(
|
||||
self,
|
||||
current_version: int,
|
||||
target_version: int,
|
||||
resolved_state: ResolvedSchemaVersion,
|
||||
) -> DatabaseMigrationState:
|
||||
"""构建无迁移动作的数据库状态对象。
|
||||
|
||||
Args:
|
||||
current_version: 当前数据库版本号。
|
||||
target_version: 当前初始化流程期望达到的目标版本号。
|
||||
resolved_state: 已解析的数据库版本状态。
|
||||
|
||||
Returns:
|
||||
DatabaseMigrationState: 不包含迁移步骤的状态对象。
|
||||
"""
|
||||
return DatabaseMigrationState(
|
||||
resolved_version=resolved_state,
|
||||
target_version=target_version,
|
||||
plan=MigrationPlan(current_version=current_version, target_version=target_version, steps=[]),
|
||||
)
|
||||
|
||||
|
||||
def create_database_migration_bootstrapper(
|
||||
engine: Engine,
|
||||
registry: Optional[MigrationRegistry] = None,
|
||||
resolver: Optional[SchemaVersionResolver] = None,
|
||||
version_store: Optional[SQLiteUserVersionStore] = None,
|
||||
latest_schema_version: int = LATEST_SCHEMA_VERSION,
|
||||
) -> DatabaseMigrationBootstrapper:
|
||||
"""创建数据库迁移启动桥接器。
|
||||
|
||||
Args:
|
||||
engine: 目标数据库引擎。
|
||||
registry: 迁移步骤注册表;未提供时使用默认注册表。
|
||||
resolver: 数据库版本解析器;未提供时使用默认解析器。
|
||||
version_store: 版本存储器;未提供时使用默认存储器。
|
||||
latest_schema_version: 当前代码支持的最新 schema 版本号。
|
||||
|
||||
Returns:
|
||||
DatabaseMigrationBootstrapper: 配置完成的数据库迁移启动桥接器。
|
||||
"""
|
||||
migration_registry = registry or build_default_migration_registry()
|
||||
migration_resolver = resolver or build_default_schema_version_resolver()
|
||||
migration_version_store = version_store or SQLiteUserVersionStore()
|
||||
migration_manager = DatabaseMigrationManager(
|
||||
engine=engine,
|
||||
registry=migration_registry,
|
||||
resolver=migration_resolver,
|
||||
version_store=migration_version_store,
|
||||
)
|
||||
return DatabaseMigrationBootstrapper(
|
||||
manager=migration_manager,
|
||||
latest_schema_version=latest_schema_version,
|
||||
)
|
||||
Reference in New Issue
Block a user