Instructor worked edition · 90 minutes of dedicated work · 2026-09-26
This is the worked edition of Chapter 8, Unit B. It contains:
- complete answers;
- the instructor explanation;
- holdout cases that the student edition does not show.
Use it after a first attempt, or to rehearse the session. It runs on Google Colab (Python 3.12) or any local Python 3.12+ kernel, using only the standard library.
| Minutes | Dedicated work | Saved evidence |
|---|---|---|
| 0–10 | Predict what a stop between finishing and reporting leaves | Written prediction |
| 10–25 | Two invariants, the network boundary, and your starting evidence | Worked outputs and handoff |
| 25–55 | Construct finish and pass the visible cases | Learner code and grade table |
| 55–70 | Drain Unit A's inbox and send across a lost reply | Independent observation |
| 70–85 | Changed-constraint task: price a resend policy | Transfer results |
| 85–90 | Explain the result and save evidence | Retained submission |
Scroll horizontally to view columns.
Run the self-contained setup
The unit needs only Python's standard library. The collapsed cell below creates your work folder and defines the supplied parts of the unit:
- The work and report tables, with triggers for the state machine and for report bodies that never change.
- The supplied moves:
claimandsend_one. - A controlled messaging service,
FakeService, which can accept a message and then lose its reply. prepare, a stand-in for your Chapter 3 loop. It returns a fixed answer for a request's text. The book's checkpoint runs your real loop; a Colab kernel does not have your chapter files.- An independent observer, which reads the file through its own connection.
Run setup on every fresh kernel. Your saved work lives in practical-work/ch08-b.
Supplied setup, tables, moves, service and observer
The complete supplied setup cell is in the downloadable notebook. Run it there before attempting the cells below. This web edition omits only that compressed setup payload.
Commit to a prediction before the examples
A worker finishes Lucy's opening brief. The program records it in two steps: it marks the work finished, then it stores the report. The process stops between the two.
Before you run anything below, write what an independent reader will find, and whether Lucy will ever receive the brief.
prediction_notes = {
"prediction": "Write what a reader finds after a stop between finishing and reporting.",
"reason": "Name the rule behind that prediction.",
"falsifier": "Name an observation that would prove the explanation wrong.",
"revision": "After execution, explain what changed in your understanding.",
}Two invariants the outbox must keep
When work finishes, the shop owes the requester a report. Write this as two invariants:
- R1: every finished item has a report.
- R2: every report belongs to finished work.
Each invariant is a query an independent reader can run. The supplied observer runs both. It lists finished work that has no report under unreported, and reports R2 as r2.
The example below finishes one item in two separate statements, and stops between them. It then does the same inside one transaction. Predict both observations.
intro_rows = [("tg-101", "lucy", "Prepare the opening brief", "running")]
def intro_stop():
raise RuntimeError("stopped between finish and report")
for intro_label in ["two statements", "one transaction"]:
intro_path = fresh_database("intro-" + intro_label.replace(" ", "-"), intro_rows)
intro_db = connect(intro_path)
if intro_label == "one transaction":
intro_db.execute("BEGIN IMMEDIATE")
try:
intro_db.execute("UPDATE work SET state = 'finished' WHERE work_id = 1")
intro_stop()
intro_db.execute(
"INSERT INTO reports (report_id, work_id, generation, body, delivery)"
" VALUES ('r1.1', 1, 1, 'brief', 'pending')"
)
except RuntimeError:
if intro_db.in_transaction:
intro_db.execute("ROLLBACK")
intro_db.close()
print(intro_label, "->", observe(intro_path))With two statements, R1 broke. The brief is finished, and nothing on disk says Lucy is owed a report, so no later process will ever send one. In one transaction, the rollback removed the finish as well, and the work is still running, which is honest.
Chapter 4's atomicity is what makes this work. If the finish transaction preserves R1 and R2 whenever it runs in full, and nobody can observe it run in part, then both invariants hold in every state a reader can see.
The network is outside the transaction
Sending a report means calling a service on the other side of a network. No database transaction can include that call: the service keeps its own records, and a crash can happen at any instant around the call.
The dangerous case is a send that the service accepts but whose reply never arrives. Predict what the caller sees, and what the service has recorded.
intro_service = FakeService()
intro_service.lose_reply = True
try:
intro_service("r1.1", "brief")
intro_caller_saw = "a receipt"
except TransportUncertainError as intro_error:
intro_caller_saw = f"an error: {intro_error}"
print("the caller saw: ", intro_caller_saw)
print("the service accepted: ", intro_service.accepted)The caller heard nothing it could trust, yet the service holds the message. From the caller's side, "lost before arrival" and "accepted, reply lost" look identical.
So the outbox gives each report its own small state machine, and send_one follows it:
- The report is marked sending, and that is committed before the call. If the process dies during the call, a reader can see that a send may have happened.
- A receipt marks it confirmed.
- An uncertain failure marks it unknown. An unknown report is never picked up again automatically. Whether to resend it is a decision, and the transfer task below prices that decision.
Choose an explicit starting point
This unit starts from Unit A's inbox. To use your own, replace None with the path to your practical-work/ch08-a/ch08-unit-a-handoff-v1.json. Leave it as None to start from the supplied reference. Your submission records which you chose.
LEARNER_HANDOFF = Noneimport shutil
COURSE_INPUT = COURSE_WORK / "ch08-unit-a-handoff-v1.json"
if LEARNER_HANDOFF is not None:
learner_input = Path(LEARNER_HANDOFF).expanduser().resolve()
if not learner_input.is_file():
raise FileNotFoundError("The selected learner handoff does not exist")
if learner_input != COURSE_INPUT.resolve():
shutil.copy2(learner_input, COURSE_INPUT)
HANDOFF_ORIGIN = "LEARNER_SELECTED"
else:
reference_handoff = {
"unit": "ch08-a",
"status": "COMPLETED",
"work": [
["tg-101", "lucy", "Prepare the opening brief", "finished"],
["till-7", "till", "Count vanilla", "pending"],
["till-8", "till", "Count vanilla", "pending"],
["tg-102", "lucy", "Order everything", "pending"],
],
}
COURSE_INPUT.write_text(json.dumps(reference_handoff, indent=2, sort_keys=True) + "\n")
HANDOFF_ORIGIN = "SUPPLIED_REFERENCE"
print("Starting evidence:", HANDOFF_ORIGIN)handoff = json.loads(COURSE_INPUT.read_text(encoding="utf-8"))
handoff_status = "VERIFIED" if handoff.get("status") == "COMPLETED" else "INVALID"
shop_path = fresh_database("unit-b-shop", handoff["work"])
print("UNIT_A_HANDOFF", handoff_status)
print("rebuilt inbox:", observe(shop_path))The observer already reports unreported: ['tg-101']. Unit A's worker finished the brief with finish_plain, which kept no answer, so R1 was broken before this unit began.
No code can repair that row honestly. The answer was never stored, and inventing a report would tell Lucy something no worker said. The finish you build now makes the fault impossible for every item that comes after.
Main practical: construct, connect and challenge
1. Construct finish
finish(connection, work_id, worker_id, body, between=None) receives a connection from connect, the claimed item's work_id, the claiming worker_id, and the answer body. It must:
- check that the item is
runningfor this worker, and otherwise raiseTransitionError, writing nothing; - in one transaction, move the item to
finishedand insert its report withdelivery'pending'; - name the report
r{work_id}.{generation}, wheregenerationis one more than the number of reports this item already has (sor1.1for the first); - call
between(), if given, after the state change and before the report insert, and leave neither write if anything raises; - return the report id, and leave no transaction open.
The starter below writes the two halves one after the other. Run the visible cases to see where it fails, then repair it.
def finish(connection, work_id, worker_id, body, between=None):
"""Mark claimed work finished and create its report; return the report id."""
connection.execute("BEGIN IMMEDIATE")
try:
row = connection.execute(
"SELECT state, worker_id FROM work WHERE work_id = ?", (work_id,)
).fetchone()
if row != ("running", worker_id):
raise TransitionError(f"work {work_id} is not running for this worker")
connection.execute("UPDATE work SET state = 'finished' WHERE work_id = ?", (work_id,))
if between is not None:
between()
(generation,) = connection.execute(
"SELECT COUNT(*) + 1 FROM reports WHERE work_id = ?", (work_id,)
).fetchone()
report_id = f"r{work_id}.{generation}"
connection.execute(
"INSERT INTO reports (report_id, work_id, generation, body, delivery)"
" VALUES (?, ?, ?, ?, 'pending')",
(report_id, work_id, generation, body),
)
except BaseException:
connection.execute("ROLLBACK")
raise
connection.execute("COMMIT")
return report_idHint 1: who may finish
Read state and worker_id for the item before you change anything. Only ("running", worker_id) may finish. Anything else is a TransitionError, which is clearer to the caller than the trigger's refusal.
Hint 2: the boundary
Everything from that read to the report insert belongs in one BEGIN IMMEDIATE … COMMIT. Any exception inside it must reach a ROLLBACK before it continues to the caller.
Hint 3: the generation
SELECT COUNT(*) + 1 FROM reports WHERE work_id = ?, read inside the transaction. Chapter 13's recovery can create a second report for the same work. It will be r{work_id}.2, and it will never rewrite r{work_id}.1, which may already be on Lucy's phone.
import copy
class StopBetweenError(Exception):
"""The failure injected between the finish and the report."""
def stop_between():
raise StopBetweenError("stopped between finish and report")
def seeded():
"""Item 1 running for w1, item 2 pending, item 3 finished with report r3.1."""
path = fresh_database(
"visible",
[
("tg-101", "lucy", "Prepare the opening brief", "running"),
("till-7", "till", "Count vanilla", "pending"),
("till-8", "till", "Count vanilla", "finished"),
],
)
seed = connect(path)
seed.execute("UPDATE work SET worker_id = 'w1' WHERE work_id = 1")
seed.execute(
"INSERT INTO reports (report_id, work_id, generation, body, delivery)"
" VALUES ('r3.1', 3, 1, 'Counted 6 tubs.', 'pending')"
)
seed.close()
return path
UNCHANGED = {
"work": [["tg-101", "running"], ["till-7", "pending"], ["till-8", "finished"]],
"reports": [["r3.1", "till-8", "pending"]],
}
VISIBLE_CASES = [
(
"finish claimed work",
(1, "w1", "brief", None),
"r1.1",
{
"work": [["tg-101", "finished"], ["till-7", "pending"], ["till-8", "finished"]],
"reports": [["r1.1", "tg-101", "pending"], ["r3.1", "till-8", "pending"]],
},
),
(
"a stop between finish and report",
(1, "w1", "brief", stop_between),
"StopBetweenError",
UNCHANGED,
),
("a worker that did not claim it", (1, "w2", "brief", None), "TransitionError", UNCHANGED),
("pending work nobody claimed", (2, "w1", "brief", None), "TransitionError", UNCHANGED),
("finished work, finished again", (3, "w1", "brief", None), "TransitionError", UNCHANGED),
]
def grade_finish(candidate, cases):
rows = []
for label, arguments, expected, expected_state in cases:
path = seeded()
connection = connect(path)
try:
observed = candidate(connection, *arguments)
except Exception as error:
observed = type(error).__name__
if connection.in_transaction:
connection.execute("ROLLBACK")
observed = f"{observed} (left a transaction open)"
connection.close()
seen = observe(path)
state = {"work": seen["work"], "reports": seen["reports"]}
passed = observed == expected and state == expected_state and not seen["unreported"]
rows.append(
{
"case": label,
"expected": expected,
"observed": observed,
"status": "PASS" if passed else "FAIL",
}
)
return rows
visible_results = grade_finish(finish, VISIBLE_CASES)
VISIBLE_PASSED = all(row["status"] == "PASS" for row in visible_results)
for visible_row in visible_results:
print(visible_row["status"], visible_row["case"], "->", visible_row["observed"])
print("VISIBLE_CONTRACT", "PASSED" if VISIBLE_PASSED else "NEEDS_WORK")2. Drain the inbox, then send across a lost reply
Once the visible cases pass, the cell below runs the rest of Lucy's morning on the file rebuilt from Unit A:
- One worker claims each pending item, prepares an answer, and finishes it with your
finish. - The supplied
send_onesends the reports. The service loses the reply to the second send. - The independent observer reads the file.
Before running it, predict:
- each report's delivery state;
- what the service accepted;
- which finished item is still unreported, and why.
connected = None
if VISIBLE_PASSED:
shop = connect(shop_path)
finished_reports = []
while (claimed := claim(shop, "w1")) is not None:
finished_reports.append(finish(shop, claimed[0], "w1", prepare(claimed[1])))
service = FakeService()
sends = [send_one(shop, service)]
service.lose_reply = True
sends.append(send_one(shop, service))
service.lose_reply = False
sends.append(send_one(shop, service))
sends.append(send_one(shop, service))
shop.close()
connected = observe(shop_path)
print("reports:", finished_reports)
print("sends:", sends)
print("service accepted:", service.accepted)
print("reopened:", connected)
assert finished_reports == ["r2.1", "r3.1", "r4.1"]
assert sends == [("r2.1", "confirmed"), ("r3.1", "unknown"), ("r4.1", "confirmed"), None]
assert service.accepted == ["r2.1", "r3.1", "r4.1"]
assert connected["unreported"] == ["tg-101"] and connected["r2"]
else:
print("CONNECTION_NOT_READY — repair finish, then run again.")Look at r3.1. The service accepted it, and the shop's record says unknown. That record is not a failure of the outbox. It is the outbox telling the truth: the shop does not know whether the till's count reached anyone.
tg-101 is still unreported, inherited from Unit A. Every item finished by your finish has its report.
Exit ticket
Answer three questions:
- Why must the finish and the report commit together?
- Why is
sendingcommitted before the call? - Why does
send_onenever pick up an unknown report again by itself?
exercise_report = {
"unit": "ch08-b",
"attempted": 1,
"completed": int(VISIBLE_PASSED),
"failed": int(not VISIBLE_PASSED),
"skipped": 0,
"connection": "PASSED" if connected else "NOT_READY",
"handoff": handoff_status,
}
print("EXERCISE_REPORT=" + json.dumps(exercise_report, sort_keys=True))Changed-constraint construction: price a resend policy
Allow fifteen minutes: three to predict, eight to implement and check, and four for a case of your own.
Should the shop resend an unknown report? Answer with arithmetic, not a feeling.
The model. Each send is lost before the service sees it with probability . Otherwise the service accepts it, and its reply is lost with probability . The sender cannot tell these two cases apart, so the probability of hearing nothing is
The policy. Resend until a reply arrives, with at most sends. Send happens only if sends all went unanswered, which has probability . Each send that happens is accepted with probability . So the expected number of copies the service accepts is
Delivery. The report is missed only if all sends were lost before arrival, so .
Exactly one copy arrives in one of two ways:
- the first accepted send's reply arrives, whichever send that is;
- only one send is ever accepted, its reply is lost, and every other send is lost before arrival.
Your task. Implement resend_model(ell, p, k). It returns a dictionary with "delivered", "copies" and "two_or_more". The driver rounds each value to four decimals and compares it with a value worked out by hand, by listing every outcome.
Hint: the sum Add the terms with a loop rather than using the closed form . The closed form divides by zero when every send is lost before arrival (, so ). The loop has no such case.
def resend_model(ell, p, k):
unanswered = ell + (1 - ell) * p
copies = (1 - ell) * sum(unanswered ** (send - 1) for send in range(1, k + 1))
delivered = 1 - ell**k
exactly_one = (1 - p) * delivered + k * p * (1 - ell) * ell ** (k - 1)
return {"delivered": delivered, "copies": copies, "two_or_more": delivered - exactly_one}TRANSFER_CASES = [
(
"never resend (k = 1)",
[0.02, 0.05, 1],
{"delivered": 0.98, "copies": 0.98, "two_or_more": 0.0},
),
(
"the chapter's network, at most 3 sends",
[0.02, 0.05, 3],
{"delivered": 1.0, "copies": 1.0523, "two_or_more": 0.0499},
),
("a perfect network", [0.0, 0.0, 3], {"delivered": 1.0, "copies": 1.0, "two_or_more": 0.0}),
(
"replies never lost, requests sometimes",
[0.1, 0.0, 3],
{"delivered": 0.999, "copies": 0.999, "two_or_more": 0.0},
),
(
"every other reply lost, 2 sends",
[0.0, 0.5, 2],
{"delivered": 1.0, "copies": 1.5, "two_or_more": 0.5},
),
]
def run_transfer(candidate, cases):
observations = []
for label, arguments, expected in cases:
supplied = copy.deepcopy(arguments)
try:
result = candidate(*supplied)
actual = {key: round(value, 4) + 0.0 for key, value in sorted(result.items())}
except NotImplementedError:
actual = {"unfinished": True}
except Exception as error:
actual = {"raises": type(error).__name__}
passed = actual == expected and supplied == arguments
observations.append(
{"case": label, "expected": expected, "observed": actual, "passed": passed}
)
print("PASS" if passed else "NEEDS_WORK", label, "expected", expected, "observed", actual)
return observations
transfer_observations = run_transfer(resend_model, TRANSFER_CASES)
TRANSFER_PASSED = all(row["passed"] for row in transfer_observations)
print("TRANSFER_STATUS", "PASS" if TRANSFER_PASSED else "NEEDS_WORK")Check the derivation against sends, and retrieve the mechanism
A formula can be wrong in a way that the hand-worked cases do not catch. The cell below simulates 20,000 reports on the chapter's network, sending each one to a FakeService that loses requests and replies at random. It prints the model beside what the service actually accepted. Run it once your model passes.
Then answer: with and , what does resending up to three times cost per hundred reports, and what does it buy? The chapter's conclusion is that only the receiving side can turn at-least-once sending into an exactly-once effect. Show it: rerun the simulation with FakeService(idempotent=True) and compare the copies.
import random
def simulate(ell, p, k, reports, *, idempotent=False, seed=7):
rng = random.Random(seed)
service = FakeService(idempotent=idempotent)
delivered = two_or_more = 0
for number in range(reports):
report_id = f"r{number}.1"
before = len(service.accepted)
for _ in range(k):
if rng.random() < ell:
continue # lost before the service saw it
service.lose_reply = rng.random() < p
try:
service(report_id, "brief")
break
except TransportUncertainError:
continue
copies = len(service.accepted) - before
delivered += copies > 0
two_or_more += copies > 1
return {
"delivered": delivered / reports,
"copies": len(service.accepted) / reports,
"two_or_more": two_or_more / reports,
}
if TRANSFER_PASSED:
print(
"model: ", {key: round(value, 4) for key, value in resend_model(0.02, 0.05, 3).items()}
)
print("simulated:", simulate(0.02, 0.05, 3, 20_000))
print("idempotent receiver:", simulate(0.02, 0.05, 3, 20_000, idempotent=True))
else:
print("Finish resend_model first; then compare it with the simulation.")Instructor explanation and additional transfer cases
Three misconceptions come up.
"A transaction makes the send safe." It cannot. The service's record is outside the database. A transaction that "includes" the call only rolls back the shop's own record of it, and leaves the service holding a message the shop now denies sending.
"Unknown means failed, so resend." Unknown means "possibly delivered". Resending then trades a missing report for a duplicate one. The model prices that trade: about one duplicate in twenty on the chapter's network.
"The report's text is its identity." The identity r{work}.{generation} is what lets an idempotent receiver drop repeats, and what lets recovery add r{work}.2 without rewriting a report Lucy may already hold.
The cases below add an unreachable service (, where the closed form divides by zero) and a two-send case enumerated by hand. With , , :
- ;
- ;
- .
INSTRUCTOR_TRANSFER_CASES = [
(
"an unreachable service",
[1.0, 0.3, 3],
{"delivered": 0.0, "copies": 0.0, "two_or_more": 0.0},
),
(
"two sends, both losses",
[0.1, 0.2, 2],
{"delivered": 0.99, "copies": 1.152, "two_or_more": 0.162},
),
]
instructor_observations = run_transfer(resend_model, INSTRUCTOR_TRANSFER_CASES)
assert TRANSFER_PASSED and all(row["passed"] for row in instructor_observations)holdout_path = fresh_database(
"holdout",
[("h1", "lucy", "Brief", "pending"), ("h2", "till", "Count", "pending")],
)
holdout = connect(holdout_path)
first, second = claim(holdout, "w1"), claim(holdout, "w1")
assert finish(holdout, first[0], "w1", "brief") == "r1.1"
try:
finish(holdout, second[0], "w1", "count", stop_between)
except StopBetweenError:
pass
assert not holdout.in_transaction
middle = observe(holdout_path)
assert middle["work"] == [["h1", "finished"], ["h2", "running"]] and not middle["unreported"]
assert finish(holdout, second[0], "w1", "count") == "r2.1"
try:
holdout.execute("UPDATE reports SET body = 'rewritten' WHERE report_id = 'r1.1'")
rewritten = True
except sqlite3.IntegrityError:
rewritten = False
holdout.close()
final = observe(holdout_path)
assert not rewritten and not final["unreported"] and final["r2"]
assert [row[0] for row in final["reports"]] == ["r1.1", "r2.1"]
print("HOLDOUT_RESULT=" + json.dumps({"status": "PASSED", "unit": "ch08-b"}, sort_keys=True))Save your evidence and explain the result
Fill in the prediction notes and your explanation before saving. Include:
- the exact observed value, and the input that caused it;
- your code's invocation point;
- one failed hypothesis;
- the strongest claim the evidence still cannot support.
This unit stopped a finish with a raised exception inside one process, and it simulated a network. It did not kill the process, and it did not send to a real phone.
explanation_notes = {
"causal_trace": "Explain the input, learner invocation and observed result.",
"failed_hypothesis": "Describe a prediction the evidence changed.",
"remaining_limit": "Name the guarantee not established by this experiment.",
}
course_submission = {
"unit": "ch08-b",
"planned_minutes": 90,
"starting_evidence": globals().get("HANDOFF_ORIGIN", "INDEPENDENT_UNIT_A"),
"prediction": prediction_notes,
"explanation": explanation_notes,
"core_report": exercise_report,
"transfer": transfer_observations,
"explanation_review": "HUMAN_REVIEW_REQUIRED",
}
submission_path = COURSE_WORK / "ch08-b-submission-v1.json"
submission_path.write_text(
json.dumps(course_submission, indent=2, sort_keys=True), encoding="utf-8"
)
print("Saved evidence:", submission_path)
print(
"COURSE_REPORT="
+ json.dumps(
{
"unit": "ch08-b",
"transfer_passed": TRANSFER_PASSED,
"starting_evidence": course_submission["starting_evidence"],
"edition": "instructor",
},
sort_keys=True,
)
)Keep building with Prof Rod
Found this material through a colleague, classroom or shared download? Get the complete book at profrod.ai/book and join the Prof Rod learner community. Bring one result, one question or one failure you learned from. Share this resource with another learner and keep its source links with it so they can find the full course and future updates.