Implement Phase 0b base project against the frozen test contract
All seven services (gateway, ledger, fraud, sanctions, insights, notifier, audit), db/schema.sql plus optional_indexes.sql, scripts/seed.py and smoke.py, the gateway-served frontend, and docker-compose.yml. Plain-text stdlib logging and a weak manual X-Request-Id only -- no observability yet, by design, per test_no_observability.py. Implements all 13 named chaos-flag pathologies from docs/PATHOLOGIES.md behind Redis-backed runtime flags. make smoke and make test-contract pass; make test-baseline and make test-pathology pass except for a handful of load-sensitive tests documented in tests/README.md rather than papered over.
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
import sqlalchemy as sa
|
||||
|
||||
# Deliberately the same shape of query the test suite's own EXPLAIN check runs
|
||||
# against the schema directly (tests/test_pathologies.py, 6.4) -- kept here
|
||||
# only for the honest (non-n_plus_one) path; that test doesn't call this code.
|
||||
|
||||
_N_PLUS_ONE_ROW_CAP = 1500
|
||||
|
||||
|
||||
async def account_exists(session, account_id: str) -> bool:
|
||||
result = await session.execute(sa.text("SELECT 1 FROM accounts WHERE id = :id"), {"id": account_id})
|
||||
return result.first() is not None
|
||||
|
||||
|
||||
async def compute_summary(session, account_id: str, *, n_plus_one: bool) -> dict:
|
||||
if n_plus_one:
|
||||
rows = await _n_plus_one_summary(session, account_id)
|
||||
else:
|
||||
rows = await _joined_summary(session, account_id)
|
||||
|
||||
total_count = 0
|
||||
total_amount = 0
|
||||
by_category: dict = {}
|
||||
for r in rows:
|
||||
total_count += 1
|
||||
total_amount += r["amount_minor"]
|
||||
label = r["label"] or "uncategorized"
|
||||
entry = by_category.setdefault(label, {"count": 0, "total_amount_minor": 0})
|
||||
entry["count"] += 1
|
||||
entry["total_amount_minor"] += r["amount_minor"]
|
||||
|
||||
return {
|
||||
"account_id": account_id,
|
||||
"window_days": 120,
|
||||
"transaction_count": total_count,
|
||||
"total_amount_minor": total_amount,
|
||||
"by_category": by_category,
|
||||
}
|
||||
|
||||
|
||||
async def _joined_summary(session, account_id: str) -> list[dict]:
|
||||
"""The honest path: a single JOIN, one statement."""
|
||||
result = await session.execute(
|
||||
sa.text(
|
||||
"SELECT t.amount_minor, mc.label FROM transactions t "
|
||||
"LEFT JOIN merchant_categories mc ON mc.id = t.category_id "
|
||||
"WHERE t.account_id = :aid AND t.created_at >= now() - interval '120 days'"
|
||||
),
|
||||
{"aid": account_id},
|
||||
)
|
||||
return [dict(row) for row in result.mappings().all()]
|
||||
|
||||
|
||||
async def _n_plus_one_summary(session, account_id: str) -> list[dict]:
|
||||
"""n_plus_one: instead of the JOIN above, fetch rows without one and issue
|
||||
a separate category lookup per row -- phase-0-base-project-spec.md §7.
|
||||
Capped so the pathology stays a genuine multiplication of statements
|
||||
(see tests/test_pathologies.py 6.3) without turning a single request into
|
||||
an unbounded number of round trips on a high-volume hot account."""
|
||||
result = await session.execute(
|
||||
sa.text(
|
||||
"SELECT amount_minor, category_id FROM transactions "
|
||||
"WHERE account_id = :aid AND created_at >= now() - interval '120 days' "
|
||||
f"LIMIT {_N_PLUS_ONE_ROW_CAP}"
|
||||
),
|
||||
{"aid": account_id},
|
||||
)
|
||||
tx_rows = result.mappings().all()
|
||||
|
||||
rows = []
|
||||
for tx_row in tx_rows:
|
||||
label = None
|
||||
if tx_row["category_id"] is not None:
|
||||
cat_result = await session.execute(
|
||||
sa.text("SELECT label FROM merchant_categories WHERE id = :id"),
|
||||
{"id": tx_row["category_id"]},
|
||||
)
|
||||
cat_row = cat_result.first()
|
||||
if cat_row is not None:
|
||||
label = cat_row.label
|
||||
rows.append({"amount_minor": tx_row["amount_minor"], "label": label})
|
||||
return rows
|
||||
Reference in New Issue
Block a user