go – How to make main() await until init() finishes in golang?-ThrowExceptions

Exception or error:

When I run my main.go with the following code it works normally and the client connects to mongo database.

package main

import (
    "context"
    "fmt"
    "log"

    "go.mongodb.org/mongo-driver/mongo"
    "go.mongodb.org/mongo-driver/mongo/options"
)

type Trainer struct {
    Name string
    Age  int
    City string
}

func main() {
    clientOptions := options.Client().ApplyURI("mongodb://localhost:27017")

    client, err := mongo.Connect(context.TODO(), clientOptions)
    if err != nil {
        log.Fatal(err)
    }

    err = client.Ping(context.TODO(), nil)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("Successful connection")
}

However, when I am trying to separate the code logic between init() and main() goroutines, I get a memory reference error, which is normal because main is executed before init actually has a tcp connection the db. I tried to connect them with a channel but its not working as expected.

package main

import (
    "context"
    "fmt"
    "log"

    "go.mongodb.org/mongo-driver/mongo"
    "go.mongodb.org/mongo-driver/mongo/options"
)

type Trainer struct {
    Name string
    Age  int
    City string
}

var client *mongo.Client
var c = make(chan *mongo.Client)

func init() {
    // configures the connection options
    clientOptions := options.Client().ApplyURI("mongodb://localhost:27017")

    client, err := mongo.Connect(context.TODO(), clientOptions)
    if err != nil {
        log.Fatal(err)
    }

    err = client.Ping(context.TODO(), nil)
    if err != nil {
        log.Fatal(err)
    }

    c <- client
}

func main() {
    fmt.Println(<-c)
}

Since I don’t have enough experience with golang, can anyone explain me why my solution is not working and how can I fix it? I want init() and main() to be separated if possible.

How to solve:

Your code has three issues:

1) The functions init and main are not goroutines, they are run sequentially at program start. So there is no point using the unbuffered channel.

2) The variable client is redeclared in init using the ‘:=’ operator.

3) I would not recommend to initialize a client connection in init. if necessary put the code in a helper function and call it from main.

A fix covering (1) and (2) and excluding (3) is:

package main

import (
    "context"
    "fmt"
    "log"

    "go.mongodb.org/mongo-driver/mongo"
    "go.mongodb.org/mongo-driver/mongo/options"
)

type Trainer struct {
    Name string
    Age  int
    City string
}

var client *mongo.Client
// We don't need a channel since init and main are not goroutines. Both
// are executed sequentially in the main thread.
// var c = make(chan *mongo.Client)


// It is not recommended to use init to setup database connections. It
// is definitely part of the program and belongs into main.
func init() {
    // configures the connection options
    clientOptions := options.Client().ApplyURI("mongodb://localhost:27017")

    // Don't redeclare client here. We have however to declare err.
    var err error
    // Note that we use assignment (=) and not declaration (:=).
    client, err = mongo.Connect(context.TODO(), clientOptions)
    if err != nil {
        log.Fatal(err)
    }

    err = client.Ping(context.TODO(), nil)
    if err != nil {
        log.Fatal(err)
    }
}

func main() {
    // Use the global client variable instead reading from the channel.
    fmt.Println(client)
}

Leave a Reply

Your email address will not be published. Required fields are marked *