Skip to main content
Version: 2.x

:::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)