diff --git a/go.mod b/go.mod index 9225c4b..f9ec4f5 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ module github.com/kozmod/oniontx -go 1.20 +go 1.23 diff --git a/saga/track.go b/saga/track.go index 3953c4a..3b9af8d 100644 --- a/saga/track.go +++ b/saga/track.go @@ -2,6 +2,7 @@ package saga import ( "fmt" + "slices" "strings" "sync" ) @@ -48,15 +49,15 @@ type ExecutionData struct { } // String returns a compact representation of ExecutionData. -func (t ExecutionData) String() string { +func (ed ExecutionData) String() string { var builder strings.Builder - builder.WriteString(fmt.Sprintf("{Status: %s, Calls: %d", t.Status, t.Calls)) - if len(t.Errors) > 0 { - builder.WriteString(fmt.Sprintf(", Errors: %d", len(t.Errors))) + builder.WriteString(fmt.Sprintf("{Status: %s, Calls: %d", ed.Status, ed.Calls)) + if len(ed.Errors) > 0 { + builder.WriteString(fmt.Sprintf(", Errors: %d", len(ed.Errors))) // @TODO: add errors output - //if len(t.Errors) == 1 { - // builder.WriteString(fmt.Sprintf(" [%v]", t.Errors[0])) + //if len(ed.Errors) == 1 { + // builder.WriteString(fmt.Sprintf(" [%v]", ed.Errors[0])) //} } @@ -65,13 +66,11 @@ func (t ExecutionData) String() string { } // Clone creates a deep copy of ExecutionData. -func (t ExecutionData) Clone() ExecutionData { - errors := make([]error, len(t.Errors)) - copy(errors, t.Errors) +func (ed ExecutionData) Clone() ExecutionData { return ExecutionData{ - Calls: t.Calls, - Errors: errors, - Status: t.Status, + Calls: ed.Calls, + Errors: slices.Clone(ed.Errors), + Status: ed.Status, } } diff --git a/test/bech/clone_test.go b/test/bech/clone_test.go new file mode 100644 index 0000000..c22add4 --- /dev/null +++ b/test/bech/clone_test.go @@ -0,0 +1,157 @@ +package bech + +import ( + "fmt" + "math/rand/v2" + "slices" + "testing" + "time" + + "github.com/kozmod/oniontx/saga" +) + +func Benchmark_copy(b *testing.B) { + const ( + letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + errorsLen = 100 + ) + var ( + generator = rand.New(rand.NewPCG(uint64(time.Now().UnixNano()), uint64(time.Now().UnixNano()))) + genFn = func() saga.ExecutionData { + errors := make([]error, errorsLen) + for i := 0; i < errorsLen; i++ { + b := make([]byte, 10) + for i := range b { + b[i] = letters[generator.IntN(len(letters))] + } + errors[i] = fmt.Errorf("%s", string(b)) + } + + return saga.ExecutionData{ + Calls: generator.Uint32(), + Errors: errors, + Status: saga.ExecutionStatusSuccess, + } + } + + slicesClone = func(in saga.ExecutionData) saga.ExecutionData { + return saga.ExecutionData{ + Calls: in.Calls, + Errors: slices.Clone(in.Errors), + Status: in.Status, + } + } + + oldCopy = func(in saga.ExecutionData) saga.ExecutionData { + errors := make([]error, len(in.Errors)) + copy(errors, in.Errors) + return saga.ExecutionData{ + Calls: in.Calls, + Errors: errors, + Status: in.Status, + } + } + ) + + b.Run(fmt.Sprintf("size_%d", errorsLen), func(b *testing.B) { + b.Run("copy", func(b *testing.B) { + var ( + in = genFn() + res saga.ExecutionData + ) + + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + res = oldCopy(in) + } + + _ = res + }) + + b.Run("slices.Clone", func(b *testing.B) { + var ( + in = genFn() + res saga.ExecutionData + ) + + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + res = slicesClone(in) + } + + _ = res + }) + }) + + b.Run("empty", func(b *testing.B) { + b.Run("copy", func(b *testing.B) { + var ( + in = genFn() + res saga.ExecutionData + ) + + b.ResetTimer() + b.ReportAllocs() + res.Errors = nil + for i := 0; i < b.N; i++ { + res = oldCopy(in) + } + + _ = res + }) + + b.Run("slices.Clone", func(b *testing.B) { + var ( + in = genFn() + res saga.ExecutionData + ) + + b.ResetTimer() + b.ReportAllocs() + res.Errors = nil + for i := 0; i < b.N; i++ { + res = slicesClone(in) + } + + _ = res + }) + }) + + b.Run(fmt.Sprintf("empty_with_cap_%d", errorsLen), func(b *testing.B) { + b.Run("copy", func(b *testing.B) { + + var ( + in = genFn() + res saga.ExecutionData + ) + res.Errors = nil + + b.ResetTimer() + b.ReportAllocs() + res.Errors = make([]error, cap(in.Errors)) + for i := 0; i < b.N; i++ { + res = oldCopy(in) + } + + _ = res + }) + + b.Run("slices.Clone", func(b *testing.B) { + var ( + in = genFn() + res saga.ExecutionData + ) + b.ResetTimer() + b.ReportAllocs() + res.Errors = make([]error, cap(in.Errors)) + for i := 0; i < b.N; i++ { + res = slicesClone(in) + } + + _ = res + }) + }) + +} diff --git a/test/go.mod b/test/go.mod index 8d7f25f..eaeb9e9 100644 --- a/test/go.mod +++ b/test/go.mod @@ -6,7 +6,6 @@ require ( github.com/gojuno/minimock/v3 v3.4.5 github.com/jackc/pgx/v5 v5.7.4 github.com/jmoiron/sqlx v1.4.0 - github.com/kozmod/oniontx v0.3.10 github.com/lib/pq v1.10.9 github.com/pkg/errors v0.9.1 github.com/redis/go-redis/v9 v9.7.3 diff --git a/test/go.sum b/test/go.sum index 6ae8ec9..069c495 100644 --- a/test/go.sum +++ b/test/go.sum @@ -36,8 +36,6 @@ github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= -github.com/kozmod/oniontx v0.3.10 h1:dVRKS/7gdPhn6JoD2WgyFTAiUT3Y0ZrhAjUSovBUvmk= -github.com/kozmod/oniontx v0.3.10/go.mod h1:OOa9nmU2nYLk7ZtHW+4olMx1LnoNZErVgkIgwla9Y/U= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=