Notes: Go Design Patterns - Singleton Pattern

Notes: Go Design Patterns - Singleton Pattern

This blog contains Singleton design pattern implementation by creating exactly one MongoDB connection

Table of contents

No heading

No headings in the article.

Singleton pattern makes sure that only one instance of an object is created. This pattern is used to create DB connections or connections with other external systems. We'll see its usage by trying to create a singleton MongoDB connection:

func getNewMongoClient() *mongo.Client {
    if mongoClient == nil {
        once.Do(
            mongoConnect,
        )
    } else {
        fmt.Println("mongo client already created.")
    }

    return mongoClient
}

Here once.Do makes sure that this section of the code gets executed only one time no matter how many times it is called. We could have used mutex locks as well but the right way to implement a singleton pattern in Go is to use the sync package’s Once.Do function. This function makes sure that your specified code is executed only once and never more than once.

To run the example code, first, start a mongo server using docker: docker run -d -p 27017:27017 --name mongo-server mongo:latest

Now, once you run this code, you'll see that only the first getNewMongoClient() will return success while subsequent calls will fail.

Working code to test:

package main

import (
    "context"
    "fmt"
    "log"
    "sync"

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

var once sync.Once

var mongoClient *mongo.Client

// start mongo server by using this command:
// docker run -d -p 27017:27017 --name mongo-server mongo:latest
func mongoConnect() {
    var err error

    // Set client options
    clientOptions := options.Client().ApplyURI("mongodb://localhost:27017")

    // Connect to MongoDB
    mongoClient, err = mongo.Connect(context.TODO(), clientOptions)

    if err != nil {
        log.Fatal(err)
    }

    // Check the connection
    err = mongoClient.Ping(context.TODO(), nil)

    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("connected to mongo!")
}

func getNewMongoClient() *mongo.Client {
    if mongoClient == nil {
        once.Do(
            mongoConnect,
        )
    } else {
        fmt.Println("mongo client already created.")
    }

    return mongoClient
}

func main() {
    getNewMongoClient()
    getNewMongoClient()
    getNewMongoClient()
    getNewMongoClient()
}