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

Add a Factory, FactoryFunc interfaces #103

Closed
wants to merge 3 commits into from
Closed

Add a Factory, FactoryFunc interfaces #103

wants to merge 3 commits into from

Conversation

slnt
Copy link

@slnt slnt commented Jun 10, 2020

Often I have components in my code take a BackOff. In some cases, there are concurrent pieces of code that need to be using similarly configured backoffs, but since backoffs are not safe for concurrent use, I'd need to pass the goroutine a new backoff.

It is difficult to do this when working with a BackOff directly, as the code doesn't know about then underlying implementation, and shouldn't. In this situation, a Factory would be of great use, as it would allow the code to create new backoffs on demand, and pass them into its goroutines, without having to know what the underlying implementation details of the specific backoff instance it has.

I have been using a very similar interface to this, and it has worked quite well for me. I think it would be beneficial to have it be part of the package itself, as then no separate backoffutil style package would be needed.

@coveralls
Copy link

coveralls commented Jun 10, 2020

Coverage Status

Coverage increased (+1.2%) to 86.307% when pulling 1704c7e on slnt:slnt/backoff-factory into 31cc31b on cenkalti:v4.

@slnt slnt changed the title Add a Factory, FactoryFunc interface Add a Factory, FactoryFunc interfaces Jun 10, 2020
@cenkalti
Copy link
Owner

Hi @slnt . This does not seem like a common use case to me. Why are you using backoffs concurrently? Can you tell more about your use case please?

@slnt
Copy link
Author

slnt commented Jun 11, 2020

Here's an example situation where we have found having this factory useful. foo and bar are placeholders for outgoing requests to other services.

type Foo struct {
	bf backoff.Factory
}

func (f *Foo) HandleRequest(...) error {
	var g errgroup.Group
	
	g.Go(func() error {
		b = bf.NewBackOff()
		backoff.Retry(func() error {
			return foo()
		}, b)
	})

	g.Go(func() error {
		b = bf.NewBackOff()
		backoff.Retry(func() error {
			return bar()
		}, b)
	})

	return g.Wait()
}

If Foo were to have just a BackOff here, instead of a Factory, there isn't a clean way to get a copy of the backoff. We could specify the backoff in HandleRequest, but then testing becomes difficult, as we'd be coupled to a specific backoff (eg ExponentialBackOff, but we'd want to use ZeroBackOff in our tests). The Factory makes this process a lot easier, and has proven very useful to us.

As I wrote the above it occurred to me that maybe we should have just defined all of our factories as func() backoff.BackOff, though I do still think being able to pass in ExponentialBackOff to something accepting a Factory might be a bit more "ergonomic".

@cenkalti
Copy link
Owner

I am trying to understand. How it is different from writing a function that returns a backoff instance?

type Foo struct {
	factory func() backoff.Backoff
}

func (f *Foo) HandleRequest(...) error {
	var g errgroup.Group
	
	g.Go(func() error {
		b := f.factory()
		backoff.Retry(func() error {
			return foo()
		}, b)
	})

	g.Go(func() error {
		b := f.factory()
		backoff.Retry(func() error {
			return bar()
		}, b)
	})

	return g.Wait()
}

@slnt
Copy link
Author

slnt commented Jun 11, 2020

I am trying to understand. How it is different from writing a function that returns a backoff instance?

It isn't, really, and I mentioned that at the bottom of my previous comment. The only difference is a slight "ergonomic" increase, I think.

foo := Foo{
	backoffFactory: backoff.NewExponentialBackOff(),
}

foo := Foo{
	backoffFactory: func() backoff.BackOff { return backoff.NewExponentialBackOff() },
}

The first is slightly nicer to write, but perhaps not worth adding more to the API.

@cenkalti
Copy link
Owner

I think the API is complex enough. I don't want to make it more complex by adding new methods/interfaces. I would like to reject this PR.

@slnt slnt closed this Jun 12, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants