Skip to content

FEAT: add optional RetryPolicy for transient failures on connect() (GH-682) - #751

Open
om singhal (Om-singhaI) wants to merge 1 commit into
microsoft:mainfrom
Om-singhaI:om/feat/retry-policy
Open

FEAT: add optional RetryPolicy for transient failures on connect() (GH-682)#751
om singhal (Om-singhaI) wants to merge 1 commit into
microsoft:mainfrom
Om-singhaI:om/feat/retry-policy

Conversation

@Om-singhaI

Copy link
Copy Markdown
Contributor

Work Item / Issue Reference

GitHub Issue: #682


Summary

First of two PRs for #682, connect() scope only; cursor and execute() retry follow separately. Adds mssql_python.RetryPolicy and retry_policy= on connect() / Connection(), with the constructor shape from the issue. Without a policy nothing changes.

How it works

  • The loop wraps only the native connect in Connection.__init__, below connection string parsing and any token acquired on the Python side, so every attempt reuses the same inputs.
  • The SQLSTATE is read from the SQLSTATE:XXXXX:message the C++ layer already throws. exceptions.py is untouched, so this does not preempt FEAT: Expose SQLSTATE (and native error number) as attributes on exception objects #581.
  • Retriable code with attempts left: a warning line (attempt, SQLSTATE, delay), sleep, retry. Otherwise _raise_connection_error runs exactly as today, same exception type, nothing rewrapped.
  • Default set is the seven transient SQLSTATEs from the Learn retry page: HYT00 HYT01 08001 08S01 08007 40001 40003. 08004 stays out, the page lists it under never retry. retriable_sqlstates= replaces the set.
  • max_attempts is total tries including the first, as in the issue. The Learn sample counts retries instead.

Out of scope

Azure SQL throttling. Those are engine error numbers, and the native number is dropped in SQLCheckError_Wrap. Plumbing it out is #581's territory, so it goes with the second PR.

Validation

  • tests/test_027_retry_policy.py: 60 passed, no server. The native constructor is faked as in test_006_exceptions.py, and retry._sleep / _random are patched so delay sequences are asserted exactly. No policy is one attempt and the same OperationalError; two transient failures then success is three calls with sleeps [1.0, 2.0]; exhaustion keeps the mapped type; 28000, 08004, 42000 and a message with no SQLSTATE fail once; a token_provider token is acquired once across three attempts; log lines never contain the connection string.
  • test_006_exceptions.py server free tests: 20 passed. black and flake8 with the CI flags clean.
  • Not run against a live server. The failure path is the unchanged _raise_connection_error.

…icrosoftGH-682)

I added mssql_python.retry.RetryPolicy and retry_policy= on connect() and
Connection(); cursor and execute() scope follow in a second PR. The loop wraps
only the native connect, below connection string parsing and any token acquired
on the Python side, so those run once; a deferred token factory is still
invoked by native on each attempt. It retries the seven transient SQLSTATEs
from the driver's retry logic page on Learn; without a policy nothing changes.
Copilot AI lite review requested due to automatic review settings September 3, 2026 22:56
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

It changes the core connection-establishment path (native connect invocation and retry timing/logging), which merits final human verification against real-world/native-layer behaviors beyond the included server-free tests.

Pull request overview

Adds an opt-in RetryPolicy API to the pure-Python DB-API surface so connect() / Connection(...) can automatically retry native connect failures classified as transient by SQLSTATE, without changing default behavior for existing callers.

Changes:

  • Introduces mssql_python.retry.RetryPolicy (configurable attempts, backoff, jitter, SQLSTATE allowlist) and exports it from the package.
  • Wraps the native ddbc_bindings.Connection(...) call in Connection.__init__ with retry + warning logs, leaving the existing exception mapping path intact on final failure.
  • Adds server-free tests covering retry behavior, delay sequences, non-retriable failures, token-acquisition reuse, and log redaction; updates stubs and changelog.
File summaries
File Description
tests/test_027_retry_policy.py Adds server-free unit tests validating connect-scope retry behavior, delay computation, and logging expectations.
mssql_python/retry.py Implements RetryPolicy, default transient SQLSTATE set, and deterministic seams for sleep/random in tests.
mssql_python/mssql_python.pyi Extends public type stubs with RetryPolicy and the new retry_policy parameters.
mssql_python/db_connection.py Plumbs retry_policy through the public connect() wrapper and documents the new parameter.
mssql_python/connection.py Adds SQLSTATE extraction helper and wraps native connect with policy-driven retry + warning logs.
mssql_python/__init__.py Exports RetryPolicy and includes it in __all__.
CHANGELOG.md Documents the new opt-in retry policy feature and its default semantics.
Review details
  • Files reviewed: 7/7 changed files
  • Comments generated: 0
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

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