Add JDBC client throttling — analysis document, implementation and documentation#523
Add JDBC client throttling — analysis document, implementation and documentation#523Copilot wants to merge 20 commits into
Conversation
Agent-Logs-Url: https://github.com/Open-J-Proxy/ojp/sessions/a19cc33b-051c-409c-b8cf-9f7e1c9a4e98 Co-authored-by: rrobetti <7221783+rrobetti@users.noreply.github.com>
Agent-Logs-Url: https://github.com/Open-J-Proxy/ojp/sessions/d29299c3-d775-4b03-9366-e1a89b1ecb28 Co-authored-by: rrobetti <7221783+rrobetti@users.noreply.github.com>
rrobetti
left a comment
There was a problem hiding this comment.
Evaluate the following idea:
Add new information to the SessionInfo returned to clients, the extra info will be 2 new fields:
1 - the number of clients connected to that datasource with same user and password (session hash)
2 - the max admission concurrent requests allowed in the server.
The OJP JDBC client shall use this info to create a semaphore and restrict how many concurrent requests it sends to OJP server, each cliend shall set its semaphore by calculating by the formula (max admission of ojp server divided by number of clients of that datasource) multiplied by the number of OJP servers in the cluster.
…IVE_THROTTLING_ANALYSIS.md Agent-Logs-Url: https://github.com/Open-J-Proxy/ojp/sessions/474a33bb-3629-4cce-9651-8df0768af306 Co-authored-by: rrobetti <7221783+rrobetti@users.noreply.github.com>
Evaluated in commit Summary of findings: The formula works, but two things need to be pinned before implementation:
Main risks identified:
Positive: Overall this design is materially better than purely reactive for steady-state behavior. The protocol cost (two |
|
@copilot Use Ceiling division + a 10% safety headroom and document the rational. Do include A step-limited increase (AIMD-style) and document the rational. In relation to "Semaphore resizing is non-trivial" is there any cheap(efficient) alternative to control concurrency that can be easily resized and that does not add latency in the flow? "Cross-node clientCount accuracy" I do not understand what this means, explain in simple language with simple example. "In-transaction bypass" , I am not sure if this is a problem, explain in simple language with a simple example. Keep only the analysis for now, do not change the code yet. |
…ve, AIMD, plain-language examples Agent-Logs-Url: https://github.com/Open-J-Proxy/ojp/sessions/221f3b7b-572e-4c3a-a6b3-19471bba8684 Co-authored-by: rrobetti <7221783+rrobetti@users.noreply.github.com>
Updated in commit 1. 2. 3. Ceiling division + 10% headroom. S1 now explains the rationale: floor division leaves capacity permanently wasted; ceiling slightly over-allocates, which the 10% headroom compensates for by absorbing one stale 4. AIMD step-limited increase. S4 now has the full algorithm: decrease applies immediately (fast overload response), increase is capped at 5. Semaphore alternative. The new Con #5 shows that a simple 6. Cross-node 7. In-transaction bypass — plain-language example added. C5 now walks through the step-by-step scenario where Thread A holds an open transaction, the client semaphore fills, Thread A blocks waiting for a permit, and the server's transaction timeout fires before Thread A ever sends its next statement. Fix: skip the |
rrobetti
left a comment
There was a problem hiding this comment.
@copilot we should have two modes configurable, proactive and reactive, in the proactive mode we do the client trottling with the atomic integer and fail fast as suggested, if reactive mode I want to explore and hear your opinions on keeping a record of how many simultaneous connections was the server serving before one got an admission timout, the goal with that is to send a smarter number to the client instead of the fixed total the server was configured to take, in real scenarios ig might break before that, for example, if ojp server was being able to serve 50 simultaneous requests without errors but when it tried to serve 51 it got a admission timout error, it means its limit is 50 and that is the number we should send to the client. Tell me your concerns, opinions, sugestions and questions about this new idea.
Agent-Logs-Url: https://github.com/Open-J-Proxy/ojp/sessions/6a205c32-5798-4524-9ebf-267a654a8ced Co-authored-by: rrobetti <7221783+rrobetti@users.noreply.github.com>
Evaluated and documented in commit Two-Mode Framework — a short overview table comparing proactive, reactive, and combined modes, showing what each requires and recommends. Reactive Mode with How it would work in
Main concerns raised:
Opinion (confidence: 85%): The idea is sound — it's the TCP CWND model applied to OJP. The "false floor" and recovery rate are the only significant risks and both have clear mitigations. The recommended v1 is combined mode: send both |
Agent-Logs-Url: https://github.com/Open-J-Proxy/ojp/sessions/83cf8215-d593-44ae-bfde-a072345d157f Co-authored-by: rrobetti <7221783+rrobetti@users.noreply.github.com>
Done in commit
|
… by CLIENT_REACTIVE_THROTTLING_ANALYSIS.md Agent-Logs-Url: https://github.com/Open-J-Proxy/ojp/sessions/8ea9c312-8c7e-4888-a908-9741462f67bf Co-authored-by: rrobetti <7221783+rrobetti@users.noreply.github.com>
| * Update limits from a fresh SessionInfo. | ||
| * AIMD: decrease immediately; increase capped at currentLimit + 1. | ||
| */ | ||
| public void updateFromSessionInfo(SessionInfo sessionInfo) { |
There was a problem hiding this comment.
@copilot does this run on every request response? I am worried about the performance impact
There was a problem hiding this comment.
No, updateFromSessionInfo does not run on every row. It runs when a new block of rows arrives from the server (at ResultSet block boundaries), after each executeUpdate, and after each PreparedStatement execution — not per individual row.
The cost per call is low: a few protobuf field reads, a String.split(";") over the short clusterHealth string, a handful of arithmetic operations, and two volatile writes. Amortized over potentially hundreds or thousands of rows per block, the overhead is negligible.
There was a problem hiding this comment.
updateFromSessionInfo is called only when Connection.setSession() is called — which happens on executeUpdate responses (the server returns a new SessionInfo in the OpResult) and on the initial connect. It does not run on every executeQuery response because queries return an iterator over results, not a SessionInfo.
When it does run, the cost is minimal: a few arithmetic operations (one Math.ceil, one multiply, one multiply by 0.9) plus two volatile int writes. The total overhead is in the low-nanosecond range — negligible compared to the network roundtrip to OJP server that already dominates query latency.
On the hot path (tryAcquire / release), called on every request, the cost is even cheaper: one AtomicInteger.get() + one compareAndSet — both single CPU instructions.
| ClientThrottleMode mode = this.connection.getThrottleMode(); | ||
| // getAutoCommit() may throw SQLException; evaluate before acquiring a slot | ||
| // so that release() is never called without a matching acquire. | ||
| boolean inTransaction = !this.connection.getAutoCommit(); |
There was a problem hiding this comment.
@copilot is this call being sent to OJP Server or we keep a boolean in the OJP JDBC side?
There was a problem hiding this comment.
Purely local — no network call. Connection.getAutoCommit() simply returns this.autoCommit, a local boolean field that is kept in sync on the driver side when setAutoCommit(), commit(), or rollback() is called.
There was a problem hiding this comment.
getAutoCommit() reads a local boolean autoCommit field on the Connection object — no call to OJP server. The field is updated only when setAutoCommit() is called (which does make a gRPC call to start or commit a transaction), but reading it is a plain local field access. Zero network overhead.
|
@copilot update relevant documentation including but not restricted to the eBook about this new throttling solution. Keep the explanation in simple language and provide examples. You can use mermaid diagram if required. |
…docs Agent-Logs-Url: https://github.com/Open-J-Proxy/ojp/sessions/cbeefca8-e6ea-43d7-83cc-4ccb48973b41 Co-authored-by: rrobetti <7221783+rrobetti@users.noreply.github.com>
Done in commit New eBook chapter —
Updated existing docs:
|
…ection Agent-Logs-Url: https://github.com/Open-J-Proxy/ojp/sessions/50dd60f7-2310-464c-b669-56687c5c5e07 Co-authored-by: rrobetti <7221783+rrobetti@users.noreply.github.com>
Agent-Logs-Url: https://github.com/Open-J-Proxy/ojp/sessions/50dd60f7-2310-464c-b669-56687c5c5e07 Co-authored-by: rrobetti <7221783+rrobetti@users.noreply.github.com>
Done in commits Main was already merged (the branch was up to date). Both client specification documents have been updated with the throttling feature: CLIENT_SPEC.md (human-readable guide for non-Java implementers):
CLIENT_SPEC_AI.md (normative spec for automated client generation):
|
|
|
@copilot merge main branch into this branch |
The branch is already up to date with
The branch is already up to date with |



Uh oh!
There was an error while loading. Please reload this page.