I closed the browser tab 300 ms into a request. The job kept running for another 40 seconds. Nobody told me.

That gap is where duplicate writes, mystery quota use, and "it worked in staging" bugs are born. So here is the question I now ask about every endpoint: when I walk away, what keeps working?

This is a myth-busting FAQ. Four claims I hear in code review, the evidence that breaks them, and a probe you can run yourself.

Myth 1: "Abort stops the work"

The claim: AbortController.abort() cancels the job.

What it actually does: it closes your side of the connection. The server learns about it when it next tries to write. If the handler is a long loop with no flush, that might be never.

Corrected model: cancellation is a message, not a kill signal. Treat it as best-effort.

Quick check — send a request with a unique marker, abort it, then search the server logs:

curl -s -N "http://localhost:8000/slow?marker=abort-$RANDOM" \ --max-time 0.3 || true sleep 5 grep -c "abort-" /var/log/app/requests.log # >0 means the work landed 

If that count is above zero, your abort did not stop anything downstream.

Myth 2: "A timeout means nothing happened"

The claim: the client timed out, so the write failed.

Reality: timeouts are local. The request may have been fully processed. Only the reply was lost.

Ask instead: can I tell the difference between "not done" and "done but unacknowledged"? If the answer is no, you have a reconciliation problem, not a timeout problem.

Myth 3: "Retries are free"

Three attempts at a 40-second job is 120 seconds of compute. On a metered tier, that is quota you cannot get back. On one small server, it is three workloads fighting for the same CPU.

Retries do not just multiply traffic. They multiply work. And they multiply it exactly when the service is already struggling.

Myth 4: "No response means no state change"

The claim: no body arrived, so nothing was written.

Counter-example: a request that inserts a row, then fails while serializing the response. The row exists. Your client saw a 502.

Corrected model: every mutating call needs a client-generated key. The server decides the rule: first key wins, repeats return the stored result.

A probe you can actually run

Here is the workflow I use. It is small on purpose.

  1. Generate a request id before sending anything.
  2. Send that id in a header.
  3. Abort early, or let it time out.
  4. Poll a read-only endpoint for the id.
  5. Record one of three verdicts: landed, absent, unknown.
# cancel_probe.py # Draft artifact. Not executed for this article — run it against your own service. import json, time, uuid, urllib.request, urllib.error BASE = "http://localhost:8000" def send(req_id: str, abort_after: float = 0.3) -> str: req = urllib.request.Request( f"{BASE}/jobs", data=json.dumps({"req_id": req_id}).encode(), headers={"Content-Type": "application/json", "Idempotency-Key": req_id}, method="POST", ) try: with urllib.request.urlopen(req, timeout=abort_after) as r: return f"http {r.status}" except urllib.error.URLError as e: return f"client gave up: {type(e).__name__}" def look_up(req_id: str) -> dict: with urllib.request.urlopen(f"{BASE}/jobs/{req_id}", timeout=5) as r: return json.loads(r.read().decode()) if __name__ == "__main__": rid = str(uuid.uuid4()) print("send:", send(rid)) for i in range(6): time.sleep(2) try: print(f"t+{2*(i+1)}s: found", look_up(rid)) break except urllib.error.HTTPError as e: print(f"t+{2*(i+1)}s: not found ({e.code})") 

Three outcomes matter, and only three:

  • Absent — safe to retry.
  • Present, done — do not retry; read the stored result.
  • Present, running — wait, or cancel explicitly and poll again.

Any fourth state in your system is a design bug, not an edge case.

The retry wrapper, with a budget

Unlimited retries are a quota leak. Put a number on it.

import random, time def with_budget(fn, attempts=3, base=0.5, cap=8.0): for i in range(attempts): try: return fn() except Exception: if i == attempts - 1: raise delay = min(cap, base * 2 ** i) * random.uniform(0.5, 1.5) time.sleep(delay) # budget is attempts, not wall clock 

Log total retries per request id. My rule of thumb: if one logical request needs more than three attempts, the endpoint is unhealthy, and hammering it is not debugging.

Where the cheap tier fits

I run probes like this against free model endpoints and a free server first. The point is not saving cents. The point is that an abort probe on a metered paid account makes me timid, and timid probes hide bugs.

MonkeyCode's free model access and free server option are what I use for that first pass. The model side gives me something slow enough to abort mid-flight. The server side gives me a real process to kill and restart.

Disclosure: This article was prepared as part of MonkeyCode's product outreach.

Two honest limits: free capacity is shared, so timings will be noisy. And free access is an offer, not a guarantee — do not build a latency budget on it.

Decision table

Situation Do Why
User left the page Cancel, then poll Abort is a message, not a kill
Client timed out on a write Look up by idempotency key Unknown is not failed
Server returned 5xx Retry with the same key, capped Duplicates hurt more than gaps
4xx on a bad payload Do not retry It will fail identically
Throttled Stop and back off Retry storms burn quota

Who should skip this

Skip it if your writes are already naturally idempotent. You are paying for a probe you do not need.

Skip it if you have no request ids and no way to add them. Fix that first; the probe will only tell you what you already fear.

Also skip it if you need hard cancellation guarantees. That requires cooperative cancellation inside the handler, checked at every loop boundary. A client abort will never give you that.

The mental model

Your client is not in control. It sends intent. The server decides.

Once I accepted that, my bug reports changed. "The request failed" became "this request has unknown state, here is the id." That is a much better sentence to wake up to.

Run one abort probe this week. Pick a single endpoint. You may find 40 seconds of work you thought you had cancelled.