A Client Loop Caused a Threefold Database Connection Spike

Symptom

One service’s database connections suddenly grew to roughly three times their normal level in the morning. The application logs contained no errors, and users did not see an obvious service outage.

Context

The investigation found thousands of identical requests for the history of one chat. Every request returned 200, so ordinary error monitoring did not fire. After roughly fifteen minutes, the connection pool was released and the load returned to normal.

Shortly before the spike, a user had reported that neural-network responses did not appear in one browser, while switching browsers made the problem disappear. The repeated requests during the spike came from the same scenario.

Root cause

The observed source of the load was a client loop repeatedly requesting the same chat history. The most likely trigger was a failed or incomplete SSE finalization: the client did not receive the expected end of the response and continued trying to synchronize.

The exact set of factors that started the loop in that browser was not proven. It could have involved network handling, device sleep, or connection recovery. The server did not have a limit that could stop this request pattern.

Broken assumption

We assumed that if the flow was stable in one browser, it would be stable in the others. The successful 200 status of each individual request also created a false sense that the system was healthy.

Detection

An early signal should not be limited to the error rate. It should also include the intensity of identical requests: one user or client repeatedly requests the same resource within a short interval.

This requires request metrics by resource and client, plus an alert for a spike in repeated requests without a corresponding user action.

Fix

The fix covered both boundaries:

The rate limit protects the database even when a client is broken, while the guard removes the source of unnecessary requests. Either layer alone is insufficient: the client can break again, and the server must not allow that failure to become unbounded load.

Test

The minimum regression check should simulate an incomplete SSE response and verify that the client does not start an endless history-loading loop. Separately, the server-side rate limit should be tested: after the configured number of identical requests, the server must limit further calls without acquiring a new database resource for every retry.

The original note does not record an automated test for this scenario.

General lesson

The absence of errors does not mean the absence of dangerous load. Reliability must also be measured through request behavior: repetition, frequency, and duration can be more harmful than a single failed response. Client-side streaming flows need protection at both the client and server boundaries.