feat: add break metrics for sqlx.statement (#3947)

master^2
mongobaba 9 months ago committed by GitHub
parent 88aab8f635
commit 52e5d85221
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -3,6 +3,7 @@ package sqlx
import ( import (
"context" "context"
"database/sql" "database/sql"
"errors"
"time" "time"
"github.com/zeromicro/go-zero/core/breaker" "github.com/zeromicro/go-zero/core/breaker"
@ -70,6 +71,9 @@ func (s statement) ExecCtx(ctx context.Context, args ...any) (result sql.Result,
}, func(err error) bool { }, func(err error) bool {
return s.accept(err) return s.accept(err)
}) })
if errors.Is(err, breaker.ErrServiceUnavailable) {
metricReqErr.Inc("stmt_exec", "breaker")
}
return return
} }
@ -137,7 +141,8 @@ func (s statement) QueryRowsPartialCtx(ctx context.Context, v any, args ...any)
func (s statement) queryRows(ctx context.Context, scanFn func(any, rowsScanner) error, func (s statement) queryRows(ctx context.Context, scanFn func(any, rowsScanner) error,
v any, args ...any) error { v any, args ...any) error {
var scanFailed bool var scanFailed bool
return s.brk.DoWithAcceptable(func() error {
err := s.brk.DoWithAcceptable(func() error {
return queryStmt(ctx, s.stmt, func(rows *sql.Rows) error { return queryStmt(ctx, s.stmt, func(rows *sql.Rows) error {
err := scanFn(v, rows) err := scanFn(v, rows)
if err != nil { if err != nil {
@ -148,6 +153,11 @@ func (s statement) queryRows(ctx context.Context, scanFn func(any, rowsScanner)
}, func(err error) bool { }, func(err error) bool {
return scanFailed || s.accept(err) return scanFailed || s.accept(err)
}) })
if errors.Is(err, breaker.ErrServiceUnavailable) {
metricReqErr.Inc("stmt_queryRows", "breaker")
}
return err
} }
// DisableLog disables logging of sql statements, includes info and slow logs. // DisableLog disables logging of sql statements, includes info and slow logs.

@ -222,7 +222,7 @@ func TestNilGuard(t *testing.T) {
assert.Equal(t, nilGuard{}, guard) assert.Equal(t, nilGuard{}, guard)
} }
func TestStmtScanFailed(t *testing.T) { func TestStmtBreaker(t *testing.T) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) { dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
mock.ExpectPrepare("any") mock.ExpectPrepare("any")
@ -242,6 +242,52 @@ func TestStmtScanFailed(t *testing.T) {
assert.NotErrorIs(t, err, breaker.ErrServiceUnavailable) assert.NotErrorIs(t, err, breaker.ErrServiceUnavailable)
} }
}) })
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
mock.ExpectPrepare("any")
conn := NewSqlConnFromDB(db)
stmt, err := conn.Prepare("any")
assert.NoError(t, err)
for i := 0; i < 1000; i++ {
assert.Error(t, conn.Transact(func(session Session) error {
return nil
}))
}
var breakerTriggered bool
for i := 0; i < 1000; i++ {
_, err = stmt.Exec("any")
if errors.Is(err, breaker.ErrServiceUnavailable) {
breakerTriggered = true
break
}
}
assert.True(t, breakerTriggered)
})
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
mock.ExpectPrepare("any")
conn := NewSqlConnFromDB(db)
stmt, err := conn.Prepare("any")
assert.NoError(t, err)
for i := 0; i < 1000; i++ {
assert.Error(t, conn.Transact(func(session Session) error {
return nil
}))
}
var breakerTriggered bool
for i := 0; i < 1000; i++ {
err = stmt.QueryRows(&struct{}{}, "any")
if errors.Is(err, breaker.ErrServiceUnavailable) {
breakerTriggered = true
break
}
}
assert.True(t, breakerTriggered)
})
} }
type mockedSessionConn struct { type mockedSessionConn struct {

Loading…
Cancel
Save