Skip to content

feat(connection): stop the SQL session on request - #91

Merged
jiayuasu merged 2 commits into
mainfrom
feat/stop-session
Nov 3, 2026
Merged

jiayuasu merged 2 commits into
mainfrom
feat/stop-session

Conversation

@jiayuasu

@jiayuasu jiayuasu commented Nov 2, 2026

Copy link
Copy Markdown
Member

Closing a connection leaves its SQL session running until shutdown_after_inactive_seconds pass, and the server's idle check adds up to about 3 more minutes. A batch job pays for that time after it has finished. dbt-wherobots users running scheduled builds asked for a way to end the session when the job ends (Linear WBC-1621). The API already has DELETE /sql/session/{session_id}, but the driver had no way to call it.

Changes

  • Connection.stop_session() closes the connection, then sends DELETE /sql/session/{id} with the headers connect() used.
    • It raises InterfaceError if the API refuses or can't be reached. The connection is closed anyway.
    • Stopping a session that already stopped succeeds, because the API answers 204 again.
    • A connection from connect_direct() has no API URL. On those, stop_session() raises NotSupportedError and leaves the connection open.
  • Connection.session_key is the key the API created the session under. connect() reads it from apiPayload.sessionKey in the session record it already polls until READY, so it costs no extra request. It differs from the requested session_key after a quota fallback, or when the organization has session keys disabled. A caller checks it before stopping a session that other clients might share.
  • connect() builds the stop function from the session URL and a snapshot of its headers, the same way reconnecting snapshots them. connect_direct() passes the key and the stop function through to Connection.
  • README: new section, "Stopping the SQL session".

Verification

  • Unit tests: 12 new, 327 pass in total. They cover:
    • the stored key reaching the connection (keyed, keyless, missing field, no stored request);
    • the DELETE URL, headers and timeout;
    • a refused or unreachable API raising InterfaceError that names the session;
    • stop_session() closing first and then calling the API;
    • a refused stop leaving the connection closed;
    • a connect_direct() connection raising NotSupportedError and staying open.
  • pre-commit run --all-files passes.
  • Live, 2026-11-02, micro in aws-us-west-2, against production:
    • connect(session_key=...) returned session_key equal to the requested key, with no fallback.
    • stop_session() returned in 0.39 s. The session was DESTROY_REQUESTED at +0.5 s and DESTROYED at +3.7 s.
    • Calling stop_session() a second time succeeded.
    • In an earlier probe of the same endpoint, an unkeyed connection with identical settings started its own session instead of joining a keyed one. A DELETE of an unknown session ID returned 404.

dbt-wherobots will use this behind an opt-in setting once it's released.

Closing a connection leaves its SQL session running until its inactivity
timeout passes, which a batch job pays for after it has finished.
Connection.stop_session() closes the connection, then asks the API to
stop the session (DELETE /sql/session/{id}), which stops within seconds.

Connection.session_key reports the key the API created the session under,
read from the session's stored request while connect() waits for READY,
so a caller can check that a session is its own before stopping it.

A connection from connect_direct() has no API to ask: stop_session()
raises NotSupportedError and leaves it open.
@jiayuasu
jiayuasu requested a review from a team as a code owner November 2, 2026 07:48
Comment thread README.md
... # run the job's queries
finally:
if conn.session_key == "nightly-build-1234":
conn.stop_session()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium: Session-key check does not establish exclusive ownership

If two jobs connect with the same session key, the API can reuse one SQL session for both (README.md:351-353). Both connections then pass the example's key check, so the first job to finish calls stop_session() and stops the session while the other job is still using it. The guidance should require a key unique to the job or another way to establish that no other client shares the session.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 3a791d8. The README now says that every connection asking with the same session_key can share a session, so each run of a job needs a key of its own, for example one that includes a run ID. The example builds the key from a run ID before checking conn.session_key and calling stop_session(). dbt-wherobots follows the same rule: its key is dbt-<invocation_id>, which differs for every run.

@salty-hambot salty-hambot 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.

Reviewed by Salty Hambot 🤖🧂 at 3a791d8

No issues found.
💰 $0.0993 · 82.8k in / 1.9k out tokens · ⏱️ 19.3s · us.openai.gpt-6-sol high, prompt coverage-v1
💬 To request a re-review, comment @salty-hambot review

Two jobs that send the same session_key can share one session, so a matching key proves the session is the run's own only when the key is unique to the run. The example now builds the key from a run ID.
@jiayuasu
jiayuasu merged commit 14a2a79 into main Nov 3, 2026
7 checks passed
@jiayuasu
jiayuasu deleted the feat/stop-session branch November 3, 2026 06:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants