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

What it does

How we built it

Challenges we ran into

Accomplishments that we're proud of

What we learned

What's next for DB-Pulse

Built With

Share this project:

Updates

Submission history