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

singleflight: Add an example #73

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

kevinburke
Copy link
Contributor

I was curious about the best way to initialize a Group - it turns out you just
do var g Group - but figured this package could use a package-level example
demonstrating an example use case.

I've signed the CLA.

wg.Add(1)
go func(i int) {
ctrval, _ := g.Do("key", func() (interface{}, error) {
counter++
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like how this example basically teaches people how to write data races. You have to already understand how singleflight works to understand why this perhaps is safe.

Use atomic.AddInt32 instead? And then put a same-line // atomic comment on the var counter int32 // atomic line.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@@ -0,0 +1,37 @@
package singleflight
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing copyright header.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

}
// Sleep so all of the goroutines start before we send a message on the
// channel
time.Sleep(10 * time.Millisecond)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm never a fan of sleeps in tests. Use a channel or something to wait for everybody to get in there instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I played around with this but I don't think it's possible. Thread 1 enters the function body, but I don't think threads 2-5 have a way to broadcast to thread 1 that they have also entered the Do() and are waiting for the results. The test for this functionality also uses a sleep. :(

I think the example is getting a little too complex, so I got rid of the threads and just added a comment explaining the behavior.

}

func Example() {
var g singleflight.Group
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is generally a global variable. With this style of example (func Example), it will show the whole file, right? So I think you can move this out? Then you'd want to name it something better than "g".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@kevinburke
Copy link
Contributor Author

Here's how the example looks on godoc on my machine:

@bradfitz
Copy link
Contributor

Check errors too.

@kevinburke
Copy link
Contributor Author

Done. Thank you for the review! And sorry for being so slow.

$ git rev-parse HEAD && go test ./...
8340abafe7a8d6b5443f502e62eb1d8e16198d52
ok      github.com/golang/groupcache    0.691s
ok      github.com/golang/groupcache/consistenthash 0.011s
?       github.com/golang/groupcache/groupcachepb   [no test files]
ok      github.com/golang/groupcache/lru    0.012s
ok      github.com/golang/groupcache/singleflight   0.119s
?       github.com/golang/groupcache/testpb [no test files]

@bradfitz
Copy link
Contributor

Copyright Google isn't right. You're not a Google employee I don't think?

I'd make it match the rest of Go and be copyright The Go Authors.

@kevinburke
Copy link
Contributor Author

Ah, sorry, I copied it from another file in that repo. Should I update the
other file headers or just this one?

On Saturday, October 29, 2016, Brad Fitzpatrick notifications@github.com
wrote:

Copyright Google isn't right. You're not a Google employee I don't think?

I'd make it match the rest of Go and be copyright The Go Authors.


You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
#73 (comment), or mute
the thread
https://github.com/notifications/unsubscribe-auth/AAOSI_80N7g_J8gylOG7N65zuy3FtDVoks5q439egaJpZM4KeD5v
.

Kevin Burke
925.271.7005 | kev.inburke.com

@bradfitz
Copy link
Contributor

Just one for now. Once a non-Googler touches other files they can update it.

@kevinburke
Copy link
Contributor Author

Done

func expensiveNetworkCall() (interface{}, error) {
time.Sleep(1)
atomic.AddInt32(&counter, 1)
return counter, nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a data race. You're reading from counter without atomics.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yikes. Should be fixed now. Thanks for sticking with this through several rounds.

@hblanks
Copy link

hblanks commented Dec 22, 2016

Looks like you still have that data race to fix, @kevinburke :-)

@kevinburke
Copy link
Contributor Author

kevinburke commented Dec 22, 2016 via email

}

func Example() {
// No matter how many threads call Do, only one network call per key will
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no "threads"

// No matter how many threads call Do, only one network call per key will
// be in progress at any time. Callers that share a key will get the same
// results as an in-flight call with that key.
v, err := networkGroup.Do("key", expensiveNetworkCall)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Normally this expensiveNetworkCall is a closure, closing over the key value. Maybe show an addition of +1 instead and say:

    // Imagine this is something expensive:
    return n + 1, nil

And make your "key" be:

n := 41
group.Do(strconv.Itoa(n), func(...) {..

(no need to mention 'network')

@kevinburke
Copy link
Contributor Author

Apologies for the delay, but I've finally made the changes you requested.

I was curious about the best way to initialize a Group - it turns out you just
do `var g Group` - but figured this package could use a package-level example
demonstrating an example use case.
var counter int32
var group singleflight.Group

func Example() {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be a testing case, like TestSingleFlightExample, and use testing.T for checking?

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

Successfully merging this pull request may close these issues.

None yet

5 participants