Cancel 12 queued Sume jobs: 12 writes, and a 409 that is not an error

Cancel is a write, only works before generation starts, and returns 409 job_generation_already_started after. A Python sweep that treats 409 as fine.

4 min readSume
All posts

Cancelling a Sume job is one write request each, so 12 queued jobs cost 12 writes. On Free that is 10% of the 120 writes per minute. Cancellation only succeeds before generation work starts; afterward the API returns 409 job_generation_already_started with details.cancelable: false, and the job runs to completion and bills. Treat that 409 as information, not as a failure.

Write cost by plan

The Authentication page counts cancellation as a write. The table shows a full queued backlog at the default queue capacity from Generation admission.

Cost of cancelling a full default queue (Sume docs, read 2026-10-09)
PlanDefault queue capacityWrites per minuteCancel writes as a share of one minute
Free51205 / 120 = 4.2%
Pro2030020 / 300 = 6.7%
Startup4060040 / 600 = 6.7%
Scale1001,200100 / 1,200 = 8.3%

A sweep that tolerates the 409

The function returns a label for each job. A cancel of a job that is already canceled is idempotent and returns the same canceled job, so a second pass is safe. Any other error is raised. Set QUEUED_JOB_IDS to a comma-separated list to run it.

import json
import os
import urllib.error
import urllib.request

def cancel(job_id: str) -> str:
    req = urllib.request.Request(
        f"https://api.sume.com/v1/jobs/{job_id}/cancel",
        method="POST",
        headers={"Authorization": f"Bearer {os.environ.get('SUME_API_KEY', '')}"},
    )
    try:
        urllib.request.urlopen(req, timeout=20).read()
        return "canceled"
    except urllib.error.HTTPError as err:
        code = json.load(err).get("error", {}).get("code")
        if err.code == 409 and code == "job_generation_already_started":
            return "running: will finish and bill"
        raise

if __name__ == "__main__":
    ids = os.environ.get("QUEUED_JOB_IDS", "").split(",")
    for job_id in filter(None, ids):
        print(job_id, cancel(job_id))  # one write each

What cancel does to the bill

At submit Sume reserves the estimated amount. A cancel before generation starts releases the reservation where applicable, while a job that already started completes or fails normally and captures usage on success. That is why you cancel queued jobs you no longer need, rather than letting them reach processing.

The same rule holds when you stop waiting. A client timeout or an aborted wait does not cancel the job. Store the job id and cancel explicitly.

Avoiding the 409

Cancel soon after you decide. A queued job that moves to processing can no longer be canceled, so the window is the time spent waiting in the queue, which depends on your concurrency. On a Free workspace with one slot, the queue is longer than on Scale.

If you need many cancellations, pace them: 100 cancels on Scale is 100 of 1,200 writes, which leaves room for the rest of your traffic in the same minute.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume