Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Přechodné poruchy jsou dočasné chyby, které mohou nastat při připojení k SQL Server a Azure SQL přes ovladač mssql-python. Tyto chyby se často vyřeší samy:
- Krátkodobé výpadky síťového připojení.
- Omezení zdrojů serveru.
- Omezování Azure SQL.
- Případy failoveru.
Implementace logiky opakovaných pokusů zlepšuje spolehlivost aplikací, zejména u cloudově hostovaných databází.
Nepoužívejte opakované pokusy k zakrytí chyb v konfiguraci nebo kódování. Chybějící databáze, špatné přihlašovací údaje nebo vyčerpaný okruh připojení potřebují opravu, ne další pokus.
Při poolování a výchozím poskytovateli msodbcsql18 ovladač dočasně aplikuje nativní časový limit operace připojení během ověřování znovu použitého připojení: pozitivní timeout přihlášení, tedy 15 sekund, když je timeout přihlášení nulový nebo nespecifikovaný. Není to úplný termín pro validaci nebo dokončení objednávky. Více nativních operací, vyčištění a otevření náhradního připojení může prodloužit čas. Pokud ověření selže, driver se pokusí o nahrazení místo čekání na každé zbývající nevyužité připojení. Toto zotavení nepřehrává vaše příkazy ani znovu nezkouší vyčerpání fondu opakování. Pro více informací viz Sdružování připojení.
Identifikace přechodných chyb
mssql-python nezveřejňuje číslo chyby SQL Server engine jako atribut při výjimkách. Místo toho ovladač mapuje kódy SQLSTATE na pevnou sadu podtříd PEP 249 výjimek (OperationalError, ProgrammingError, a tak dále) a na standardizovaný anglický text v atributu driver_error . Použijte tuto kombinaci jako základ pro klasifikaci přechodných jevů.
Spolehlivé přechodné signály
Následující hodnoty SQLSTATE se objevují jako OperationalError a označují podmínku, kterou stojí za to znovu vyzkoušet. Pravý sloupec ukazuje přesný driver_error text nastavený ovladačem:
| SQLSTATE |
driver_error text |
Podmínka |
|---|---|---|
HYT00 |
Timeout expired |
Časový limit na úrovni výroku. |
HYT01 |
Connection timeout expired |
Časový limit připojení. |
08001 |
Client unable to establish connection |
Nepodařilo se navázat spojení. |
08S01 |
Communication link failure |
Výpadek sítě, reset serveru, selhání TCP. |
08007 |
Connection failure during transaction |
Spojení přerušeno uprostřed transakce. |
40001 |
Serialization failure |
Oběť patové situace. |
40003 |
Statement completion unknown |
Neurčitý stav transakce. |
import mssql_python
TRANSIENT_DRIVER_ERRORS = frozenset({
"Timeout expired",
"Connection timeout expired",
"Client unable to establish connection",
"Communication link failure",
"Connection failure during transaction",
"Serialization failure",
"Statement completion unknown",
})
def is_transient_error(error: BaseException) -> bool:
"""Return True if the exception represents a retryable transient failure.
Classification is based on the driver's PEP 249 exception subclass and
on the standardized `driver_error` text that mssql-python sets from
the SQLSTATE returned by the server.
"""
if isinstance(error, mssql_python.OperationalError):
return getattr(error, "driver_error", "") in TRANSIENT_DRIVER_ERRORS
return False
Omezování Azure SQL (v rámci nejlepších možností)
Chyby zpomalování Azure SQL (40197, , 4050140613, 49918, , 49919, 49920, a související kódy) se obvykle objevují pomocí SQLSTATE 42000, který mssql-python mapuje na ProgrammingError. Číslo chyby enginu není zobrazeno jako atribut, takže jediným signálem je text zprávy serveru v atributu ddbc_error .
Zkontrolujte ddbc_error, zda obsahuje známá čísla, pokud jsou k dispozici, a standardní anglickou chybovou zprávu 40501. Číslo může chybět a text zprávy není stabilním rozhraním. Lokalizované nebo jinak formátované diagnostiky nemusí odpovídat:
import re
# Azure SQL throttling and reconfiguration error numbers.
AZURE_THROTTLING_ERRORS = frozenset({
40197, 40501, 40540, 40613, 40680, 49918, 49919, 49920, 10928, 10929,
})
_ERROR_NUMBER_RE = re.compile(r"\b(?:Error|Msg)\s+(\d+)\b")
_AZURE_40501_MESSAGE = "The service is currently busy. Retry the request after 10 seconds."
def is_azure_throttling(error: BaseException) -> bool:
"""Best-effort detection of Azure SQL throttling in ProgrammingError text."""
if not isinstance(error, mssql_python.ProgrammingError):
return False
ddbc_text = getattr(error, "ddbc_error", "") or ""
return (
_AZURE_40501_MESSAGE in ddbc_text
or any(int(m) in AZURE_THROTTLING_ERRORS for m in _ERROR_NUMBER_RE.findall(ddbc_text))
)
def is_retryable(error: BaseException) -> bool:
return is_transient_error(error) or is_azure_throttling(error)
Co neopakovat
Příklady chyb, které by měly selhat rychle místo opětovného pokusu, zahrnují neplatné přihlašovací údaje (OperationalError s textem ovladače Invalid authorization specification), chybějící databázi nebo nedostatečná oprávnění, chyby v syntaxi (ProgrammingError), chybějící objekty a vyčerpání fondu připojení. Výše uvedená is_transient_error funkce všechny tyto prvky konstrukčně vylučuje.
Základní dekorátor pro opakování
Jednoduché opakování s pevným zpožděním
Dekorátor, který znovu zkouší zabalenou funkci s konstantním zpožděním:
import time
import functools
import mssql_python
def retry_on_failure(max_retries: int = 3, delay: float = 1.0):
"""Decorator to retry database operations on transient failures."""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
last_exception = None
for attempt in range(max_retries + 1):
try:
return func(*args, **kwargs)
except mssql_python.Error as e:
last_exception = e
if not is_transient_error(e) or attempt == max_retries:
raise
print(f"Attempt {attempt + 1} failed: {e}. Retrying in {delay}s...")
time.sleep(delay)
raise last_exception
return wrapper
return decorator
# Usage
@retry_on_failure(max_retries=3, delay=2.0)
def get_user(cursor, user_id: int):
cursor.execute("SELECT * FROM Person.Person WHERE BusinessEntityID = %(id)s", {"id": user_id})
return cursor.fetchone()
Exponenciální zpoždění
Exponenciálně zvyšujte prodlevu mezi opakováními s volitelným jitterem, aby se souběžná opakování rozložila:
import time
import random
def retry_with_backoff(max_retries: int = 5,
base_delay: float = 1.0,
max_delay: float = 30.0,
jitter: bool = True):
"""Retry with exponential backoff and optional jitter."""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
last_exception = None
for attempt in range(max_retries + 1):
try:
return func(*args, **kwargs)
except mssql_python.Error as e:
last_exception = e
if not is_transient_error(e) or attempt == max_retries:
raise
# Calculate delay with exponential backoff
delay = min(base_delay * (2 ** attempt), max_delay)
if jitter:
delay = delay * (0.5 + random.random())
print(f"Attempt {attempt + 1} failed. Retrying in {delay:.2f}s...")
time.sleep(delay)
raise last_exception
return wrapper
return decorator
@retry_with_backoff(max_retries=5, base_delay=1.0, max_delay=30.0)
def execute_query(cursor, query: str, params: dict):
cursor.execute(query, params)
return cursor.fetchall()
Třída opakovaného pokusu o spojení
Správce odolného připojení
Obal spojení, který zpracovává jak opakování, tak automatické znovupřipojení:
import mssql_python
import time
import logging
# This example uses is_transient_error from the "Identify transient errors"
# section earlier in this article. Include that helper in your module.
# Configure logging so the retry and reconnect messages are visible
logging.basicConfig(level=logging.INFO)
class ResilientConnection:
"""Connection wrapper with automatic retry and reconnection."""
def __init__(self, connection_string: str, max_retries: int = 5,
base_delay: float = 1.0, max_delay: float = 60.0):
self.connection_string = connection_string
self.max_retries = max_retries
self.base_delay = base_delay
self.max_delay = max_delay
self._conn = None
self._logger = logging.getLogger(__name__)
def _connect(self) -> mssql_python.Connection:
"""Establish connection with retry logic."""
last_exception = None
for attempt in range(self.max_retries + 1):
try:
self._logger.debug(f"Connection attempt {attempt + 1}")
return mssql_python.connect(self.connection_string)
except mssql_python.Error as e:
last_exception = e
if not is_transient_error(e) or attempt == self.max_retries:
self._logger.error(f"Connection failed: {e}")
raise
delay = min(self.base_delay * (2 ** attempt), self.max_delay)
self._logger.warning(f"Connection attempt {attempt + 1} failed. "
f"Retrying in {delay:.1f}s...")
time.sleep(delay)
raise last_exception
@property
def connection(self) -> mssql_python.Connection:
"""Get or create connection."""
if self._conn is None:
self._conn = self._connect()
return self._conn
def execute(self, query: str, params: dict = None):
"""Execute query with automatic retry and reconnection."""
return self._execute_with_retry(
lambda c: self._do_execute(c, query, params)
)
def _do_execute(self, cursor, query: str, params: dict):
cursor.execute(query, params or {})
return cursor.fetchall()
def _execute_with_retry(self, operation):
"""Execute an operation with retry logic."""
last_exception = None
for attempt in range(self.max_retries + 1):
try:
cursor = self.connection.cursor()
return operation(cursor)
except mssql_python.Error as e:
last_exception = e
if not is_transient_error(e):
raise
if attempt == self.max_retries:
raise
# Try to reconnect
self._logger.warning(f"Operation failed. Reconnecting...")
self._close()
delay = min(self.base_delay * (2 ** attempt), self.max_delay)
time.sleep(delay)
raise last_exception
def _close(self):
"""Close connection."""
if self._conn:
try:
self._conn.close()
except:
pass
self._conn = None
def close(self):
"""Public close method."""
self._close()
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.close()
return False
# Usage
with ResilientConnection(connection_string) as db:
users = db.execute("SELECT * FROM Person.Person WHERE EmailPromotion = %(promo)s",
{"promo": 1})
print(f"Retrieved {len(users)} rows")
Specifické zpracování pro Azure SQL
Použijte mapování výjimek mssql-python v Identify transient errors k klasifikaci chyb z následujícího katalogu.
Následující chyby mohou být přechodné, pokud nastanou při navazování spojení nebo při odesílání požadavku serveru. Opakujte pokus po krátké, omezené prodlevě. Chyby, které přetrvají i po několika pokusech, obvykle naznačují problém s konfigurací, jako je špatný server, chybějící oprávnění, nekompatibilní šifrovací nastavení nebo vyčerpání kvóty, které opakované zkusení nevyřeší.
| Error | Typ selhání | Message | Troubleshooting |
|---|---|---|---|
64 |
Přenos během přihlášení | A connection was successfully established with the server, but then an error occurred during the login process. (provider: TCP Provider, error: 0 - The specified network name is no longer available.) |
Spojení TCP se přeruší uprostřed navazování spojení. Nejedná se o selhání přihlašovacích údajů. Pokud přetrvává, zkontrolujte nestabilitu sítě na straně klienta nebo přechodné zařízení, které ukončí částečně zřízená připojení. |
233 |
Přenos před přihlášením nebo TLS | The client was unable to establish a connection because of an error during connection initialization process before login. |
Server tuto chybu často vrací, když nemůže připojení přijmout kvůli vyčerpání zdrojů, omezení připojení nebo nepodporovanému klientovi. Nejedná se o selhání přihlašovacích údajů. Ověřte stav serveru a zkontrolujte časový limit přihlášení klienta, nastavení protokolu TLS a kompatibilitu verzí protokolu TLS klienta/serveru. |
4060 |
Dostupnost nebo přístup k databázi | Cannot open database "%.*ls" requested by the login. The login failed. |
Přihlášení se ověří, ale požadovanou databázi nejde otevřít. Mezi přechodné příčiny patří stav, kdy se databáze nachází ve stavu přechodu (převzetí služeb při selhání, obnovení, škálování) nebo je automaticky pozastavená. Trvalé příčiny (databáze neexistuje, chybějící přístup k přihlášení) nebudou opraveny opakovaným pokusem; zkontrolujte název databáze, mapování přihlášení a stav databáze. |
4221 |
Čitelný sekundární přechod | Login to read-secondary failed due to long wait on 'HADR_DATABASE_WAIT_FOR_TRANSITION_TO_VERSIONING'. |
Replika není k dispozici pro přihlášení, protože verze řádků chybí pro transakce, které byly v testovacím prostředí při recyklaci repliky. Vrácením zpět nebo potvrzením aktivních transakcí na primárním serveru problém vyřešíte. Riziko zmírníte tím, že se vyhnete dlouhým zápisovým transakcím na primárním uzlu. |
10053 |
Přerušení místní dopravy | A transport-level error has occurred when sending the request to the server. (provider: TCP Provider, error: 0 - An established connection was aborted by the software in your host machine.) |
Místní strana přeruší připojení. Zkontrolujte stav sítě na straně klienta a případnou místní bránu firewall nebo klienta VPN. |
10054 |
Reset vzdáleného transportu | A transport-level error has occurred when sending the request to the server. (provider: TCP Provider, error: 0 - An existing connection was forcibly closed by the remote host.) |
Vzdálená strana odešle resetování protokolu TCP. Běžné příčiny: partnerský proces selhal, brána firewall vynutila reset spojení nebo brána Azure SQL ukončila nečinné připojení. U resetování při nečinnosti povolte na klientovi mechanismus TCP keepalive nebo zkraťte časový limit nečinnosti fondu připojení. |
10060 |
Časový limit připojení vypršel | A connection attempt failed because the connected party did not properly respond after a period of time. |
Server nebo mezilehlé síťové zařízení neodpovědělo před vypršením TCP připojení. Zkontrolujte stav serveru, směrování, pravidla firewallu a zda jsou konfigurovaný hostitel a port dostupné. |
10928 |
Limit databázových zdrojů | Resource ID: %d. The %s limit for the database is %d and has been reached. |
Databáze překračuje limit zásad správného řízení prostředků Azure SQL. ID prostředku 1 označuje limit pracovních procesů; ID prostředku 2 označuje limit relací. Určete typ limitu z hlášení a pak snižte souběžnost, navyšte kapacitu databáze nebo zkraťte dlouhotrvající operace, které blokují prostředek. |
10929 |
Omezování výkonu databáze | Resource ID: %d. The %s minimum guarantee is %d, maximum limit is %d, and the current usage for the database is %d. However, the server is currently too busy to support requests greater than %d for this database. |
Databáze překračuje své garantované minimum a podkladový server omezuje výkon. Opakování obvykle proběhne úspěšně při poklesu zatížení souseda. Trvalé výskyty značí, že potřebujete vyšší úroveň služby nebo méně hlučné prostředí. |
40020, 40143, , 4016640540 |
Podkód převzetí služeb při selhání Azure SQL | Hlášeno v slotu Error code %d chyby 40197 během převzetí služeb při selhání. |
Podkódy vložené do zprávy 40197 při přepojování v případě failoveru, které některé cesty zobrazují jako nejvyšší úroveň chyby. Zachází s nimi stejně jako s 40197. |
40197 |
Azure SQL failover | The service has encountered an error processing your request. Please try again. Error code %d. |
Aktualizace softwaru, selhání hardwaru nebo jiná událost převzetí služeb při selhání ve službě Azure SQL. Po opětovném připojení budete přesměrováni na zdravou repliku. Obsažený kód chyby určuje typ přepnutí při selhání. Pokud chyba přetrvává, poznamenejte si ID trasování relace a obraťte se na podporu. |
40501 |
Azure SQL – omezování výkonu | The service is currently busy. Retry the request after 10 seconds. Incident ID: %ls. Code: %d. |
Azure SQL omezování motoru. Doporučené minimum je prodleva 10 sekund. Trvalé zpomalování znamená, že pracovní zátěž překročila alokaci zdrojů v databázi; Rozšiřujte úroveň služeb nebo snižujte souběžnost. |
40613 |
Nedostupná databáze | Database '%.*ls' on server '%.*ls' is not currently available. Please retry the connection later. If the problem persists, contact customer support, and provide them with the session tracing ID of '%.*ls'. |
Databáze není dostupná, obvykle během převzetí služeb při selhání nebo krátce během škálování. Opakujte pokus s postupně prodlužovaným intervalem; pokud problém přetrvává déle než několik minut, zaznamenejte ID trasování relace a vytvořte požadavek na podporu. |
42108 |
SQL pool je pozastaven | Can not connect to the SQL pool since it is paused. Please resume the SQL pool and try again. |
Vyhrazený fond SQL (Synapse) je pozastavený. Opakovaný pokus bude úspěšný až po opětovném spuštění poolu. Fond obnovte explicitně nebo naplánujte úlohu tak, aby se spustila po obnovení fondu. |
42109 |
Obnovení SQL poolu | The SQL pool is warming up. Please try again. |
Vyhrazený fond SQL se obnovuje. Opakujte pokus se zvyšujícími se prodlevami, dokud nebude pool online; inicializace obvykle trvá několik minut. |
49918 |
Nedostatek servisních zdrojů | Cannot process request. Not enough resources to process request. The service is currently busy. Please retry the request later. |
Server momentálně nemůže přidělit dostatek prostředků pro splnění požadavku. Zkuste to znovu po prodlevě. Pokud chyba přetrvává, vertikálně navyšte kapacitu databáze nebo elastického fondu. |
49919 |
Omezení rychlosti operací správy | Cannot process create or update request. Too many create or update operations in progress for subscription "%ld". |
Limit souběžnosti operací správy na úrovni předplatného Omezte souběžná volání pro vytvoření nebo aktualizaci, nebo je rozložte v čase. |
49920 |
Omezování operací v rámci předplatného | Cannot process request. Too many operations in progress for subscription "%ld". |
Limit souběžnosti probíhajících operací na úrovni předplatného. Snižte míru paralelismu nebo počkejte, až probíhající operace doběhnou. |
Chyby na úrovni příkazů SQL nejsou v tomto seznamu, protože nastávají až po navázání připojení a po selhání zůstane relace použitelná. Nejběžnější chyby příkazů, které lze opakovat, jsou 1205 (oběť uváznutí) a 1222 (časový limit požadavku na zámek vypršel). Zkuste zopakovat celou transakci, nikoli jen jednotlivý příkaz, který selhal.
Text chybové zprávy je převzat z přechodných chyb připojení Azure SQL. Tyto chyby jsou způsobilé k opakovanému pokusu napříč SQL Server, Azure SQL Database, Azure SQL Managed Instance, SQL databází v Microsoft Fabric a v dedikovaných SQL poolech v Azure Synapse Analytics.
Řešení omezování požadavků v Azure
Chyby Azure SQL způsobené omezením vyžadují delší prodlevy a více opakovaných pokusů než běžné přechodné chyby. Znovu použít is_azure_throttling z Identifikace přechodných chyb:
def execute_with_throttle_handling(cursor, query: str, params: dict,
max_retries: int = 10,
base_delay: float = 10.0):
"""Execute with extended retry for Azure SQL throttling."""
for attempt in range(max_retries + 1):
try:
cursor.execute(query, params)
return cursor.fetchall()
except mssql_python.Error as e:
if is_azure_throttling(e):
if attempt < max_retries:
# Longer delays for throttling
delay = base_delay * (2 ** min(attempt, 4)) # Cap at 16 times the base delay
print(f"Throttled. Waiting {delay}s before retry...")
time.sleep(delay)
continue
raise
Převzetí služeb při selhání
Znovu se připojit a zkusit, když Azure SQL nebo skupina dostupnosti přeruší spojení:
def execute_with_failover_retry(connect, query: str, params: dict,
max_retries: int = 3,
recovery_delay: float = 10.0):
"""Reconnect and retry during Azure SQL failover scenarios."""
failover_numbers = frozenset({40613, 40197, 40540})
last_exception = None
for attempt in range(max_retries + 1):
conn = None
try:
conn = connect()
cursor = conn.cursor()
cursor.execute(query, params)
return cursor.fetchall()
except mssql_python.Error as e:
last_exception = e
# Failover surfaces either as a transient OperationalError or as
# a ProgrammingError whose ddbc_error text contains the engine
# error number. Treat both as recoverable.
ddbc_text = getattr(e, "ddbc_error", "") or ""
is_failover = is_transient_error(e) or any(
int(m) in failover_numbers for m in _ERROR_NUMBER_RE.findall(ddbc_text)
)
if is_failover and attempt < max_retries:
print(f"Failover detected. Reconnecting in {recovery_delay}s...")
if conn is not None:
try:
conn.close()
except mssql_python.Error:
pass
time.sleep(recovery_delay)
continue
raise
raise last_exception
# Usage
connection_string = (
"Server=tcp:<server>.database.windows.net,1433;"
"Database=AdventureWorks2025;"
"Authentication=ActiveDirectoryDefault;"
"Encrypt=yes;TrustServerCertificate=no"
)
rows = execute_with_failover_retry(
lambda: mssql_python.connect(connection_string),
"SELECT TOP 10 ProductID, Name FROM Production.Product WHERE Color = %(color)s",
{"color": "Silver"}
)
Řešení uváznutí
Zopakování při zablokování
Patové situace (chyba 1205) jsou přechodné. Zkuste to znovu s krátkým náhodným zpožděním, abyste přerušili cyklus zablokování. Opakování vyřeší okamžité selhání, ale opakující se zablokování naznačuje problém v návrhu, který byste měli prozkoumat na straně serveru. Pro návod k analýze a řešení kořenové příčiny viz Chyby zablokování.
def execute_with_deadlock_retry(cursor, query: str, params: dict,
max_retries: int = 3):
"""Automatically retry deadlocked transactions.
Deadlocks (SQL Server error 1205) surface as OperationalError with
driver_error == "Serialization failure" (SQLSTATE 40001).
"""
for attempt in range(max_retries + 1):
try:
cursor.execute(query, params)
return cursor.fetchall()
except mssql_python.OperationalError as e:
if getattr(e, "driver_error", "") == "Serialization failure":
if attempt < max_retries:
delay = random.uniform(0.1, 0.5) * (attempt + 1)
print(f"Deadlock detected. Retry {attempt + 1} in {delay:.2f}s")
time.sleep(delay)
continue
raise
# Usage in transaction
conn.autocommit = False
try:
cursor = conn.cursor()
rows = execute_with_deadlock_retry(
cursor,
"SELECT TOP 5 Name, ListPrice FROM Production.Product WHERE ListPrice > %(price)s",
{"price": 100}
)
conn.commit()
except Exception:
conn.rollback()
raise
Strukturované opakování s konfigurací
Třída zásad opakování
Zapouzdřete konfiguraci opakování pokusů v datové třídě pro opětovné použití ve více různých operacích:
from dataclasses import dataclass, field
from typing import FrozenSet
import time
import random
# This example uses TRANSIENT_DRIVER_ERRORS from the "Identify transient errors"
# section earlier in this article. Include that allowlist in your module.
@dataclass
class RetryPolicy:
"""Configuration for retry behavior."""
max_retries: int = 3
base_delay: float = 1.0
max_delay: float = 30.0
exponential_base: float = 2.0
jitter: bool = True
transient_driver_errors: FrozenSet[str] = field(default_factory=lambda: TRANSIENT_DRIVER_ERRORS)
def get_delay(self, attempt: int) -> float:
"""Calculate delay for given attempt number."""
delay = min(
self.base_delay * (self.exponential_base ** attempt),
self.max_delay,
)
if self.jitter:
delay *= (0.5 + random.random())
return delay
def should_retry(self, error: BaseException, attempt: int) -> bool:
"""Determine if operation should be retried."""
if attempt >= self.max_retries:
return False
if isinstance(error, mssql_python.OperationalError):
return getattr(error, "driver_error", "") in self.transient_driver_errors
return False
def execute_with_policy(cursor, query: str, params: dict,
policy: RetryPolicy = None):
"""Execute query with configurable retry policy."""
policy = policy or RetryPolicy()
last_exception = None
for attempt in range(policy.max_retries + 1):
try:
cursor.execute(query, params)
return cursor.fetchall()
except mssql_python.Error as e:
last_exception = e
if not policy.should_retry(e, attempt):
raise
delay = policy.get_delay(attempt)
time.sleep(delay)
raise last_exception
# Usage with custom policy
aggressive_retry = RetryPolicy(max_retries=10, base_delay=0.5, max_delay=60.0)
conservative_retry = RetryPolicy(max_retries=2, base_delay=5.0, max_delay=10.0)
results = execute_with_policy(cursor, query, params, aggressive_retry)
Model jističe
Zabránit kaskádovým selháním sledováním po sobě jdoucích chyb a dočasným blokováním hovorů po dosažení určitého prahu:
import time
from enum import Enum
from threading import Lock
class CircuitState(Enum):
CLOSED = "closed" # Normal operation
OPEN = "open" # Failing, reject all calls
HALF_OPEN = "half_open" # Testing if service recovered
class CircuitBreaker:
"""Circuit breaker to prevent cascading failures."""
def __init__(self, failure_threshold: int = 5,
recovery_timeout: float = 30.0):
self.failure_threshold = failure_threshold
self.recovery_timeout = recovery_timeout
self.state = CircuitState.CLOSED
self.failure_count = 0
self.last_failure_time = None
self._lock = Lock()
def can_execute(self) -> bool:
"""Check if circuit allows execution."""
with self._lock:
if self.state == CircuitState.CLOSED:
return True
if self.state == CircuitState.OPEN:
# Check if recovery timeout has passed
if time.time() - self.last_failure_time > self.recovery_timeout:
self.state = CircuitState.HALF_OPEN
return True
return False
# HALF_OPEN: allow one test request
return True
def record_success(self):
"""Record successful operation."""
with self._lock:
self.failure_count = 0
self.state = CircuitState.CLOSED
def record_failure(self):
"""Record failed operation."""
with self._lock:
self.failure_count += 1
self.last_failure_time = time.time()
if self.failure_count >= self.failure_threshold:
self.state = CircuitState.OPEN
# Usage
circuit = CircuitBreaker(failure_threshold=5, recovery_timeout=30.0)
def execute_with_circuit_breaker(cursor, query: str, params: dict):
if not circuit.can_execute():
raise Exception("Circuit breaker is open")
try:
cursor.execute(query, params)
result = cursor.fetchall()
circuit.record_success()
return result
except mssql_python.Error as e:
if is_transient_error(e):
circuit.record_failure()
raise
Nezkoušejte znovu chyby v konfiguraci
Ne každá chyba je přechodná. Opakování chyby v konfiguraci nebo kódování ztrácí čas a může zakrýt skutečný problém. Zkoušejte jen chyby, které by se mohly samy vyřešit. Protože mssql-python nezpřístupňuje číslo chyby enginu jako atribut, klasifikujte podle podtřídy výjimky plus driver_error text.
Nikdy to nezkoušejte znovu ( opravte kód nebo konfiguraci):
| Podmínka | Typ výjimky |
driver_error text |
Opravit |
|---|---|---|---|
| Neplatný název objektu (modul 208) | ProgrammingError |
Base table or view not found |
Tabulka neexistuje. Opravte dotaz nebo vytvořte tabulku. |
| Neplatný název sloupce (modul 207) | ProgrammingError |
Column not found |
Sloupek neexistuje. Zkontrolujte schéma. |
| Nesprávná syntaxe (engine 102) | ProgrammingError |
Syntax error or access violation |
Opravte dotaz. |
| Přihlášení selhalo (motor 18456) | OperationalError |
Invalid authorization specification |
Špatné kvalifikace. Opravte připojovací řetězec. |
| Chybějící databáze nebo odepřený přístup (engine 4060) | OperationalError |
Server rejected the connection |
Databáze neexistuje nebo není přístupná k přihlášení. Opravte cíl nebo oprávnění. |
| Vyčerpání fondu připojení | OperationalError |
(mění) | Zvyšte kapacitu poolu, rychle uvolněte spojení nebo snižte souběžnost. |
ConnectionStringParseError |
Samostatné | N/A | Překlep v klíčovém slově připojovacího řetězce. Opravte řetězec. |
| Nepodporovaná funkce | NotSupportedError |
Optional feature not implemented |
Použijte alternativní přístup. |
Vždy to zkuste znovu ( vyřeší se samy):
| Podmínka | Typ výjimky |
driver_error text |
|---|---|---|
| Vypršení časového limitu příkazu | OperationalError |
Timeout expired |
| Časový limit připojení | OperationalError |
Connection timeout expired |
| Nepodařilo se otevřít spojení | OperationalError |
Client unable to establish connection |
| Výpadek sítě | OperationalError |
Communication link failure |
| Spojení bylo přerušeno uprostřed transakce | OperationalError |
Connection failure during transaction |
| Oběť zablokování (motor 1205) | OperationalError |
Serialization failure |
| Neurčitý transakční stav | OperationalError |
Statement completion unknown |
| Azure SQL throttling (40197, 40501, 40613, 49918–49920) | ProgrammingError |
Syntax error or access violation (pomocí is_azure_throttling porovnejte s textem ddbc_error) |