Creational Created: 2026-01-27 Updated: 2026-01-27

Singleton

Ensure a single instance of a type exists using sync.Once for thread-safe lazy initialization.

Contributors

When to Use

  • When exactly one instance of a type should exist
  • For shared resources like database connections or configuration
  • When lazy initialization is needed for expensive resources

When NOT to Use

  • When dependency injection would make testing easier
  • For simple state that can be passed as parameters
  • When multiple instances don't cause problems

The Singleton pattern ensures only one instance of a type exists throughout the application’s lifetime. In Go, this is idiomatically implemented using sync.Once for thread-safe lazy initialization.

Implementation

package database

import (
    "database/sql"
    "sync"
)

type Database struct {
    conn *sql.DB
}

var (
    instance *Database
    once     sync.Once
)

// GetInstance returns the singleton database instance
func GetInstance() *Database {
    once.Do(func() {
        // This block executes only once, even with concurrent calls
        conn, err := sql.Open("postgres", "connection-string")
        if err != nil {
            panic(err) // Or handle error appropriately
        }
        
        instance = &Database{conn: conn}
    })
    
    return instance
}

func (db *Database) Query(query string) error {
    // Use the connection
    _, err := db.conn.Exec(query)
    return err
}

// Alternative: Package-level singleton with init
var Config *AppConfig

func init() {
    Config = &AppConfig{
        Host: "localhost",
        Port: 8080,
    }
}

type AppConfig struct {
    Host string
    Port int
}

Usage

package main

import (
    "fmt"
)

func main() {
    // Multiple calls return the same instance
    db1 := database.GetInstance()
    db2 := database.GetInstance()
    
    fmt.Println(db1 == db2) // true - same pointer
    
    // Use the singleton
    db1.Query("SELECT * FROM users")
    
    // Access package-level config singleton
    fmt.Printf("Server: %s:%d\n", database.Config.Host, database.Config.Port)
}

Benefits

  • Single Instance: Guarantees only one instance exists
  • Thread-Safe: sync.Once ensures safe concurrent initialization
  • Lazy Loading: Instance created only when first needed