Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Verbindingen met SQL Server en Azure SQL kunnen tijdelijk mislukken om redenen die niets te maken hebben met uw code:
- Een AlwaysOn-beschikbaarheidsgroep voert een failover uit.
- Het netwerk laat een pakket vallen tijdens het instellen van de verbinding.
- Resource Governor beperkt de databaseprestaties.
- Een Azure SQL-replica wordt opnieuw geïnitialiseerd tijdens het schalen of bij een upgrade.
De meeste van deze fouten zijn binnen enkele seconden duidelijk. In dit artikel wordt beschreven hoe u in een Django-toepassing die gebruikmaakt van de mssql-django-backend, opnieuw kunt proberen na tijdelijke fouten, en hoe u Django en het ODBC-stuurprogramma configureert om automatisch te herstellen na verbindingsverbrekingen door inactiviteit.
Tijdelijke fouten
Tijdelijke fouten zijn tijdelijke fouten die zelfstandig worden opgelost. Het opnieuw proberen van de bewerking na een korte vertraging slaagt meestal.
De volgende fouten zijn tijdelijk wanneer ze optreden tijdens het tot stand komen van de verbinding of tijdens het verzenden van een aanvraag naar de server. Probeer het na een korte, begrensde wachttijd opnieuw. Fouten die zich na enkele nieuwe pogingen voordoen, geven meestal een configuratieprobleem aan (verkeerde server, ontbrekende machtigingen, uitgeput quotum) die opnieuw proberen niet worden opgelost.
| Fout | Message | Troubleshooting |
|---|---|---|
64 |
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.) |
De TCP-verbinding valt halverwege de handshake weg. Geen referentiefout. Als dit zich blijft voordoen, controleert u op instabiliteit van het netwerk aan de clientzijde of een tussenliggend apparaat dat half tot stand gebrachte verbindingen afneemt. |
233 |
The client was unable to establish a connection because of an error during connection initialization process before login. |
Transportfout vóór het aanmelden of TLS-fout. De server retourneert deze meestal wanneer deze de verbinding niet kan accepteren (resourceuitputting, max-connections bereikt of een niet-ondersteunde client). Geen referentiefout. Controleer de status van de server en controleer vervolgens de time-out voor clientaanmelding, de TLS-instellingen en de compatibiliteit van de TLS-versies van client en server. |
4060 |
Cannot open database "%.*ls" requested by the login. The login failed. |
De aanmelding wordt geverifieerd, maar kan de aangevraagde database niet openen. Tijdelijke oorzaken zijn onder andere dat de database bezig is met een overgang (failover, herstel, schalen) of automatisch is gepauzeerd. Aanhoudende oorzaken (database bestaat niet, login heeft geen toegang) worden niet opgelost door opnieuw te proberen; controleer de databasenaam, de loginkoppeling en de status van de database. |
4221 |
Login to read-secondary failed due to long wait on 'HADR_DATABASE_WAIT_FOR_TRANSITION_TO_VERSIONING'. |
De replica is niet beschikbaar voor aanmelden omdat voor transacties die nog in uitvoering waren toen de replica werd gerecycled, rijversies ontbreken. Draai de actieve transacties op de primaire server terug of leg ze vast om het probleem op te lossen. Beperk dit door lange schrijftransacties op de primaire schijf te voorkomen. |
10053 |
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.) |
De lokale kant breekt de verbinding af. Controleer de netwerkstatus aan de clientzijde en een lokale firewall of VPN-client. |
10054 |
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.) |
De externe zijde verzendt een TCP-reset. Veelvoorkomende oorzaken: het peerproces is vastgelopen, een firewall heeft een reset geïnjecteerd of de Azure SQL-gateway heeft een niet-actieve verbinding gesloten. Schakel voor patronen van resets door inactiviteit TCP-keepalive in op de client of verkort de inactiviteitstime-out van de connectionpool. |
10928 |
Resource ID: %d. The %s limit for the database is %d and has been reached. See 'http://go.microsoft.com/fwlink/?LinkId=267637' for assistance. |
De database overschrijdt een Azure SQL limiet voor resourcebeheer. Resource-id 1 geeft de werkrollimiet aan; Resource-id 2 geeft de sessielimiet aan. Identificeer het limiettype van het bericht en verminder de gelijktijdigheid, schaal de database omhoog of verkort langlopende bewerkingen die de resource bevatten. |
10929 |
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. |
De database zit boven de minimumgarantie en de onderliggende server beperkt de prestaties. Het opnieuw proberen slaagt meestal wanneer de load van de buur afneemt. Aanhoudende incidenten geven aan dat u een hoger serviceniveau of een minder rumoerige omgeving nodig hebt. |
40020,40143,40166,40540 |
Gerapporteerd in de Error code %d sleuf van fout 40197 tijdens de failover. |
Subcodes die zijn ingebed in een 40197-failoverbericht en die sommige paden weergeven als foutnummer op topniveau. Behandel ze hetzelfde als 40197. |
40197 |
The service has encountered an error processing your request. Please try again. Error code %d. |
Een software-upgrade, een hardwarefout of een andere failovergebeurtenis in Azure SQL. Opnieuw verbinden leidt u naar een gezonde replica. De ingesloten foutcode identificeert het failovertype. Als de fout zich blijft voordoen, legt u de sessietracerings-id vast en neemt u contact op met de ondersteuning. |
40501 |
The service is currently busy. Retry the request after 10 seconds. Incident ID: %ls. Code: %d. |
Azure SQL motorbeperking. De aanbevolen ondergrens is een wachttijd van 10 seconden. Aanhoudende throttling geeft aan dat de belasting de aan de database toegewezen resources heeft overschreden; schaal het serviceniveau op of verminder het aantal gelijktijdige aanvragen. |
40613 |
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'. |
De database is niet beschikbaar, meestal halverwege een failover of kort tijdens een schaalbewerking. Probeer het na enige wachttijd opnieuw; als het probleem na enkele minuten aanhoudt, leg dan de sessietracerings-id vast en maak een supportticket aan. |
42108 |
Can not connect to the SQL pool since it is paused. Please resume the SQL pool and try again. |
De toegewezen SQL-pool (Synapse) is onderbroken. Opnieuw proberen lukt pas nadat de pool is hervat. Hervat de pool expliciet of plan de workload zodanig dat deze wordt uitgevoerd nadat de pool is hervat. |
42109 |
The SQL pool is warming up. Please try again. |
De toegewezen SQL-pool wordt hervat. Blijf het opnieuw proberen met steeds langere tussenpozen totdat de pool online is; het opstarten duurt meestal een paar minuten. |
49918 |
Cannot process request. Not enough resources to process request. The service is currently busy. Please retry the request later. |
De server kan momenteel onvoldoende resources toewijzen om aan de aanvraag te voldoen. Opnieuw proberen na een wachttijd. Als de fout zich blijft voordoen, schaalt u de database of elastische pool omhoog. |
49919 |
Cannot process create or update request. Too many create or update operations in progress for subscription "%ld". |
Gelijktijdigheidslimiet op abonnementsniveau voor beheerbewerkingen. Verminder parallelle aanroepen voor maken of bijwerken, of spreid ze in de tijd. |
49920 |
Cannot process request. Too many operations in progress for subscription "%ld". |
Gelijktijdigheidslimiet op abonnementsniveau voor bewerkingen in vlucht. Verminder het parallelisme of wacht tot de lopende bewerkingen zijn voltooid. |
Fouten op instructieniveau staan niet in deze lijst, omdat ze optreden nadat de verbinding tot stand is gekomen en de sessie na de fout bruikbaar blijft. De meest voorkomende fouten in opnieuw uit te voeren instructies zijn 1205 (deadlockslachtoffer) en 1222 (time-out bij een vergrendelingsaanvraag). Voer de hele transactie opnieuw uit in plaats van de enkele mislukte instructie.
De tekst van het foutbericht is afkomstig van Azure SQL tijdelijke verbindingsfouten. Afzonderlijke stuurprogramma's onderhouden hun eigen ingebouwde lijsten voor nieuwe pogingen; in deze catalogus wordt beschreven welke fouten in aanmerking komen voor nieuwe pogingen in SQL Server, Azure SQL Database, Azure SQL Managed Instance, SQL-database in Microsoft Fabric en toegewezen SQL-pools in Azure Synapse Analytics.
Veerkracht van inactieve verbindingen van het ODBC-stuurprogramma
Het Microsoft ODBC-stuurprogramma voor SQL Server biedt ingebouwde niet-actieve verbindingstolerantie via de ConnectRetryCount trefwoorden en ConnectRetryInterval verbindingsreeks trefwoorden. Deze instellingen verwerken verbroken inactieve verbindingen op stuurprogrammaniveau voordat uw toepassingscode wordt betrokken.
Niet-actieve verbindingstolerantie inschakelen in extra_params:
DATABASES = {
"default": {
"ENGINE": "mssql",
"NAME": "<your-database>",
"HOST": "<your-server>.database.windows.net",
"PORT": "1433",
"OPTIONS": {
"driver": "ODBC Driver 18 for SQL Server",
"extra_params": "ConnectRetryCount=3;ConnectRetryInterval=10",
},
},
}
| Keyword | Default | Description |
|---|---|---|
ConnectRetryCount |
1 | Aantal pogingen om automatisch opnieuw verbinding te maken voor niet-actieve verbindingen. |
ConnectRetryInterval |
10 | Seconden tussen pogingen om opnieuw verbinding te maken. |
Note
De tolerantie van niet-actieve verbindingen maakt opnieuw verbinding met verbindingen die zijn verbroken tijdens inactiviteit. Het probeert mislukte query's niet opnieuw uit te voeren en herstelt niet van fouten die optreden tijdens actieve transacties. Gebruik voor deze scenario's de logica voor opnieuw proberen op toepassingsniveau.
Django-database-middleware voor nieuwe pogingen
Maak een Django-middleware die tijdelijke fouten onderschept en de databasebewerking opnieuw probeert uit te voeren. Deze aanpak werkt voor het verwerken van aanvragen op weergaveniveau:
# myproject/middleware.py
import random
import re
import time
import logging
from django.db import OperationalError, connection
logger = logging.getLogger(__name__)
TRANSIENT_ERROR_CODES = {
"64", "233", "4221",
"10053", "10054", "10928", "10929",
"40197", "40501", "40613",
"49918", "49919", "49920",
# Include "4060" only if targeting Azure SQL with geo-replication failover.
# It is usually a permanent error (wrong database name or missing permissions).
}
# Microsoft ODBC driver formats native error codes as "(<number>)" in the
# message. Extracting parenthesized codes avoids false positives that a plain
# substring match would produce for short codes like "64".
_CODE_RE = re.compile(r"\((\d+)\)")
def is_transient(error):
codes_in_message = set(_CODE_RE.findall(str(error)))
return bool(codes_in_message & TRANSIENT_ERROR_CODES)
class DatabaseRetryMiddleware:
"""Retry database operations on transient errors."""
def __init__(self, get_response):
self.get_response = get_response
self.max_retries = 3
self.base_delay = 1 # seconds; doubled each attempt
self.max_delay = 30 # cap on a single sleep, regardless of attempt
def __call__(self, request):
for attempt in range(self.max_retries + 1):
try:
return self.get_response(request)
except OperationalError as e:
if attempt < self.max_retries and is_transient(e):
# Exponential backoff with full jitter, capped at max_delay.
# Jitter spreads simultaneous retries so many clients
# don't hammer the server in lock-step during an outage.
capped = min(self.max_delay, self.base_delay * (2 ** attempt))
delay = random.uniform(0, capped)
logger.warning(
"Transient DB error (attempt %d/%d), retrying in %.2fs: %s",
attempt + 1, self.max_retries, delay, e
)
connection.close()
time.sleep(delay)
continue
raise
Registreer de middleware in settings.py:
MIDDLEWARE = [
"myproject.middleware.DatabaseRetryMiddleware",
"django.middleware.security.SecurityMiddleware",
# ... other middleware
]
Important
Plaats DatabaseRetryMiddleware vóór andere middleware die toegang hebben tot de database, zodat deze tijdelijke fouten uit de hele requestpijplijn kan onderscheppen en opnieuw kan proberen.
Decorator opnieuw proberen voor specifieke bewerkingen
Gebruik voor fijnmazige controle een decorator voor afzonderlijke functies:
import random
import re
import time
import functools
import logging
from django.db import OperationalError, connection
logger = logging.getLogger(__name__)
TRANSIENT_ERROR_CODES = {
"64", "233", "4221",
"10053", "10054", "10928", "10929",
"40197", "40501", "40613",
"49918", "49919", "49920",
# Include "4060" only if targeting Azure SQL with geo-replication failover.
}
_CODE_RE = re.compile(r"\((\d+)\)")
def is_transient(error):
codes_in_message = set(_CODE_RE.findall(str(error)))
return bool(codes_in_message & TRANSIENT_ERROR_CODES)
def retry_on_transient(max_retries=3, base_delay=1, max_delay=30):
"""Retry on transient database errors with exponential backoff and full jitter."""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(max_retries + 1):
try:
return func(*args, **kwargs)
except OperationalError as e:
if attempt < max_retries and is_transient(e):
# Exponential cap doubled per attempt, then jittered
# within [0, cap] and limited by max_delay.
capped = min(max_delay, base_delay * (2 ** attempt))
delay = random.uniform(0, capped)
logger.warning(
"Transient error in %s (attempt %d/%d), retrying in %.2fs: %s",
func.__name__, attempt + 1, max_retries, delay, e
)
connection.close()
time.sleep(delay)
continue
raise
return wrapper
return decorator
Pas de decorator toe op database-intensieve functies:
from myproject.retry import retry_on_transient
@retry_on_transient(max_retries=3, base_delay=2)
def process_order(order_id):
"""Process an order with automatic retry on transient failures."""
order = Order.objects.select_for_update().get(id=order_id)
order.status = "processing"
order.save()
return order
Opnieuw proberen met transacties
Wanneer er een tijdelijke fout optreedt in een transactie, wordt de hele transactie teruggedraaid door de server. Voer de volledige transactie opnieuw uit, niet alleen de mislukte instructie:
from django.db import transaction
@retry_on_transient(max_retries=3)
def transfer_funds(from_account_id, to_account_id, amount):
"""Transfer funds between accounts with retry."""
with transaction.atomic():
from_account = Account.objects.select_for_update().get(id=from_account_id)
to_account = Account.objects.select_for_update().get(id=to_account_id)
from_account.balance -= amount
to_account.balance += amount
from_account.save()
to_account.save()
Waarschuwing
Probeer het niet opnieuw in transaction.atomic(). De retry-decorator moet het volledige atomic()-blok omvatten, zodat bij elke nieuwe poging een nieuwe transactie wordt gestart.
Fouten op instructieniveau
De foutenlijst in de vorige sectie bevat fouten op verbindingsniveau. Twee andere fouten worden vaak opnieuw uitgevoerd op statementniveau:
- 1205: De sessie werd aangewezen als het deadlock-slachtoffer. Voer de transactie opnieuw uit.
-
1222: Er is een time-out voor vergrendelingsaanvragen overschreden. Voer de transactie opnieuw uit of verhoog
LOCK_TIMEOUTvoor de sessie als de standaardwaarde te agressief is.
ConnectRetryCount probeert verbroken verbindingen opnieuw, dus dit is niet van toepassing op deze fouten op opdrachtniveau. Verwerk ze met hetzelfde decoratorpatroon door "1205" en "1222" toe te voegen aan TRANSIENT_ERROR_CODES voor transacties die veilig opnieuw kunnen worden uitgevoerd.
CONN_MAX_AGE en verouderde verbindingen
Django hergebruikt databaseverbindingen tussen aanvragen wanneer CONN_MAX_AGE deze is ingesteld. Een langlevende verbinding kan verouderen als de server deze sluit (bijvoorbeeld tijdens een Azure SQL-schaalactie of een firewalltime-out).
Ingesteld CONN_MAX_AGE op evenwicht tussen hergebruik en veroudering:
DATABASES = {
"default": {
"ENGINE": "mssql",
"NAME": "<your-database>",
"HOST": "<your-server>.database.windows.net",
"OPTIONS": {
"driver": "ODBC Driver 18 for SQL Server",
},
"CONN_MAX_AGE": 600, # Close and reopen connections after 10 minutes
},
}
-
CONN_MAX_AGE=0(standaard): Sluit de verbinding aan het einde van elke aanvraag. Veiligste maar langzaamste. -
CONN_MAX_AGE=600: Verbindingen gedurende 10 minuten opnieuw gebruiken. Goede balans voor de meeste webtoepassingen. -
CONN_MAX_AGE=None: Houd verbindingen voor onbepaalde tijd open. Alleen gebruiken met een mechanisme om verouderde verbindingen opnieuw te proberen.
CONN_HEALTH_CHECKS (Django 4.1 en hoger)
Django 4.1 geïntroduceerd CONN_HEALTH_CHECKS, waarmee een opnieuw gebruikte verbinding wordt gevalideerd voor elke aanvraag. Schakel dit samen met CONN_MAX_AGE in om verouderde verbindingen automatisch te detecteren:
DATABASES = {
"default": {
"ENGINE": "mssql",
"NAME": "<your-database>",
"HOST": "<your-server>.database.windows.net",
"OPTIONS": {
"driver": "ODBC Driver 18 for SQL Server",
},
"CONN_MAX_AGE": 600,
"CONN_HEALTH_CHECKS": True,
},
}
Als statuscontroles zijn ingeschakeld, geeft Django een lichtgewicht validatiequery uit voordat een verbinding opnieuw wordt gebruikt. Als de verbinding verbroken is, opent Django transparant een nieuwe in plaats van een fout op te geven.
Beste praktijken
- Gebruik exponentiële back-off met volledige jitter. Verdubbel bij elke poging de limiet en wacht vervolgens een willekeurige tijd binnen
[0, cap]. Jitter voorkomt dat veel clients tijdens een regionale storing gelijktijdig opnieuw proberen, wat anders een kortdurende storing kan doen uitgroeien tot aanhoudende overbelasting. Beperk de slaapstand per poging (bijvoorbeeld 30 seconden), zodat de totale hersteltijd gebonden blijft. - Stel een plafond voor opnieuw proberen in. Drie herpogingen met exponentiële wachttijd is een redelijke standaardinstelling. Meer dan vijf nieuwe pogingen duiden meestal op een niet-tijdelijk probleem.
- Sluit de verbinding voordat u het opnieuw probeert. Bel
connection.close()zodat Django een nieuwe verbinding opent bij de volgende poging. - Registreer elke nieuwe poging. Herhaalde pogingen die ongemerkt slagen, kunnen prestatieproblemen verbergen. Meld u aan op
WARNINGniveau zodat u de frequentie kunt bijhouden. - Probeer niet opnieuw niet-tijdelijke fouten. Verificatiefouten, machtigingsfouten en syntaxisfouten profiteren niet van nieuwe pogingen.
- Voer de hele transactie opnieuw uit. Plaats
transaction.atomic()binnen de retrylogica, niet andersom. -
Inschakelen
CONN_HEALTH_CHECKS(Django 4.1 en hoger) voor webtoepassingen die gebruikmaken vanCONN_MAX_AGE.