TT Lab
Get started
Learn Learning paths Courses

Irreversible Changes

Encoding the Point Where You Stop

Continue in TT Lab

Goal

Before changing production data, decide the place to stop and make the code actually enforce that criterion. You also check that no half-applied state is left behind when the criterion trips.

Request

Please change the PENDING orders in the EAST region to CANCELLED. It is about 120 rows.

Environment

You work under /root/change. You build the site yourself — it is preparation, not the assignment.

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"

What to create

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

apply.sh takes one request file as an argument.

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

How it is graded

Each time, after restoring the site to its original state, the grader runs your apply.sh directly with three requests and compares the DB before and after the run.

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

The third one matters. If it only blocks and gets no work done, that is a failure too.

Steps

  1. Count before you change anything. Write the actual target and the request's expectation side by side.
  2. In abort.md, write two or more abort criteria. Each must have all three: the indicator to observe, the value that must not be exceeded, and the action to take then.
  3. restore.sh — takes a snapshot path and rolls back. The grader deliberately breaks the DB and then checks whether this script recovers it.
  4. The scope criterion of apply.sh — if the actual target exceeds twice the expectation, change nothing and exit with a non-zero code.
  5. The invariant criterion of apply.sh — if the PENDING count of any region falls below 20, stop and roll back even what has already been applied.
  6. Under the narrowed condition, exactly that many rows must be applied.
  7. split.md — split an irreversible change into two reversible steps.
  8. stopped.md — the fact you stopped, the reason, the scope you rolled back, and the conditions for trying again.

Notes

Step 5 is the hardest place in this lab. Check on every batch, but when it trips, you must not stand there. A half-changed state is in no document, so stopping usually means rolling back. The snapshot you made in step 3 is used here — if you do not build the means to roll back first, you have no means to execute the abort criteria even if you have them.

Count before you change

Count before you change anything. Write the actual target and the request's expectation side by side.

The request said 120 rows. First count how many there actually are, and write the two side by side. If you learn this difference during the run, it is already too late.

Write the place to stop on paper

In abort.md, write two or more abort criteria. Each must have all three: the indicator to observe, the value that must not be exceeded, and the action to take then.

Each criterion needs all three things — the indicator to observe, the value that must not be exceeded (an inequality and a number), and the action to take then. If even one of the three is missing, room for interpretation appears during the run, and room for interpretation is always used in favor of continuing.

Build the means to roll back first

restore.sh — takes a snapshot path and rolls back. The grader deliberately breaks the DB and then checks whether this script recovers it.

Take the snapshot path as an argument and roll back. The grader deliberately breaks the DB and then checks whether this script recovers it — if it only prints a message and does not actually roll back, you fail.

The scope criterion blocks the run

The scope criterion of apply.sh — if the actual target exceeds twice the expectation, change nothing and exit with a non-zero code.

If the actual target exceeds twice the expectation, change nothing and exit with a non-zero code. The grader compares the DB before and after the run, so if even one row changes, you fail.

Roll back if it trips midway

The invariant criterion of apply.sh — if the PENDING count of any region falls below 20, stop and roll back even what has already been applied.

Check the invariant on every batch, and if it trips, roll back using the snapshot from step 3. You must not stand there — a half-changed state is in no document.

It must pass when narrowed

Under the narrowed condition, exactly that many rows must be applied.

If it only blocks and gets no work done, that is a failure too. Respect limit so that exactly that many rows are applied, and do not go below the lower bound either.

Split what cannot be undone

split.md — split an irreversible change into two reversible steps.

The best abort criterion is not to create an irreversible change at all. If you leave a deletion marker instead of deleting and delete after a grace period, one irreversible step becomes two reversible steps.

The fact that you stopped is also something to report

stopped.md — the fact you stopped, the reason, the scope you rolled back, and the conditions for trying again.

If you pass over it saying nothing happened because you rolled back, the customer will later find the trace in the logs and read it as you hiding it. The fact you stopped, the reason, the scope you rolled back, and the conditions for trying again — these four make one cycle.