Skip to content

Generalize SqlBackup's retry/backoff policy into a reusable SqlRetryPolicy #554

Description

@Yaraslaut

Summary

SqlBackup has a real, working retry/backoff policy (RetrySettings — max retries, exponential backoff, max delay; see src/Lightweight/SqlBackup/SqlBackup.hpp), but it is private to the backup engine. There is no equivalent policy object available to ordinary connection acquisition or query execution — a transient failure (e.g. a dropped connection, a deadlock-losing transaction, a momentary network blip) anywhere outside SqlBackup has no built-in retry path.

Motivation

userver's DB drivers apply retry/backoff (and congestion control) as a first-class, framework-level concern, not something bolted onto one feature. Lightweight already proved out the mechanism inside SqlBackup — this is about generalizing it, in keeping with this project's own "data-driven design" principle (a descriptor/config-driven policy object, not scattered retry loops per call site).

Related but distinct: #13 ("Add support for ConnectRetryInterval and ...Count") is scoped specifically to the ODBC connect-time retry attributes (SQLSetConnectAttr) with a manual fallback if unsupported. This issue is broader: a retry/backoff policy usable at the query/transaction level (not just initial connection establishment), for errors classified as transient (deadlock, connection reset, etc.) vs. non-retryable (constraint violation, syntax error).

Proposal

  • Extract SqlBackup::RetrySettings (or a generalized sibling) into a shared, reusable policy type — e.g. SqlRetryPolicy — usable by SqlConnection, Pool, and/or SqlTransaction.
  • Define which SqlErrorCode/native error categories are considered retryable per DBMS — this classification necessarily goes through SqlQueryFormatter-style per-DBMS dispatch (e.g. Postgres serialization-failure SQLSTATE 40001 vs. MSSQL deadlock 1205), not ad-hoc if (server == ...) checks in business logic.
  • Should compose with Add support for ConnectRetryInterval and ...Count #13's connect-level retry (different layer: connection establishment vs. in-flight query/transaction) and with a deadline abstraction if one exists (see companion issue) so retries stop once a deadline is exceeded rather than retrying indefinitely.
  • Consider whether Pool should apply the policy transparently on Acquire() for connection-level failures.

Acceptance criteria

  • New SqlRetryPolicy (or similar) is a standalone, testable type — injectable per AGENT.md's dependency-injection guidance, not hardcoded — with unit tests simulating transient vs. non-retryable failures per DBMS.
  • SqlBackup optionally migrated to use the shared policy (avoiding two parallel implementations), if that refactor doesn't destabilize the existing backup retry behavior/tests.
  • Full suite green on sqlite3, mssql2022, postgres.
  • Documented in docs/.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions