world_sync/world_sync/pbl_runtime_errors.py
2026-09-20 17:59:38 +08:00

128 lines
4.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- coding: utf-8 -*-
"""M11b-2 运行时单事务写入 —— 异常定义(唯一真源)。
设计要点(对应需求点 3「错误处理事务失败时回滚并抛出明确异常不吞错误」
1. 所有异常统一继承 :class:`PblRuntimeError`,携带机器可判定的 ``code`` 与结构化 ``detail``
2. 事务失败一律「先 ROLLBACK、再抛出」原始异常通过 ``raise ... from exc`` 保留 cause
3. 并发冲突(乐观锁 / 行锁下版本被他人推进)单独成类 :class:`ConcurrentStateConflict`
让上层可以区分「可重试」与「真失败」,而不是笼统的 500。
本模块零依赖(只用标准库),不引入广播 / 轮询相关任何东西。
M11b-2a 迁移补充(响应 QC 退回意见 #1「补齐异常别名」宿主挂载层
``world_sync/init.py`` 与已迁移的测试用例使用一组**语义别名**
``RuntimeWriteError`` / ``InvalidWriteRequest`` / ``ConnectionUnavailable`` /
``TransactionAborted``)。别名与真源类是**同一个类对象**``is`` 相等),
因此 ``except`` 与 ``assertRaises`` 在两套名字下行为完全一致,不存在双真源。
"""
__all__ = [
"PblRuntimeError",
"TxConfigError",
"ConnFactoryNotConfigured",
"EventWriteError",
"StateWriteError",
"ConcurrentStateConflict",
"TxAbortError",
# --- M11b-2a 语义别名(与上方真源同一对象,供宿主层/测试层使用)---
"RuntimeWriteError",
"InvalidWriteRequest",
"ConnectionUnavailable",
"TransactionAborted",
]
class PblRuntimeError(Exception):
"""M11b-2 运行时写入异常基类。
:param message: 人读信息
:param detail: 结构化上下文tenant/world/entity/version/cause 等),便于日志与断言
"""
code = "PBL_RUNTIME_ERROR"
http_hint = 500
def __init__(self, message="", **detail):
super(PblRuntimeError, self).__init__(message or self.__class__.__doc__ or "")
self.message = message or ""
self.detail = dict(detail)
def to_dict(self):
"""机器可读形式,供接口层 / 测试断言使用。"""
return {
"code": self.code,
"message": self.message,
"detail": self.detail,
"http_hint": self.http_hint,
}
# 兼容旧调用名init.py / 测试用 as_dict()
as_dict = to_dict
def __str__(self):
if self.detail:
parts = ", ".join(
"%s=%r" % (k, self.detail[k]) for k in sorted(self.detail.keys())
)
return "[%s] %s {%s}" % (self.code, self.message, parts)
return "[%s] %s" % (self.code, self.message)
class TxConfigError(PblRuntimeError):
"""配置/参数错误(缺字段、方言不认识、配置文件不可解析)。属于「调用方错误」。"""
code = "PBL_TX_CONFIG_ERROR"
http_hint = 500
class ConnFactoryNotConfigured(PblRuntimeError):
"""fail-closed三级优先级都拿不到 conn_factory 时抛出,绝不偷偷连库。"""
code = "PBL_TX_CONN_FACTORY_MISSING"
http_hint = 500
class EventWriteError(PblRuntimeError):
"""pbl_runtime_event 插入失败(整事务已回滚)。"""
code = "PBL_RUNTIME_EVENT_WRITE_FAILED"
http_hint = 500
class StateWriteError(PblRuntimeError):
"""pbl_entity_state 更新/写入失败(整事务已回滚)。"""
code = "PBL_ENTITY_STATE_WRITE_FAILED"
http_hint = 500
class ConcurrentStateConflict(PblRuntimeError):
"""并发冲突:乐观锁 expected_version 不匹配,或 UPDATE 影响行数 != 1。
出现该异常意味着「不出现脏写/丢失更新」这条验收标准被守住了:
后到的事务被拒绝,而不是覆盖先到的写入。上层可重读版本后重试。
"""
code = "PBL_ENTITY_STATE_CONFLICT"
http_hint = 409
class TxAbortError(PblRuntimeError):
"""事务在提交阶段失败commit 抛错),已执行 ROLLBACK。"""
code = "PBL_TX_ABORTED"
http_hint = 500
# ===========================================================================
# M11b-2a 语义别名 —— 同一个类对象,不是新异常族(避免双真源)
# RuntimeWriteError → PblRuntimeError基类宿主层统一 except 用)
# InvalidWriteRequest → TxConfigError调用方参数/配置错误)
# ConnectionUnavailable → ConnFactoryNotConfigured拿不到连接工厂fail-closed
# TransactionAborted → TxAbortError提交阶段失败已回滚
# ===========================================================================
RuntimeWriteError = PblRuntimeError
InvalidWriteRequest = TxConfigError
ConnectionUnavailable = ConnFactoryNotConfigured
TransactionAborted = TxAbortError