2 Achegas 4c3b53349d ... f743a87ded

Autor SHA1 Mensaxe Data
  jys f743a87ded 补充性能测试 hai 9 meses
  jys 7ebc64f7cd 服务码设置并发安全问题处理 hai 9 meses
Modificáronse 5 ficheiros con 97 adicións e 59 borrados
  1. 53 22
      bench_test.go
  2. 16 3
      code.go
  3. 3 3
      errors_test.go
  4. 19 27
      public.go
  5. 6 4
      public_test.go

+ 53 - 22
bench_test.go

@@ -5,34 +5,23 @@ import (
 	"testing"
 )
 
-// 针对于 New、Wrap 两种构建错误的方式进行性能测试对比,结果如下(堆栈深度为10、100、1000):
+// 针对于 New、Wrap 两种构建错误的方式进行性能测试对比,结果如下(堆栈深度为10、100、1000),golang版本1.22:
 // New
 // fserr
-// 3029 ns/op
-// 3788 ns/op
-// 14480 ns/op
-//
-// pkgerr
-// 2982 ns/op
-// 4434 ns/op
-// 15177 ns/op
-//
+// 801 ns/op
+// 1650 ns/op
+// 6262 ns/op
 //
 // Wrap
 // fserr
-// 41.08 ns/op
-// 483.5 ns/op
-// 4722 ns/op
-//
-// pkgerr
-// 38.52 ns/op
-// 585.9 ns/op
-// 5148 ns/op
+// 826.2 ns/op
+// 1623 ns/op
+// 6201 ns/op
 //
-// WithCode性能如下:
-// 4657 ns/op
-// 5143 ns/op
-// 14912 ns/op
+// WithCode
+// 2077 ns/op
+// 2765 ns/op
+// 7628 ns/op
 
 func yesErrors(at, depth int, how func() error) error {
 	if at >= depth {
@@ -45,6 +34,48 @@ func yesErrors(at, depth int, how func() error) error {
 // preventing the compiler from optimising the benchmark functions away.
 var GlobalE interface{}
 
+func BenchmarkNew(b *testing.B) {
+	for _, r := range []int{
+		10,
+		100,
+		1000,
+	} {
+		name := fmt.Sprintf("fserr-stack-%d", r)
+		b.Run(name, func(b *testing.B) {
+			var err error
+			b.ReportAllocs()
+			for i := 0; i < b.N; i++ {
+				err = yesErrors(0, r, func() error {
+					return New("success")
+				})
+			}
+			b.StopTimer()
+			GlobalE = err
+		})
+	}
+}
+
+func BenchmarkWrap(b *testing.B) {
+	for _, r := range []int{
+		10,
+		100,
+		1000,
+	} {
+		name := fmt.Sprintf("fserr-stack-%d", r)
+		b.Run(name, func(b *testing.B) {
+			var err error
+			b.ReportAllocs()
+			for i := 0; i < b.N; i++ {
+				err = yesErrors(0, r, func() error {
+					return Wrap(New("origin"), "success")
+				})
+			}
+			b.StopTimer()
+			GlobalE = err
+		})
+	}
+}
+
 func BenchmarkWithCode(b *testing.B) {
 	NewOK(ErrDb, "success")
 	for _, r := range []int{

+ 16 - 3
code.go

@@ -2,6 +2,7 @@ package fserr
 
 import (
 	"net/http"
+	"sync/atomic"
 )
 
 type codeType interface {
@@ -9,7 +10,19 @@ type codeType interface {
 		uint8 | uint16 | uint32 | uint64 | uint
 }
 
-var serviceCode int
+type atomicServiceCode struct {
+	atomic.Int64
+}
+
+func (a *atomicServiceCode) Load() int {
+	return int(a.Int64.Load())
+}
+
+func (a *atomicServiceCode) Store(v int64) {
+	a.Int64.Store(v)
+}
+
+var serviceCode atomicServiceCode
 
 const (
 	ErrBasic = iota + 1
@@ -37,12 +50,12 @@ type ErrCode struct {
 // 当错误码匹配失败时,提供的备选方案,已内置默认错误码,
 // 它的HTTP码为200,业务码和信息均为零值
 func SetDefault(httpCode, businessCode int, message string) {
-	defaultErrCode = ErrCode{httpCode, serviceCode + businessCode, message}
+	defaultErrCode = ErrCode{httpCode, serviceCode.Load() + businessCode, message}
 }
 
 // NewCode 创建指定信息的错误码
 func NewCode(httpCode, businessCode int, message string) ErrCode {
-	code := ErrCode{httpCode, serviceCode + businessCode, message}
+	code := ErrCode{httpCode, serviceCode.Load() + businessCode, message}
 	register(code)
 	return code
 }

+ 3 - 3
errors_test.go

@@ -56,10 +56,10 @@ func (s *TestErrorsSuite) TestStack() {
 func (s *TestErrorsSuite) TestMessage() {
 	s.Equal(s.originStack, s.originMessage.Cause())
 	s.Equal(s.originStack, s.originMessage.Unwrap())
-	s.Equal("origin message: origin fundamental", s.originMessage.Error())
-	s.Equal("origin message: origin fundamental",
+	s.Equal("origin message", s.originMessage.Error())
+	s.Equal("origin message",
 		fmt.Sprintf("%s", s.originMessage))
-	s.Equal(`"origin message: origin fundamental"`,
+	s.Equal(`"origin message"`,
 		fmt.Sprintf("%q", s.originMessage))
 }
 

+ 19 - 27
public.go

@@ -39,7 +39,7 @@ func Wrap(err error, format string, args ...any) error {
 // WithCode 创建带有错误码的error,支持格式化占位符
 // 使用option可以替换其中信息
 func WithCode[T codeType](err error, businessCode T, options ...Option) error {
-	code := getCode(serviceCode + int(businessCode))
+	code := getCode(serviceCode.Load() + int(businessCode))
 	ret := &withCode{
 		cause:        wrapStack(err),
 		Msg:          code.Message,
@@ -101,20 +101,20 @@ func As(err error, target any) bool { return errors.As(err, target) }
 // 若想得到原始的错误码错误,可以使用As方法
 func ParseCode(err error) *withCode {
 	var target *withCode
-	if !As(err, &target) {
+	if As(err, &target) {
 		return &withCode{
-			cause:        nil,
-			Msg:          err.Error(),
-			HttpCode:     defaultErrCode.HttpCode,
-			BusinessCode: serviceCode,
+			cause:        target.cause,
+			Msg:          outerMsg(err),
+			HttpCode:     target.HttpCode,
+			BusinessCode: target.BusinessCode,
 		}
 	}
 
 	return &withCode{
-		cause:        target.cause,
-		Msg:          outerMsg(err),
-		HttpCode:     target.HttpCode,
-		BusinessCode: target.BusinessCode,
+		cause:        nil,
+		Msg:          err.Error(),
+		HttpCode:     defaultErrCode.HttpCode,
+		BusinessCode: serviceCode.Load(),
 	}
 }
 
@@ -124,14 +124,14 @@ func IsCode[T codeType](err error, code T) bool {
 	if !As(err, &target) {
 		return false
 	}
-	return target.BusinessCode == serviceCode+int(code)
+	return target.BusinessCode == serviceCode.Load()+int(code)
 }
 
 // SetAppCode 设置服务错误码
 // 模块码、模块错误码一共四位,指定应用码将拼接在前
 // 例如应用码位101,模块码为1,模块错误码为21,那么最终业务错误码为:1010121
 func SetAppCode[T codeType](code T) {
-	serviceCode = int(code) * 10000
+	serviceCode.Store(int64(code) * 10000)
 }
 
 // outerMsg 获取最外层的错误信息
@@ -152,21 +152,13 @@ func outerMsg(err error) string {
 }
 
 func wrapStack(err error) error {
-	if _, ok := err.(*fundamental); ok {
-		return err
-	}
-	if _, ok := err.(*withCode); ok {
-		return err
-	}
-	if _, ok := err.(*withMessage); ok {
-		return err
-	}
-	if _, ok := err.(*withStack); ok {
+	switch err.(type) {
+	case *fundamental, *withCode, *withMessage, *withStack:
 		return err
-	}
-
-	return &withStack{
-		error: err,
-		stack: callers(),
+	default:
+		return &withStack{
+			error: err,
+			stack: callers(),
+		}
 	}
 }

+ 6 - 4
public_test.go

@@ -13,7 +13,7 @@ type TestPublicSuite struct {
 	suite.Suite
 	outerErr,
 	newErr, newFmtErr, stackErr,
-	wrapErr, wrapFmtErr, wrapOuterErr, wrapNilErr,
+	wrapErr, wrapFmtErr, wrapOuterErr, wrapNilErr, wrapEmptyErr,
 	codeErr, codeOptionErr, codeOuterErr, codeNilErr error
 	errBasicMsg string
 }
@@ -32,6 +32,7 @@ func (s *TestPublicSuite) SetupTest() {
 	s.wrapFmtErr = Wrap(s.newErr, "wrap %s", "error")
 	s.wrapOuterErr = Wrap(s.outerErr, "wrap error")
 	s.wrapNilErr = Wrap(nil, "wrap error")
+	s.wrapEmptyErr = Wrap(s.newErr, " ")
 
 	// code
 	s.codeErr = WithCode(s.newErr, ErrBasic)
@@ -48,9 +49,10 @@ func (s *TestPublicSuite) TestNew() {
 }
 
 func (s *TestPublicSuite) TestWrap() {
-	s.Equal("wrap error: new error", s.wrapErr.Error())
-	s.Equal("wrap error: new error", s.wrapFmtErr.Error())
-	s.Equal("wrap error: outer error", s.wrapOuterErr.Error())
+	s.Equal("wrap error", s.wrapErr.Error())
+	s.Equal("wrap error", s.wrapFmtErr.Error())
+	s.Equal("wrap error", s.wrapOuterErr.Error())
+	s.Equal("new error", s.wrapEmptyErr.Error())
 	s.Nil(s.wrapNilErr)
 }