debug-env / debug_env /tasks /incident1 /buggy_server.py
hugsbun's picture
feat: add SRE incident benchmark (Pivot 2 - Scenario A)
a9f2957
Raw
History Blame Contribute Delete
3.13 kB
"""
Buggy FastAPI server for the incident1 scenario.
Serves three endpoints and runs a background job worker thread.
The worker crash-loops because it hits a poisoned DB record.
"""
import json
import logging
import os
import sqlite3
import sys
import threading
import time
import uvicorn
from fastapi import FastAPI
from fastapi.responses import JSONResponse
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s %(levelname)s %(name)s: %(message)s",
stream=sys.stdout,
)
logger = logging.getLogger("buggy_server")
app = FastAPI()
_DB_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "app.db")
def _get_conn() -> sqlite3.Connection:
conn = sqlite3.connect(_DB_PATH)
conn.row_factory = sqlite3.Row
return conn
# ── Endpoints ──────────────────────────────────────────────────────────────
@app.get("/health")
def health():
return {"status": "ok"}
@app.get("/jobs")
def list_jobs():
conn = _get_conn()
rows = conn.execute(
"SELECT id, payload, status FROM jobs ORDER BY id"
).fetchall()
conn.close()
return {"jobs": [dict(r) for r in rows]}
@app.post("/checkout")
def checkout():
"""Process the next pending job. Returns 500 if the payload is malformed."""
conn = _get_conn()
row = conn.execute(
"SELECT * FROM jobs WHERE status='pending' ORDER BY id LIMIT 1"
).fetchone()
if not row:
conn.close()
return {"status": "no_pending_jobs"}
try:
data = json.loads(row["payload"])
conn.execute(
"UPDATE jobs SET status='completed' WHERE id=?", (row["id"],)
)
conn.commit()
conn.close()
return {"status": "ok", "job_id": row["id"], "data": data}
except Exception as exc:
conn.close()
return JSONResponse(
status_code=500,
content={"error": f"Serialization Error: {exc}", "job_id": row["id"]},
)
# ── Worker loop ────────────────────────────────────────────────────────────
def _worker_loop() -> None:
"""Run worker.process_jobs() in a tight restart loop (crash-loop visible in logs)."""
# Ensure the workdir (where worker.py lives) is importable
workdir = os.path.dirname(os.path.abspath(__file__))
if workdir not in sys.path:
sys.path.insert(0, workdir)
import worker
while True:
try:
worker.process_jobs()
except Exception as exc:
logger.error("[WORKER CRASH] %s: %s", type(exc).__name__, exc)
time.sleep(0.5)
@app.on_event("startup")
def _start_worker() -> None:
t = threading.Thread(target=_worker_loop, daemon=True, name="worker-loop")
t.start()
logger.info("Worker thread started")
if __name__ == "__main__":
port = int(os.environ.get("BUGGY_SERVER_PORT", 9000))
uvicorn.run(app, host="0.0.0.0", port=port, log_config=None)