Lazy Module Loading e Graph Pruning
Introdução
Seção intitulada “Introdução”Go 1.17 (agosto de 2021) introduziu duas otimizações no sistema de módulos:
- Module Graph Pruning (Poda do Grafo de Módulos)
- Lazy Module Loading (Carregamento Preguiçoso de Módulos)
O que isso muda no dia a dia? O Go pode resolver os pacotes usados pelo projeto lendo menos arquivos go.mod. Essas otimizações continuam presentes no Go 1.27.
O problema: Go ≤ 1.16
Seção intitulada “O problema: Go ≤ 1.16”Em módulos com go 1.16 ou inferior, a seleção de versões considera todo o grafo transitivo de requisitos. Isso exige ler arquivos go.mod de módulos que podem não fornecer nenhum pacote usado no build.
Ler um go.mod e baixar o código de um módulo são operações diferentes. O Go pode precisar dos requisitos de uma dependência para selecionar versões sem precisar compilar seus pacotes.
A solução: Go 1.17+
Seção intitulada “A solução: Go 1.17+”Module Graph Pruning (Poda do Grafo)
Seção intitulada “Module Graph Pruning (Poda do Grafo)”Se o módulo principal declara go 1.17 ou superior, o grafo inclui apenas os requisitos imediatos das dependências que também declaram Go 1.17+. Dependências com go 1.16 ou inferior ainda exigem seu grafo transitivo completo, inclusive quando alcançam módulos mais novos.
Para isso funcionar, go mod tidy registra no go.mod os módulos que fornecem pacotes importados direta ou indiretamente pelo código e pelos testes do módulo principal.
Seu módulo (go 1.27) ├── Dependência A (go 1.17+) │ └── Requisitos imediatos de A └── Dependência B (go 1.16) └── Grafo transitivo completo de BPodar requisitos de um módulo não significa remover esse módulo do resultado de go list -m all. Sua versão selecionada continua conhecida e seus pacotes podem ser carregados quando necessários.
Lazy Module Loading
Seção intitulada “Lazy Module Loading”Com go 1.17 ou superior, o Go tenta carregar os pacotes pedidos usando os requisitos do módulo principal antes de carregar o restante do grafo:
1. Lê o go.mod do módulo principal2. Procura os pacotes nos módulos exigidos3. Se faltar informação, carrega o restante do grafo sob demanda4. Confere os requisitos dos módulos que fornecem os pacotes usadosIsso evita trabalho quando os requisitos já são suficientes para executar o comando.
Como funciona na prática
Seção intitulada “Como funciona na prática”Estrutura do go.mod
Seção intitulada “Estrutura do go.mod”Continuando o tutorial básico, o projeto importa rsc.io/quote, que usa pacotes de outros módulos:
module example.com/hello
go 1.27.1
require rsc.io/quote v1.5.2
require ( golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c // indirect rsc.io/sampler v1.3.0 // indirect)O comentário // indirect indica que o módulo principal não importa diretamente pacotes daquela dependência. Esses requisitos ajudam o Go a localizar os pacotes sem carregar todo o grafo antes de começar.
Blocos de require separados
Seção intitulada “Blocos de require separados”Desde Go 1.17, go mod tidy separa os requisitos indiretos dos diretos. No Go 1.27, para módulos com go 1.27 ou superior, também consolida blocos duplicados em até dois blocos.
# Conferir sem modificar os arquivosgo mod tidy -diff
# Aplicar a organizaçãogo mod tidyVeja Go 1.26-1.27 para um exemplo de consolidação.
Impacto no go.sum
Seção intitulada “Impacto no go.sum”O go.sum guarda os hashes necessários para autenticar arquivos .mod e o conteúdo das versões dos módulos. Não existe uma proporção fixa entre o número de dependências, o tamanho do arquivo e o tempo de build.
Por padrão, go mod tidy preserva também os checksums necessários para a versão do Go anterior à declarada. Por isso, um módulo go 1.17 pode manter hashes para o grafo completo usado pelo Go 1.16. Um módulo go 1.18 já considera a compatibilidade com o grafo podado do Go 1.17.
# Para um módulo go 1.17, manter compatibilidade de checksums com 1.16go mod tidy -go=1.17 -compat=1.16Esse comando só se aplica se o código e as dependências forem compatíveis com a versão escolhida. -compat controla a conferência das versões selecionadas e os checksums preservados; não faz o código aceitar recursos de linguagem mais antigos.
Atualizando um projeto
Seção intitulada “Atualizando um projeto”Para um projeto que vai passar a exigir Go 1.27:
go get go@1.27.0go mod tidygo test ./...git diff -- go.mod go.sumRevise as mudanças e teste também com o menor toolchain que o projeto promete suportar. Mudar a linha go afeta a versão da linguagem e o requisito mínimo, além do comportamento de resolução de módulos.
Inspecionando o grafo
Seção intitulada “Inspecionando o grafo”# Requisitos que formam o grafogo mod graph
# Versões selecionadasgo list -m all
# Dependências diretas do módulo principalgo list -m -f '{{if and (not .Main) (not .Indirect)}}{{.Path}} {{.Version}}{{end}}' allA quantidade de módulos, sozinha, não comprova que o lazy loading está ativo. A diretiva go e os requisitos de cada dependência determinam o comportamento.
Comportamento com workspaces
Seção intitulada “Comportamento com workspaces”Em um workspace, cada módulo mantém a versão da linguagem definida em seu go.mod. O grafo do workspace combina os requisitos dos módulos principais. Uma dependência antiga pode exigir a expansão de parte desse grafo.
# Testar todos os módulos do workspace (Go 1.25+)go test work
# Testar um módulo sem o workspacecd appGOWORK=off go test ./...Veja Workspace Mode.
Troubleshooting
Seção intitulada “Troubleshooting”Problema: go mod tidy está lento
Seção intitulada “Problema: go mod tidy está lento”Confira a versão declarada e os módulos envolvidos. O tidy considera imports dos testes e arquivos para diferentes plataformas e build tags, por isso pode precisar de mais dependências que um build comum.
go mod edit -jsongo mod graphProblema: go.sum está grande
Seção intitulada “Problema: go.sum está grande”Execute go mod tidy e revise o diff. O arquivo pode conter hashes de várias versões porque o Go precisa ler seus requisitos. Seu tamanho não indica, por si só, um problema.
go mod tidygit diff -- go.sumProblema: builds estão lentos
Seção intitulada “Problema: builds estão lentos”Meça antes de mudar configurações. Limpar o cache com go clean -modcache força novos downloads e não é uma otimização de build. Separe o tempo gasto em downloads, compilação e testes ao investigar.