depth of field photography of file arrangement

Como listar arquivos de um diretório

Nesse post vou mostrar três formas diferentes para ler um diretório com Golang.

[SPOILER ALERT] No final desse post vou mostrar como eu apliquei um dos exemplos para poder ler um diretório que continha mais de 3.6 milhões de arquivos.

Vamos iniciar os exemplos utilizando a função ReadDir do package ioutil.

package main

import (
    "fmt"
    "io/ioutil"
    "log"
)

func main() {
    files, err := ioutil.ReadDir("/tmp/")
    if err != nil {
        log.Fatal(err)
    }

    for _, file := range files {
        fmt.Println(file.Name(), file.IsDir())
    }
}
Leia mais »
gray concrete road near forest

Mutex ou channels? Como resolver race condition

Existem basicamente duas formas para resolver problemas de race condition. Mutex e Channels.

Se você não está familiarizado com o termo race condition, não se preocupe. Esse termo é utilizado para descrever um pedaço do código que será executado por múltiplas goroutines e que, a cada execução, seu resultado pode variar devido a forma como o Go alterna a execução entre goroutines.

Para ficar um pouco mais claro, vamos dar uma olhada no código abaixo.

package main

import (
	"fmt"
	"sync"
)

var total = 0

func count(wg *sync.WaitGroup) {
	total++
	wg.Done()
}

func main() {
	var wg sync.WaitGroup

	for i := 0; i < 1000; i++ {
		wg.Add(1)
		go count(&wg)
	}
	wg.Wait()
	fmt.Println("total: ", total)
}
Leia mais »
snow top mountain under clear sky

Como diminuir o tamanho da sua aplicação com ldflags

Como você deve ter visto no vídeo que postamos no canal, uma das formas de reduzir o tamanho de uma imagem docker para aplicações Go é utilizando a imagem scratch como base.

Mas e o binário? Como podemos reduzi-lo sem remover código?

É isso que vamos ver nesse post.

Para que tenhamos uma base de programa para testar os comandos que vamos ver nesse post, vamos escrever uma API bem simples.

package main

import (
	"fmt"
	"log"
	"net/http"
)

func main() {
	http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(rw, "Olá Mundo\\n")
	})

	log.Fatal(http.ListenAndServe(":8080", nil))
}
Leia mais »
view of elephant in water

Como funciona a gestão de conexões com banco de dados do package database/sql

Uma coisa que sempre tive dúvidas era sobre como o package database/sql geria as conexões com banco de dados. A forma como eu costumava trabalhar era abrir uma nova conexão a cada request recebida, fechando-a ao final.

Mas será que essa é a melhor forma?

A resposta curta é NÃO. Mas como eu sei que você, assim como eu não se satisfaz com uma resposta simples dessa, vamos entender melhor como o package padrão do Go gerencia as conexões.

A primeira coisa que precisamos ter em mente, é que sempre que abrimos uma nova conexão utilizando o sql.Open, ele nos retornará uma struct do tipo *sql.DB.

Essa struct, além de ser segura para ser utilizada em múltiplas goroutines, ou seja, “thread-safe”, ela não lida somente com uma conexão, mas sim com um pool de conexões.

Leia mais »

Quando utilizar Goroutines?

Uma dúvida que assombra a maioria dos desenvolvedores Go, e não exclusivamente iniciantes, é sobre quando utilizar goroutines.

Nesse post vou dar algumas dicas para ajudar na análise e tomada de decisão na hora de adotar ou não a utilização de goroutines em seu projeto.

Antes de mais nada, assim como qualquer coisa relacionada a tecnologia, nem todo projeto faz sentido utilizar goroutines. Você pode até pensar, “meu sistema está lento. Já sei, vou usar goroutines para resolver.”, gastar muito tempo, já que SÓ colocar um go antes da chamada da função pode não ser o suficiente, e no final não ver melhora nenhuma ou até mesmo notar uma piora no desempenho.

Leia mais »

Implementando um worker pool

Agora que já falamos praticamente tudo que havia para ser falado sobre goroutines e channels, vamos utilizar esse conhecimento para implementar um worker pool.

Se você não conhece o termo, um worker pool é basicamente uma coleção de threads que ficam esperando tarefas serem atribuídas a elas. Quando a thread finaliza a tarefa que foi atribuída, se torna disponível novamente para execução de uma nova tarefa.

Antes de começar a meter a mão na massa, vou deixar aqui o link para os outros posts da série sobre goroutines e channels.

O worker pool que vamos implementar irá somar os dígitos passados e armazenar o resultado.

Leia mais »

Buildando aplicações com Bazel (parte 1)

Bazel é um ferramenta criada e mantida pela Google que ajuda no processo de build de várias linguagens, sendo uma delas nosso querido Golang.

Duas de suas grandes vantagens são:

  • Build de multiplas aplicações em monorepo sem precisar ficar entrando e saindo de pastas.
  • Cache remoto das etapas de build (para mais detalhes, leia o post “Como Bazel funciona internamente“).

Embora para maioria das linguagens toda criação e manutenção dos arquivos do Bazel tenha que ser feita manualmente, para o Go temos o gazelle, uma ferramenta que nos auxilia nesse processo.

Se você ainda não tem o Bazel instalado na sua máquina, siga o tutorial do próprio site oficial segundo o OS que você utiliza. Se você já tem, execute um bazel version para garantir que você está utilizando a última versão (4.2.2).

Como aplicação exemplo, vamos utilizar o mesmo código do post Implementando uma API com gorilla/mux, mas separando as duas funções de “handler” em um novo package chamado handlers.

Leia mais »

Como encontrar pontos de melhoria de performance

Nesse post vamos falar sobre a técnica de profiling, que consegue nos ajudar muito na hora de encontrar melhorias em relação a performance dos nossos programas, principalmente, consumo de memória e CPU.

Para realizar os testes, vamos recuperar o código que escrevemos no post “Qual a melhor forma de aumentar um array?

Apenas para relembrar, nesse post escrevemos 3 funções e 3 testes, um para cada função. Cada uma das funções mostrar uma forma diferente de expandir um array.

Tendo relembrado isso, vamos agora fazer o profiling de cada uma das funções e analisar o resultado.

Para coletar dados de cpu e memória, vamos adicionar as flags -memprofile e -cpuprofile no comando que executamos para fazer benchmark das funções.

Leia mais »

Benchmark: API com gorilla mux usando goroutines vs sem goroutines

Já faz um certo tempo que eu queria dedicar algumas horas para testar um cenário onde os dados que uma request deveria apresentar fossem obtidos com goroutines vs sem goroutines.

Finalmente esse dia chegou, mas antes de apresentar os resultados, vamos construir juntos uma simples API onde vamos executar os testes para medir a performance.

O objetivo da request será obter o nome e a quantidade total de pedidos que uma pessoa já realizou.

Para não ter que envolver banco de dados, vamos criar duas variáveis contendo os dados que podemos retornar.

var (
    people = [][]string{
        []string{"1", "Tiago Temporin"},
        []string{"2", "João Silva"},
        []string{"3", "Mateus Cardoso"},
        []string{"4", "Maria Lina"},
        []string{"5", "Camila Manga"},
        []string{"6", "Joice Santos"},
        []string{"7", "Lucas Leal"},
        []string{"8", "Vanessa da Terra"},
        []string{"9", "Mateus de Morais"},
        []string{"10", "Maria Luiza"},
    }

    orders = [][]string{
        []string{"1", "5"},
        []string{"2", "10"},
        []string{"3", "0"},
        []string{"4", "0"},
        []string{"5", "2"},
        []string{"6", "9"},
        []string{"7", "3"},
        []string{"8", "15"},
        []string{"9", "3"},
        []string{"10", "7"},
    }
)
Leia mais »