Skip to content

feat: report timeout terminations with SQLSTATE - #6016

Draft
Snehil-Shah wants to merge 2 commits into
sysown:v3.0from
Snehil-Shah:kill-reason-sqlstate
Draft

feat: report timeout terminations with SQLSTATE#6016
Snehil-Shah wants to merge 2 commits into
sysown:v3.0from
Snehil-Shah:kill-reason-sqlstate

Conversation

@Snehil-Shah

@Snehil-Shah Snehil-Shah commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Currenlty, timeouts directly kill connections without sending any fitting ErrorResponse. This brings it into parity with Postgres's true behavior.

I have added a new kill_reason to session state, making the design extendable.

Summary by CodeRabbit

  • New Features

    • PostgreSQL clients now receive clear error messages and SQLSTATE codes when sessions end due to session-idle or transaction-idle timeouts.
    • Timeout termination reasons are tracked for more accurate reporting.
  • Bug Fixes

    • Improved PostgreSQL compatibility and consistency for timeout-related connection termination.
  • Tests

    • Added coverage for session and transaction-idle timeout behavior, including expected SQLSTATE responses.

Signed-off-by: Snehil Shah <snehilshah.989@gmail.com>
@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 4b244582-b4f6-4b81-8dcd-5e350c3575e7

📥 Commits

Reviewing files that changed from the base of the PR and between 397b03f and a086c58.

📒 Files selected for processing (1)
  • test/tap/tests/pgsql-wait_timeout-t.cpp
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/tap/tests/pgsql-wait_timeout-t.cpp

📝 Walkthrough

Walkthrough

The change records PostgreSQL session kill reasons, maps timeout reasons to SQLSTATE errors, sends termination responses to clients, and adds TAP coverage for session and transaction-idle timeouts.

Changes

PostgreSQL timeout error flow

Layer / File(s) Summary
Kill reason contract and initialization
include/Base_Session.h, lib/MySQL_Session.cpp, lib/PgSQL_Session.cpp
Adds SESSION_KILL_REASON and Base_Session::kill_reason. Session constructors initialize the field to SESSION_KILL_REASON_NONE.
Timeout recording and PostgreSQL error response
lib/PgSQL_Thread.cpp, lib/PgSQL_Session.cpp
Timeout paths record idle-session or idle-in-transaction reasons. PgSQL_Session::handler() sends the mapped PostgreSQL error response before termination.
Timeout integration tests
test/tap/tests/pgsql-wait_timeout-t.cpp, test/tap/groups/groups.json
Adds TAP coverage for SQLSTATE 57P05 and 25P03, with timeout setup, cleanup, and test-group registration.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant PgSQL_Thread
  participant PgSQL_Session
  participant PostgreSQLClient
  PgSQL_Thread->>PgSQL_Session: Record timeout kill reason
  PgSQL_Session->>PgSQL_Session: Map reason to SQLSTATE and message
  PgSQL_Session->>PostgreSQLClient: Send ErrorResponse and flush output
  PgSQL_Session->>PostgreSQLClient: Terminate processing
Loading

Poem

A rabbit tracks each timeout’s cause,

Sends SQLSTATE without pause.
The client receives the final sign,
Six TAP checks keep behavior in line.
Defaults return when tests are through.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: reporting timeout terminations with SQLSTATE values.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/tap/tests/pgsql-wait_timeout-t.cpp`:
- Around line 140-143: Update the test cleanup around the existing timeout setup
and early returns to save the original values of every modified runtime
variable, including pgsql-poll_timeout, and restore them on every exit path.
Ensure cleanup and LOAD PGSQL VARIABLES TO RUNTIME failures cause the test to
fail, including when execution reaches the returns near the existing test flow
and the final cleanup block.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ba3cd9d6-7dd5-47a5-a948-97ff1e1f2461

📥 Commits

Reviewing files that changed from the base of the PR and between a7af833 and 397b03f.

📒 Files selected for processing (6)
  • include/Base_Session.h
  • lib/MySQL_Session.cpp
  • lib/PgSQL_Session.cpp
  • lib/PgSQL_Thread.cpp
  • test/tap/groups/groups.json
  • test/tap/tests/pgsql-wait_timeout-t.cpp
📜 Review details
🧰 Additional context used
📓 Path-based instructions (3)
include/**/*.h

📄 CodeRabbit inference engine (CLAUDE.md)

Header include guards use the #ifndef __CLASS_*_H convention.

Files:

  • include/Base_Session.h
**/*.{cpp,h,hpp}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{cpp,h,hpp}: Class names must use PascalCase with protocol prefixes such as MySQL_, PgSQL_, and ProxySQL_.
Member variables must use snake_case.
Constants and macros must use UPPER_SNAKE_CASE.
Use C++17, and gate conditional code with #ifdef PROXYSQL31, #ifdef PROXYSQL40, #ifdef PROXYSQLFFTO, #ifdef PROXYSQLTSDB, and #ifdef PROXYSQLCLICKHOUSE; PROXYSQLGENAI must not guard core code outside plugins/genai/.
Consider performance implications when changing hot paths or other performance-critical code.
Use RAII for resource management and jemalloc for allocation.
Use pthread mutexes for synchronization and std::atomic<> for counters.

Files:

  • include/Base_Session.h
  • lib/PgSQL_Thread.cpp
  • lib/MySQL_Session.cpp
  • lib/PgSQL_Session.cpp
  • test/tap/tests/pgsql-wait_timeout-t.cpp
test/tap/tests/**/*.cpp

📄 CodeRabbit inference engine (CLAUDE.md)

test/tap/tests/**/*.cpp: Test files in test/tap/tests/ must follow the naming pattern test_*.cpp or *-t.cpp.
To add a new TAP test, add the <testname>-t.cpp file and register it in test/tap/tests/Makefile/groups.json; no special Makefile target is needed because make <testname>-t is generated by pattern rule.

Files:

  • test/tap/tests/pgsql-wait_timeout-t.cpp
🧠 Learnings (1)
📚 Learning: 2026-01-20T09:34:19.124Z
Learnt from: yuji-hatakeyama
Repo: sysown/proxysql PR: 5307
File: test/tap/tests/reg_test_5306-show_warnings_with_comment-t.cpp:39-48
Timestamp: 2026-01-20T09:34:19.124Z
Learning: In ProxySQL's TAP test suite, resource leaks (e.g., not calling mysql_close() on early return paths) are commonly tolerated because test processes are short-lived and OS frees resources on exit. This pattern applies to all C++ test files under test/tap/tests. When reviewing, recognize this as a project-wide test convention and focus on test correctness and isolation rather than insisting on fixing such leaks in these test files.

Applied to files:

  • test/tap/tests/pgsql-wait_timeout-t.cpp
🔇 Additional comments (5)
include/Base_Session.h (1)

29-34: LGTM!

Also applies to: 93-93

lib/MySQL_Session.cpp (1)

652-652: LGTM!

lib/PgSQL_Session.cpp (1)

305-305: LGTM!

Also applies to: 3129-3151, 3834-3843

lib/PgSQL_Thread.cpp (1)

3414-3416: LGTM!

Also applies to: 3751-3752, 3761-3762, 3880-3880

test/tap/groups/groups.json (1)

164-164: LGTM!

Comment on lines +140 to +143
// restore defaults so the short timeouts don't leak into later tests in the group
admin_q(admin, "SET pgsql-wait_timeout=28800000");
admin_q(admin, "SET pgsql-max_transaction_idle_time=14400000");
admin_q(admin, "LOAD PGSQL VARIABLES TO RUNTIME");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Restore every modified runtime variable on every exit path.

Lines 67 and 95 set pgsql-poll_timeout=500, but this cleanup does not restore it. Lines 131 and 136 also return before this block runs. This can change timeout behavior for later TAP tests.

Save the prior values, run cleanup before every return, and fail the test if cleanup or LOAD PGSQL VARIABLES TO RUNTIME fails.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/tap/tests/pgsql-wait_timeout-t.cpp` around lines 140 - 143, Update the
test cleanup around the existing timeout setup and early returns to save the
original values of every modified runtime variable, including
pgsql-poll_timeout, and restore them on every exit path. Ensure cleanup and LOAD
PGSQL VARIABLES TO RUNTIME failures cause the test to fail, including when
execution reaches the returns near the existing test flow and the final cleanup
block.

Signed-off-by: Snehil Shah <snehilshah.989@gmail.com>
@Snehil-Shah

Snehil-Shah commented Aug 9, 2026

Copy link
Copy Markdown
Contributor Author

@renecannao I'm not sure how do we map this to MySQL/MariaDB. These error codes map cleanly to Postgres. Open to suggestions. Should we send our own ProxySQL error here? But then, that would cause assymetry with PG.

@sonarqubecloud

sonarqubecloud Bot commented Aug 9, 2026

Copy link
Copy Markdown

int rc = run_q(proxy, "SELECT 1");
ok(rc == 0, (rc == 0 ? "Connection alive" : "Connection killed"));

sleep(9);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Quality: Timeout test relies on fixed sleeps and wall-clock timing

pgsql-wait_timeout-t.cpp drives both cases with real timeouts plus a hard-coded sleep(9) (~18s total), and depends on the maintenance loop firing within that window. On loaded CI this wall-clock dependence makes the test slow and potentially flaky. Consider polling for termination in a loop with a bounded deadline instead of a single fixed sleep, so the test succeeds as soon as the connection is killed and fails fast otherwise.

Was this helpful? React with 👍 / 👎

@gitar-bot

gitar-bot Bot commented Aug 10, 2026

Copy link
Copy Markdown
Code Review 👍 Approved with suggestions 0 resolved / 1 findings

Adds SQLSTATE reporting and error messages for PostgreSQL idle timeouts to match native behavior. Consider replacing fixed sleeps in the timeout test with synchronization to avoid flakiness.

💡 Quality: Timeout test relies on fixed sleeps and wall-clock timing

📄 test/tap/tests/pgsql-wait_timeout-t.cpp:86 📄 test/tap/tests/pgsql-wait_timeout-t.cpp:121

pgsql-wait_timeout-t.cpp drives both cases with real timeouts plus a hard-coded sleep(9) (~18s total), and depends on the maintenance loop firing within that window. On loaded CI this wall-clock dependence makes the test slow and potentially flaky. Consider polling for termination in a loop with a bounded deadline instead of a single fixed sleep, so the test succeeds as soon as the connection is killed and fails fast otherwise.

🤖 Prompt for agents
Code Review: Adds SQLSTATE reporting and error messages for PostgreSQL idle timeouts to match native behavior. Consider replacing fixed sleeps in the timeout test with synchronization to avoid flakiness.

1. 💡 Quality: Timeout test relies on fixed sleeps and wall-clock timing
   Files: test/tap/tests/pgsql-wait_timeout-t.cpp:86, test/tap/tests/pgsql-wait_timeout-t.cpp:121

   pgsql-wait_timeout-t.cpp drives both cases with real timeouts plus a hard-coded sleep(9) (~18s total), and depends on the maintenance loop firing within that window. On loaded CI this wall-clock dependence makes the test slow and potentially flaky. Consider polling for termination in a loop with a bounded deadline instead of a single fixed sleep, so the test succeeds as soon as the connection is killed and fails fast otherwise.

Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Showing less information.

Comment with these commands to change the behavior for this request:

Auto-apply Compact
gitar auto-apply:on         
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

Copy link
Copy Markdown
Contributor

Thank you @Snehil-Shah for working on this. Reporting the PostgreSQL timeout SQLSTATE and message to clients is a valuable compatibility improvement.

I found two items that should be addressed before merging:

  1. In the --idle-threads path, idle_thread_to_kill_idle_sessions() sets killed and kill_reason, but does not set to_process = 1. After the session is handed back to a worker, PgSQL_Session::handler() can return immediately at its to_process == 0 guard, and the worker then deletes the killed session without reaching the new ErrorResponse generation. In that configuration, an idle timeout can therefore still produce a bare disconnect instead of FATAL 57P05. Please either mark the session processable when the idle thread kills it or allow a killed session with a reportable reason to bypass that early return.

  2. The new TAP test changes pgsql-wait_timeout, pgsql-max_transaction_idle_time, and pgsql-poll_timeout, but it does not restore pgsql-poll_timeout, restores hard-coded defaults rather than the prior values, skips cleanup on early returns, and ignores cleanup failures. Please save and restore all three original values through an unconditional cleanup path and make cleanup/LOAD failures fail the test.

Thanks again for the contribution. With those paths covered, this will be a solid improvement.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants