How does golang defer work in cycles? [duplicate] - go

This question already has answers here:
Proper way to release resources with defer in a loop?
(3 answers)
Closed 11 months ago.
I'm trying to handle reconnections to MongoDB. To do this I try to perform every operation three times (in case it fails with io.EOF)
type MongoDB struct {
session *mgo.Session
DB *mgo.Database
}
func (d MongoDB) performWithReconnect(collection string,
operation func(*mgo.Collection) error) error {
var err error
for i := 0; i < 3; i++ {
session := d.session.Copy()
defer session.Close()
err = operation(session.DB(Config.MongoDb).C(collection))
if err == io.EOF{
continue
}
if err == nil{
return err
}
}
return err
}
So the question is about defer. Will it close all sessions as I suppose or it is going to behave some other way?
If you know some good practices to handle this different way I will be happy to read them.

Consider the following program
package main
import (
"fmt"
)
func print(s string, i int) {
fmt.Println(s, i)
}
func main() {
for i := 0; i < 3; i++ {
defer print("loop", i)
}
fmt.Println("after loop 1")
for i := 0; i < 3; i++ {
func(i int) {
defer print("func", i)
}(i)
}
fmt.Println("after loop 2")
}
It will print
after loop 1
func 0
func 1
func 2
after loop 2
loop 2
loop 1
loop 0
The deferred function calls will be put on stack and then executed in a reverse order at the end of surrounding function. In your case it will be quite bad as you will have connections waiting to be closed.
I recommend wrapping the contents of loop into an inline function. It will call deferred function just as you want it.

From A Tour of Go:
A defer statement defers the execution of a function until the surrounding function returns.
So in your code, you're creating three (identical) defer functions, which will all run when the function exits.
If you need a defer to run inside of a loop, you have to put it inside of a function. This can be done in an anonymous function thusly:
for i := 0; i < 3; i++ {
err := func() error {
session := d.session.Copy()
defer session.Close()
return operation(session.DB(Config.MongoDb).C(collection))
}()
if err == io.EOF {
continue
}
if err != nil {
return err
}
}

Related

Are creating go routines asynchrnous?

I'm trying to fetch the content of an API with numerous goroutines.
I'm using a for loop to iterate over different character, but it seems like the forloop reaches its final value, before the requests are sent off.
package main
import (
"encoding/json"
"fmt"
"net/http"
"sync"
)
type people struct {
Name string `json:"name"`
}
func main(){
names := make(chan string, 25)
var wg sync.WaitGroup
for i := 0; i < 25; i++ {
wg.Add(1)
go func() {
defer wg.Done()
var p people
url := fmt.Sprintf("https://swapi.dev/api/people/%d", i)
getJSON(url, &p)
names <- p.Name
}()
}
name := <-names
fmt.Println(name)
wg.Wait()
}
func getJSON(url string, target interface{}) error {
r, err := http.Get(url)
if err != nil {
return err
}
defer r.Body.Close()
json.NewDecoder(r.Body).Decode(target)
return nil
}
Also, if somebody could improve my code quality, I'd be very grateful, I'm very new to Golang and don't have anybody to learn from!
You go routines are all using the same variable i. So on the first loop, you launch a goroutine that makes a url from i, and on the next loop i is incremented before that routine has a chance to run.
It's a common mistake in GoLang. The solution is to make a variable for each loop, and pass that one forward. You can either do it with a closure like this (playground).
for i := 0; i < 25; i++ {
wg.Add(1)
localI := i
go func() {
defer wg.Done()
var p people
// Use LocalI here
url := fmt.Sprintf("https://swapi.dev/api/people/%d", localI)
getJSON(url, &p)
names <- p.Name
}()
}
Or as an argument to the function (playground)
for i := 0; i < 25; i++ {
wg.Add(1)
localI := i
go func(localI int) {
defer wg.Done()
var p people
// Use LocalI here
url := fmt.Sprintf("https://swapi.dev/api/people/%d", localI)
getJSON(url, &p)
names <- p.Name
// Pass i here. Since I is a primitive, it is passed by value, not reference.
// Meaning a copy is made.
}(i)
}
Here is a good writeup on the mistake you made:
https://github.com/golang/go/wiki/CommonMistakes#using-goroutines-on-loop-iterator-variables
And the one above it is good to read too!

Go routines not executing

The following is the code that is giving me problem. What i want to achieve is to create those many tables in parallel. After all the tables are created I want to exit the functions.
func someFunction(){
....
gos := 5
proc := make(chan bool, gos)
allDone := make(chan bool)
for i:=0; i<gos; i++ {
go func() {
for j:=i; j<len(tables); j+=gos {
r, err := db.Exec(tables[j])
fmt.Println(r)
if err != nil {
methods.CheckErr(err, err.Error())
}
}
proc <- true
}()
}
go func() {
for i:=0; i<gos; i++{
<-proc
}
allDone <- true
}()
for {
select {
case <-allDone:
return
}
}
}
I'm creating two channels 1 to keep track of number of tables created (proc) and other (allDone) to see if all are done.
When i run this code then the go routine to create table starts execution but before it completes someFunction gets terminated.
However there is no problem if run the code sequentially
What is the mistake in my design pattern and also how do i correct it.
The usual pattern for what you're trying to achieve uses WaitGroup.
I think the problem you're facing is that i is captured by each goroutine and it keeps getting incremented by the outer loop. Your inner loop starts at i and since the outer loop has continued, each goroutine starts at 5.
Try passing the iterator as parameter to the goroutine so that you get a new copy each time.
func someFunction(){
....
gos := 5
var wg sync.WaitGroup
wg.Add(gos)
for i:=0; i< gos; i++ {
go func(n int) {
defer wg.Done()
for j:=n; j<len(tables); j+=gos {
r, err := db.Exec(tables[j])
fmt.Println(r)
if err != nil {
methods.CheckErr(err, err.Error())
}
}
}(i)
}
wg.Wait();
}
I'm not sure what you're trying to achieve here, each goroutine does db.Exec on all the tables above the one it started with so the first one treats all the tables, the second one treats all but the first one and so on. Is this what you intended?

Why does this WaitGroup sometimes not wait for all goroutines?

The code below outputs 2 sometimes. Why isn't the wait group waiting for all the goroutines to complete ?
type Scratch struct {
//sync.RWMutex
Itch []int
}
func (s *Scratch) GoScratch(done chan bool, j int) error {
var ws sync.WaitGroup
if len(s.Itch) == 0 {
s.Rash = make([]int, 0)
}
for i := 0; i < j; i++ {
ws.Add(1)
go func (i int) {
defer ws.Done()
s.Rash = append(s.Rash, i)
}(i)
}
ws.Wait()
done<- true
return nil
}
func main() {
done := make(chan bool, 3)
s := &Scratch{}
err := s.GoScratch(done, 3)
if err != nil {
log.Println("Error:%v",err)
}
<-done
log.Println("Length: ", len(s.Rash))
}`
Strangely i can't get it to output 2 with a main function but when I use a test case it outputs 2 sometimes.
There is a race condition in your code. It is right here:
go func (i int) {
defer ws.Done()
// race condition on s.Rash access
s.Rash = append(s.Rash, i)
}(i)
Since all the goroutines access s.Rash concurrently, this may cause the slice updates to be overwritten. Try running the same code with sync.Mutex locking to prevent this:
// create a global mutex
var mutex = &sync.Mutex{}
// use mutex to prevent race condition
go func (i int) {
defer ws.Done()
defer mutex.Unlock() // ensure that mutex unlocks
// Lock the resource before accessing it
mutex.Lock()
s.Rash = append(s.Rash, i)
}(i)
You can read more about this here and here.
If you run your code with race detector
go test -race .
you will find out race condition on slice s.Rash.

What am I missing on concurrency?

I have a very simple script that makes a get request and then does some thing with the response. I have 2 version one using a go routine and one without I bencharmaked both and there was no difference in speed. Here is a dumb down version of what I'm doing:
Regular Version:
func main() {
url := "http://finance.yahoo.com/q?s=aapl"
for i := 0; i < 250; i++ {
resp, err := http.Get(url)
if err != nil {
fmt.Println(err)
}
fmt.Println(resp.Status)
}
}
Go Routine:
func main() {
url := "http://finance.yahoo.com/q?s=aapl"
for i := 0; i < 250; i++ {
wg.Add(1)
go run(url, &wg)
wg.Wait()
}
}
func run(url string, wg *sync.WaitGroup) {
defer wg.Done()
resp, err := http.Get(url)
if err != nil {
fmt.Println(err)
}
fmt.Println(resp.Status)
}
In most cases when I used a go routine the program took longer to execute. What concept am I missing to understand using concurrency efficiently?
The main problem with your example is that you're calling wg.Wait() within the for loop. This causes execution to block until you the deferred wg.Done() call inside of run. As a result, the execution isn't concurrent, it happens in a goroutine but you block after starting goroutine i and before starting i+1. If you place that statement after the loop instead like below then your code won't block until after the loop (all goroutines have been started, some may have already completed).
func main() {
url := "http://finance.yahoo.com/q?s=aapl"
for i := 0; i < 250; i++ {
wg.Add(1)
go run(url, &wg)
// wg.Wait() don't wait here cause it serializes execution
}
wg.Wait() // wait here, now that all goroutines have been started
}

Passing parameters to function closure

I'm trying to understand the difference in Go between creating an anonymous function which takes a parameter, versus having that function act as a closure. Here is an example of the difference.
With parameter:
func main() {
done := make(chan bool, 1)
go func(c chan bool) {
time.Sleep(50 * time.Millisecond)
c <- true
}(done)
<-done
}
As closure:
func main() {
done := make(chan bool, 1)
go func() {
time.Sleep(50 * time.Millisecond)
done <- true
}()
<-done
}
My question is, when is the first form better than the second? Would you ever use a parameter for this kind of thing? The only time I can see the first form being useful is when returning a func(x, y) from another function.
The difference between using a closure vs using a function parameter has to do with sharing the same variable vs getting a copy of the value. Consider these two examples below.
In the Closure all function calls will use the value stored in i. This value will most likely already reach 3 before any of the goroutines has had time to print it's value.
In the Parameter example each function call will get passed a copy of the value of i when the call was made, thus giving us the result we more likely wanted:
Closure:
for i := 0; i < 3; i++ {
go func() {
fmt.Println(i)
}()
}
Result:
3
3
3
Parameter:
for i := 0; i < 3; i++ {
go func(v int) {
fmt.Println(v)
}(i)
}
Result:
0
1
2
Playground: http://play.golang.org/p/T5rHrIKrQv
When to use parameters
Definitely the first form is preferred if you plan to change the value of the variable which you don't want to observe in the function.
This is the typical case when the anonymous function is inside a for loop and you intend to use the loop's variables, for example:
for i := 0; i < 10; i++ {
go func(i int) {
fmt.Println(i)
}(i)
}
Without passing the variable i you might observe printing 10 ten times. With passing i, you will observe numbers printed from 0 to 9.
When not to use parameters
If you don't want to change the value of the variable, it is cheaper not to pass it and thus not create another copy of it. This is especially true for large structs. Although if you later alter the code and modify the variable, you may easily forget to check its effect on the closure and get unexpected results.
Also there might be cases when you do want to observe changes made to "outer" variables, such as:
func GetRes(name string) (Res, error) {
res, err := somepack.OpenRes(name)
if err != nil {
return nil, err
}
closeres := true
defer func() {
if closeres {
res.Close()
}
}()
// Do other stuff
if err = otherStuff(); err != nil {
return nil, err // res will be closed
}
// Everything went well, return res, but
// res must not be closed, it will be the responsibility of the caller
closeres = false
return res, nil // res will not be closed
}
In this case the GetRes() is to open some resource. But before returning it other things have to be done which might also fail. If those fail, res must be closed and not returned. If everything goes well, res must not be closed and returned.
This is a example of parameter from net/Listen
package main
import (
"io"
"log"
"net"
)
func main() {
// Listen on TCP port 2000 on all available unicast and
// anycast IP addresses of the local system.
l, err := net.Listen("tcp", ":2000")
if err != nil {
log.Fatal(err)
}
defer l.Close()
for {
// Wait for a connection.
conn, err := l.Accept()
if err != nil {
log.Fatal(err)
}
// Handle the connection in a new goroutine.
// The loop then returns to accepting, so that
// multiple connections may be served concurrently.
go func(c net.Conn) {
// Echo all incoming data.
io.Copy(c, c)
// Shut down the connection.
c.Close()
}(conn)
}
}

Resources