fix(msync): Saga 增强与已知缺陷全量修复,版本升至 1.1.0
- 自动重试:Execute 内按 CanRetry 内联重试(与手动 RetryStep 共享计数); RetryStep 补 select_for_update 行锁 - 步骤数据流:前置步骤 StepResult 合并进后续步骤载荷(不落库,向后兼容) - 空闲超时:IsTimedOut 改以 UpdateTime 起算,Execute 每步推进刷新计时 - TransactionResult:Execute/Compensate 终态写入步骤汇总快照 - 幂等键:_call_service 携带 Idempotency-Key(事务码:步骤号:重试次数), local_tx 侧按 key 内存去重(TTL 300s) - 修复 RetryCompensate 成功后残留 compensate_error 导致事务误判 FAILED - 迁移 0001 FK 去除 db_column(列名对齐模型 attname,消除 makemigrations 漂移) - 新增 gvsdsdk_check_schema 命令:模型与数据库 schema 一致性校验 - 新增 VERSION_MATRIX.md 版本连接状态表;版本 1.0.0 -> 1.1.0
This commit is contained in:
1 parent
53af9884df
commit
4d8e6d4225
8 files changed
+305
-56
No files matched your search
@@ -0,0 +1,80 @@
|
||||
"""gvsdsdk_check_schema — gvsdsdk/msync 模型与数据库 schema 一致性校验
|
||||
|
||||
对比模型 db_table 的实际表结构与模型字段清单,检测:
|
||||
- 模型有但数据库缺失的字段(需迁移)
|
||||
- 数据库有但模型未映射的字段(模型漂移)
|
||||
|
||||
用法:
|
||||
python manage.py gvsdsdk_check_schema # 校验 gvsdsdk + msync
|
||||
python manage.py gvsdsdk_check_schema --app msync # 仅校验 msync
|
||||
python manage.py gvsdsdk_check_schema --check # 仅退出码检查(CI 用)
|
||||
"""
|
||||
|
||||
from django.apps import apps as django_apps
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.db import connections, router
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = '校验 gvsdsdk/msync 模型与数据库 schema 一致性(对照 db_table 字段清单)'
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'--app', nargs='*', default=['gvsdsdk', 'msync'],
|
||||
help='要校验的 app_label(默认 gvsdsdk msync)',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--check', action='store_true',
|
||||
help='仅检查(存在差异时退出码非 0),不输出明细之外的汇总',
|
||||
)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
app_labels = options['app']
|
||||
check_only = options['check']
|
||||
|
||||
issues = []
|
||||
checked = 0
|
||||
for app_label in app_labels:
|
||||
try:
|
||||
config = django_apps.get_app_config(app_label)
|
||||
except LookupError:
|
||||
self.stderr.write(f'未安装的 app: {app_label}')
|
||||
issues.append(f'{app_label}: 未安装')
|
||||
continue
|
||||
|
||||
for model in config.get_models():
|
||||
checked += 1
|
||||
table = model._meta.db_table
|
||||
db = router.db_for_read(model) or 'default'
|
||||
try:
|
||||
cursor = connections[db].cursor()
|
||||
cursor.execute(f'SHOW COLUMNS FROM `{table}`')
|
||||
db_columns = {row[0] for row in cursor.fetchall()}
|
||||
cursor.close()
|
||||
except Exception as e:
|
||||
issues.append(f'{app_label}.{table}: 表查询失败: {e}')
|
||||
continue
|
||||
|
||||
model_columns = {f.column for f in model._meta.fields}
|
||||
missing = sorted(model_columns - db_columns)
|
||||
extra = sorted(db_columns - model_columns)
|
||||
if missing or extra:
|
||||
for col in missing:
|
||||
issues.append(f'{app_label}.{table}.{col}: 模型有但数据库缺失')
|
||||
for col in extra:
|
||||
issues.append(f'{app_label}.{table}.{col}: 数据库有但模型未映射')
|
||||
|
||||
if issues:
|
||||
for issue in issues:
|
||||
self.stderr.write(self.style.ERROR(f'[差异] {issue}'))
|
||||
self.stderr.write(self.style.ERROR(
|
||||
f'\n检查 {checked} 个模型,发现 {len(issues)} 处差异。'
|
||||
'若为模型新增字段,请先生成并执行迁移;若为模型漂移,请同步 SDK 模型。'
|
||||
))
|
||||
if check_only:
|
||||
raise SystemExit(1)
|
||||
return
|
||||
|
||||
self.stdout.write(self.style.SUCCESS(
|
||||
f'OK: 检查 {checked} 个模型,schema 与模型定义一致'
|
||||
))
|
||||
Reference in new issue
Block a user