LabHub
学习 学习路径 课程

不可撤销的变更

把「该停手的地方」写进代码里

在 LabHub 中继续学习

目标

在更改生产数据之前,先确定停止点,并让代码实际执行该标准。 还要确认触发标准时不会留下只完成一半的状态。

请求单

请将 EAST 地区的 PENDING 订单改为 CANCELLED。约 120 条

环境

/root/change 下工作。现场由你亲自创建——这是准备工作,不是任务。

mkdir -p /root/change && cd /root/change
python3 - <<'PY'
import sqlite3, random, datetime
random.seed(23)
con = sqlite3.connect('app.db'); cur = con.cursor()
cur.execute("create table orders(id integer primary key, status text, region text, amount int, created_at text)")
base = datetime.datetime(2026, 8, 1); rows = []; oid = 1
for region, n in (('EAST', 480), ('WEST', 210), ('NORTH', 60), ('SOUTH', 35)):
    for _ in range(n):
        rows.append((oid, 'PENDING', region, random.randint(1000, 90000),
                     (base + datetime.timedelta(days=random.randint(0, 30))).isoformat())); oid += 1
for region in ('EAST', 'WEST', 'NORTH', 'SOUTH'):
    for _ in range(random.randint(200, 400)):
        rows.append((oid, random.choice(['PAID', 'SHIPPED']), region, random.randint(1000, 90000),
                     (base + datetime.timedelta(days=random.randint(0, 30))).isoformat())); oid += 1
cur.executemany("insert into orders values (?,?,?,?,?)", rows); con.commit(); con.close()
PY
sqlite3 app.db "select region, count(*) from orders where status='PENDING' group by 1"

要创建的文件

scope.txt    요청서 예상과 실제 대상의 차이
abort.md     중단 기준 (지표 · 임계값 · 행동)
restore.sh   스냅숏에서 되돌리는 스크립트
apply.sh     배치로 적용하되 기준에 걸리면 되돌리는 스크립트
split.md     되돌릴 수 없는 변경을 두 단계로 쪼개는 방법
stopped.md   멈췄다는 사실을 알리는 보고

apply.sh 接受一个请求文件作为参数。

region=EAST
expected=120
limit=0        0 이면 전부, 숫자면 그만큼만

评分方式

评分程序会在每次将现场恢复到原始状态后,使用三种请求直接运行你的 apply.sh, 并比较执行前后的数据库。

region=WEST expected=150 limit=150   통과해야 한다 — 정확히 150건만 바뀐다

第三种情况至关重要。如果只会阻止执行,却无法完成工作,同样算失败。

步骤

  1. 更改前先计数。并列写出实际目标数量和请求单中的预期数量。
  2. abort.md 中写出至少两项中止标准。每项都必须具备所观测的指标、 不得超过的值以及届时采取的行动这三个要素。
  3. restore.sh——接收快照路径并执行恢复。评分程序会故意破坏数据库, 然后确认能否通过此脚本恢复。
  4. apply.sh范围标准——如果实际目标超过预期的 2 倍,则不做任何更改, 并以非 0 状态码退出。
  5. apply.sh不变量标准——如果任何地区的 PENDING 数量少于 20 条, 就中止并连同已经应用的更改一起回滚。
  6. 条件缩小后,必须准确应用指定的数量。
  7. split.md——将不可逆的更改拆成两个可逆阶段。
  8. stopped.md——停止的事实、原因、已回滚的范围以及再次尝试的条件。

参考

第 5 步是本练习最难的部分。每个批次都要检查,但触发标准时, 不能停留在触发时的状态。 只更改了一半是任何文档中都不存在的状态, 因此所谓停止通常意味着回滚。此处要用到第 3 步创建的快照——如果没有先准备 回滚手段,那么即使制定了中止标准,也没有办法执行它。

更改前先计数

更改前先计数。并列写出实际目标数量和请求单中的预期数量。

请求单称有 120 条。先计算实际有多少条,再将两者并列写出。如果在执行过程中才发现这一差异,就已经太晚了。

把停止点写在纸面上

abort.md 中写出至少两项中止标准。每项都必须具备所观测的指标、 不得超过的值以及届时采取的行动这三个要素。

每项标准必须同时包含三个要素——要观测的指标、不得超过的值(不等号和数字),以及届时采取的行动。缺少其中任何一个,执行过程中就会留下解释空间,而解释空间总是被用来支持继续执行。

先准备回滚手段

restore.sh——接收快照路径并执行恢复。评分程序会故意破坏数据库, 然后确认能否通过此脚本恢复。

接收快照路径作为参数并执行恢复。评分程序会故意破坏数据库,然后确认能否通过该脚本恢复——如果只输出消息而不实际回滚,就无法通过。

范围标准阻止执行

apply.sh范围标准——如果实际目标超过预期的 2 倍,则不做任何更改, 并以非 0 状态码退出。

如果实际目标超过预期的 2 倍,就必须不做任何更改,并以非 0 状态码退出。评分程序会比较执行前后的数据库,因此只要更改一条就无法通过。

途中触发标准就回滚

apply.sh不变量标准——如果任何地区的 PENDING 数量少于 20 条, 就中止并连同已经应用的更改一起回滚。

每个批次都要检查不变量;一旦触发,就使用第 3 步的快照回滚。不能停留在触发时的状态——只更改了一半是任何文档中都不存在的状态。

缩小范围后必须通过

条件缩小后,必须准确应用指定的数量。

如果只会阻止执行,却无法完成工作,同样算失败。遵守 limit,准确应用指定的数量,同时不得突破下限。

拆分不可逆操作

split.md——将不可逆的更改拆成两个可逆阶段。

最好的中止标准,是从一开始就不制造不可逆的更改。先留下删除标记,等待宽限期结束后再删除,就能把一次不可逆操作变成两次可逆操作。

停止的事实也需要报告

stopped.md——停止的事实、原因、已回滚的范围以及再次尝试的条件。

不能因为已经回滚,就当作什么都没发生。客户日后在日志中发现痕迹时,会认为这是刻意隐瞒。停止的事实、原因、已回滚的范围和再次尝试的条件,四者共同构成一个完整周期。