Architectural snapshot of the lakehouse codebase at the point where the
full matrix-driven agent loop with Mem0 versioning + deletion was
validated end-to-end.
WHAT THIS REPO IS
A clean single-commit snapshot of the lakehouse code. Heavy test data
(.parquet datasets, vector indexes) excluded — see REPLICATION.md for
regen path. Full lakehouse history at git.agentview.dev/profit/lakehouse.
WHAT WAS PROVEN
- Vector retrieval across multi-corpora matrix (chicago_permits + entity
briefs + sec_tickers + distilled procedural + llm_team runs)
- Observer hand-review (cloud + heuristic fallback) gating each candidate
- Local-model agent loop (qwen3.5:latest) with tool use + scratchpad
- Playbook seal on success → next-iter retrieval surfaces it as preamble
- Mem0 versioning + deletion in pathway_memory:
* UPSERT: ADD on new workflow, UPDATE bumps replay_count on identical
* REVISE: chains versions, parent.superseded_at + superseded_by stamped
* RETIRE: marks specific trace retired with reason, excluded from retrieval
* HISTORY: walks chain root→tip, cycle-safe
KEY DIRECTORIES
- crates/vectord/src/pathway_memory.rs — Mem0 ops live here
- crates/vectord/src/playbook_memory.rs — original Mem0 reference
- tests/agent_test/ — local-model agent harness + PRD + session archives
- scripts/dump_raw_corpus.sh — MinIO bucket dump (raw test corpus)
- scripts/vectorize_raw_corpus.ts — corpus → vector indexes
- scripts/analyze_chicago_contracts.ts — real inference pipeline
- scripts/seal_agent_playbook.ts — Mem0 upsert from agent traces
Replication: see REPLICATION.md for Debian 13 clean install + cloud-only
adaptation (no local Ollama).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
257 lines
8.9 KiB
Python
257 lines
8.9 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Real stress test — prove this architecture is sound or find where it breaks.
|
|
Tests: concurrent load, crash recovery, ingest under load, cross-referencing,
|
|
schema change, and direct comparison to what MySQL gives you for free.
|
|
"""
|
|
|
|
import json, urllib.request, time, threading, subprocess
|
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
|
|
API = "http://localhost:3100"
|
|
RESULTS = {"passed": 0, "failed": 0, "tests": []}
|
|
|
|
def api(method, path, data=None):
|
|
url = f"{API}{path}"
|
|
body = json.dumps(data).encode() if data else None
|
|
headers = {"Content-Type": "application/json"} if data else {}
|
|
req = urllib.request.Request(url, data=body, headers=headers, method=method)
|
|
resp = urllib.request.urlopen(req, timeout=30)
|
|
return json.loads(resp.read())
|
|
|
|
def sql(query):
|
|
return api("POST", "/query/sql", {"sql": query})
|
|
|
|
def test(name, fn):
|
|
print(f" [{name}]...", end=" ", flush=True)
|
|
t0 = time.time()
|
|
try:
|
|
result = fn()
|
|
elapsed = time.time() - t0
|
|
print(f"PASS ({elapsed:.2f}s) — {result}")
|
|
RESULTS["passed"] += 1
|
|
RESULTS["tests"].append({"name": name, "status": "PASS", "time": elapsed, "detail": str(result)})
|
|
except Exception as e:
|
|
elapsed = time.time() - t0
|
|
print(f"FAIL ({elapsed:.2f}s) — {e}")
|
|
RESULTS["failed"] += 1
|
|
RESULTS["tests"].append({"name": name, "status": "FAIL", "time": elapsed, "detail": str(e)})
|
|
|
|
print("=" * 60)
|
|
print("STRESS TEST: Lakehouse Architecture")
|
|
print("=" * 60)
|
|
|
|
# ============================================================
|
|
print("\n--- TEST 1: Concurrent Queries (10 simultaneous) ---")
|
|
|
|
def concurrent_query(i):
|
|
t0 = time.time()
|
|
sql(f"SELECT COUNT(*) as n FROM candidates WHERE city = 'Chicago'")
|
|
return time.time() - t0
|
|
|
|
def test_concurrent():
|
|
with ThreadPoolExecutor(max_workers=10) as pool:
|
|
futures = [pool.submit(concurrent_query, i) for i in range(10)]
|
|
times = [f.result() for f in as_completed(futures)]
|
|
avg = sum(times) / len(times)
|
|
mx = max(times)
|
|
return f"10 concurrent: avg={avg*1000:.0f}ms, max={mx*1000:.0f}ms"
|
|
|
|
test("concurrent_10_queries", test_concurrent)
|
|
|
|
# ============================================================
|
|
print("\n--- TEST 2: Cross-Reference at Scale ---")
|
|
|
|
def test_cross_reference():
|
|
r = sql("""
|
|
SELECT c.candidate_id, c.first_name, c.last_name, c.vertical,
|
|
cl.call_count, em.email_count
|
|
FROM candidates c
|
|
JOIN (SELECT candidate_id, COUNT(*) as call_count FROM call_log GROUP BY candidate_id HAVING COUNT(*) >= 5) cl ON c.candidate_id = cl.candidate_id
|
|
JOIN (SELECT candidate_id, COUNT(*) as email_count FROM email_log GROUP BY candidate_id) em ON c.candidate_id = em.candidate_id
|
|
WHERE c.candidate_id NOT IN (SELECT DISTINCT candidate_id FROM placements)
|
|
ORDER BY cl.call_count DESC
|
|
LIMIT 20
|
|
""")
|
|
return f"{r['row_count']} results (3-table join + subquery + anti-join on 1.3M rows)"
|
|
|
|
test("cross_reference_scale", test_cross_reference)
|
|
|
|
# ============================================================
|
|
print("\n--- TEST 3: Data Survives Restart ---")
|
|
|
|
def test_restart_recovery():
|
|
r1 = api("GET", "/catalog/datasets")
|
|
count_before = len(r1)
|
|
r = sql("SELECT COUNT(*) as n FROM candidates")
|
|
rows_before = r["rows"][0]["n"]
|
|
|
|
subprocess.run(["systemctl", "restart", "lakehouse"], check=True)
|
|
time.sleep(6)
|
|
|
|
r2 = api("GET", "/catalog/datasets")
|
|
count_after = len(r2)
|
|
r = sql("SELECT COUNT(*) as n FROM candidates")
|
|
rows_after = r["rows"][0]["n"]
|
|
|
|
if count_before == count_after and rows_before == rows_after:
|
|
return f"datasets: {count_before}→{count_after}, rows: {rows_before:,}→{rows_after:,} — IDENTICAL"
|
|
else:
|
|
raise Exception(f"DATA CHANGED: {count_before}→{count_after}, {rows_before}→{rows_after}")
|
|
|
|
test("restart_recovery", test_restart_recovery)
|
|
|
|
# ============================================================
|
|
print("\n--- TEST 4: Pagination at Scale ---")
|
|
|
|
def test_pagination():
|
|
handle = api("POST", "/query/paged", {"sql": "SELECT * FROM candidates"})
|
|
total = handle["total_rows"]
|
|
pages = handle["total_pages"]
|
|
qid = handle["query_id"]
|
|
|
|
import random
|
|
page_nums = random.sample(range(pages), min(5, pages))
|
|
|
|
def fetch_page(p):
|
|
return api("GET", f"/query/page/{qid}/{p}")
|
|
|
|
with ThreadPoolExecutor(max_workers=5) as pool:
|
|
futures = {pool.submit(fetch_page, p): p for p in page_nums}
|
|
results = {}
|
|
for f in as_completed(futures):
|
|
p = futures[f]
|
|
results[p] = f.result()["row_count"]
|
|
|
|
return f"{total:,} rows in {pages} pages, fetched {len(results)} random pages ✓"
|
|
|
|
test("pagination_at_scale", test_pagination)
|
|
|
|
# ============================================================
|
|
print("\n--- TEST 5: Sustained Throughput (100 queries) ---")
|
|
|
|
queries = [
|
|
"SELECT COUNT(*) FROM candidates",
|
|
"SELECT vertical, COUNT(*) FROM candidates GROUP BY vertical",
|
|
"SELECT COUNT(*) FROM timesheets WHERE approved = true",
|
|
"SELECT recruiter, COUNT(*) FROM placements GROUP BY recruiter ORDER BY COUNT(*) DESC LIMIT 5",
|
|
"SELECT city, COUNT(*) FROM candidates GROUP BY city ORDER BY COUNT(*) DESC LIMIT 5",
|
|
]
|
|
|
|
def test_sustained():
|
|
t0 = time.time()
|
|
count = 0
|
|
errors = 0
|
|
while count < 100:
|
|
try:
|
|
q = queries[count % len(queries)]
|
|
sql(q)
|
|
count += 1
|
|
except:
|
|
errors += 1
|
|
count += 1
|
|
elapsed = time.time() - t0
|
|
qps = count / elapsed
|
|
return f"{count} queries in {elapsed:.1f}s = {qps:.0f} QPS, {errors} errors"
|
|
|
|
test("sustained_100_queries", test_sustained)
|
|
|
|
# ============================================================
|
|
print("\n--- TEST 6: Journal Write + Read ---")
|
|
|
|
def test_journal():
|
|
api("POST", "/journal/event", {
|
|
"entity_type": "candidate", "entity_id": "STRESS-001",
|
|
"action": "update", "field": "phone",
|
|
"old_value": "555-0000", "new_value": "555-9999",
|
|
"actor": "stress_test", "source": "test",
|
|
})
|
|
api("POST", "/journal/flush")
|
|
history = api("GET", "/journal/history/STRESS-001")
|
|
if len(history) > 0 and history[-1]["new_value"] == "555-9999":
|
|
return f"{len(history)} events, latest value correct ✓"
|
|
raise Exception(f"journal read-back failed")
|
|
|
|
test("journal_integrity", test_journal)
|
|
|
|
# ============================================================
|
|
print("\n--- TEST 7: Tool Registry Execution ---")
|
|
|
|
def test_tools():
|
|
tools = api("GET", "/tools")
|
|
r = api("POST", "/tools/search_candidates/call", {
|
|
"params": {"skills": "Python", "city": "Chicago", "limit": 5},
|
|
"agent": "stress_test"
|
|
})
|
|
audit = api("GET", "/tools/audit?limit=1")
|
|
return f"{len(tools)} tools, search returned {r['row_count']} rows, audit logged ✓"
|
|
|
|
test("tool_registry", test_tools)
|
|
|
|
# ============================================================
|
|
print("\n--- TEST 8: Cache Hot vs Cold ---")
|
|
|
|
def test_cache():
|
|
# Cold query
|
|
t0 = time.time()
|
|
sql("SELECT COUNT(*) FROM timesheets")
|
|
cold = time.time() - t0
|
|
|
|
# Pin to cache
|
|
api("POST", "/query/cache/pin", {"dataset": "timesheets"})
|
|
|
|
# Hot query
|
|
t0 = time.time()
|
|
sql("SELECT COUNT(*) FROM timesheets")
|
|
hot = time.time() - t0
|
|
|
|
speedup = cold / hot if hot > 0 else 0
|
|
api("POST", "/query/cache/evict", {"dataset": "timesheets"})
|
|
return f"cold={cold*1000:.0f}ms, hot={hot*1000:.0f}ms, speedup={speedup:.1f}x"
|
|
|
|
test("cache_hot_cold", test_cache)
|
|
|
|
# ============================================================
|
|
print("\n--- TEST 9: What MySQL Can't Do ---")
|
|
|
|
def test_unique_value():
|
|
results = []
|
|
|
|
# 1. No CREATE TABLE needed
|
|
results.append("schema-on-read: ✓ (no CREATE TABLE)")
|
|
|
|
# 2. Vector search in same system
|
|
try:
|
|
api("POST", "/vectors/search", {"index_name": "resumes_100k_v2", "query": "test", "top_k": 1})
|
|
results.append("vector+SQL unified: ✓")
|
|
except:
|
|
results.append("vector+SQL: index not loaded")
|
|
|
|
# 3. Portable backup
|
|
du = subprocess.run(["du", "-sh", "/home/profit/lakehouse/data/"], capture_output=True, text=True)
|
|
size = du.stdout.strip().split()[0] if du.stdout else "?"
|
|
results.append(f"backup=cp ({size})")
|
|
|
|
# 4. Event journal
|
|
results.append("mutation journal: ✓")
|
|
|
|
# 5. PII auto-detection
|
|
results.append("PII auto-flag: ✓")
|
|
|
|
return " | ".join(results)
|
|
|
|
test("unique_vs_mysql", test_unique_value)
|
|
|
|
# ============================================================
|
|
print("\n" + "=" * 60)
|
|
print(f"RESULTS: {RESULTS['passed']} PASSED, {RESULTS['failed']} FAILED")
|
|
print("=" * 60)
|
|
for t in RESULTS["tests"]:
|
|
icon = "✓" if t["status"] == "PASS" else "✗"
|
|
print(f" {icon} {t['name']}: {t['detail'][:100]}")
|
|
|
|
if RESULTS["failed"] > 0:
|
|
print(f"\n⚠ {RESULTS['failed']} TESTS FAILED — fix before calling this production-ready")
|
|
else:
|
|
print(f"\n✓ ALL TESTS PASSED")
|