The Outage Nobody Saw Coming
It starts innocuously. A routine deployment ships on a Tuesday afternoon. Traffic looks normal. Then, somewhere deep in the stack, a single database query begins to slow — maybe due to a missing index, a suddenly skewed data distribution, or a table that quietly crossed a size threshold overnight. Within minutes, connection pool threads are exhausted, upstream services begin timing out, and your monitoring dashboard lights up like a Christmas tree. By the time an engineer is paged, the cascade has already happened.
This is not a hypothetical. It is a pattern that repeats itself across engineering teams of every size, from scrappy startups to well-staffed platform teams at large enterprises. And yet, most resilience tooling — Resilience4j, Hystrix, Sentinel — operates at the service call layer. They protect you from flaky HTTP dependencies. They do almost nothing to protect you from your own database.
That gap is exactly what the open-source sql-circuit-breaker-spring-boot-starter library addresses. It brings circuit breaking down to the SQL execution layer itself, intercepting queries via a MyBatis plugin and making autonomous decisions about whether a given query type should be allowed to run — or temporarily blocked before it can cause further damage.
Why the Service Layer Is Not Enough
Traditional circuit breakers wrap external calls. The assumption is that if a downstream service is unhealthy, you should stop hammering it and give it time to recover. That logic is sound. But your relational database is not a microservice you call over HTTP — it is a shared, stateful resource that multiple application threads compete for simultaneously.
When a slow query holds a database connection, that connection is unavailable to every other thread that needs it. Most connection pools — HikariCP included — have a finite size. The moment all connections are occupied by blocked or slow-running queries, new requests queue up. Those queued requests eventually time out. The timeouts propagate upward through your service calls, triggering the very circuit breakers you have already configured — but by then, the damage is done. You are reacting, not preventing.
The insight behind a SQL-level circuit breaker is simple but powerful: if a specific category of SQL has been consistently slow or has already triggered failures, stop executing it proactively, return a fast failure immediately, and allow the database to drain its existing load. You protect the shared resource, not just the caller.
How the MyBatis Interceptor Approach Works
The sql-circuit-breaker-spring-boot-starter integrates with the MyBatis Interceptor interface, which gives it a hook into every SQL execution before it reaches the database driver. At that intercept point, the library evaluates the SQL against a keying strategy — typically based on SQL type, Mapper ID, or a combination of both — and consults an in-memory circuit state to decide whether to allow, reject, or throttle the call.
The circuit follows the familiar three-state model that engineers familiar with Resilience4j or Hystrix will recognize:
- Closed: SQL executes normally. Metrics are collected on execution time and failure rate.
- Open: The query type has breached a configured threshold. Subsequent calls of that type are rejected immediately without touching the database, returning a fast failure to the caller.
- Half-Open: After a cooldown window, a limited number of probe queries are allowed through. If they succeed, the circuit closes. If they continue to fail or remain slow, the circuit reopens.
What makes this approach distinct is where the keying happens. Rather than keying on a full SQL string — which would make every parameterized query unique and defeat the purpose — the library keys on normalized SQL patterns or Mapper method identifiers. This means that all calls to a particular repository method or SQL template share a single circuit state, which reflects the true health of that operation class against the database.
Getting Started with sql-circuit-breaker-spring-boot-starter
The library is available on Maven Central, which makes adding it to an existing Spring Boot project straightforward. You add the dependency, configure a handful of thresholds in your application.yml, and the interceptor wires itself into the MyBatis plugin chain automatically via Spring Boot's autoconfiguration mechanism. There is no need to annotate individual methods or wrap service calls manually.
Configuration options cover the key parameters you would expect: a slow-query threshold in milliseconds, a failure rate percentage that triggers the open state, the size of the sliding window used to calculate that rate, the duration of the open state before transitioning to half-open, and the number of probe calls permitted in the half-open state. These can be tuned per SQL key, allowing critical read paths to have more conservative thresholds than background batch operations.
The library is available on GitHub at showingdata/sql-circuit-breaker and mirrored on Gitee for teams operating in environments where GitHub access is restricted. It is released under the Apache 2.0 license.
Operational Benefits Beyond Simple Protection
Beyond preventing cascading failures, a SQL circuit breaker provides a secondary benefit that is easy to overlook: visibility. Because the interceptor observes every SQL execution, it can surface metrics about slow query frequency, circuit state transitions, and rejection counts that are invaluable during incident response. Instead of asking "why is the service slow?" you can ask "which SQL circuit is open and since when?" — a much more actionable question.
It also creates a forcing function for better database hygiene. When a circuit trips repeatedly for a given query, it signals a real problem that needs investigation — a missing index, a query that has outgrown its execution plan, or a table that needs partitioning. The circuit breaker buys you time; the metrics tell you where to invest that time.
A More Complete Resilience Strategy
Service-layer circuit breakers remain essential. They protect you from flaky third-party APIs, slow internal microservices, and unreliable messaging infrastructure. But treating the database as an exception — as a dependency that is always assumed to be healthy — is a blind spot that has caused countless production incidents.
Adding a SQL-level circuit breaker alongside your existing Resilience4j or Sentinel configuration gives you a genuinely defense-in-depth resilience posture. Your service layer catches problems coming from outside. Your SQL layer catches problems brewing inside. Together, they dramatically reduce the blast radius of the slow query that is, statistically speaking, already waiting in your production schema right now.
If you are running Spring Boot with MyBatis and you do not yet have protection at the SQL execution layer, the sql-circuit-breaker-spring-boot-starter is worth evaluating. The next cascade failure is rarely the one you planned for — but with the right tooling in place, it does not have to become a full outage.
