把「该停手的地方」写进代码里
目标
在更改生产数据之前,先确定停止点,并让代码实际执行该标准。 还要确认触发标准时不会留下只完成一半的状态。
请求单
请将 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건만 바뀐다
第三种情况至关重要。如果只会阻止执行,却无法完成工作,同样算失败。
步骤
- 更改前先计数。并列写出实际目标数量和请求单中的预期数量。
- 在
abort.md中写出至少两项中止标准。每项都必须具备所观测的指标、 不得超过的值以及届时采取的行动这三个要素。 restore.sh——接收快照路径并执行恢复。评分程序会故意破坏数据库, 然后确认能否通过此脚本恢复。apply.sh的范围标准——如果实际目标超过预期的 2 倍,则不做任何更改, 并以非 0 状态码退出。apply.sh的不变量标准——如果任何地区的 PENDING 数量少于 20 条, 就中止并连同已经应用的更改一起回滚。- 条件缩小后,必须准确应用指定的数量。
split.md——将不可逆的更改拆成两个可逆阶段。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——停止的事实、原因、已回滚的范围以及再次尝试的条件。
不能因为已经回滚,就当作什么都没发生。客户日后在日志中发现痕迹时,会认为这是刻意隐瞒。停止的事实、原因、已回滚的范围和再次尝试的条件,四者共同构成一个完整周期。