scripts.slot_lease

Reference › scripts › slot_lease

scripts/slot_lease.py reserves worker slots for parallel swarms that share the ORCHESTRATA_WORKER_SLOT numbering and the orchkg_w<N> per-worker test databases, so two swarms never use the same slot. A human or a swarm launcher runs it; it has no internal imports. See start_test_services for how the per-worker databases are created and swarm_supervisor for the companion supervision tool.

State is a file; _locked takes an exclusive cross-process lock on its sibling .lock file around every read-modify-write (_read_state, _write_state). Subcommands: acquire --count N --owner NAME [--ttl-minutes M] reserves a contiguous range of slots (find_contiguous_range) and prints export SLOT_LEASE_OWNER=... and export SLOT_LEASE_BASE=... lines for the caller to evaluate, with a lease lifetime of 60 minutes by default and no expiry when the value is zero or negative; release --owner NAME frees every slot that owner holds; list shows current leases; and check SLOT [--owner NAME] tells whether a slot is free or owned by that owner. reap_expired is a pure function that drops leases whose expiry has passed. A refused acquire (invalid count, or no free range) is printed as [slot_lease] error: with exit 1.

At a glance

Source scripts/slot_lease.py
Lines of code 320
Classes 0
Functions 13
Imports (internal) none
Imported by none
Tests that import it none

Class diagram

No classes; see Functions.

Dependency diagram

No internal imports and no importers.

API

Functions

Function Signature Summary
default_state_path default_state_path() -> Path
_locked contextlib.contextmanager _locked(path: Path) Exclusive cross-PROCESS lock on path's sibling .lock file — real fcntl.flock, so two real subprocesses racing an acquire genuinely serialize rather than merely appearing to in one…
_read_state _read_state(path: Path) -> dict[str, Any]
_write_state _write_state(path: Path, state: dict[str, Any]) -> None
reap_expired reap_expired(state: dict[str, Any], now: float) -> dict[str, Any] Drop every lease whose expires_at has passed.
find_contiguous_range find_contiguous_range(state: dict[str, Any], count: int, total: int = TOTAL_SLOTS) -> list[int] | None The lowest free contiguous count-slot range in 0..total-1, or None when none exists.
acquire acquire(owner: str, count: int, *, ttl_minutes: float | None = 60.0, state_path: Path | None = None, now: float | None = None) -> list[int] Reserve the lowest free contiguous count-slot range for owner.
release release(owner: str, *, state_path: Path | None = None, now: float | None = None) -> list[int] Release every slot currently leased to owner.
list_leases list_leases(*, state_path: Path | None = None, now: float | None = None) -> dict[str, Any] Current leases, after reaping expired ones (persisted — "Expired TTLs are reaped on any command").
active_owner active_owner(slot: int, *, state_path: Path | None = None, now: float | None = None) -> str | None Owner currently holding slot (after reaping), or None if free.
check check(slot: int, owner: str, *, state_path: Path | None = None, now: float | None = None) -> bool True when slot is free or already owned by owner; False when it is leased to someone else.
_fmt_ts _fmt_ts(ts: float | None) -> str
main main(argv: list[str] | None = None) -> int

Source

Permalink: https://gitlab.com/MSamy/orchestrata-ultimate-architecture/-/blob/a4cbc2bdf/scripts/slot_lease.py

#!/usr/bin/env python3
"""slot_lease.py — worker-slot reservation for parallel swarms.

``ORCHESTRATA_WORKER_SLOT=slot<N>`` labels map to per-slot test databases
(``orchkg_w<N>``). When more than one swarm (Claude subagents + kimi +
minimax, per the IRC #agents delegation fleet) runs tests concurrently
against the same graph cluster, two swarms picking overlapping slot ranges
by hand silently corrupt each other's test data. This CLI is the missing
reservation layer: a flock-protected lease file, contiguous-range
allocation over slots ``0..31``, TTL-based expiry, and a plain owner-name
identity so ``scripts/sweep_test_pollution.py`` (see its ``--paths`` sibling
edit) can tell "my slot" from "someone else's live slot" before sweeping it.

State file: ``~/.local/state/orchestrata/slot-leases.json``, overridable via
the ``SLOT_LEASE_STATE`` environment variable (tests point this at a
``tmp_path`` file rather than touching the real one).

CLI:
    python scripts/slot_lease.py acquire --count 4 --owner swarmA --ttl-minutes 90
    python scripts/slot_lease.py list
    python scripts/slot_lease.py check 5 --owner swarmA
    python scripts/slot_lease.py release --owner swarmA

``acquire`` prints shell-exportable ``export KEY=value`` lines (including
``SLOT_LEASE_OWNER``, so a downstream ``sweep_test_pollution.py`` call that
``eval``s the acquire output automatically identifies itself).
"""

from __future__ import annotations

import argparse
import contextlib
import fcntl
import json
import os
import sys
import time
from pathlib import Path
from typing import Any

#: Slots this project's test infra recognizes (``ORCHESTRATA_WORKER_SLOT=
#: slot0``..``slot31``, per ``tests/conftest.py``'s per-worker database map).
TOTAL_SLOTS = 32

STATE_ENV = "SLOT_LEASE_STATE"
_DEFAULT_STATE = Path.home() / ".local" / "state" / "orchestrata" / "slot-leases.json"


def default_state_path() -> Path:
    override = os.environ.get(STATE_ENV, "").strip()
    return Path(override) if override else _DEFAULT_STATE


# ---------------------------------------------------------------------------
# State I/O — flock-protected read-modify-write
# ---------------------------------------------------------------------------


@contextlib.contextmanager
def _locked(path: Path):
    """Exclusive cross-PROCESS lock on ``path``'s sibling ``.lock`` file —
    real ``fcntl.flock``, so two real subprocesses racing an ``acquire``
    genuinely serialize rather than merely appearing to in one process.
    """
    path.parent.mkdir(parents=True, exist_ok=True)
    lock_path = path.with_name(path.name + ".lock")
    with open(lock_path, "a+") as lock_fh:
        fcntl.flock(lock_fh.fileno(), fcntl.LOCK_EX)
        try:
            yield
        finally:
            fcntl.flock(lock_fh.fileno(), fcntl.LOCK_UN)


def _read_state(path: Path) -> dict[str, Any]:
    if not path.exists():
        return {"leases": {}}
    try:
        with path.open(encoding="utf-8") as fh:
            data = json.load(fh)
    except json.JSONDecodeError, OSError:
        return {"leases": {}}
    if not isinstance(data, dict) or not isinstance(data.get("leases"), dict):
        return {"leases": {}}
    return data


def _write_state(path: Path, state: dict[str, Any]) -> None:
    tmp = path.with_name(path.name + ".tmp")
    with tmp.open("w", encoding="utf-8") as fh:
        json.dump(state, fh, indent=2, sort_keys=True)
    tmp.replace(path)


def reap_expired(state: dict[str, Any], now: float) -> dict[str, Any]:
    """Drop every lease whose ``expires_at`` has passed. Pure — no I/O — so
    TTL-expiry tests can drive it directly with a synthetic clock instead of
    sleeping minutes.
    """
    leases = state.get("leases", {})
    kept = {
        slot: info
        for slot, info in leases.items()
        if info.get("expires_at") is None or info["expires_at"] > now
    }
    return {"leases": kept}


# ---------------------------------------------------------------------------
# Allocation
# ---------------------------------------------------------------------------


def find_contiguous_range(
    state: dict[str, Any], count: int, total: int = TOTAL_SLOTS
) -> list[int] | None:
    """The lowest free contiguous ``count``-slot range in ``0..total-1``, or
    ``None`` when none exists."""
    leased = {int(s) for s in state.get("leases", {})}
    for start in range(0, total - count + 1):
        block = range(start, start + count)
        if all(s not in leased for s in block):
            return list(block)
    return None


def acquire(
    owner: str,
    count: int,
    *,
    ttl_minutes: float | None = 60.0,
    state_path: Path | None = None,
    now: float | None = None,
) -> list[int]:
    """Reserve the lowest free contiguous ``count``-slot range for ``owner``.

    Raises ``ValueError`` for a nonsensical ``count`` and ``RuntimeError``
    when no such range is currently free — never silently hands out a
    partial or overlapping range.
    """
    if count < 1 or count > TOTAL_SLOTS:
        raise ValueError(f"count must be between 1 and {TOTAL_SLOTS}, got {count}")
    path = state_path or default_state_path()
    ts = now if now is not None else time.time()
    with _locked(path):
        state = reap_expired(_read_state(path), ts)
        block = find_contiguous_range(state, count)
        if block is None:
            raise RuntimeError(
                f"no contiguous free range of {count} slot(s) available in "
                f"0..{TOTAL_SLOTS - 1} — run `slot_lease.py list` to see current leases"
            )
        expires_at = ts + ttl_minutes * 60.0 if ttl_minutes else None
        for slot in block:
            state["leases"][str(slot)] = {
                "owner": owner,
                "acquired_at": ts,
                "ttl_minutes": ttl_minutes,
                "expires_at": expires_at,
            }
        _write_state(path, state)
    return block


def release(
    owner: str, *, state_path: Path | None = None, now: float | None = None
) -> list[int]:
    """Release every slot currently leased to ``owner``. Returns the
    (sorted) list of slots that were released."""
    path = state_path or default_state_path()
    ts = now if now is not None else time.time()
    with _locked(path):
        state = reap_expired(_read_state(path), ts)
        removed = [
            int(slot)
            for slot, info in state["leases"].items()
            if info.get("owner") == owner
        ]
        for slot in removed:
            del state["leases"][str(slot)]
        _write_state(path, state)
    return sorted(removed)


def list_leases(
    *, state_path: Path | None = None, now: float | None = None
) -> dict[str, Any]:
    """Current leases, after reaping expired ones (persisted — "Expired
    TTLs are reaped on any command")."""
    path = state_path or default_state_path()
    ts = now if now is not None else time.time()
    with _locked(path):
        state = reap_expired(_read_state(path), ts)
        _write_state(path, state)
    return state


def active_owner(
    slot: int, *, state_path: Path | None = None, now: float | None = None
) -> str | None:
    """Owner currently holding ``slot`` (after reaping), or ``None`` if free.

    The read-only-feeling seam ``scripts/sweep_test_pollution.py`` imports
    directly (as a bare sibling-script import, matching ``_irc_send.py``'s
    existing ``_irc_conversation`` precedent) to decide whether an
    ``orchkg_w<N>`` database is safe to sweep.
    """
    path = state_path or default_state_path()
    ts = now if now is not None else time.time()
    with _locked(path):
        state = reap_expired(_read_state(path), ts)
        _write_state(path, state)
    info = state["leases"].get(str(slot))
    return info.get("owner") if info else None


def check(
    slot: int,
    owner: str,
    *,
    state_path: Path | None = None,
    now: float | None = None,
) -> bool:
    """``True`` when ``slot`` is free or already owned by ``owner``;
    ``False`` when it is leased to someone else."""
    holder = active_owner(slot, state_path=state_path, now=now)
    return holder is None or holder == owner


# ---------------------------------------------------------------------------
# CLI
# ---------------------------------------------------------------------------


def _fmt_ts(ts: float | None) -> str:
    if ts is None:
        return "never"
    return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(ts))


def main(argv: list[str] | None = None) -> int:
    parser = argparse.ArgumentParser(
        description=(
            "Worker-slot reservation for parallel swarms sharing "
            "ORCHESTRATA_WORKER_SLOT / orchkg_w<N> test databases."
        )
    )
    sub = parser.add_subparsers(dest="command", required=True)

    acq = sub.add_parser("acquire", help="Reserve a contiguous range of slots")
    acq.add_argument("--count", type=int, required=True)
    acq.add_argument("--owner", required=True)
    acq.add_argument(
        "--ttl-minutes",
        type=float,
        default=60.0,
        help="Lease lifetime in minutes (default: 60; 0/negative disables expiry)",
    )

    rel = sub.add_parser("release", help="Release every slot owned by --owner")
    rel.add_argument("--owner", required=True)

    sub.add_parser("list", help="Show current leases")

    chk = sub.add_parser("check", help="Is a slot free, or owned by --owner?")
    chk.add_argument("slot", type=int)
    chk.add_argument("--owner", default="")

    args = parser.parse_args(argv)

    if args.command == "acquire":
        ttl = args.ttl_minutes if args.ttl_minutes > 0 else None
        try:
            block = acquire(args.owner, args.count, ttl_minutes=ttl)
        except (ValueError, RuntimeError) as exc:
            print(f"[slot_lease] error: {exc}", file=sys.stderr)
            return 1
        print(f"export SLOT_LEASE_OWNER={args.owner}")
        print(f"export SLOT_LEASE_BASE={block[0]}")
        print(f"export SLOT_LEASE_COUNT={len(block)}")
        print(f"export SLOT_LEASE_SLOTS={','.join(str(s) for s in block)}")
        return 0

    if args.command == "release":
        removed = release(args.owner)
        print(
            f"[slot_lease] released {len(removed)} slot(s) for {args.owner!r}: {removed}"
        )
        return 0

    if args.command == "list":
        state = list_leases()
        leases = state.get("leases", {})
        if not leases:
            print("(no active leases)")
            return 0
        print(f"{'SLOT':<6}{'OWNER':<24}{'ACQUIRED':<22}{'EXPIRES'}")
        for slot in sorted(leases, key=int):
            info = leases[slot]
            print(
                f"{slot:<6}{info.get('owner', ''):<24}"
                f"{_fmt_ts(info.get('acquired_at')):<22}{_fmt_ts(info.get('expires_at'))}"
            )
        return 0

    if args.command == "check":
        ok = check(args.slot, args.owner)
        holder = active_owner(args.slot)
        if ok:
            state_note = "free" if holder is None else f"owned by you ({holder})"
            print(f"[slot_lease] slot {args.slot} is {state_note}")
        else:
            print(f"[slot_lease] slot {args.slot} is leased to {holder!r}")
        return 0 if ok else 1

    return 1


if __name__ == "__main__":
    sys.exit(main())