Pattern Singleton com Golang
Published: 2023-04-12 19:32:47

Sempre me deparo com necessidade de implementar o [**Pattern Singleton**](https://pt.wikipedia.org/wiki/Singleton) em meus projetos, mas em [**Golang**](http://golang.org/) existem algumas particularidades que temos que tomar cuidado. Neste artigo, vou apresentar duas formas de implementar o Singleton usando Golang, a forma “***Not Thread Safe***” e a forma “***Thread Safe***”. O objetivo é apresentar de forma prática e técnica as formas de implementação e quando temos que implementar o ***patterns singleton.***

## **Problema a ser resolvido**

Temos que abrir uma conexão com o banco de dados e mantê-la em uma única instância para não sobrecarregar o banco e termos problemas ou erros com o limite de conexões simultâneas excedido, além, é claro, de otimizarmos nosso código. Sabemos que uma conexão é muito custosa computacionalmente.

Como o banco não dá suporte à pool de conexões de forma nativa, então precisaremos controlar todas as conexões que nossa aplicação irá abrir para se comunicar com ele, assim, em cada ação que formos executar, seja um select, delete, insert ou update, precisaremos de um objeto de conexão em memória de forma atômica e para isso vamos utilizar o ***pattern singleton***.

Antes de implementarmos nossa solução usando **Pattern Singleton** para resolver o problema do pool de conexões, vamos conferir alguns outros exemplos da necessidade da utilização do ***pattern singleton.***

Abaixo alguns **exemplos** de diversas formas de tentar proteger nossa ***variável global.***

## **Protegendo variável Global Exemplo 1**

```go
// type global
type singleton map[string]string

var (
	instance singleton
)

func NewClass() singleton {

	if instance == nil {

		instance = make(singleton) // <-- not thread safe
	}

	return instance
}
```

## Protegendo variável Global Exemplo 2

```go
var lock = &sync.Mutex{}

// type global
type singleton map[string]string

var (
	instance singleton
)

func NewClass() singleton {

	lock.Lock()
	defer lock.Unlock()

	if instance == nil {

		instance = make(singleton) // <-- thread safe
	}

	return instance
}
```

## Protegendo variável Global Exemplo 3

```go
var once sync.Once

// type global
type singleton map[string]string

var (
	instance singleton
)

func NewClass() singleton {

	once.Do(func() { // <-- atomic, does not allow repeating

		instance = make(singleton) // <-- thread safe

	})

	return instance
}
```

Acima é mostrado três formas de proteger nossa variável global ***“instance”,*** para soluções síncronas o *exemplo 1* já irá funcionar, para soluções assíncronas o *exemplo 2 e 3* são o mais indicados sendo que o *exemplo 3* é a melhor solução para protegermos de “[***race condition***](https://blog.golang.org/race-detector)” garantindo que a instância seja declarada uma única vez tornando-se atômica.

O objetivo do artigo é mostrar exatamente as formas de tentar proteger a ***variável global***, em Golang temos diversas maneiras de fazermos isso.  
Abaixo temos mais alguns exemplos para complementar o raciocínio, mostrando como fazemos as instanciações do nosso singleton, seja ele de forma síncrona ou assíncrona usando goroutine.

## Exemplos de como instanciar nosso Singleton

O código abaixo é um bom exemplo de aberturas de diversas conexões de forma ***síncrona***, dependendo da forma que implementarmos nossa instância de conexão o banco não suportaria.

```go
func main() {

	objeto := NewClass() // instance make

	objeto["this"] = "Object NewClass"
	objeto2 := NewClass() // return instance

	fmt.Println("This is:", objeto2["this"])

	objeto3 := NewClass() // return instance
	objeto4 := NewClass() // return instance
	objeto5 := NewClass() // return instance

	fmt.Println("This is:", objeto3["this"])
	fmt.Println("This is:", objeto4["this"])
	fmt.Println("This is:", objeto5["this"])
}
```

Agora vamos visualizar um exemplo de abertura de conexões de forma ***assíncrona*** utilizando ***goroutine***. Fizemos centenas de goroutines e milhares de conexões simultâneas em nosso exemplo abaixo, funciona 100% sem travar e ou bugar a quantidade de conexões do banco de dados.

```go
func main() {

	objeto := NewClass() // instance make

	go func() {

		for i := 0; i < 1000; i++ {

			objeto = NewClass() // <-- return instance
		}
	}()

	go func() {

		for i := 0; i < 100; i++ {

			go func() {

				objeto = NewClass() // <-- return instance
			}()
		}
	}()

	fmt.Scanln()
}
```

# Goroutines e o pattern singleton

Quando pensamos nas possíveis soluções para a implementação do ***pattern singleton*** usando Golang, esbarramos nos *Goroutines* ele será responsável por nos proporcionar que nosso código execute de forma ***assíncrona*** e ***concorrente*** e quando usamos Goroutine em nossa aplicação toda nossa forma de pensar e implementar muda, ou seja não é mais uma aplicação ***síncrona*** e devido a isso temos que pensar da forma ***Golang de ser*.**

As [***Goroutines***](https://tour.golang.org/concurrency/1) é um poderoso recurso e quando usado corretamente ele torna-se um forte aliado para combatermos as batalhas do dia a dia. Toda vez que implementamos códigos que usam *concorrência* temos alguns cenários já conhecidos que temos que tratar, o escopo do programa, ***variáveis globais***, locais, passagem de parâmetros, ponteiros tudo isso tem que ser tratado para que possamos trabalhar com concorrência de forma correta e otimizada.

Um bom exemplo de possíveis problemas usando concorrência é a utilização de ***variáveis globais***. Devido as goroutines nossa implementação do ***Pattern Singleton*** e nossas possíveis soluções será escrita para aceitar o uso de concorrência. Vamos escrever o nosso código usando boas práticas de programação, para tentar mitigar os possíveis bugs sigilosos que podem ocorrer em tempo de execução do seu programa escrito em *Golang*.

## 200 goroutines sendo inicializadas

Em nosso exemplo abaixo está sendo criado *200 goroutines* e colocando todas em concorrência e *dez mil interações* estão sendo feitas em nosso banco de dados ou seja “*connect e faça: select email from login where id =?*”.

```go
	for x := 0; x < 200; x++ {
    
		go func(x int) {

			for j := 0; j < 10000; j++ {

				fmt.Println(" login: ", Conn.Connet().GetUserEmail(x))
				time.Sleep(time.Millisecond * 150)
			}
		}(x)
	}
```

O bom entendimento sobre as *goroutines* irá nos ajudar a escrever códigos melhores e mais poderosos em *Golang*, isto não tenho dúvidas. Em um próximo artigo iremos descrever alguns cases usando goroutines, estou ansioso para escrever sobre Goroutines e mostrar como resolvemos alguns de nossos problemas na empresa.

O assunto sobre **Pattern Singleton** é tão interessante que tive que fazer uma pesquisa bem mais profunda sobre o assunto antes de escrever este artigo e como uso muito em meu dia a dia decidi colaborar um pouco mostrando alguns pontos importantes deste pattern e por que muita das vezes ele é considerado um ***Anti Pattern***. Descrevendo os detalhes técnicos no artigo percebemos claramente diversas boas práticas que poderemos utilizar em nosso dia dia quando estamos codando em *Golang*, boas práticas, formas clean de implementação, implementações menos complexas e menos custosas e com maior desempenho.

# O que é Pattern Singleton?

A transcrição do que é um Pattern Singleton seria: \*“\****Singleton*** \*é um padrão de projeto de software. Este padrão garante a existência de apenas uma instância de uma classe, mantendo um ponto global de acesso ao seu objeto”.\*Singleton é um padrão de design que restringe a instanciação a um objeto, temos que garantir que isto ocorra somente uma única vez.

Basicamente **singleton** é uma maneira de usar ***variáveis ​​globais***. Sabemos o quanto é perigoso o uso de variáveis globais, nosso código fica vulnerável ao acesso da *variável global* ou seja em qualquer parte do sistema podemos alterar seu valor. Portanto, ao tentarmos debugar ou depurar nosso programa não será uma tarefa fácil descobrir qual caminho de código leva ao estado atual, este é um dos motivos que não considero o ***Pattern Singleton*** um ***Anti Pattern*** e sim uma formas de proteger as ***variáveis globais.***

No entanto, o problema com ***Singleton*** usando concorrência que será nosso objetivo, em um ambiente multiencadeado, a inicialização deve ser protegida para evitar a reinicialização, de forma atômica.

O ***Pattern Singleton*** é uma característica proveniente do paradigma orientado a objeto, então como iremos implementar em *Golang* se o mesmo não possui suporte a OO ?

Para responder esta pergunta temos que entender que a ***Programação*** ***Orientada a Objeto*** é um conceito e pode ser implementando em qualquer linguagem de programação mesmo sendo de outros paradigmas. É claro que o nível de abstração e dificuldade torna-se uma tarefa árdua aumentando muito o nível de complexidade do código dependendo da linguagem, nossa intenção é puramente didática com intuito de entendermos melhor o cenário proposto quando falamos em ***pattern singleton***\*.\*

# go run -race singleton.go

> Executa todos os códigos usando -race como parâmetro: go run -race ..

O ***“-race detector”*** é um recurso que temos disponível em Golang para detectar acessos indevidos em memória quando estamos usando concorrência em nossa aplicação. É possível gerar relatório que contém rastreamentos de pilha para acessos conflitantes, bem como pilhas nas quais as goroutines envolvidas foram criadas, em breve vou criar um artigo abordando exatamente este assunto e vamos falar sobre ***“Profiling“*** em Go.  
Abaixo as formas que podemos fazer a chamada do “-race”.

```go
$ go test -race seupkg    // to test the package
$ go run -race seusrc.go  // to run the source file
$ go build -race seucmd   // to build the command
$ go install -race seupkg // to install the package
```

# Pattern Singleton em Golang

A solução apresentada abaixo seria ideal se nossa aplicação fosse ***síncrona,*** e o problema seria resolvido com código abaixo, mas como nosso objetivo é implementar usando concorrência a solução abaixo está longe de ser a ideal.  
Vamos conferir o código abaixo e iniciar nossas possibilidades de implementação:

```go
type DriverPg struct {
	conn string
}

var instance *DriverPg

func Connect() *DriverPg {

	if instance == nil {
		// <--- NOT THREAD SAFE / Quando usarmos Goroutine
		instance = &DriverPg{conn: "DriverConnectPostgres"}
	}

	return instance
}

func main() {

	// chamada
	go func() {

		for i := 0; i < 100; i++ {
			time.Sleep(time.Millisecond * 600)
			fmt.Println(*Connect(), " - ", i)
		}
	}()

	go func() {

		fmt.Println(*Connect())
	}()

	fmt.Scanln()
}
```

Na implementação acima, temos nossa primeira abordagem implementando ***Singleton*** em Golang e com goroutines para executar simultaneamente o nosso ***Singleton***.

O problema visível neste código é que várias rotinas de **goroutine** poderiam avaliar a primeira verificação e todas criariam uma instância do tipo **singleton** e substituiriam uma à outra. Não há garantia de qual *instância* será retornada no código acima, e outras operações adicionais na instância podem ser inconsistentes com as expectativas do desenvolvedor e problemas sigilosos podem ocorrer em tempo de execução.

Muito ruim esta abordagem, diversos erros muitos sutis podem ocorrer, se as referências à instância singleton estiverem sendo mantidas por meio do código, existe uma grande chance de haver potencialmente várias instâncias do tipo com estados diferentes, gerando potenciais comportamentos de código. Ele também se torna um verdadeiro pesadelo durante a depuração e se torna realmente difícil detectar o bug, já que no tempo de depuração nada parece estar errado devido às pausas de tempo de execução, minimizando o potencial de uma execução “***Not Thread Safe***”, ofuscando totalmente o problema para quem está codando.

# Bloqueios com Mutex

No código abaixo é uma solução *pobre* para a tentativa de resolver o problema de “***Thread Safe***”. Na verdade, isso resolve o problema “***Thread Safe***”, mas cria outros problemas sérios em potencial. Ele introduz uma contenção nas goroutines executando um ***bloqueio agressivo*** de toda a função, vamos conferir o código abaixo:

```go
// nosso lock mutex
var lock = &sync.Mutex{}

type DriverPg struct {
	conn string
}

var instance *DriverPg

func Connect() *DriverPg {
	// <--- Desnecessario a lock
	// se a instancia já tiver
	// sido criada muito agressivo
	lock.Lock()
	defer lock.Unlock()

	if instance == nil {
		// ainda não é a melhor implementação devido
		// os bloqueios
		instance = &DriverPg{conn: "DriverConnectPostgres"}
	}
	
	return instance
}

func main() {

	go func() {
		for i := 0; i < 100; i++ {
			time.Sleep(time.Millisecond * 600)
			fmt.Println(*Connect(), " - ", i)
		}
	}()

	go func() {
		fmt.Println(*Connect())
	}()

	fmt.Scanln()
}
```

O problema de “***Thread Safe***” foi resolvido com a implementação acima usando **sync.Mutex** onde ocorre o Bloqueio antes de criar a instância **Singleton**. O grande problema desta abordagem é o bloqueio excessivo mesmo quando não seria necessário fazer isso, no caso de a instância já ter sido criada e deveríamos simplesmente ter retornado a instância **singleton**. Se nosso programa for escrito para tornar-se altamente concorrente, isso pode gerar um gargalo, pois somente uma rotina de goroutine pode obter a instância **singleton** de cada vez, tornando-se nossa solução mais lenta.

Vamos conferir outra solução, por que está acima não é nossa melhor abordagem.

# Check-Lock-Check em Go

Um forma de melhorar e a segurar a garantia de um mínimo de bloqueio e ainda ser seguro para a goroutine é utilizar o padrão chamado “***Check-Lock-Check***\*”\*, ao adquirir bloqueios. Usamos este mesmo Patter em C e C++.  
O padrão funciona com a ideia de verificar primeiro, para minimizar qualquer bloqueio agressivo, já que uma instrução *IF* é menos dispendiosa do que o *bloqueio*.

No próximo momento teríamos de esperar e adquirir o bloqueio exclusivo para que apenas uma execução esteja dentro desse bloco em uma única vez. Com a primeira verificação e com bloqueio exclusivo, poderia haver outra goroutine que adquiriu o bloqueio, portanto, precisaríamos verificar novamente dentro do bloqueio para evitar a substituição da instância por outro.

***Confira o código abaixo:***

```go
var lock = &sync.Mutex{}

type DriverPg struct {
	conn string
}

var instance *DriverPg

func Connect() *DriverPg {
	
	// ainda não está perfeita, não é totalmente atomico
	if instance == nil {
		lock.Lock()
		defer lock.Unlock()
		instance = &DriverPg{conn: "DriverConnectPostgres"}
	}
	return instance
}

func main() {

	go func() {

		for i := 0; i < 100; i++ {
			time.Sleep(time.Millisecond * 600)
			fmt.Println(*Connect(), " - ", i)
		}
	}()

	go func() {

		fmt.Println(*Connect())
	}()

	fmt.Scanln()
}
```

Está abordagem acima é a melhor até o momento, mas ainda **não** é perfeita. Não há uma verificação atômica no estado de armazenamento da instância. Levando em consideração todas as considerações técnicas, isso ainda não é perfeito. Mas é muito melhor que as abordagens iniciais.

Usando o pacote *sync/atomic*, podemos carregar e definir de forma atômica um sinalizador que indicará se inicializamos ou não nossa instância.

***Confira o código abaixo:***

```go
// manter o estado
var atomicinz uint64

// lock mutex
var lock = &sync.Mutex{}

// driver
type DriverPg struct {
	conn string
}

// instancia Global
var instance *DriverPg

// funcao retornando
// o ponteiro de nossa
// struct
func Connect() *DriverPg {

	// garantindo que já entrou
	if atomic.LoadUint64(&atomicinz) == 1 {

		return instance
	}

	lock.Lock()
	defer lock.Unlock()

	// entra somente uma
	// únic vez
	if atomicinz == 0 {

		instance = &DriverPg{conn: "DriverConnectPostgres"}
		atomic.StoreUint64(&atomicinz, 1)
	}

	return instance
}

func main() {

	// chamada
	go func() {
		time.Sleep(time.Millisecond * 600)
		fmt.Println(*Connect())
	}()

	// 50 goroutine
	for i := 0; i < 50; i++ {
		go func(i int) {
			for {
				time.Sleep(time.Millisecond * 60)
				fmt.Println(Connect().conn, " - ", i)
			}
		}(i)
	}

	fmt.Scanln()
}
```

A biblioteca **sync/atomic** nos permite sinalizar e armazenar um conteúdo de forma segura e garantindo sua unicidade, bem parecido com [sync.Map](http://sync.Map), onde ele está armazenando em um storage e aceitando concorrência em sua implementação. O problema que tivemos que usar mais uma funcionalidade, mais recurso e consecutivamente um pouco mais lento nossa execução.

# sync.Once clean e poderoso

Temos o tipo “***Once***” na biblioteca **sync**, lembrando que esta biblioteca nativa em Golang é muito poderosa e temos que explora-la o máximo que conseguirmos, o objeto ***sync.Once*** executará uma ação exatamente uma vez e não mais, era o que faltava para nosso código ficar ainda mais poderoso e clean.

```go
// call somente
// uma unica vez
var once sync.Once

type DriverPg struct {
	conn string
}

// variavel Global
var instance *DriverPg

func Connect() *DriverPg {

	once.Do(func() {

		instance = &DriverPg{conn: "DriverConnectPostgres"}
	})

	return instance
}

func main() {

	// chamada
	go func() {
		time.Sleep(time.Millisecond * 600)
		fmt.Println(*Connect())
	}()

	// 100 goroutine
	for i := 0; i < 100; i++ {

		go func(ix int) {
			time.Sleep(time.Millisecond * 60)
			fmt.Println(ix, " = ", Connect().conn)
		}(i)
	}

	fmt.Scanln()
}
```

Com esta abordagem e cenário proposto nosso código além de ficar mais clean ficou bem melhor, a função ***sync.Once*** garante a **unicidade** de nossa instância, nosso código agora pode ter *100 goroutines* ou muito mais conforme a sua necessidade, coloca-los em concorrência não teremos problemas de “***Thread Safe***” ou checagem agressiva como vimos acima em outras abordagens. Uma forma simples e segura de escrever o código Golang para implementação do Pattern Singleton.

# `init( ) Uma outra abordagem`

Uma outra abordagem e também válida é utilizar o ***init( )***, ele é executado somente uma única vez e é chamado antes da função maim.  
Confere o código abaixo:

```go
type DriverPg struct {
	conn string
}

var instance *DriverPg

func Connect() *DriverPg {

	instance = &DriverPg{conn: "DriverConnectPostgres"}
	return instance
}

func init() {

	Connect()
}

func main() {

	// chamada
	go func() {
		time.Sleep(time.Millisecond * 600)
		fmt.Println(instance.conn)
	}()

	go func() {

		fmt.Println(*Connect())
	}()

	// 100 goroutine
	for i := 0; i < 100; i++ {

		go func(ix int) {
			time.Sleep(time.Millisecond * 60)
			fmt.Println(ix, " = ", instance.conn)
		}(i)
	}

	fmt.Scanln()
}
```

Porém esta abordagem existe uma desvantagem quando usamos **init( )**. Percebesse claramente que não é segura, nada impede de fazer uma chamada direta na função “***Connect***” como ocorreu na linha ***#28*** além de que existe uma limitação no uso do ***init*( )** com relação ao seu tempo de carregamento e o mais importante em Golang podemos ter vários ***init*** sendo executados não somente em um arquivo ou pacote mas em vários, e existe uma ordem de execução entre eles.

A *função* **init( )** não aceita argumentos nem retorna nenhum valor. Em contraste com nossa abordagem usando ***sync.Once***, o identificador **init()** não é declarado, portanto não pode ser referenciado.

O melhor cenário é escrever códigos que não dependam da ordem de inicialização, em versões anteriores do *golang* houve algumas reclamações e diversos problemas relatados, não escrevam códigos em um \*\*\*init( )\*\*\*que você precise de garantias de execução em determinado momento. A solução quando precisa de garantia explícita é escrever chamadas explícitas.

Para ter mais detalhes tem um página só disto em Golang [https://golang.org/doc/effective\_go.html#initialization,](https://golang.org/doc/effective_go.html#initialization) vale a pena a leitura detalhada sobre o ***init***( ), é uma implementação poderosa e robusta nas versões atuais do ***Golang*** mas sempre é bom ficar atento.

# `Variável recebendo a função`

Uma outra abordagem e também válida é utilizar uma variável global recebendo a função em um ***escopo global***, sabemos que em *Golang* as variáveis são atribuídas e declaradas antes da chamada do **init**( ) e da função main, então nesta abordagem o método retorna exatamente uma única vez a instância. Confere o código abaixo:

```go
type DriverPg struct {
	conn string
}

var instance *DriverPg

var instanceNew = *Connect()

func Connect() *DriverPg {

	if instance == nil {

		// <--- NOT THREAD SAFE
		instance = &DriverPg{conn: "DriverConnectPostgres"}
	}

	return instance
}

func main() {

	// chamada
	go func() {
		time.Sleep(time.Millisecond * 600)
		fmt.Println("goroutine 1: ", instanceNew.conn)
	}()

	go func() {

		fmt.Println("goroutine 2: ", *Connect())
	}()

	fmt.Scanln()
}
```

Esta abordagem é falha por que nada garante que a função irá ser chamado novamente em alguma parte do código, como ocorreu na linha ***#25*** a função foi chamada novamente, na linha ***#30*** é feito a instância do nosso singleton, porém nada garante esta unicidade.

# Conclusão

O ideal sem dúvida é utilização do ***sync.Once*** que garante com certeza a unicidade e que seja ***“Thread Safe”*** nos garantindo que não ocorra uma “[***race condition***](https://blog.golang.org/race-detector)”, ele só permite que a função seja executada somente uma única vez Golang flexibilizou e automatizou toda complexidade que teríamos em outras langs se fôssemos trabalhar com concorrência e simultaneidade. Realmente Golang ficou poderoso nestas abordagens, tornando-se simples de implementar e entender.

Quando falamos de concorrência toda nossa forma de pensar e codar as soluções utilizando ***Golang*** mudam drasticamente. Existem vários cenários que precisamos aplicar padrões e práticas em nossos projetos para aproveitar o máximo do poder que o ***Golang*** oferece.

Todo projeto que participo torna-se quase um “*mantra*” entre as equipes de desenvolvimento fazer diversos testes e revisões para aproveitar o máximo que a linguagem proporciona. Quanto mais aprofundamos nos estudos mais descobrimos que pouco sabemos sobre a linguagem. O legal de tudo é o desafio, quando trabalhamos com goroutines em ***Golang*** precisamos entender o quanto antes as funcionalidades e o que é possível fazer, comportamentos, segurança das goroutines são todos essenciais para melhorar cada vez mais o nosso código.

Caso tenha interesse em acompanhar ou trocar ideias sejam elas a nível iniciante, intermediário ou “***hardcore***” sobre a linguagem **Golang** existe alguns grupos de Golang espalhados na internet dois deles em especial sempre que possível estou presente é o do [**https://t.me/go\_br**](https://t.me/go_br) **(GoBr)** e o [**gophers.slack.com**](http://gophers.slack.com), comunidades ativas usando *Golang*, sempre estão compartilhando ideias, projetos, vagas e dúvidas todo o tempo e a todo momento estamos reaprendendo.

Espero ter ajudado a esclarecer um pouco as dúvidas sobre utilização do ***Pattern Singleton***, utilizando **variáveis globais**, **goroutines**, ***sync.Once***, **Init**( ), mutex e diversos outros pontos importantes e técnicos abordados no artigo.

Logo abaixo está todos os códigos fontes que utilizamos como exemplo no artigo caso tenha interesse de baixar e testar fique a vontade.

## Códigos fontes e exemplos do artigo

[https://github.com/jeffotoni/medium-posts](https://github.com/jeffotoni/medium-posts)

Gostaria de agradecer alguns colegas que ajudaram na revisão deste artigo para mantermos a qualidade do conteúdo e deixa-lo denso sem ficar complexo e jato. ***Obrigado*** :

[Joelson](https://medium.com/u/3f6c1dee6dad?source=post_page-----938d1debe626--------------------------------)

, [Marco Paganinii,](https://medium.com/u/a1237dcc0ed2?source=post_page-----938d1debe626--------------------------------) [Francisco Oliveira](https://medium.com/u/79525c7eb6d6?source=post_page-----938d1debe626--------------------------------)

No próximo artigo que está no forno será sobre lambdas, como escrever lambdas em Golang, será mais um desafio bem interessante, que estou preparando.

Quem curtiu e chegou até o final deixa uma palminha aí… Obrigado 🤘🤓