|
3 | 3 | from __future__ import annotations |
4 | 4 |
|
5 | 5 | import json |
| 6 | +import os |
6 | 7 | import re |
7 | 8 | import tempfile |
8 | 9 | from collections.abc import Callable, Mapping |
@@ -161,6 +162,36 @@ def record_marker( |
161 | 162 | path.parent.mkdir(parents=True, exist_ok=True) |
162 | 163 | path.write_text(encoded, encoding="utf-8") |
163 | 164 |
|
| 165 | + def claim_marker(self, marker_key: str, *, metadata: Mapping[str, Any] | None = None) -> bool: |
| 166 | + """Atomically reserve an execution identity before broker submission.""" |
| 167 | + if not str(marker_key or "").strip(): |
| 168 | + raise ValueError("execution claim requires a marker key") |
| 169 | + payload = json.dumps({ |
| 170 | + "schema_version": "execution_claim.v1", |
| 171 | + "marker_key": str(marker_key), |
| 172 | + "claimed_at": datetime.now(timezone.utc).isoformat(), |
| 173 | + "state": "claimed", |
| 174 | + "metadata": dict(metadata or {}), |
| 175 | + }, ensure_ascii=False, indent=2, sort_keys=True) |
| 176 | + if self.cloud_prefix_uri: |
| 177 | + create = getattr(self._object_store(), "create_text", None) |
| 178 | + if not callable(create): |
| 179 | + raise RuntimeError("cloud execution store lacks atomic create-only support") |
| 180 | + return bool(create(self._cloud_uri(marker_key), payload, "application/json")) |
| 181 | + if self.local_dir: |
| 182 | + path = self._local_path(marker_key) |
| 183 | + path.parent.mkdir(parents=True, exist_ok=True) |
| 184 | + try: |
| 185 | + fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600) |
| 186 | + except FileExistsError: |
| 187 | + return False |
| 188 | + with os.fdopen(fd, "w", encoding="utf-8") as handle: |
| 189 | + handle.write(payload) |
| 190 | + handle.flush() |
| 191 | + os.fsync(handle.fileno()) |
| 192 | + return True |
| 193 | + raise RuntimeError("execution state store has no durable claim backend") |
| 194 | + |
164 | 195 | def has_prior_execution_report( |
165 | 196 | self, |
166 | 197 | *, |
|
0 commit comments