:::info Executable tutorial
This page is generated from a tested Jupyter notebook. Open it in Google Colab or view the source notebook.
:::
Protect long-running work with leases
A lease reserves a worst-case cost before long-running work begins, then settles the measured cost or releases the hold. This tutorial exercises reservation, settlement, release, renewal, expiry, concurrency limits, and policy snapshots.
Learning objectives
After completing this tutorial, you can:
- Reserve capacity before starting uncertain-cost work
- Settle actual usage or release an unused hold
- Renew and expire leases safely
- Explain how plan changes and concurrency limits affect active leases
Prerequisites
- Complete the plans and allowances tutorial
- Read the financial safety guide
- Start the notebook server from
samples/python/notebooks/
Setup
Same sandbox: a temporary Postgres store, USER_ADA on pro with 100 credits. The pricing config prices execution at 0.04 credits per job, so a single job's worst case is exactly 0.040000.
from decimal import Decimal
from time import sleep
from bursar.metrics import UsageMetrics
from shared import cleanup, base_config, publish_config, start_postgres_store, USER_ADA
def execution(jobs=Decimal(1)):
return UsageMetrics(
operation="execution",
measures={"jobs": jobs, "compute_seconds": Decimal(30)},
dimensions={"model": "gpt-4o"},
)
store, pgdata = start_postgres_store()
bursar = publish_config(store, base_config())
bursar.accounts.on_account_created(USER_ADA, "signup")
bursar.credits.set_user_plan(USER_ADA, "pro")
bursar.credits.add_credits(USER_ADA, Decimal("100.00"), entry_type="purchase", idempotency_key="buy")
print("ready")
Reserving before work
reserve admits the request and holds the worst-case 0.040000. The balance is untouched, but get_available shows the money is now spoken for: reserved 0.040000, available 99.960000. billing_mode=strict means the hold is a hard floor — settlement can never take the balance negative.
from bursar.credits.service_types import ReserveOptions, SettleOptions
lease = bursar.credits.reserve(
USER_ADA, execution(), ReserveOptions(ttl=120, idempotency_key="lease:initial")
)
print("lease_id:", lease.lease_id)
print("hold:", lease.amount, "| billing_mode:", lease.billing_mode)
print("expires_at:", lease.expires_at[:19])
avail = bursar.credits.get_available(USER_ADA)
print(f"balance={avail.balance} reserved={avail.reserved} available={avail.available}")
Settling bills the actual
The run completes and reports the actual metrics: 240 jobs instead of the 250 that were estimated. Settlement debits the actual cost (9.600000) and releases the rest of the hold. Settling the same lease again is idempotent and returns the same entry.
actual = execution(jobs=Decimal(240))
settled = bursar.credits.settle(
USER_ADA, lease.lease_id, actual, SettleOptions(idempotency_key="lease:initial:settle")
)
print("entry:", settled.entry_id[:12], "| amount:", settled.amount, "| balance after:", settled.balance_after)
replay = bursar.credits.settle(
USER_ADA, lease.lease_id, actual, SettleOptions(idempotency_key="lease:initial:settle")
)
print("replay idempotent:", replay.idempotent, "| same entry:", replay.entry_id == settled.entry_id)
avail = bursar.credits.get_available(USER_ADA)
print("available after settle:", avail.available)
One call: run_billed
run_billed is the whole lifecycle in one call: reserve with the estimate, run do_work, settle with whatever it returns. Here the estimate is 250 jobs (10.00) but the work really used 240 (9.60) — the hold is sized at worst case and the charge follows reality.
from bursar.credits.service_types import RunBilledOptions
run = bursar.credits.run_billed(
USER_ADA,
RunBilledOptions(
estimate=execution(jobs=Decimal(250)),
do_work=lambda: ("ok", execution(jobs=Decimal(240))),
operation_type="execution",
operation_key="voice-1",
),
)
print("result:", run.result)
print("charged:", run.deduction.amount, "| balance after:", run.deduction.balance_after)
Release when things fail
When work fails or is cancelled, release returns the hold without charging anything.
lease = bursar.credits.reserve(
USER_ADA, execution(), ReserveOptions(ttl=120, idempotency_key="lease:release")
)
print("reserved:", bursar.credits.get_available(USER_ADA).reserved)
rel = bursar.credits.release(USER_ADA, lease.lease_id)
print("released:", rel.released, "| reason:", rel.reason)
print("available after release:", bursar.credits.get_available(USER_ADA).available)
Leases expire
A lease is only valid for its TTL. After a 2-second TTL passes, settling raises LeaseExpiredError — the hold is already gone. renew extends a live lease: same lease id, later expires_at.
lease = bursar.credits.reserve(
USER_ADA, execution(), ReserveOptions(ttl=2, idempotency_key="lease:expiry")
)
sleep(3)
try:
bursar.credits.settle(
USER_ADA,
lease.lease_id,
execution(jobs=Decimal(1)),
SettleOptions(idempotency_key="lease:expiry:settle"),
)
except Exception as exc:
print("settle after expiry:", type(exc).__name__)
lease = bursar.credits.reserve(
USER_ADA, execution(), ReserveOptions(ttl=120, idempotency_key="lease:renew")
)
renewed = bursar.credits.renew(USER_ADA, lease.lease_id, ttl=300)
print("same lease id:", renewed.lease_id == lease.lease_id, "| expires_at:", renewed.expires_at[:19])
bursar.credits.settle(
USER_ADA,
lease.lease_id,
execution(jobs=Decimal(1)),
SettleOptions(idempotency_key="lease:renew:settle"),
)
print("settled after renew | available:", bursar.credits.get_available(USER_ADA).available)
The in-flight cap
The pro admission policy allows 4 concurrent execution leases. Holds 1–4 pass; the 5th is refused without touching the balance — reserved stays 0.160000 and available stays put.
Known issue: a refused reservation currently surfaces as a pydantic ValidationError (the RPC returns no lease row), not the typed ConcurrencyLimitError; the underlying RPC reports max_concurrent_reached.
held = []
for i in range(4):
held.append(bursar.credits.reserve(
USER_ADA,
execution(),
ReserveOptions(ttl=120, idempotency_key=f"lease:concurrency:{i}"),
))
before = bursar.credits.get_available(USER_ADA)
print(f"4 leases held: reserved={before.reserved} available={before.available}")
try:
bursar.credits.reserve(
USER_ADA,
execution(),
ReserveOptions(ttl=120, idempotency_key="lease:concurrency:limit"),
)
except Exception as exc:
print("5th lease:", type(exc).__name__)
after = bursar.credits.get_available(USER_ADA)
print(f"still reserved={after.reserved} available={after.available} (unchanged)")
for l in held:
bursar.credits.release(USER_ADA, l.lease_id)
print("after releasing all:", bursar.credits.get_available(USER_ADA).available)
Settling after a downgrade
Financial safety means the hold is honored even if the world changes mid-flight: a lease reserved on pro settles cleanly after the account is downgraded to free — the minimum balance was captured at reservation time, and settlement is de-clamped, billing the actual 0.400000 for 10 jobs.
lease = bursar.credits.reserve(
USER_ADA, execution(), ReserveOptions(ttl=120, idempotency_key="lease:plan-change")
)
print("reserved on pro:", lease.billing_mode, lease.amount)
bursar.credits.set_user_plan(USER_ADA, "free")
settled = bursar.credits.settle(
USER_ADA,
lease.lease_id,
execution(jobs=Decimal(10)),
SettleOptions(idempotency_key="lease:plan-change:settle"),
)
print("settled on free:", settled.amount, "| balance after:", settled.balance_after)