Skip to content

第二章练习答案 ​

事件状态 ​

在正文 TRANSITIONS 定义后加入下面规则,在 apply 定义后运行检查:

python
TRANSITIONS[('queued', 'reject')] = 'failed'
initial = {'status': 'queued', 'seq': 0}
rejected = apply(initial, {'seq': 1, 'type': 'reject'})
assert rejected['status'] == 'failed'
for state, event, reason in [
    (rejected, {'seq': 2, 'type': 'start'}, 'invalid transition'),
    (initial, {'seq': 2, 'type': 'start'}, 'event gap'),
]:
    try:
        apply(state, event)
    except ValueError as error:
        assert str(error) == reason
    else:
        raise AssertionError('invalid event accepted')
print('rejection terminal')

输出 rejection terminal。按钮禁用只影响当前页面,另一个客户端或后台 worker 仍能发送事件,终态规则必须由负责持久化状态的服务统一执行。

提交边界 ​

在正文 conn.close() 之前加入:

python
before = conn.execute('SELECT COUNT(*) FROM refunds').fetchone()[0]
try:
    refund('A', 'r2', 'order2', 100, None)
except PermissionError as error:
    assert str(error) == 'approval mismatch'
else:
    raise AssertionError('approval bypass')
assert conn.execute('SELECT COUNT(*) FROM refunds').fetchone()[0] == before
assert refund('A','r1','order1',500,approval) == 'committed:r1'
assert refund('B','r1','order1',500,('B','order1',500)) == 'committed:r1'
assert conn.execute('SELECT COUNT(*) FROM refunds').fetchone()[0] == before + 1
print('approval and tenant ok')

得到 approval and tenant ok。注意返回文本相同,但它属于不同租户的账本记录;查询也必须带 tenant。未经批准的操作不会进入事务,异参冲突会回滚;之后的合法调用可正常完成。

外部服务超时先进入 unknown,保留相同下游幂等键并查询交易。确认成功后补记凭据,确认未执行且合同允许才重试,查不到时转人工对账。unknown 不是可以随意重跑的失败。

返回章节

用心记录每一步成长