Hibakezelés és újrapróbálkozási minták go-mssqldb-vel

A Production Go alkalmazásoknak strukturált hibakezelésre van szükségük, hogy megkülönböztessék az átmeneti hibákat, amelyeket újra meg lehet próbálni, és az emberi beavatkozást igénylő tartós hibáktól. Ez a cikk a hibaosztályozást, az újrapróbálási mintákat és a vezetői ellenálló képesség stratégiáit go-mssqldb tárgyalja.

SQL Server hibaszerkezete

Amikor az SQL Server hibát ad vissza, az go-mssqldb illesztőprogram egy mssql.Error struktúrába csomagolja azt. Használjon típusállítást a strukturált hibamezők eléréséhez:

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)
    }
}

Hibamezők

Field Típus Description
Number int32 SQL Server hibaszám. Térképek a sys.messages.
State uint8 Hibaállapot. További kontextust ad ugyanahhoz a hibaszámhoz.
Class uint8 Súlyossági szint (0-25). A 11–16 közötti súlyossági szintek a felhasználó által javíthatók. 17+ súlyosság erőforrás- vagy rendszerproblémákat jelez.
Message string Ember által olvasható hibaüzenet a szerverről.
ServerName string Az SQL Server példány neve, amely a hibát okozta.
ProcName string A hiba bekövetkezett helyén tárolt eljárás vagy függvénynév. Üres ad hoc lekérdezésekhez.
LineNo int32 A sor száma a Transact-SQL (T-SQL) kötetben vagy tárolt eljárásban.

Súlyossági szintek

Súlyossági tartomány Meaning Action
0-10 Tájékoztató üzenetek Nincs hiba. Naplózd, ha szükséges.
11-16 Felhasználó által javítható hibák Javítsd meg a lekérdezést, paramétereket vagy jogosultságokat.
17-19 Erőforráshibák Próbálkozzon újra. Lehet, hogy a szerver terhelés alatt vagy erőforrásai fogynak.
20-25 Végzetes hibák A kapcsolat megszakadt. Csatlakozzon újra, és próbálja újra.

Osztályozza a hibákat átmenetiként vagy állandóként

Átmeneti hibák ideiglenes állapotok, amelyek önmaguktól oldódnak, mint például hálózati zavarok, kapcsolati korlátozások vagy rövid erőforrás-vita. A tartós hibák kód- vagy konfigurációs változtatásokat igényelnek.

Gyakori átmeneti hibaszámok

Használja az alábbi megosztott katalógust az átmeneti kapcsolat-létrehozás és kérés-út szállítási hibák kanonikus listájáként:

A következő hibák átmenetiek, amikor a kapcsolat létrehozásakor vagy a kiszolgálónak küldött kérések során fordulnak elő. Próbálkozzon újra rövid, korlátozott várakozási idő után. A néhány újrapróbálkozást követő hibák általában olyan konfigurációs problémát jeleznek (hibás kiszolgáló, hiányzó engedélyek, kimerült kvóta), amely az újrapróbálkozást nem oldja meg.

Error Message Hibaelhárítás
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.) A TCP-kapcsolat kézfogás közben megszakad. Ez nem hitelesítési hiba. Ha továbbra is fennáll, ellenőrizze az ügyféloldali hálózati instabilitást, vagy egy olyan köztes eszközt, amely megszakítja a félig létrehozott kapcsolatokat.
233 The client was unable to establish a connection because of an error during connection initialization process before login. Bejelentkezés előtti átvitel vagy TLS-hiba. A kiszolgáló általában akkor adja vissza, ha nem tudja elfogadni a kapcsolatot (erőforrás-kimerültség, elért maximális kapcsolatok vagy nem támogatott ügyfél). Ez nem hitelesítési hiba. Ellenőrizze a kiszolgáló állapotát, majd ellenőrizze az ügyfél bejelentkezési időtúllépését, a TLS-beállításokat és az ügyfél/kiszolgáló TLS-verziókompatibilitását.
4060 Cannot open database "%.*ls" requested by the login. The login failed. A bejelentkezés hitelesít, de nem tudja megnyitni a kért adatbázist. Az átmeneti okok közé tartozik, ha az adatbázis átmeneti állapotban van (feladatátvétel, visszaállítás, skálázás miatt), vagy automatikusan szüneteltetve van. Az állandó okok (az adatbázis nem létezik, a bejelentkezés nem rendelkezik hozzáféréssel) nem lesznek javítva újrapróbálkozással; ellenőrizze az adatbázis nevét, a bejelentkezési leképezést és az adatbázis állapotát.
4221 Login to read-secondary failed due to long wait on 'HADR_DATABASE_WAIT_FOR_TRANSITION_TO_VERSIONING'. A replika nem érhető el bejelentkezéshez, mert a sorverziók hiányoznak a replika újrafeldolgozásakor repülés közben végrehajtott tranzakciókhoz. A probléma megoldásához vonja vissza vagy véglegesítse az aktív tranzakciókat az elsődleges kiszolgálón. Csökkentse ezt azzal, hogy kerüli a hosszú írási tranzakciókat az elsődleges csomóponton.
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.) A helyi oldal megszakítja a kapcsolatot. Ellenőrizze az ügyféloldali hálózat állapotát, valamint a helyi tűzfal vagy VPN-ügyfél állapotát.
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.) A távoli oldal TCP-alaphelyzetbe állítást küld. Gyakori okok: a partnerfolyamat összeomlott, egy tűzfal resetet küldött, vagy az Azure SQL-átjáró lezárt egy inaktív kapcsolatot. Tétlenség miatti kapcsolatbontási minták esetén engedélyezze a TCP keepalive funkciót az ügyféloldalon, vagy csökkentse a kapcsolatpool tétlenségi időkorlátját.
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. Az adatbázis túllépi a Azure SQL erőforrás-szabályozási korlátot. Az 1. erőforrás-azonosító a feldolgozói korlátot jelzi; A 2. erőforrás-azonosító a munkamenetkorlátot jelzi. Azonosítsa az üzenet korláttípusát, majd csökkentse az egyidejűséget, skálázza fel az adatbázist, vagy lerövidítse az erőforrást tartalmazó hosszú ideig futó műveleteket.
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. Az adatbázis meghaladja a számára garantált minimumot, és a mögöttes kiszolgáló korlátozza a teljesítményét. Az újrapróbálkozás általában akkor sikeres, ha a szomszéd terhelés csökken. A tartós előfordulások azt jelzik, hogy magasabb szolgáltatási szintre vagy kevésbé zajos környezetre van szüksége.
40020, 40143, 40166, 40540 Error code %d A 40197-es hibahelyen jelentve a feladatátvétel során. A 40197-es feladatátvételi üzenetbe beágyazott alkódok, amelyek egyes útvonalakon legfelső szintű hibaszámként jelennek meg. Kezelje őket ugyanúgy, mint a 40197-et.
40197 The service has encountered an error processing your request. Please try again. Error code %d. Szoftverfrissítés, hardverhiba vagy más feladatátvételi esemény az Azure SQL-ben. Az újracsatlakozás egy működőképes replikához kapcsol. A beágyazott hibakód azonosítja a feladatátvétel típusát. Ha a hiba továbbra is fennáll, rögzítse a munkamenet-nyomkövetés azonosítóját, és forduljon az ügyfélszolgálathoz.
40501 The service is currently busy. Retry the request after 10 seconds. Incident ID: %ls. Code: %d. Azure SQL motor szabályozása. Az ajánlott minimális várakozási idő 10 másodperc. A tartós korlátozás azt jelzi, hogy a munkaterhelés túllépte az adatbázis erőforrás-kiosztását; növelje a szolgáltatási szintet, vagy csökkentse a párhuzamosságot.
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'. Az adatbázis nem érhető el, általában feladatátvétel alatt vagy rövid ideig egy skálázási művelet közben. Próbálkozzon újra egy visszalépéssel; ha néhány perc elteltével is megmarad, rögzítse a munkamenet nyomkövetési azonosítóját, és nyisson meg egy támogatási esetet.
42108 Can not connect to the SQL pool since it is paused. Please resume the SQL pool and try again. A dedikált SQL-készlet (Synapse) szüneteltetett állapotban van. Az újrapróbálás csak a készlet újraindítása után sikerül. Állítsa vissza explicit módon a készletet működő állapotba, vagy ütemezze a munkaterhelés futtatását a készlet folytatását követő időpontra.
42109 The SQL pool is warming up. Please try again. A dedikált SQL-készlet újra működik. Próbálkozzon újra egyre hosszabb várakozási időkkel, amíg a pool online nem lesz; a bemelegítés általában néhány percet vesz igénybe.
49918 Cannot process request. Not enough resources to process request. The service is currently busy. Please retry the request later. A kiszolgáló jelenleg nem tud elegendő erőforrást lefoglalni a kérés teljesítéséhez. Próbálkozzon újra egy visszalépéssel. Ha a hiba továbbra is fennáll, skálázza fel az adatbázist vagy a rugalmas készletet.
49919 Cannot process create or update request. Too many create or update operations in progress for subscription "%ld". Előfizetési szintű egyidejűségi korlát a felügyeleti műveletekre. Csökkentse a párhuzamos létrehozási vagy frissítési hívások számát, vagy időben tolja el őket.
49920 Cannot process request. Too many operations in progress for subscription "%ld". Előfizetésszintű egyidejűségi korlát a repülési műveletekre. Csökkentse a párhuzamosságot, vagy várja meg, amíg a folyamatban lévő műveletek befejeződnek.

Az utasításszintű hibák nem szerepelnek ezen a listán, mert a kapcsolat létrejötte után jelentkeznek, és a hiba ellenére a munkamenet továbbra is használható marad. A leggyakoribb újrapróbálkozható utasításhibák az 1205 (holtpont áldozata) és az 1222 (zárolási kérelem időtúllépése). Az egyetlen sikertelen utasítás helyett próbálkozzon újra a teljes tranzakcióval.

A hibaüzenet szövege Azure SQL átmeneti kapcsolati hibákból származik. Az egyes illesztőprogramok saját beépített újrapróbálkozási listákat tartanak fenn; ez a katalógus azt írja le, hogy mely hibák alkalmasak az újrapróbálkozásra az SQL Server, az Azure SQL Database, az Azure SQL Managed Instance, a Microsoft Fabric SQL-adatbázisa és az Azure Synapse Analytics dedikált SQL-készletei esetén.

A következő isTransient függvény egy Go megvalósítási mintát mutat az újrapróbálkozás osztályozásához. Kezeld a fenti megosztott katalógust az igazság forrásának, és tartsd a kódkeresésed összhangban vele.

// 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
}

Ha konfigurációs és kvótahibákat tapasztalsz, javítsd meg az alapkapacitást, adatbázist vagy hálózati konfigurációt, mielőtt újra próbálnád. Ide sorolhatóak például a következők:

  • 40544 (adatbázis méretkvóta)
  • 4060 (nem lehet adatbázist nyitni)
  • 40615 (tűzfal szabály)

Hálózati hibák észlelése

A hálózati szintű hibák nem hoznak mssql.Error létre értékeket. Ellenőrizd a Go hálózati gyakori hibatípusokat:

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
}

Újrapróbálkozás megvalósítása exponenciális késleltetéssel

Átmeneti hibák esetén próbálkozzon újra, az egyes próbálkozások között növekvő várakozási idővel. Ez a megközelítés időt ad a szervernek a helyreállításra, és elkerüli, hogy gyors próbálkozásokkal túlterhelje azt.

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)
}

Holtpontok kezelése

A holtpontok (hiba 1205) a leggyakoribb átmeneti hibák többfelhasználós alkalmazásokban. Az SQL Server automatikusan megszakítja az egyik ütköző munkamenetet, és a 1205-ös hibát adja vissza az áldozatként kijelölt munkamenetnek.

Patthelyzet észlelése

Ellenőrizd, hogy egy SQL Server hiba holtpont-e (hiba 1205).

func isDeadlock(err error) bool {
    var mssqlErr mssql.Error
    if errors.As(err, &mssqlErr) {
        return mssqlErr.Number == 1205
    }
    return false
}

Tranzakciók újrapróbálása holtpontok után

Ha holtpont keletkezik egy tranzakción belül, a szerver visszahúzza az egész tranzakciót. Újra kell próbálnod a teljes tranzakciót, nem csak a sikertelen kijelentést:

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

Csökkentse a holtpontokat azzal, hogy minden tranzakció során következetes sorrendben ér el a táblákat, és röviden tartja a tranzakciókat.

Az újrapróbálás helyes válasz az alkalmazáskódban, de ugyanazon lekérdezésen ismételt holtpont esetén tervezési problémát jeleznek. Használd az SQL Server holtpontgráfját (amelyet az Extended Events vagy a system_health munkamenet rögzít) a versengő utasítások és zárolási típusok azonosításához. A holthelyzetek elemzésének és megelőzésének teljes áttekintéséért lásd a Deadlocks útmutatót. A tranzakciókra jellemző holthelyzetkezelési stratégiákról lásd: Deadlock kezelés.

A kapcsolatkészlet kimerülésének kezelése

Amikor a poolban lévő összes kapcsolat használatban van, és elérik a MaxOpenConns értéket, az új hívók várakoznak, amíg egy kapcsolat elérhetővé nem válik, vagy le nem jár a kontextus határideje. Ez a helyzet lassú kérések vagy kontextus-határidő túllépési hibák formájában nyilvánul meg, nem pedig közvetlen poolkimerülési hibákként.

Medencenyomás érzékelése

Figyeld a pool statisztikákat, és jelezd, ha nő a várakakozási szám.

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)
            }
        }
    }
}

Gyakori okok és megoldások

Hibajelenség A probléma oka Solution
WaitCount folyamatosan növekszik MaxOpenConns túl alacsony Növeld MaxOpenConns értékét, hogy megfeleljen az egyidejűségnek.
InUse hosszabb ideig egyenlő MaxOpenConns értékével A csatlakozások nem kerülnek vissza a medencébe Zárd be *sql.Rows-t, hajtsd végre vagy vond vissza *sql.Tx-et, és azonnal zárd be *sql.Conn-t.
OpenConnections Folyamatosan nőnek A kapcsolatok gyorsabban szivárognak el, mint ahogy a MaxIdleConns újra tudná hasznosítani őket. Állítsa be a ConnMaxLifetime és ConnMaxIdleTime értékét a kapcsolat élettartamának korlátozásához.
A kontextus határideje túllépett a lekérdezések során A medence telített, és a hívók túl sokáig várnak Növelje a pool méretét, csökkentse a lekérdezés végrehajtási idejét, vagy adjon hozzá lekérdezési időtúlolásokat.

Konkrét SQL Server-hibák kezelése

Korlátozások megsértése

Az egyedi kulcs- és idegen kulcsok megsértése tartós hibák, amelyek logikai problémát jeleznek az alkalmazásban:

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
}

Felfelé irányuló minta konfliktusészleléssel

Használj utasítást MERGE egy sor atomiszerű beillesztésére vagy frissítésére:

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
}

Jogosultsági hibák

Azonosítsa a gyakori engedély nélküli hibaszámokat, hogy világos üzenetet biztosítson a hívóknak:

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
}

Kezeld a(z) sql.ErrNoRows hibát.

sql.ErrNoRowsnem SQL Server hiba. A QueryRowContext.Scan metódus akkor adja vissza, ha a lekérdezés nem ad sorokat. Kezeld ezt kifejezetten úgy, hogy meg lehessen különböztetni a „not found” állapotot a valódi hibáktól:

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
}

Csomagolási hibák kontextussal

Adjon kontextust a hibákhoz, hogy a hívók megértsék, hol történt a hiba:

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
}

A(z) %w használata megőrzi a hibaláncot, így a hívók továbbra is használhatják a(z) errors.As és errors.Is elemeket a mögöttes hiba ellenőrzésére.

Hibakezelési ellenőrzőlista

Area Recommendation
Típusassertálás Deklaráld var mssqlErr mssql.Error, majd használd errors.As(err, &mssqlErr) az SQL Server hibamezők eléréséhez.
Átmeneti észlelés Sorolj hibákat szám és súlyosság szerint, mielőtt eldöntenéd, hogy újra próbálkoznak-e.
Újrapróbálkozás logikája Exponenciális visszalépés használata jitterrel. Állíts be maximális próbálkozásszámot és az általános időkorlátot a kontextus alapján.
Deadlocks Próbáld meg újra az egész tranzakciót, nem az egyéni kimutatásokat. Csökkentsd a holtpontokat azzal, hogy következetesen hozzáférsz a táblákhoz.
Medence kimerülése Figyeld db.Stats() és állíts be kontextushatárokat minden adatbázis-hívásnál.
ErrNoRows A(z) QueryRowContext esetén kezelje explicit módon a(z) sql.ErrNoRows elemet. Ez nem szerverhiba.
Hibacsomagolás Használd a fmt.Errorf elemet a %w elemmel együtt, hogy kontextust adj hozzá, miközben megőrzöd a hibaláncot.
Korlátozások megsértése Ellenőrizze a 2627-es, 2601-es (egyedi) és 547-es (idegen kulcs) hibaszámokat, hogy gondosan kezelje az ütközéseket.