111 lines
43 KiB
JSON
111 lines
43 KiB
JSON
{
|
|
"run_id": "20260831T123006Z",
|
|
"created_at": "2026-08-31T12:30:06.184278+00:00",
|
|
"runtime": {
|
|
"name": "ollama",
|
|
"kind": "ollama",
|
|
"base_url": "http://127.0.0.1:11434",
|
|
"model": "qwen3.8:27b-mxfp8"
|
|
},
|
|
"host": {
|
|
"platform": "macOS-15.6-arm64-arm-64bit",
|
|
"machine": "arm64",
|
|
"python": "3.12.8"
|
|
},
|
|
"package_versions": {
|
|
"vllm": "0.28.0+cpu",
|
|
"vllm-metal": "0.4.0.dev20260831112704",
|
|
"mlx": "0.32.0",
|
|
"mlx-lm": "0.31.3",
|
|
"mlx-vlm": "0.6.4"
|
|
},
|
|
"settings": {
|
|
"temperature": 0,
|
|
"seed": 42,
|
|
"max_tokens": 3072,
|
|
"context_tokens": 8192,
|
|
"raw_completion": false,
|
|
"chat_template": true,
|
|
"thinking": false,
|
|
"ollama_speculative_drafting": false
|
|
},
|
|
"warmup": {
|
|
"task": "warmup",
|
|
"runtime": "ollama",
|
|
"model": "qwen3.8:27b-mxfp8",
|
|
"started_at": "2026-08-31T12:22:38.815059+00:00",
|
|
"prompt_chars": 27,
|
|
"max_tokens": 8,
|
|
"ttft_s": 0.44539537496166304,
|
|
"total_s": 0.44552537496201694,
|
|
"prompt_tokens": 41,
|
|
"output_tokens": 1,
|
|
"decode_tps": 8.241328039906357,
|
|
"finish_reason": "stop",
|
|
"output_chars": 5,
|
|
"output_sha256": "c2e3ac47f4a325469c1a2d5f117e463ec943c721986d5d9f09ac4540b7d80526",
|
|
"output": "READY",
|
|
"error": null
|
|
},
|
|
"tasks": {
|
|
"python_async_cache": "Act as a senior Python engineer. Implement a production-ready, single-file Python 3.12 module\ncontaining a generic AsyncTTLCache[K, V]. Requirements: weighted LRU capacity; per-entry TTL based\non time.monotonic; single-flight get_or_create so concurrent misses for one key invoke the async\nfactory exactly once; cancellation of one waiter must not cancel the shared factory; exceptions are\nnever cached; invalidating a key while its factory is running must prevent stale publication; never\nhold the cache lock while awaiting user code; clear(), invalidate(), and immutable hit/miss/eviction\nstatistics. Include concise pytest-asyncio tests covering races, cancellation, expiry, weighted\ncapacity, factory failure, and invalidation during creation. Use only the standard library plus\npytest. Output only the complete module, without Markdown fences or prose.",
|
|
"php_idempotent_transfer": "Act as a senior Laravel 12 engineer. Produce one self-contained PHP file that demonstrates an\nidempotent money-transfer service and its PHPUnit tests. Store money as integer cents. Requirements:\nvalidate positive amount and distinct accounts; use a database transaction; lock both account rows\nin deterministic ID order; reject insufficient funds; use a unique client idempotency key so retries\nreturn the original transfer; retry deadlocks without duplicating effects; persist an outbox event\nin the same transaction; and prove with tests that duplicate and competing requests preserve\nbalances, create one transfer, and create one outbox event. Include minimal model/table assumptions\nas comments.\nOutput only valid PHP, without Markdown fences or prose.",
|
|
"typescript_dag_scheduler": "Act as a senior TypeScript engineer. Implement a strict-mode, dependency-free DAG task scheduler in\none TypeScript file. Each task has an ID, dependency IDs, and an async run(signal) function.\nRequirements: validate unknown dependencies and duplicate IDs; report an explicit cycle path; stable\ninput-order scheduling; configurable maximum concurrency; AbortSignal support; fail fast after the\nfirst error while awaiting already-running tasks; never start dependents of failed tasks; return a\nMap of typed results on success; and avoid unhandled promise rejections. Include a compact test\nharness covering a diamond graph, concurrency enforcement, stable order, cycle detection, external\ncancellation, and rejection. Output only TypeScript, without Markdown fences or prose."
|
|
},
|
|
"results": [
|
|
{
|
|
"task": "python_async_cache",
|
|
"runtime": "ollama",
|
|
"model": "qwen3.8:27b-mxfp8",
|
|
"started_at": "2026-08-31T12:22:39.260933+00:00",
|
|
"prompt_chars": 857,
|
|
"max_tokens": 3072,
|
|
"ttft_s": 1.1107976250350475,
|
|
"total_s": 149.8235780420364,
|
|
"prompt_tokens": 211,
|
|
"output_tokens": 3072,
|
|
"decode_tps": 20.63948064778098,
|
|
"finish_reason": "length",
|
|
"output_chars": 13492,
|
|
"output_sha256": "3e5ca7654fc69293040b7cc1b554ccddbd1a99b82710d361074fee260803182f",
|
|
"output": "import asyncio\nimport time\nfrom collections import OrderedDict\nfrom dataclasses import dataclass\nfrom typing import (\n Any,\n Awaitable,\n Callable,\n Generic,\n Optional,\n TypeVar,\n)\n\nK = TypeVar(\"K\")\nV = TypeVar(\"V\")\n\n\n@dataclass(frozen=True)\nclass CacheStats:\n hits: int\n misses: int\n evictions: int\n\n\nclass _Entry:\n __slots__ = (\"value\", \"expires_at\", \"weight\")\n\n def __init__(self, value: Any, expires_at: float, weight: int) -> None:\n self.value = value\n self.expires_at = expires_at\n self.weight = weight\n\n\nclass _Inflight:\n __slots__ = (\"event\", \"value\", \"error\", \"cancelled\")\n\n def __init__(self) -> None:\n self.event = asyncio.Event()\n self.value: Any = None\n self.error: Optional[BaseException] = None\n self.cancelled: bool = False\n\n\nclass AsyncTTLCache(Generic[K, V]):\n \"\"\"\n A generic async cache with:\n - Weighted LRU capacity (evict least-recently-used entries until total weight <= max_weight).\n - Per-entry TTL based on time.monotonic.\n - Single-flight get_or_create: concurrent misses for the same key invoke the async factory\n exactly once.\n - Cancellation of one waiter does not cancel the shared factory.\n - Exceptions are never cached.\n - Invalidating a key while its factory is running prevents stale publication.\n - The cache lock is never held while awaiting user code.\n - clear(), invalidate(), and immutable hit/miss/eviction statistics.\n \"\"\"\n\n def __init__(\n self,\n max_weight: int,\n default_ttl: float,\n weight_fn: Optional[Callable[[V], int]] = None,\n ) -> None:\n if max_weight <= 0:\n raise ValueError(\"max_weight must be positive\")\n if default_ttl <= 0:\n raise ValueError(\"default_ttl must be positive\")\n\n self._max_weight: int = max_weight\n self._default_ttl: float = default_ttl\n self._weight_fn: Callable[[V], int] = weight_fn or (lambda _v: 1)\n\n self._lock: asyncio.Lock = asyncio.Lock()\n self._store: OrderedDict[K, _Entry] = OrderedDict()\n self._current_weight: int = 0\n self._inflight: dict[K, _Inflight] = {}\n\n self._hits: int = 0\n self._misses: int = 0\n self._evictions: int = 0\n\n # ------------------------------------------------------------------\n # Public API\n # ------------------------------------------------------------------\n\n async def get_or_create(\n self,\n key: K,\n factory: Callable[[], Awaitable[V]],\n *,\n ttl: Optional[float] = None,\n ) -> V:\n \"\"\"\n Return the cached value for *key*, or invoke *factory* to create it.\n\n If multiple coroutines call get_or_create for the same key concurrently\n and the key is not yet cached, the factory is invoked exactly once.\n All waiters receive the same result (or the same exception).\n \"\"\"\n effective_ttl = ttl if ttl is not None else self._default_ttl\n\n # Fast path: check cache under lock\n async with self._lock:\n entry = self._store.get(key)\n if entry is not None:\n if time.monotonic() < entry.expires_at:\n # Move to end (most recently used)\n self._store.move_to_end(key)\n self._hits += 1\n return entry.value\n else:\n # Expired: remove and fall through to factory\n self._store.pop(key, None)\n self._current_weight -= entry.weight\n\n # Check if there is already an in-flight factory for this key\n inflight = self._inflight.get(key)\n if inflight is not None:\n # Join the existing in-flight operation\n pass\n else:\n # Start a new in-flight operation\n inflight = _Inflight()\n self._inflight[key] = inflight\n self._misses += 1\n\n # If we joined an existing in-flight, wait for it\n if inflight.cancelled:\n # The in-flight was invalidated; we need to start our own\n # Re-acquire lock to check again\n async with self._lock:\n entry = self._store.get(key)\n if entry is not None and time.monotonic() < entry.expires_at:\n self._store.move_to_end(key)\n self._hits += 1\n return entry.value\n # Check if another in-flight started\n inflight = self._inflight.get(key)\n if inflight is not None and not inflight.cancelled:\n pass # fall through to wait\n else:\n # Start a new in-flight\n inflight = _Inflight()\n self._inflight[key] = inflight\n self._misses += 1\n\n # Wait for the in-flight to complete\n await inflight.event.wait()\n\n if inflight.error is not None:\n raise inflight.error\n return inflight.value\n\n async def _run_factory(\n self,\n key: K,\n factory: Callable[[], Awaitable[V]],\n ttl: float,\n inflight: _Inflight,\n ) -> None:\n \"\"\"\n Run the factory outside the lock. This is called by get_or_create\n after acquiring the lock and setting up the inflight entry.\n \"\"\"\n try:\n value = await factory()\n except BaseException as exc:\n # Mark the inflight as failed\n inflight.error = exc\n inflight.event.set()\n # Remove inflight entry\n async with self._lock:\n # Only remove if it's still the same inflight\n if self._inflight.get(key) is inflight:\n del self._inflight[key]\n return\n\n # Check if the key was invalidated while the factory was running\n async with self._lock:\n if self._inflight.get(key) is not inflight:\n # The inflight was replaced (e.g., by invalidate), so this result is stale\n return\n if inflight.cancelled:\n # The inflight was cancelled, so this result is stale\n return\n\n # Check if the key is already in the cache (another coroutine may have\n # published a value, though this shouldn't happen with single-flight)\n if key in self._store:\n # Already cached, don't overwrite\n del self._inflight[key]\n return\n\n # Publish the value\n weight = self._weight_fn(value)\n if weight > self._max_weight:\n # Single item exceeds max weight; don't cache it\n del self._inflight[key]\n inflight.value = value\n inflight.event.set()\n return\n\n # Evict if necessary\n self._evict_for_weight(weight)\n\n expires_at = time.monotonic() + ttl\n self._store[key] = _Entry(value, expires_at, weight)\n self._current_weight += weight\n\n # Mark the inflight as complete\n del self._inflight[key]\n inflight.value = value\n inflight.event.set()\n\n async def invalidate(self, key: K) -> None:\n \"\"\"\n Invalidate a key. If a factory is currently running for this key,\n the result will not be published.\n \"\"\"\n async with self._lock:\n # Remove from cache if present\n entry = self._store.pop(key, None)\n if entry is not None:\n self._current_weight -= entry.weight\n\n # Cancel any in-flight factory for this key\n inflight = self._inflight.get(key)\n if inflight is not None:\n inflight.cancelled = True\n inflight.event.set()\n del self._inflight[key]\n\n async def clear(self) -> None:\n \"\"\"\n Clear all entries and cancel all in-flight factories.\n \"\"\"\n async with self._lock:\n self._store.clear()\n self._current_weight = 0\n # Cancel all in-flight factories\n for inflight in self._inflight.values():\n inflight.cancelled = True\n inflight.event.set()\n self._inflight.clear()\n\n @property\n def stats(self) -> CacheStats:\n \"\"\"\n Return an immutable snapshot of cache statistics.\n \"\"\"\n return CacheStats(\n hits=self._hits,\n misses=self._misses,\n evictions=self._evictions,\n )\n\n # ------------------------------------------------------------------\n # Internal helpers\n # ------------------------------------------------------------------\n\n def _evict_for_weight(self, incoming_weight: int) -> None:\n \"\"\"\n Evict least-recently-used entries until there is room for incoming_weight.\n This is called while holding the lock.\n \"\"\"\n while self._current_weight + incoming_weight > self._max_weight:\n if not self._store:\n break\n # Pop the least-recently-used entry (first in OrderedDict)\n key, entry = self._store.popitem(last=False)\n self._current_weight -= entry.weight\n self._evictions += 1\n\n # ------------------------------------------------------------------\n # Override get_or_create to properly handle the factory execution\n # ------------------------------------------------------------------\n\n async def get_or_create( # type: ignore[override]\n self,\n key: K,\n factory: Callable[[], Awaitable[V]],\n *,\n ttl: Optional[float] = None,\n ) -> V:\n \"\"\"\n Return the cached value for *key*, or invoke *factory* to create it.\n\n If multiple coroutines call get_or_create for the same key concurrently\n and the key is not yet cached, the factory is invoked exactly once.\n All waiters receive the same result (or the same exception).\n \"\"\"\n effective_ttl = ttl if ttl is not None else self._default_ttl\n\n # Fast path: check cache under lock\n async with self._lock:\n entry = self._store.get(key)\n if entry is not None:\n if time.monotonic() < entry.expires_at:\n # Move to end (most recently used)\n self._store.move_to_end(key)\n self._hits += 1\n return entry.value\n else:\n # Expired: remove and fall through to factory\n self._store.pop(key, None)\n self._current_weight -= entry.weight\n\n # Check if there is already an in-flight factory for this key\n inflight = self._inflight.get(key)\n if inflight is not None and not inflight.cancelled:\n # Join the existing in-flight operation\n pass\n else:\n # Start a new in-flight operation\n inflight = _Inflight()\n self._inflight[key] = inflight\n self._misses += 1\n # We need to run the factory\n should_run = True\n else:\n should_run = False\n\n if should_run:\n # Run the factory outside the lock\n await self._run_factory(key, factory, effective_ttl, inflight)\n\n # Wait for the in-flight to complete\n await inflight.event.wait()\n\n if inflight.error is not None:\n raise inflight.error\n return inflight.value\n\n\n# ------------------------------------------------------------------\n# Tests\n# ------------------------------------------------------------------\n\nimport pytest\n\n\n@pytest.mark.asyncio\nasync def test_basic_get_or_create() -> None:\n cache = AsyncTTLCache[int, str](max_weight=10, default_ttl=10.0)\n call_count = 0\n\n async def factory() -> str:\n nonlocal call_count\n call_count += 1\n return \"value\"\n\n result = await cache.get_or_create(1, factory)\n assert result == \"value\"\n assert call_count == 1\n\n # Second call should hit the cache\n result = await cache.get_or_create(1, factory)\n assert result == \"value\"\n assert call_count == 1\n assert cache.stats.hits == 1\n assert cache.stats.misses == 1\n\n\n@pytest.mark.asyncio\nasync def test_single_flight() -> None:\n cache = AsyncTTLCache[int, str](max_weight=10, default_ttl=10.0)\n call_count = 0\n\n async def factory() -> str:\n nonlocal call_count\n call_count += 1\n await asyncio.sleep(0.05)\n return \"value\"\n\n # Launch multiple concurrent get_or_create for the same key\n results = await asyncio.gather(\n cache.get_or_create(1, factory),\n cache.get_or_create(1, factory),\n cache.get_or_create(1, factory),\n )\n\n assert all(r == \"value\" for r in results)\n assert call_count == 1\n assert cache.stats.misses == 1\n\n\n@pytest.mark.asyncio\nasync def test_cancellation_does_not_cancel_factory() -> None:\n cache = AsyncTTLCache[int, str](max_weight=10, default_ttl=10.0)\n call_count = 0\n factory_completed = asyncio.Event()\n\n async def factory() -> str:\n nonlocal call_count\n call_count += 1\n await asyncio.sleep(0.05)\n factory_completed.set()\n return \"value\"\n\n # Start a get",
|
|
"error": null
|
|
},
|
|
{
|
|
"task": "php_idempotent_transfer",
|
|
"runtime": "ollama",
|
|
"model": "qwen3.8:27b-mxfp8",
|
|
"started_at": "2026-08-31T12:25:09.083177+00:00",
|
|
"prompt_chars": 756,
|
|
"max_tokens": 3072,
|
|
"ttft_s": 1.6688512500259094,
|
|
"total_s": 149.76781020802446,
|
|
"prompt_tokens": 182,
|
|
"output_tokens": 3072,
|
|
"decode_tps": 20.71737613932144,
|
|
"finish_reason": "length",
|
|
"output_chars": 12642,
|
|
"output_sha256": "71ee449c3f573828250542e04f7a63db5a8d23087bdec89f0a9a0bb31d52a3b5",
|
|
"output": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * Minimal table assumptions (run once before tests):\n *\n * CREATE TABLE accounts (\n * id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,\n * balance_cents BIGINT NOT NULL DEFAULT 0\n * );\n *\n * CREATE TABLE transfers (\n * id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,\n * idempotency_key VARCHAR(255) NOT NULL,\n * from_account_id BIGINT UNSIGNED NOT NULL,\n * to_account_id BIGINT UNSIGNED NOT NULL,\n * amount_cents BIGINT NOT NULL,\n * created_at DATETIME NOT NULL,\n * UNIQUE KEY uq_idempotency_key (idempotency_key)\n * );\n *\n * CREATE TABLE outbox_events (\n * id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,\n * transfer_id BIGINT UNSIGNED NOT NULL,\n * event_type VARCHAR(255) NOT NULL,\n * payload JSON NOT NULL,\n * created_at DATETIME NOT NULL,\n * UNIQUE KEY uq_transfer_id (transfer_id)\n * );\n *\n * Model assumptions:\n * - Account model has: id, balance_cents (integer cents)\n * - Transfer model has: id, idempotency_key, from_account_id, to_account_id, amount_cents, created_at\n * - OutboxEvent model has: id, transfer_id, event_type, payload, created_at\n *\n * This file is self-contained: it defines the service class and a PHPUnit test class.\n * Run with: phpunit --bootstrap vendor/autoload.php\n */\n\nuse Illuminate\\Support\\Facades\\DB;\nuse Illuminate\\Support\\Str;\nuse PHPUnit\\Framework\\TestCase;\n\n/**\n * Idempotent money-transfer service.\n *\n * All monetary values are stored as integer cents to avoid floating-point errors.\n */\nclass MoneyTransferService\n{\n /**\n * Maximum number of deadlock retries.\n */\n private const MAX_DEADLOCK_RETRIES = 5;\n\n /**\n * Base delay in milliseconds between deadlock retries.\n */\n private const DEADLOCK_RETRY_BASE_DELAY_MS = 50;\n\n /**\n * Perform an idempotent money transfer between two accounts.\n *\n * @param int $fromAccountId Source account ID\n * @param int $toAccountId Destination account ID\n * @param int $amountCents Amount to transfer in cents (must be > 0)\n * @param string $idempotencyKey Unique client-supplied key for idempotency\n *\n * @return array{\n * transfer_id: int,\n * from_account_id: int,\n * to_account_id: int,\n * amount_cents: int,\n * idempotency_key: string,\n * created_at: string,\n * was_duplicate: bool\n * }\n *\n * @throws \\InvalidArgumentException If amount is not positive or accounts are the same\n * @throws \\RuntimeException If insufficient funds or after exhausting deadlock retries\n */\n public function transfer(\n int $fromAccountId,\n int $toAccountId,\n int $amountCents,\n string $idempotencyKey\n ): array {\n // Validate positive amount\n if ($amountCents <= 0) {\n throw new \\InvalidArgumentException('Transfer amount must be a positive integer number of cents.');\n }\n\n // Validate distinct accounts\n if ($fromAccountId === $toAccountId) {\n throw new \\InvalidArgumentException('Source and destination accounts must be distinct.');\n }\n\n // Validate idempotency key is non-empty\n if (trim($idempotencyKey) === '') {\n throw new \\InvalidArgumentException('Idempotency key must be a non-empty string.');\n }\n\n $lastException = null;\n\n for ($attempt = 0; $attempt < self::MAX_DEADLOCK_RETRIES; $attempt++) {\n try {\n return $this->executeTransfer(\n $fromAccountId,\n $toAccountId,\n $amountCents,\n $idempotencyKey\n );\n } catch (\\Throwable $e) {\n // Detect deadlock (MySQL error 1213, PostgreSQL 40P01, etc.)\n if ($this->isDeadlockException($e)) {\n $lastException = $e;\n // Exponential backoff with jitter\n $delayMs = self::DEADLOCK_RETRY_BASE_DELAY_MS * (2 ** $attempt) + random_int(0, 20);\n usleep($delayMs * 1000);\n continue;\n }\n // Non-deadlock exception: rethrow immediately\n throw $e;\n }\n }\n\n // All retries exhausted due to deadlocks\n throw new \\RuntimeException(\n 'Transfer failed after ' . self::MAX_DEADLOCK_RETRIES . ' deadlock retries.',\n 0,\n $lastException\n );\n }\n\n /**\n * Execute the transfer inside a database transaction.\n *\n * @return array{\n * transfer_id: int,\n * from_account_id: int,\n * to_account_id: int,\n * amount_cents: int,\n * idempotency_key: string,\n * created_at: string,\n * was_duplicate: bool\n * }\n */\n private function executeTransfer(\n int $fromAccountId,\n int $toAccountId,\n int $amountCents,\n string $idempotencyKey\n ): array {\n return DB::transaction(function () use ($fromAccountId, $toAccountId, $amountCents, $idempotencyKey) {\n // Check for existing transfer with this idempotency key (idempotency check)\n $existingTransfer = DB::table('transfers')\n ->where('idempotency_key', $idempotencyKey)\n ->first();\n\n if ($existingTransfer !== null) {\n // Return the original transfer result; no state changes\n return [\n 'transfer_id' => (int) $existingTransfer->id,\n 'from_account_id' => (int) $existingTransfer->from_account_id,\n 'to_account_id' => (int) $existingTransfer->to_account_id,\n 'amount_cents' => (int) $existingTransfer->amount_cents,\n 'idempotency_key' => $existingTransfer->idempotency_key,\n 'created_at' => $existingTransfer->created_at,\n 'was_duplicate' => true,\n ];\n }\n\n // Lock both account rows in deterministic ID order to prevent deadlocks.\n // We always lock the lower ID first, then the higher ID.\n $lowerId = min($fromAccountId, $toAccountId);\n $higherId = max($fromAccountId, $toAccountId);\n\n // Lock the lower-ID account row\n $lowerAccount = DB::table('accounts')\n ->where('id', $lowerId)\n ->lockForUpdate()\n ->first();\n\n if ($lowerAccount === null) {\n throw new \\RuntimeException(\"Account with ID {$lowerId} not found.\");\n }\n\n // Lock the higher-ID account row\n $higherAccount = DB::table('accounts')\n ->where('id', $higherId)\n ->lockForUpdate()\n ->first();\n\n if ($higherAccount === null) {\n throw new \\RuntimeException(\"Account with ID {$higherId} not found.\");\n }\n\n // Identify which locked row is the source and which is the destination\n if ($fromAccountId === $lowerId) {\n $fromAccount = $lowerAccount;\n $toAccount = $higherAccount;\n } else {\n $fromAccount = $higherAccount;\n $toAccount = $lowerAccount;\n }\n\n // Check sufficient funds\n if ($fromAccount->balance_cents < $amountCents) {\n throw new \\RuntimeException(\n \"Insufficient funds: account {$fromAccountId} has \" .\n $fromAccount->balance_cents . \" cents, but {$amountCents} cents are required.\"\n );\n }\n\n // Perform the transfer: debit source, credit destination\n DB::table('accounts')\n ->where('id', $fromAccountId)\n ->update([\n 'balance_cents' => DB::raw('balance_cents - ' . $amountCents),\n ]);\n\n DB::table('accounts')\n ->where('id', $toAccountId)\n ->update([\n 'balance_cents' => DB::raw('balance_cents + ' . $amountCents),\n ]);\n\n // Insert the transfer record\n $now = date('Y-m-d H:i:s');\n $transferId = DB::table('transfers')->insertGetId([\n 'idempotency_key' => $idempotencyKey,\n 'from_account_id' => $fromAccountId,\n 'to_account_id' => $toAccountId,\n 'amount_cents' => $amountCents,\n 'created_at' => $now,\n ]);\n\n // Persist an outbox event in the same transaction\n DB::table('outbox_events')->insert([\n 'transfer_id' => $transferId,\n 'event_type' => 'transfer.completed',\n 'payload' => json_encode([\n 'transfer_id' => $transferId,\n 'from_account_id' => $fromAccountId,\n 'to_account_id' => $toAccountId,\n 'amount_cents' => $amountCents,\n 'idempotency_key' => $idempotencyKey,\n ]),\n 'created_at' => $now,\n ]);\n\n return [\n 'transfer_id' => (int) $transferId,\n 'from_account_id' => $fromAccountId,\n 'to_account_id' => $toAccountId,\n 'amount_cents' => $amountCents,\n 'idempotency_key' => $idempotencyKey,\n 'created_at' => $now,\n 'was_duplicate' => false,\n ];\n });\n }\n\n /**\n * Determine whether a Throwable represents a database deadlock.\n *\n * @param \\Throwable $e\n * @return bool\n */\n private function isDeadlockException(\\Throwable $e): bool\n {\n // MySQL/InnoDB deadlock: SQLSTATE 40001, error code 1213\n // PostgreSQL deadlock: SQLSTATE 40P01\n // We check the message and any nested exception for common deadlock indicators.\n\n $message = $e->getMessage();\n\n // Check for common deadlock error codes/messages\n if (\n str_contains($message, '1213') ||\n str_contains($message, 'Deadlock found') ||\n str_contains($message, 'deadlock') ||\n str_contains($message, '40P01') ||\n str_contains($message, '40001')\n ) {\n return true;\n }\n\n // Check if it's a PDOException with a deadlock SQLSTATE\n if ($e instanceof \\PDOException) {\n $sqlState = $e->getCode();\n if ($sqlState === '40001' || $sqlState === '40P01') {\n return true;\n }\n }\n\n // Check previous exception in the chain\n if ($e->getPrevious() !== null) {\n return $this->isDeadlockException($e->getPrevious());\n }\n\n return false;\n }\n}\n\n/**\n * PHPUnit test suite for MoneyTransferService.\n *\n * These tests use an in-memory SQLite database to verify:\n * 1. Duplicate requests (same idempotency key) return the original transfer without\n * duplicating effects.\n * 2. Competing requests (concurrent transfers with different keys) preserve balances\n * and create exactly one transfer and one outbox event per successful transfer.\n * 3. Insufficient funds are rejected.\n * 4. Invalid inputs (zero/negative amount, same account) are rejected.\n */\nclass MoneyTransferServiceTest extends TestCase\n{\n private MoneyTransferService $service;\n\n protected function setUp(): void\n {\n parent::setUp();\n\n // Configure an in-memory SQLite database for testing\n $this->configureInMemoryDatabase();\n\n $this->service = new MoneyTransferService();\n }\n\n /**\n * Configure Laravel's DB facade to use an in-memory SQLite database\n * and create the required tables.\n */\n private function configureInMemoryDatabase(): void\n {\n // We need to bootstrap a minimal Laravel container for the DB facade.\n // In a real Laravel 12 app, this would be handled by the framework.\n // Here we simulate it using the Illuminate\\Database Capsule.\n\n $capsule = new \\Illuminate\\Database\\Capsule\\Manager();\n $capsule->addConnection([\n 'driver' => 'sqlite',\n 'database' => ':memory:',\n 'prefix' => '',\n ]);\n $capsule->setAsGlobal();\n $capsule->bootEloquent();\n\n // Create tables\n $schema = $capsule->schema();\n\n $schema->create('accounts', function ($table) {\n $table->bigIncrements('id');\n $table->big",
|
|
"error": null
|
|
},
|
|
{
|
|
"task": "typescript_dag_scheduler",
|
|
"runtime": "ollama",
|
|
"model": "qwen3.8:27b-mxfp8",
|
|
"started_at": "2026-08-31T12:27:38.904048+00:00",
|
|
"prompt_chars": 773,
|
|
"max_tokens": 3072,
|
|
"ttft_s": 1.6032058749697171,
|
|
"total_s": 147.27845333400182,
|
|
"prompt_tokens": 183,
|
|
"output_tokens": 3072,
|
|
"decode_tps": 21.060449812696582,
|
|
"finish_reason": "length",
|
|
"output_chars": 11368,
|
|
"output_sha256": "a3d74aa8a8183babda562bc53b32dda3b1b3ad318788d2384cc8babff3df3223",
|
|
"output": "// dag-scheduler.ts\n// Strict-mode, dependency-free DAG task scheduler.\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface Task<T = unknown> {\n readonly id: string;\n readonly deps: readonly string[];\n readonly run: (signal: AbortSignal) => Promise<T>;\n}\n\nexport interface ScheduleOptions {\n readonly maxConcurrency?: number;\n readonly signal?: AbortSignal;\n}\n\nexport type ScheduleResult<T> = Map<string, T>;\n\n// ---------------------------------------------------------------------------\n// Errors\n// ---------------------------------------------------------------------------\n\nexport class CycleError extends Error {\n readonly path: readonly string[];\n constructor(path: readonly string[]) {\n super(`Cycle detected: ${path.join(\" -> \")}`);\n this.name = \"CycleError\";\n this.path = path;\n }\n}\n\nexport class UnknownDependencyError extends Error {\n readonly taskId: string;\n readonly depId: string;\n constructor(taskId: string, depId: string) {\n super(`Task \"${taskId}\" has unknown dependency \"${depId}\"`);\n this.name = \"UnknownDependencyError\";\n this.taskId = taskId;\n this.depId = depId;\n }\n}\n\nexport class DuplicateIdError extends Error {\n readonly id: string;\n constructor(id: string) {\n super(`Duplicate task id: \"${id}\"`);\n this.name = \"DuplicateIdError\";\n this.id = id;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Detect a cycle in a directed graph using iterative DFS.\n * Returns the cycle path (list of node ids) or null if acyclic.\n */\nfunction detectCycle(\n nodes: readonly string[],\n adj: Map<string, readonly string[]>\n): string[] | null {\n const WHITE = 0;\n const GRAY = 1;\n const BLACK = 2;\n\n const color = new Map<string, number>();\n for (const n of nodes) color.set(n, WHITE);\n\n // parent map for path reconstruction\n const parent = new Map<string, string | null>();\n\n for (const start of nodes) {\n if (color.get(start) !== WHITE) continue;\n\n // Iterative DFS with explicit stack of [node, nextChildIndex]\n const stack: Array<{ node: string; idx: number }> = [{ node: start, idx: 0 }];\n parent.set(start, null);\n\n while (stack.length > 0) {\n const frame = stack[stack.length - 1];\n const node = frame.node;\n const neighbors = adj.get(node) ?? [];\n\n if (frame.idx < neighbors.length) {\n const next = neighbors[frame.idx];\n frame.idx++;\n\n const c = color.get(next);\n if (c === GRAY) {\n // Found a cycle: reconstruct path from `next` back to `node`\n const path: string[] = [next, node];\n let cur: string | null = node;\n while (cur !== null && cur !== next) {\n const p = parent.get(cur);\n if (p === undefined || p === null) break;\n path.push(p);\n cur = p;\n }\n path.reverse();\n return path;\n }\n if (c === WHITE) {\n color.set(next, GRAY);\n parent.set(next, node);\n stack.push({ node: next, idx: 0 });\n }\n } else {\n color.set(node, BLACK);\n stack.pop();\n }\n }\n }\n\n return null;\n}\n\n/**\n * Compute a stable topological order using Kahn's algorithm.\n * Ties are broken by original input order (stable).\n */\nfunction topologicalOrder(\n tasks: readonly Task[]\n): string[] {\n const idToIndex = new Map<string, number>();\n for (let i = 0; i < tasks.length; i++) {\n idToIndex.set(tasks[i].id, i);\n }\n\n const inDegree = new Map<string, number>();\n const adj = new Map<string, string[]>();\n\n for (const t of tasks) {\n inDegree.set(t.id, 0);\n adj.set(t.id, []);\n }\n\n for (const t of tasks) {\n for (const dep of t.deps) {\n // dep -> t edge\n adj.get(dep)!.push(t.id);\n inDegree.set(t.id, (inDegree.get(t.id) ?? 0) + 1);\n }\n }\n\n // Use a min-heap-like approach: pick the ready node with the smallest\n // original index to ensure stable input-order scheduling.\n const ready: string[] = [];\n for (const t of tasks) {\n if ((inDegree.get(t.id) ?? 0) === 0) {\n ready.push(t.id);\n }\n }\n // Sort ready by original index for stability\n ready.sort((a, b) => (idToIndex.get(a)! - idToIndex.get(b)!));\n\n const order: string[] = [];\n while (ready.length > 0) {\n // Pick the first (smallest original index)\n const node = ready.shift()!;\n order.push(node);\n\n const children = adj.get(node) ?? [];\n for (const child of children) {\n const deg = (inDegree.get(child) ?? 0) - 1;\n inDegree.set(child, deg);\n if (deg === 0) {\n // Insert into ready maintaining sorted order by original index\n const idx = idToIndex.get(child)!;\n // Binary search for insertion point\n let lo = 0;\n let hi = ready.length;\n while (lo < hi) {\n const mid = (lo + hi) >> 1;\n if (idToIndex.get(ready[mid])! < idx) {\n lo = mid + 1;\n } else {\n hi = mid;\n }\n }\n ready.splice(lo, 0, child);\n }\n }\n }\n\n return order;\n}\n\n// ---------------------------------------------------------------------------\n// Scheduler\n// ---------------------------------------------------------------------------\n\nexport async function schedule<T = unknown>(\n tasks: readonly Task<T>[],\n options: ScheduleOptions = {}\n): Promise<Map<string, T>> {\n const maxConcurrency = options.maxConcurrency ?? Infinity;\n const externalSignal = options.signal;\n\n // 1. Validate: duplicate IDs\n const seen = new Set<string>();\n for (const t of tasks) {\n if (seen.has(t.id)) {\n throw new DuplicateIdError(t.id);\n }\n seen.add(t.id);\n }\n\n // 2. Validate: unknown dependencies\n for (const t of tasks) {\n for (const dep of t.deps) {\n if (!seen.has(dep)) {\n throw new UnknownDependencyError(t.id, dep);\n }\n }\n }\n\n // 3. Build adjacency and detect cycles\n const ids = tasks.map((t) => t.id);\n const adj = new Map<string, readonly string[]>();\n for (const t of tasks) {\n adj.set(t.id, t.deps);\n }\n\n const cycle = detectCycle(ids, adj);\n if (cycle !== null) {\n throw new CycleError(cycle);\n }\n\n // 4. Compute stable topological order\n const topo = topologicalOrder(tasks);\n\n // 5. Build a lookup from id to task\n const taskById = new Map<string, Task<T>>();\n for (const t of tasks) {\n taskById.set(t.id, t);\n }\n\n // 6. Compute in-degree for scheduling\n const inDegree = new Map<string, number>();\n const dependents = new Map<string, string[]>(); // dep -> list of task ids that depend on it\n\n for (const t of tasks) {\n inDegree.set(t.id, t.deps.length);\n for (const dep of t.deps) {\n if (!dependents.has(dep)) {\n dependents.set(dep, []);\n }\n dependents.get(dep)!.push(t.id);\n }\n }\n\n // 7. Scheduling state\n const results = new Map<string, T>();\n const running = new Set<Promise<void>>();\n const failed = new Set<string>(); // tasks that failed or were skipped due to failure\n let firstError: unknown = null;\n let hasError = false;\n\n // AbortController for internal cancellation\n const internalAbort = new AbortController();\n const onExternalAbort = () => {\n internalAbort.abort();\n };\n if (externalSignal) {\n if (externalSignal.aborted) {\n internalAbort.abort();\n } else {\n externalSignal.addEventListener(\"abort\", onExternalAbort, { once: true });\n }\n }\n\n // Track which tasks have been \"started\" (their run() has been called)\n const started = new Set<string>();\n\n // Helper: check if a task should be skipped because a dependency failed\n function shouldSkip(taskId: string): boolean {\n const t = taskById.get(taskId)!;\n for (const dep of t.deps) {\n if (failed.has(dep)) {\n return true;\n }\n }\n return false;\n }\n\n // Helper: mark a task and all its transitive dependents as failed/skipped\n function markFailedAndDependents(taskId: string): void {\n if (failed.has(taskId)) return;\n failed.add(taskId);\n const queue: string[] = [taskId];\n while (queue.length > 0) {\n const cur = queue.shift()!;\n const deps = dependents.get(cur) ?? [];\n for (const dep of deps) {\n if (!failed.has(dep)) {\n failed.add(dep);\n queue.push(dep);\n }\n }\n }\n }\n\n // Helper: try to start a task if all deps are done and it's not failed\n function tryStart(taskId: string): void {\n if (started.has(taskId) || failed.has(taskId)) return;\n const t = taskById.get(taskId)!;\n // Check all deps are completed (in results) and none failed\n for (const dep of t.deps) {\n if (!results.has(dep)) return; // dep not yet done\n if (failed.has(dep)) {\n markFailedAndDependents(taskId);\n return;\n }\n }\n // All deps satisfied and none failed\n started.add(taskId);\n\n const p = (async () => {\n try {\n const result = await t.run(internalAbort.signal);\n if (hasError) return; // already failed, don't record\n results.set(taskId, result);\n } catch (err) {\n if (!hasError) {\n hasError = true;\n firstError = err;\n markFailedAndDependents(taskId);\n // Abort all running tasks\n internalAbort.abort();\n }\n // Re-throw to let the outer catch handle it, but we don't want\n // unhandled rejection. We'll handle it in the running set.\n throw err;\n }\n })();\n\n // Attach a catch to prevent unhandled rejection\n p.catch(() => {\n // Already handled above; this is just to prevent unhandled rejection\n });\n\n running.add(p);\n p.finally(() => {\n running.delete(p);\n });\n }\n\n // 8. Initial pass: start all tasks with no dependencies\n for (const taskId of topo) {\n const t = taskById.get(taskId)!;\n if (t.deps.length === 0) {\n tryStart(taskId);\n }\n }\n\n // 9. Main loop: wait for tasks to complete, then start newly-ready tasks\n // We use a promise-based approach: wait for at least one running task to finish,\n // then check for newly ready tasks.\n\n // We need to handle the case where no tasks are running but there are still\n // tasks to start (shouldn't happen in a valid DAG, but be safe).\n\n // Use a \"wake\" mechanism: when a task completes, we check for newly ready tasks.\n // We'll use a simple approach: poll the running set.\n\n // Actually, a cleaner approach: use a \"completion\" event.\n // Let's restructure: we'll use a queue of \"ready\" tasks and a running set.\n\n // Reset and use a cleaner scheduling loop\n // Clear previous state\n running.clear();\n started.clear();\n results.clear();\n failed.clear();\n hasError = false;\n firstError = null;\n\n // Ready queue: tasks whose deps are all satisfied\n const readyQueue: string[] = [];\n for (const taskId of topo) {\n const t = taskById.get(taskId)!;\n if (t.deps.length === 0) {\n readyQueue.push(taskId);\n }\n }\n\n // Process ready queue: start tasks up to maxConcurrency\n function processReady(): void {\n while (readyQueue.length > 0 && running.size < maxConcurrency && !hasError) {\n const taskId = readyQueue.shift()!;\n if (failed.has(taskId)) continue;\n if (started.has",
|
|
"error": null
|
|
}
|
|
]
|
|
} |