Skip to content

Lease: the Control Lease (X-Lease-Id, 409, 423)

Control lease summary: take it with POST /api/lease (409 when someone else holds it), send X-Lease-Id with every request, renew before it expires, release when done. Everyone else gets 423 Locked for changes and forced reads meanwhile.

The control lease lets one client at a time change the station, typically a test station for the length of a test sequence, so that nobody else changes a setting in the middle of it.

While another client holds the lease, this client's changes (every POST, PUT and DELETE that changes something) and forced reads (resources.get_value, resources.get_values without max_age_ms) raise AccordionQ2ApiError with status 423. Reads with max_age_ms and subscriptions keep working; they get the values the holder's own reads keep up to date. Anyone may ask who holds it.

After acquire() the client sends the lease id (the X-Lease-Id header) with every request, so its own changes go through as if there were no lease.

Methods: Acquire, Renew and Release the Control Lease

Method Returns Description
get() LeaseDto Who holds the lease, if anyone.
acquire(owner, ttl_ms=30000) LeaseDto Take the lease for ttl_ms (1 000 to 600 000 ms). Raises 409 while someone else holds it.
renew(ttl_ms=None) LeaseDto Renew the lease this client holds, optionally with a new time to live. Raises 404 once it has ended, and RuntimeError when this client holds none.
release() — Release the lease this client holds; does nothing when it holds none.
hold(owner, ttl_ms=30000) context manager yielding LeaseDto Acquire, renew in the background, and release when the block ends.
held_lease_id str or None Property: the id of the lease this client holds.

A lease not renewed within its time to live ends by itself, so a crashed test station doesn't lock the station.

Examples

Holding the Lease for a Sequence

with client.lease.hold("TAT station 3", ttl_ms=30000):
    client.resources.set_value("0.4.ESH10000662.VSET1", "5")
    voltage = client.resources.get_value("0.4.ESH10000662.VMON1")

hold renews the lease every third of ttl_ms on a background thread and releases it when the block ends, also on an exception. If the script dies, the lease ends within ttl_ms.

Who Holds It

lease = client.lease.get()
if lease.held:
    print(f"Held by {lease.owner} for another {lease.expires_in_ms / 1000:.0f} s")

Acquiring and Releasing by Hand

from accordionq2 import AccordionQ2ApiError

try:
    client.lease.acquire("TAT station 3", ttl_ms=60000)
except AccordionQ2ApiError as e:
    if e.status_code == 409:
        print("Someone else holds the lease:", e)
    raise

try:
    client.resources.set_value("0.4.ESH10000662.VSET1", "5")
    client.lease.renew()          # within ttl_ms, or the lease ends
finally:
    client.lease.release()

Handling 423 in Another Client

try:
    client.resources.set_value("0.4.ESH10000662.VSET1", "5")
except AccordionQ2ApiError as e:
    if e.status_code == 423:
        print("The station is held by another client:", e)

Pilot Is Not Bound by the Lease

The lease is enforced by the WebApi, which from 7.0 is the only way to the station, so every client is bound by it. On a 6.x station Accordion Pilot talks over the byte protocol, not the REST API, so it is not bound by the lease: it can change the station while someone holds it. Keep Pilot away from a 6.x station during a leased test sequence.

Model

LeaseDto

Field Type Description
held bool True while someone holds the lease
owner str or None The owner name the holder gave
expires_in_ms float Time left before the lease ends unless renewed
lease_id str or None The lease id, on the LeaseDto that acquire() returns; None from get()