About DB-Pulse
Inspiration
Every backend engineer has had the same bad morning: the database is slow, the log is ten thousand lines, and buried somewhere in it is a single repeating pattern that explains everything. The information is right there. Finding it still takes hours, because the signal is structural — it lives in the shape of the log, not in any one line of it.
That asymmetry is what got us. A human reads a slow-query log line by line and runs out of patience around line four hundred. A language model reads the whole thing at once and sees the shape immediately. This is a genuinely good fit for an LLM, and not in the way most "AI for X" demos are — the model isn't being asked to be creative, it's being asked to pattern-match across a context window larger than human attention.
The canonical example is the N+1 query. Rendering one page issues
$$T_{\text{before}} = t_{\text{query}} \cdot (1 + N)$$
round-trips, where $N$ is the number of rows. At $N = 200$ and $t \approx 3\,$ms that's over 600 ms of pure network latency before the database does any real work. The fix collapses it to a single joined query:
$$T_{\text{after}} = t_{\text{join}} = O(1)$$
The fix is obvious once you see it. Seeing it is the whole problem.
What it does
Paste a messy slow-query log, deadlock report, or ORM stack trace. Pick your engine. DB-Pulse returns a two-sentence structural diagnosis and the exact migration that fixes it, in that engine's dialect — PostgreSQL and MySQL get SQL, MongoDB gets a mongosh compound index following the Equality-Sort-Range rule.
It reliably identifies N+1 query patterns, missing and mis-ordered composite
indexes, unindexed ORDER BY filesorts, collection scans, and non-deterministic
lock ordering in deadlocks.
How we built it
Python and Streamlit for the interface, with a deliberately thin architecture:
llm_providers.py— one interface over Groq, OpenAI, Together AI and Google Gemini, spoken over plain HTTPS with no vendor SDKs. Three are OpenAI-compatible; Gemini is not, so it gets its own request shaper. The app auto-detects whichever API key is present and runs on it with no code change.prompts.py— the DBRE system prompt plus a format contract that pins the response into parseable sections.parsing.py— splits the response into diagnosis and patch.app.py— the interface and the rollout panel.
Avoiding SDKs was a deliberate call. It kept requirements.txt to two lines,
which made the deploy fast and meant swapping providers mid-demo costs nothing.
Challenges we ran into
Model IDs rot faster than you expect. Our first live run returned HTTP 404 —
this model is no longer available. The model we'd hardcoded had been retired.
We queried the provider's live model list, pinned to versions we'd actually
verified responding, and made the app surface the provider's own error text
instead of failing silently — so the next time it rots, whoever hits it knows
exactly why.
Providers fall over under load. Mid-testing we got HTTP 503 — this model is
currently experiencing high demand. On a demo day, that's a red error box in
front of judges. We added bounded retries with backoff on 429 and 5xx. It has
already saved a run.
The prompt was quietly lying about the output format. Our format contract
hardcoded a ``sql code fence. The model dutifully copied that tag even for
MongoDB, so mongosh JavaScript came back labelled as SQL — wrong syntax
highlighting, and the download button handed you a.jsfile named.sql`. The
fence tag now derives from the selected engine, with a parser fallback that
corrects an obvious mongosh script regardless of how it's tagged. This one is
our favourite bug, because the code was correct and the prompt was the defect.
Models drift from any format you give them. Sometimes you get
### DIAGNOSIS, sometimes 1. Diagnosis, sometimes no code fence at all. The
parser degrades through all of those rather than dropping the answer on the
floor. Eighteen tests cover those paths, including empty and entirely
unstructured responses.
Charting libraries have opinions. Streamlit's built-in bar chart sorts categories alphabetically, so "After patch" rendered to the left of "Before patch" — which reads backwards to anyone watching a demo. We replaced it with hand-rolled bars.
What we learned
The interesting failures were not in the model. Every real bug lived in the seams: a retired model ID, a saturated endpoint, a prompt that hardcoded an assumption, a chart library's default sort. Making an LLM feature reliable is mostly ordinary defensive engineering around a component that is itself non-deterministic and occasionally unavailable.
We also learned that being specific in the prompt about the target engine changes the output quality far more than we expected. Telling it "PostgreSQL" versus "MySQL" doesn't just change syntax — it changes which fix the model reaches for, because the engines have genuinely different indexing strategies.
Honest scope
The log analysis and patch generation are real, live LLM inference on whatever
you paste. The Apply Fix to Production rollout is a labelled simulation: the
app holds no database connection, and the latency figures it reports are fixed
demo constants defined in app.py. The UI says so on screen, above the button.
We could have quietly shipped the animation and let it read as a benchmark. We'd rather show the panel as the reporting surface a full agent loop would report through, and be straight about which half is real.
What's next
Close the loop for real: run the generated migration against a shadow replica
inside a transaction, measure with EXPLAIN ANALYZE before and after, roll back,
and report the measured delta instead of a constant. The interface is already
shaped for it — that panel is waiting for real numbers.## Inspiration
Log in or sign up for Devpost to join the conversation.