Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

should we move from juju/errors to pkg/errors? #7125

Closed
lysu opened this issue Jul 23, 2018 · 3 comments
Closed

should we move from juju/errors to pkg/errors? #7125

lysu opened this issue Jul 23, 2018 · 3 comments
Labels

Comments

@lysu
Copy link
Collaborator

lysu commented Jul 23, 2018

Why

Now, we are using juju/errors to handle go error, but in these days we find some questions related it(#7114, #7120), let us rethink about it.

Go though the surface question, the question make me confuse is Why we should call errors.Trace for every method call?

After learn juju, I found that we call errors.Trace every call, just solve the question that save call stack in error.

I don't feel it's NOT a good choose to do that:

  • redundant: we do nothing but write if err != nil {return errors.Trace(err)} again and again
  • performance: we can get full stack when we new error, why call SetLocation make heavy runtime.Caller in every level method?
  • easy to mistake: in plan, executor: check b.err after buildSort and buildLimit in builUnion #7114, code is classical builder pattern, error retun in final build() method is naturally, but due to juju we cannot. and we have see many code review comment to remind miss errors.Trace call
  • hard to improve: we can easy improve perf like this in central point

So, rather than forcing the caller to manually annotate each stack frame in the return path, we need a errors that can just record the entire stack trace at the point that an error is created by the errors package.

After search, https://github.com/pkg/errors is what we hoped, and author meet the same question and move from manual trace way to new error save stack way, too~

Perf

just write a simple demo to test two way.

package main

import (
	"testing"
	jerrors "github.com/juju/errors"
	perrors "github.com/pkg/errors"
)

//go:noinline
func jf1() error {
	err := jf2()
	if err != nil {
		return jerrors.Trace(err)
	}
	return nil
}

//go:noinline
func jf2() error {
	err := jf3()
	if err != nil {
		return jerrors.Trace(err)
	}
	return nil
}

//go:noinline
func jf3() error {
	err := jf4()
	if err != nil {
		return jerrors.Trace(err)
	}
	return nil
}

//go:noinline
func jf4() error {
	err := jf5()
	if err != nil {
		return jerrors.Trace(err)
	}
	return nil
}

//go:noinline
func jf5() error {
	err := jf6()
	if err != nil {
		return jerrors.Trace(err)
	}
	return nil
}

//go:noinline
func jf6() error {
	err := jf7()
	if err != nil {
		return jerrors.Trace(err)
	}
	return nil
}

//go:noinline
func jf7() error {
	err := jf8()
	if err != nil {
		return jerrors.Trace(err)
	}
	return nil
}

//go:noinline
func jf8() error {
	err := jf9()
	if err != nil {
		return jerrors.Trace(err)
	}
	return nil
}

//go:noinline
func jf9() error {
	err := jf10()
	if err != nil {
		return jerrors.Trace(err)
	}
	return nil
}

//go:noinline
func jf10() error {
	return jerrors.New("juju error")
}

//go:noinline
func pf1() error {
	return pf2()
}

//go:noinline
func pf2() error {
	return pf3()
}

//go:noinline
func pf3() error {
	return pf4()
}

//go:noinline
func pf4() error {
	return pf5()
}

//go:noinline
func pf5() error {
	return pf6()
}

//go:noinline
func pf6() error {
	return pf7()
}

//go:noinline
func pf7() error {
	return pf8()
}

//go:noinline
func pf8() error {
	return pf9()
}

//go:noinline
func pf9() error {
	return pf10()
}

//go:noinline
func pf10() error {
	return perrors.New("pkg error")
}

func BenchmarkJuJuError(b *testing.B) {
	for i := 0; i < b.N; i++ {
		jf1()
	}
}

func BenchmarkPkgError(b *testing.B) {
	for i := 0; i < b.N; i++ {
		pf1()
	}
}

result:

go test --benchmem -bench .                                       
goos: linux
goarch: amd64
pkg: github.com/juju/errors/test2
BenchmarkJuJuError-8   	  200000	      8143 ns/op	     800 B/op	      10 allocs/op
BenchmarkPkgError-8    	 1000000	      1143 ns/op	     320 B/op	       3 allocs/op
PASS
ok  	github.com/juju/errors/test2	2.876s

cpu-perf for juju all error:

image

cpu-perf for pkg all error:

TODO

  • replace juju import to pkg
  • remove errors.Trace call

Can we do this?~

@coocood
Copy link
Member

coocood commented Jul 23, 2018

We need to find a way to do the refactoring step by step.

@XuHuaiyu
Copy link
Contributor

XuHuaiyu commented Sep 5, 2018

It seems that the cpu-perf for pkg all error is lost in the description.😄

@lysu lysu closed this as completed Sep 12, 2018
@Kuri-su
Copy link

Kuri-su commented Dec 8, 2020

i have same question, but this issue solved my doubts , thx 😆

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants