Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Aplikasi Production Go memerlukan penanganan kesalahan terstruktur untuk membedakan antara kegagalan sementara yang dapat Anda coba lagi dan kesalahan permanen yang memerlukan intervensi manusia. Artikel ini membahas klasifikasi kesalahan, pola percobaan ulang, dan strategi ketahanan untuk driver go-mssqldb.
Struktur kesalahan SQL Server
Saat SQL Server mengembalikan kesalahan, go-mssqldb driver membungkusnya dalam mssql.Error struktur. Gunakan pernyataan jenis untuk mengakses bidang kesalahan terstruktur:
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)
}
}
Kolom kesalahan
| Ladang | Type | Description |
|---|---|---|
Number |
int32 |
Nomor kesalahan SQL Server. Dipetakan ke sys.messages. |
State |
uint8 |
Status kesalahan. Memberikan konteks tambahan untuk nomor kesalahan yang sama. |
Class |
uint8 |
Tingkat keparahan (0-25). Tingkat keparahan 11-16 dapat diperbaiki oleh pengguna. Tingkat keparahan 17+ menunjukkan masalah sumber daya atau sistem. |
Message |
string |
Teks kesalahan yang dapat dibaca manusia dari server. |
ServerName |
string |
Nama instans SQL Server yang memunculkan kesalahan. |
ProcName |
string |
Prosedur tersimpan atau nama fungsi tempat kesalahan terjadi. Kosongkan untuk kueri ad hoc. |
LineNo |
int32 |
Nomor baris dalam batch Transact-SQL (T-SQL) atau prosedur tersimpan. |
Tingkat keparahan
| Rentang keparahan | Meaning | Action |
|---|---|---|
| 0-10 | Pesan informasi | Tidak ada kesalahan. Catat jika bermanfaat. |
| 11-16 | Kesalahan yang dapat diperbaiki pengguna | Perbaiki kueri, parameter, atau izin. |
| 17-19 | Kesalahan sumber daya | Coba lagi. Server mungkin berada di bawah beban atau kehabisan sumber daya. |
| 20-25 | Kesalahan fatal | Koneksinya rusak. Sambungkan kembali dan coba lagi. |
Mengklasifikasikan kesalahan sebagai sementara atau permanen
Kesalahan sementara adalah kondisi sementara yang diselesaikan dengan sendirinya, seperti gangguan jaringan, pembatasan koneksi, atau perselisihan sumber daya singkat. Kesalahan permanen memerlukan perubahan kode atau konfigurasi.
Nomor kesalahan transien umum
Gunakan katalog bersama berikut sebagai daftar kanonis kesalahan pembuatan koneksi sementara dan transportasi jalur permintaan:
Kesalahan berikut bersifat sementara ketika terjadi selama pembentukan koneksi atau saat mengirim permintaan ke server. Coba lagi setelah penundaan singkat yang dibatasi. Kesalahan yang bertahan melewati beberapa percobaan ulang biasanya menunjukkan masalah konfigurasi (server yang salah, izin yang hilang, kuota habis) yang coba lagi tidak akan diperbaiki.
| Kesalahan | 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.) |
Koneksi TCP terputus di tengah proses handshake. Bukan kegagalan kredensial. Jika berlanjut, periksa ketidakstabilan jaringan sisi klien atau perangkat perantara yang menghilangkan koneksi yang setengah dibuat. |
233 |
The client was unable to establish a connection because of an error during connection initialization process before login. |
Transportasi pra-login atau kegagalan TLS. Server biasanya mengembalikannya ketika tidak dapat menerima koneksi (kelelahan sumber daya, koneksi maks tercapai, atau klien yang tidak didukung). Bukan kegagalan kredensial. Verifikasi kesehatan server, lalu periksa batas waktu masuk klien, pengaturan TLS, dan kompatibilitas versi TLS klien/server. |
4060 |
Cannot open database "%.*ls" requested by the login. The login failed. |
Login mengautentikasi tetapi tidak dapat membuka database yang diminta. Penyebab sementara antara lain database sedang dalam transisi (failover, pemulihan, penskalaan) atau dijeda secara otomatis. Penyebab yang bersifat permanen (database tidak ada, login tidak memiliki akses) tidak akan teratasi dengan mencoba lagi; periksa nama database, pemetaan login, dan status database. |
4221 |
Login to read-secondary failed due to long wait on 'HADR_DATABASE_WAIT_FOR_TRANSITION_TO_VERSIONING'. |
Replika tidak tersedia untuk masuk karena versi baris hilang untuk transaksi yang sedang dalam penerbangan saat replika didaur ulang. Batalkan atau komitkan transaksi yang aktif di server primer untuk menyelesaikan masalah. Kurangi risikonya dengan menghindari transaksi penulisan yang berlangsung lama pada node utama. |
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.) |
Pihak lokal memutuskan koneksi. Periksa kesehatan jaringan sisi klien dan firewall lokal atau klien VPN apa pun. |
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.) |
Sisi jarak jauh mengirimkan reset TCP. Penyebab umum: proses peer mengalami kegagalan, firewall mengirim reset, atau gateway Azure SQL menutup koneksi yang tidak aktif. Untuk pola reset saat idle, aktifkan TCP keepalive pada klien atau perpendek waktu tunggu idle pada pool koneksi. |
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. |
Database melebihi batas tata kelola sumber daya Azure SQL. ID Sumber Daya 1 menunjukkan batas pekerja; ID Sumber Daya 2 menunjukkan batas sesi. Identifikasi jenis batas berdasarkan pesan, lalu kurangi tingkat konkurensi, tingkatkan kapasitas database, atau persingkat operasi berjalan lama yang menggunakan sumber daya tersebut terlalu lama. |
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. |
Database telah melampaui batas jaminan minimumnya, dan server yang mendasarinya sedang mengalami pembatasan. Percobaan ulang biasanya berhasil saat beban pada node tetangga menurun. Kejadian berkelanjutan menunjukkan Bahwa Anda memerlukan tingkat layanan yang lebih tinggi atau lingkungan yang kurang bising. |
40020,40143,40166,40540 |
Dilaporkan pada slot Error code %d pada kesalahan 40197 selama failover. |
Sub-kode yang disematkan dalam pesan failover 40197 bahwa beberapa jalur muncul sebagai nomor kesalahan tingkat atas. Perlakukan mereka sama seperti 40197. |
40197 |
The service has encountered an error processing your request. Please try again. Error code %d. |
Peningkatan perangkat lunak, kegagalan perangkat keras, atau peristiwa failover lainnya di Azure SQL. Penyambungan ulang akan mengarahkan Anda ke replika yang sehat. Kode kesalahan yang disematkan mengidentifikasi jenis failover. Jika kesalahan berlanjut, ambil ID pelacakan sesi dan hubungi dukungan. |
40501 |
The service is currently busy. Retry the request after 10 seconds. Incident ID: %ls. Code: %d. |
Pelambatan mesin Azure SQL. Batas minimum yang direkomendasikan adalah backoff 10 detik. Throttling yang berkelanjutan menunjukkan bahwa beban kerja telah melebihi alokasi sumber daya database; tingkatkan tingkat layanan atau kurangi tingkat konkurensi. |
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'. |
Database tidak tersedia, biasanya saat failover sedang berlangsung atau sebentar selama operasi penskalaan. Coba lagi pada backoff; jika bertahan beberapa menit, ambil ID pelacakan sesi dan buka kasus dukungan. |
42108 |
Can not connect to the SQL pool since it is paused. Please resume the SQL pool and try again. |
Kumpulan SQL terdedikasi (Synapse) berada dalam status dijeda. Percobaan ulang berhasil hanya setelah pool dilanjutkan kembali. Aktifkan kembali pool secara eksplisit, atau jadwalkan beban kerja agar berjalan setelah pool diaktifkan kembali. |
42109 |
The SQL pool is warming up. Please try again. |
Pool SQL khusus sedang dilanjutkan. Coba kembali dengan jeda bertahap hingga pool aktif; inisialisasi awal biasanya memerlukan beberapa menit. |
49918 |
Cannot process request. Not enough resources to process request. The service is currently busy. Please retry the request later. |
Server saat ini tidak dapat mengalokasikan sumber daya yang cukup untuk memenuhi permintaan. Coba lagi pada backoff. Jika kesalahan berlanjut, tingkatkan database atau kumpulan elastis. |
49919 |
Cannot process create or update request. Too many create or update operations in progress for subscription "%ld". |
Batas konkurensi pada tingkat langganan untuk operasi manajemen. Kurangi panggilan pembuatan/pembaruan secara paralel atau beri jeda di antaranya. |
49920 |
Cannot process request. Too many operations in progress for subscription "%ld". |
Batas konkurensi tingkat langganan pada operasi dalam penerbangan. Kurangi paralelisme atau tunggu hingga operasi dalam penerbangan terkuras. |
Kesalahan pada tingkat pernyataan tidak ada dalam daftar ini karena kesalahan tersebut terjadi setelah koneksi terjalin dan kegagalan tersebut membuat sesi tetap dapat digunakan. Kesalahan pernyataan yang dapat dicoba ulang yang paling umum adalah 1205 (korban deadlock) dan 1222 (batas waktu permintaan penguncian). Ulangi seluruh transaksi alih-alih satu pernyataan yang gagal.
Teks pesan kesalahan berasal dari kesalahan koneksi sementara Azure SQL. Driver individual mempertahankan daftar coba lagi bawaan mereka sendiri; katalog ini menjelaskan kesalahan mana yang memenuhi syarat untuk mencoba kembali di SQL Server, Azure SQL Database, Azure SQL Managed Instance, database SQL di Microsoft Fabric, dan kumpulan SQL khusus di Azure Synapse Analytics.
Fungsi berikut isTransient menunjukkan salah satu pola implementasi di Go untuk klasifikasi percobaan ulang. Perlakukan katalog bersama di atas sebagai sumber kebenaran, dan jaga agar pencarian kode Anda selaras dengannya.
// 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
}
Jika Anda mengalami kesalahan konfigurasi dan kuota, perbaiki kapasitas, database, atau konfigurasi jaringan yang mendasarinya sebelum mencoba lagi. Contohnya meliputi:
-
40544(kuota ukuran database) -
4060(tidak dapat membuka database) -
40615(aturan firewall)
Mendeteksi kesalahan jaringan
Kesalahan tingkat jaringan tidak menghasilkan mssql.Error nilai. Periksa jenis error jaringan Go yang umum:
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
}
Menerapkan percobaan ulang dengan backoff eksponensial
Coba lagi jika terjadi kesalahan sementara, dengan jeda yang semakin lama di antara setiap percobaan. Pendekatan ini memberi waktu bagi server untuk pulih dan mencegah server kewalahan akibat percobaan ulang yang terlalu cepat.
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)
}
Menangani kebuntuan
Deadlock (kesalahan 1205) adalah kesalahan sementara yang paling umum dalam aplikasi multi-pengguna. SQL Server secara otomatis mengakhiri salah satu sesi yang bersaing dan mengembalikan kesalahan 1205 ke korban.
Deteksi kebuntuan
Periksa apakah kesalahan SQL Server adalah kebuntuan (kesalahan 1205).
func isDeadlock(err error) bool {
var mssqlErr mssql.Error
if errors.As(err, &mssqlErr) {
return mssqlErr.Number == 1205
}
return false
}
Coba lagi transaksi setelah kebuntuan
Ketika kebuntuan terjadi di dalam transaksi, server mengembalikan seluruh transaksi. Anda harus mencoba kembali transaksi lengkap, bukan hanya laporan yang gagal:
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
Kurangi kebuntuan dengan mengakses tabel dalam urutan yang konsisten di semua transaksi dan menjaga transaksi tetap singkat.
Mencoba ulang adalah respons yang benar dalam kode aplikasi, tetapi kebuntuan berulang pada kueri yang sama menunjukkan masalah desain. Gunakan grafik deadlock SQL Server (yang ditangkap melalui Extended Events atau sesi system health) untuk mengidentifikasi pernyataan yang berkonflik dan jenis penguncian. Untuk panduan lengkap tentang analisis dan pencegahan deadlock, lihat Panduan Deadlock. Untuk strategi penanganan kebuntuan khusus untuk transaksi, lihat Penanganan kebuntuan.
Menangani kelelahan kolam koneksi
Ketika semua koneksi dalam kumpulan sedang digunakan dan MaxOpenConns tercapai, pemanggil baru memblokir hingga koneksi tersedia atau tenggat waktu konteks berakhir. Situasi ini bermanifestasi sebagai permintaan lambat atau kesalahan tenggat waktu konteks, bukan sebagai kesalahan kelelahan kumpulan eksplisit.
Mendeteksi tekanan kolam
Pantau statistik kumpulan dan peringatkan saat jumlah tunggu meningkat.
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)
}
}
}
}
Penyebab Umum dan Solusi
| Gejala | Penyebab | Solusi |
|---|---|---|
WaitCount meningkat terus |
MaxOpenConns terlalu rendah |
Tingkatkan MaxOpenConns untuk menyesuaikan dengan tingkat konkurensi Anda. |
InUse sama dengan MaxOpenConns untuk jangka waktu yang lama |
Koneksi tidak dikembalikan ke pool | Tutup *sql.Rows, lakukan commit atau batalkan *sql.Tx, dan tutup *sql.Conn segera. |
OpenConnections terus tumbuh |
Koneksi bocor lebih cepat daripada MaxIdleConns bisa mendaur ulang |
Atur ConnMaxLifetime dan ConnMaxIdleTime untuk membatasi usia koneksi. |
| Batas waktu konteks terlampaui saat menjalankan kueri | Kolam renang jenuh dan penelepon menunggu terlalu lama | Tingkatkan ukuran kumpulan, kurangi waktu eksekusi kueri, atau tambahkan batas waktu kueri. |
Menangani kesalahan SQL Server tertentu
Pelanggaran batasan
Pelanggaran kunci unik dan kunci asing adalah kesalahan permanen yang menunjukkan masalah logika dalam aplikasi:
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
}
Pola upsert dengan deteksi konflik
Gunakan MERGE pernyataan untuk menyisipkan atau memperbarui baris secara atomis:
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
}
Kesalahan Hak Akses
Mendeteksi nomor kesalahan umum yang ditolak izin untuk memberikan pesan yang jelas kepada penelepon:
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
}
Tangani sql.ErrNoRows
sql.ErrNoRowsbukan kesalahan SQL Server. Metode QueryRowContext.Scan mengembalikan nilai tersebut saat kueri tidak menghasilkan baris apa pun. Tangani hal ini secara eksplisit untuk membedakan "tidak ditemukan" dari kesalahan yang sebenarnya:
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
}
Membungkus kesalahan dengan konteks
Tambahkan konteks ke kesalahan sehingga pemanggil dapat memahami di mana kegagalan terjadi:
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
}
Menggunakan %w mempertahankan rantai kesalahan sehingga pemanggil masih dapat menggunakan errors.As dan errors.Is memeriksa kesalahan yang mendasarinya.
Daftar periksa penanganan kesalahan
| Area | Recommendation |
|---|---|
| Pernyataan jenis | Deklarasikan var mssqlErr mssql.Error, lalu gunakan errors.As(err, &mssqlErr) untuk mengakses bidang kesalahan SQL Server. |
| Deteksi sementara | Klasifikasikan kesalahan berdasarkan jumlah dan tingkat keparahan sebelum memutuskan apakah akan mencoba lagi. |
| Logika percobaan ulang | Gunakan backoff eksponensial dengan jitter. Tetapkan jumlah percobaan maksimum dan batas waktu keseluruhan melalui konteks. |
| Kebuntuan | Coba lagi seluruh transaksi, bukan laporan individual. Kurangi kebuntuan dengan mengakses tabel secara konsisten. |
| Kehabisan pool | Pantau db.Stats() dan tetapkan tenggat waktu konteks pada semua panggilan database. |
| ErrNoRows | Menangani sql.ErrNoRows secara eksplisit untuk QueryRowContext. Ini bukan kesalahan server. |
| Pembungkusan kesalahan | Gunakan fmt.Errorf dengan %w untuk menambahkan konteks sambil mempertahankan rantai kesalahan. |
| Pelanggaran batasan | Periksa nomor kesalahan 2627, 2601 (unik), dan 547 (kunci asing) untuk menangani konflik dengan baik. |