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.
Aplikace Production Go potřebují strukturované řešení chyb, aby se rozlišily mezi přechodnými selháními, která lze zopakovat, a trvalými chybami, které vyžadují lidský zásah. Tento článek se zabývá klasifikací chyb, vzorci opakovaného pokusu a strategiemi odolnosti řidiče go-mssqldb .
Struktura chyb v SQL Server
Když SQL Server vrátí chybu, ovladač go-mssqldbji zabalí do struktury mssql.Errorstruct. Použijte typ aserce pro přístup ke strukturovaným chybovým polím:
import (
"database/sql"
"errors"
"fmt"
mssql "github.com/microsoft/go-mssqldb"
)
func handleError(err error) {
var mssqlErr mssql.Error
if errors.As(err, &mssqlErr) {
fmt.Printf("Number: %d\n", mssqlErr.Number)
fmt.Printf("State: %d\n", mssqlErr.State)
fmt.Printf("Class: %d\n", mssqlErr.Class)
fmt.Printf("Message: %s\n", mssqlErr.Message)
fmt.Printf("Server: %s\n", mssqlErr.ServerName)
fmt.Printf("Proc: %s\n", mssqlErr.ProcName)
fmt.Printf("Line: %d\n", mssqlErr.LineNo)
}
}
Chybová pole
| Obor | Typ | Description |
|---|---|---|
Number |
int32 |
Číslo chyby serveru SQL Server. Mapuje se na sys.messages. |
State |
uint8 |
Stav chyby. Poskytuje další kontext pro stejné chybové číslo. |
Class |
uint8 |
Závažnost (0-25). Závažnost 11-16 je uživatelsky korigovatelná. Závažnost 17+ znamená problémy se zdroji nebo systémem. |
Message |
string |
Lidsky čitelný chybový text ze serveru. |
ServerName |
string |
Název instance SQL Server, která způsobila tuto chybu. |
ProcName |
string |
Uložený název procedury nebo funkce, kde chyba nastala. Prázdné pro ad hoc dotazy. |
LineNo |
int32 |
Číslo řádku v dávce nebo uložené proceduře jazyka Transact-SQL (T-SQL). |
Úrovně závažnosti
| Rozsah závažnosti | Meaning | Action |
|---|---|---|
| 0-10 | Informační zprávy | Žádná chyba. Pokud je to užitečné, dejte si do záznamu. |
| 11-16 | Chyby opravitelné uživatelem | Opravte dotaz, parametry nebo oprávnění. |
| 17-19 | Chyby prostředků | Zkuste to znovu. Server může být pod zátěží nebo bez zdrojů. |
| 20-25 | Fatální chyby | Připojení je přerušené. Znovu se připojte a zkuste to znovu. |
Klasifikovat chyby jako přechodné nebo trvalé
Přechodné chyby jsou dočasné stavy, které se samy vyřeší, například výkyvy v síti, omezení spojení nebo krátké spory o zdroje. Trvalé chyby vyžadují změny kódu nebo konfigurace.
Běžná čísla přechodných chyb
Použijte následující sdílený katalog jako kanonický seznam přechodných chyb při navázání spojení a přenosu cesty požadavků:
Následující chyby jsou přechodné, pokud k nim dojde během vytváření připojení nebo při odesílání požadavku na server. Opakujte pokus po krátké, omezené prodlevě. Chyby, které přetrvávají i po několika opakováních, obvykle ukazují na problém s konfigurací (nesprávný server, chybějící oprávnění, vyčerpaná kvóta), který opakování pokusu nevyřeší.
| Error | 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.) |
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 |
The client was unable to establish a connection because of an error during connection initialization process before login. |
Přenos před přihlášením nebo selhání protokolu TLS Server ho obvykle vrací, když nemůže přijmout připojení (vyčerpání prostředků, dosažení maximálního počtu připojení nebo nepodporovaného klienta). 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 |
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 |
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 |
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 |
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í. |
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. |
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 |
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 |
Hlášeno v slotu Error code %d chyby 40197 během převzetí služeb při selhání. |
Dílčí kódy obsažené ve zprávě o převzetí služeb při selhání 40197 se v některých případech zobrazují jako chybový kód nejvyšší úrovně. Zachází s nimi stejně jako s 40197. |
40197 |
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 |
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. Dlouhodobé omezování výkonu znamená, že pracovní zátěž překročila přidělené prostředky databáze; přejděte na vyšší úroveň služby nebo snižte souběžnost. |
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'. |
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 |
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 |
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 |
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 |
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 |
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. Jednotlivé ovladače udržují vlastní integrované seznamy opakování; tento katalog popisuje, u kterých chyb lze operaci opakovat v prostředích SQL Server, Azure SQL Database, Azure SQL Managed Instance, databáze SQL v Microsoft Fabric a vyhrazené fondy SQL v Azure Synapse Analytics.
Následující isTransient funkce ukazuje jeden vzor implementace Go pro klasifikaci opakovaných pokusů. Sdílený katalog výše berte jako směrodatný zdroj a vyhledávání kódů udržujte v souladu s tímto katalogem.
// isTransient returns true if the error is a transient SQL Server error
// that is likely to succeed on retry.
func isTransient(err error) bool {
var mssqlErr mssql.Error
if !errors.As(err, &mssqlErr) {
// Network errors, context deadlines, and connection resets
// are also transient.
return isNetworkError(err)
}
if isTransientSQLNumber(mssqlErr.Number) {
return true
}
// Severity 17-19 indicates resource issues that are typically transient.
return mssqlErr.Class >= 17 && mssqlErr.Class <= 19
}
// Keep this lookup synchronized with the shared transient catalog above.
var transientSQLNumbers = map[int32]struct{}{
64: {}, // Transport/connection error.
1205: {}, // Deadlock victim.
40197: {}, // Service error processing request.
40501: {}, // Service is currently busy.
40613: {}, // Database is currently unavailable.
49918: {}, // Cannot process request: not enough resources.
49919: {}, // Cannot process create/update request.
49920: {}, // Cannot process request: too many operations.
}
func isTransientSQLNumber(number int32) bool {
_, ok := transientSQLNumbers[number]
return ok
}
Pokud se objeví chyby v konfiguraci a kvótách, opravte základní kapacitu, databázi nebo síťovou konfiguraci před opětovným pokusem. Mezi příklady patří:
-
40544(kvóta velikosti databáze) -
4060(nelze otevřít databázi) -
40615(pravidlo firewallu)
Detekce síťových chyb
Chyby na úrovni sítě nevytvářejí mssql.Error hodnoty. Zkontrolujte běžné typy chyb v síti Go:
import (
"context"
"errors"
"net"
"io"
)
func isNetworkError(err error) bool {
if err == nil {
return false
}
// Context deadline exceeded or canceled
if errors.Is(err, context.DeadlineExceeded) {
return true
}
// Connection reset or broken pipe
var netErr *net.OpError
if errors.As(err, &netErr) {
return true
}
// Unexpected EOF (server dropped the connection)
if errors.Is(err, io.ErrUnexpectedEOF) || errors.Is(err, io.EOF) {
return true
}
return false
}
Implementujte opakované pokusy s exponenciálním ústupem
Opakujte přechodné chyby s rostoucími prodlevami mezi pokusy. Tento přístup dává serveru čas na zotavení a zabraňuje jeho přetížení rychlými pokusy.
import (
"context"
"database/sql"
"errors"
"fmt"
"log"
"math"
"math/rand"
"time"
mssql "github.com/microsoft/go-mssqldb"
)
// RetryConfig controls retry behavior.
type RetryConfig struct {
MaxAttempts int // Maximum number of attempts (including the first).
BaseDelay time.Duration // Initial delay before the first retry.
MaxDelay time.Duration // Upper bound on delay between retries.
}
// DefaultRetryConfig provides sensible defaults for SQL Server workloads.
var DefaultRetryConfig = RetryConfig{
MaxAttempts: 5,
BaseDelay: 100 * time.Millisecond,
MaxDelay: 10 * time.Second,
}
// RetryFunc executes fn with retries for transient errors.
func RetryFunc(ctx context.Context, cfg RetryConfig, fn func(ctx context.Context) error) error {
var lastErr error
for attempt := 0; attempt < cfg.MaxAttempts; attempt++ {
lastErr = fn(ctx)
if lastErr == nil {
return nil
}
if !isTransient(lastErr) {
return lastErr // Permanent error, don't retry.
}
if attempt == cfg.MaxAttempts-1 {
break // Last attempt, don't sleep.
}
delay := calculateDelay(attempt, cfg.BaseDelay, cfg.MaxDelay)
select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(delay):
}
}
return lastErr
}
func calculateDelay(attempt int, baseDelay, maxDelay time.Duration) time.Duration {
// Exponential backoff: base * 2^attempt
delay := time.Duration(float64(baseDelay) * math.Pow(2, float64(attempt)))
if delay > maxDelay {
delay = maxDelay
}
// Add jitter: +/- 25% to avoid thundering herd
jitter := time.Duration(rand.Int63n(int64(delay) / 2))
return delay/2 + jitter
}
// isTransient classifies retryable SQL Server errors.
// For a fuller example, see "Classify errors as transient or permanent" earlier in this article.
func isTransient(err error) bool {
var mssqlErr mssql.Error
if !errors.As(err, &mssqlErr) {
return errors.Is(err, context.DeadlineExceeded)
}
switch mssqlErr.Number {
case 1205, 40197, 40501, 40613, 49918, 49919, 49920:
return true
}
return mssqlErr.Class >= 17 && mssqlErr.Class <= 19
}
// getEmployeeCount wraps a query with automatic retry.
func getEmployeeCount(ctx context.Context, db *sql.DB) (int, error) {
var count int
err := RetryFunc(ctx, DefaultRetryConfig, func(ctx context.Context) error {
// This query executes on every retry attempt until success or exhaustion.
return db.QueryRowContext(ctx, "SELECT COUNT(*) FROM HumanResources.Employee").Scan(&count)
})
return count, err
}
// Example call site (assumes db is already initialized).
func example(ctx context.Context, db *sql.DB) {
queryCtx, cancel := context.WithTimeout(ctx, 15*time.Second)
defer cancel()
count, err := getEmployeeCount(queryCtx, db)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Employee count: %d\n", count)
}
Zpracování vzájemných zablokování
Patové situace (chyba 1205) jsou nejčastější přechodnou chybou v aplikacích s více uživateli. SQL Server automaticky ukončí jednu z konkurenčních relací a vrátí oběť chybu 1205.
Detekovat patovou situaci
Zkontrolujte, zda se jedná o chybu SQL Serveru způsobenou uváznutím (chyba 1205).
func isDeadlock(err error) bool {
var mssqlErr mssql.Error
if errors.As(err, &mssqlErr) {
return mssqlErr.Number == 1205
}
return false
}
Znovu zkusit transakce po zablokování
Když dojde k zablokování uvnitř transakce, server celou transakci vrátí zpět. Musíte zopakovat celou transakci, nejen neúspěšný příkaz:
func transferInventory(ctx context.Context, db *sql.DB, productID, fromLocationID, toLocationID int, qty int) error {
return RetryFunc(ctx, DefaultRetryConfig, func(ctx context.Context) error {
tx, err := db.BeginTx(ctx, &sql.TxOptions{
Isolation: sql.LevelReadCommitted,
})
if err != nil {
return err
}
defer tx.Rollback()
_, err = tx.ExecContext(ctx,
"UPDATE Production.ProductInventory SET Quantity = Quantity - @qty WHERE ProductID = @pid AND LocationID = @lid",
sql.Named("qty", qty),
sql.Named("pid", productID),
sql.Named("lid", fromLocationID))
if err != nil {
return err
}
_, err = tx.ExecContext(ctx,
"UPDATE Production.ProductInventory SET Quantity = Quantity + @qty WHERE ProductID = @pid AND LocationID = @lid",
sql.Named("qty", qty),
sql.Named("pid", productID),
sql.Named("lid", toLocationID))
if err != nil {
return err
}
return tx.Commit()
})
}
Tip
Snižte patové situace přístupem k tabulkám v konzistentním pořadí napříč všemi transakcemi a udržujte transakce krátké.
Opakování je správná odpověď v aplikačním kódu, ale opakované zablokování na stejném dotazu naznačuje problém v návrhu. Použijte graf uváznutí v SQL Serveru (zachycený prostřednictvím Extended Events nebo relace System Health) k určení vzájemně si konkurujících příkazů a typů zámků. Úplný návod k analýze a prevenci uváznutí viz v průvodci uváznutími. Pro strategie řešení patových situací specifických pro transakce viz Řešení patových bodů.
Řešení vyčerpání fondu připojení
Když jsou všechna spojení v poolu obsazena a MaxOpenConns dosažena, noví volající blokují, dokud se spojení neuvolní nebo dokud nevyprší termín kontextu. Tato situace se projevuje pomalými požadavky nebo chybami způsobenými vypršením časového limitu kontextu, nikoli jako jednoznačné chyby vyčerpání poolu.
Detekovat tlak v bazénu
Sledujte statistiky poolu a upozorňujte, když se počet čekací hodin zvýší.
func monitorPool(ctx context.Context, db *sql.DB) {
ticker := time.NewTicker(10 * time.Second)
defer ticker.Stop()
var lastWaitCount int64
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
stats := db.Stats()
newWaits := stats.WaitCount - lastWaitCount
lastWaitCount = stats.WaitCount
if newWaits > 0 {
log.Printf("Pool pressure: open=%d inUse=%d idle=%d newWaits=%d waitDuration=%v",
stats.OpenConnections, stats.InUse, stats.Idle,
newWaits, stats.WaitDuration)
}
}
}
}
Běžné příčiny a jejich řešení
| Symptom | Příčina | Solution |
|---|---|---|
WaitCount Postupně se zvyšuje |
MaxOpenConns je příliš nízko |
Zvyšte MaxOpenConns, aby odpovídalo úrovni vaší souběžnosti. |
InUse se po delší období rovná MaxOpenConns |
Připojení se nevracejí do fondu připojení | Zavřete *sql.Rows, potvrďte nebo vraťte zpět *sql.Tx a neprodleně zavřete *sql.Conn. |
OpenConnections Stále roste |
K únikům připojení dochází rychleji, než je MaxIdleConns dokáže recyklovat |
Nastavte ConnMaxLifetime a ConnMaxIdleTime, abyste omezili stáří připojení. |
| Termín kontextu překročen během dotazů | Bazén je přesycený a volající čekají příliš dlouho | Zvětšit velikost poolu, zkrátit dobu provádění dotazu nebo přidat časové limity dotazů. |
Řešení specifických chyb SQL Server
Porušení omezení
Porušení unikátního a cizího klíče jsou trvalé chyby, které naznačují logický problém v aplikaci:
func isUniqueViolation(err error) bool {
var mssqlErr mssql.Error
if errors.As(err, &mssqlErr) {
return mssqlErr.Number == 2627 || // Unique constraint violation
mssqlErr.Number == 2601 // Unique index violation
}
return false
}
func isForeignKeyViolation(err error) bool {
var mssqlErr mssql.Error
if errors.As(err, &mssqlErr) {
return mssqlErr.Number == 547 // FK constraint violation
}
return false
}
Upsert vzor s detekcí konfliktů
Použijte MERGE příkaz k atomárnímu vložení nebo aktualizaci řádku:
func upsertDepartment(ctx context.Context, db *sql.DB, id int, name, groupName string) error {
_, err := db.ExecContext(ctx, `
MERGE INTO HumanResources.Department AS target
USING (SELECT @id AS DepartmentID, @name AS Name, @grp AS GroupName) AS source
ON target.DepartmentID = source.DepartmentID
WHEN MATCHED THEN
UPDATE SET Name = source.Name, GroupName = source.GroupName
WHEN NOT MATCHED THEN
INSERT (Name, GroupName) VALUES (source.Name, source.GroupName);`,
sql.Named("id", id),
sql.Named("name", name),
sql.Named("grp", groupName))
return err
}
Chyby oprávnění
Rozpoznat běžné chybové kódy „oprávnění odepřeno“, aby volající dostali jasnou zprávu:
func isPermissionError(err error) bool {
var mssqlErr mssql.Error
if errors.As(err, &mssqlErr) {
return mssqlErr.Number == 229 || // SELECT permission denied
mssqlErr.Number == 230 || // Column permission denied
mssqlErr.Number == 262 || // CREATE permission denied
mssqlErr.Number == 300 || // VIEW permission denied
mssqlErr.Number == 15247 // User doesn't have permission
}
return false
}
Zvládni SQL. ErrNoRows
sql.ErrNoRowsnení chyba SQL Server. Metoda QueryRowContext.Scan jej vrátí, když dotaz nevrátí žádné řádky. Řešte to explicitně, abyste rozlišili "nenalezeno" od skutečných chyb:
func getEmployee(ctx context.Context, db *sql.DB, id int) (*Employee, error) {
var emp Employee
err := db.QueryRowContext(ctx,
"SELECT TOP (1) BusinessEntityID, FirstName + ' ' + LastName AS Name, CountryRegionName AS Location FROM Sales.vSalesPerson WHERE BusinessEntityID = @p1",
sql.Named("p1", id)).Scan(&emp.Id, &emp.Name, &emp.Location)
if errors.Is(err, sql.ErrNoRows) {
return nil, nil // Not found, not an error.
}
if err != nil {
return nil, fmt.Errorf("query employee %d: %w", id, err)
}
return &emp, nil
}
Opatřete chyby kontextem
Přidejte kontext k chybám, aby volající pochopili, kde k selhání došlo:
func getEmployeesByLocation(ctx context.Context, db *sql.DB, location string) ([]Employee, error) {
rows, err := db.QueryContext(ctx,
"SELECT BusinessEntityID, FirstName + ' ' + LastName AS Name, CountryRegionName AS Location FROM Sales.vSalesPerson WHERE CountryRegionName = @p1",
sql.Named("p1", location))
if err != nil {
return nil, fmt.Errorf("query employees by location %q: %w", location, err)
}
defer rows.Close()
var employees []Employee
for rows.Next() {
var emp Employee
if err := rows.Scan(&emp.Id, &emp.Name, &emp.Location); err != nil {
return nil, fmt.Errorf("scan employee row: %w", err)
}
employees = append(employees, emp)
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("iterate employee rows: %w", err)
}
return employees, nil
}
Použití %w zachovává chybový řetězec, takže volající mohou stále používat errors.As a errors.Is kontrolovat základní chybu.
Kontrolní seznam pro zpracování chyb
| Area | Recommendation |
|---|---|
| Kontrolní výraz typu | Deklarujte var mssqlErr mssql.Error, poté použijte errors.As(err, &mssqlErr) k přístupu k chybovým polím SQL Server. |
| Detekce přechodných jevů | Klasifikujte chyby podle počtu a závažnosti, než se rozhodnete, zda to zkusit znovu. |
| Logika opakování | Používejte exponenciální ústup s jitterem. Nastavte maximální počet pokusů a celkový timeout podle kontextu. |
| Deadlocks | Zkuste znovu celou transakci, ne jednotlivé výpisy. Omezte uváznutí konzistentním přístupem k tabulkám. |
| Vyčerpání bazénu | Monitorujte db.Stats() a nastavte časové limity kontextu u všech volání do databáze. |
| ErrNoRows | Zpracovávejte sql.ErrNoRows explicitně pro QueryRowContext. Není to chyba serveru. |
| Chybové obalování | Použijte fmt.Errorf s %w pro přidání kontextu při zachování chybového řetězce. |
| Porušení omezení | Zkontrolujte chybová čísla 2627, 2601 (unikátní) a 547 (cizí klíč), abyste konflikty řešili elegantně. |