Dependency Management¶
O Go Overlay fornece um sistema robusto de gerenciamento de dependências que permite controlar a ordem de inicialização dos serviços. Isso é essencial quando você tem serviços que dependem de outros estarem operacionais antes de iniciar.
Overview¶
Em ambientes com múltiplos serviços, é comum ter dependências entre eles. Por exemplo:
- Uma aplicação web precisa que o banco de dados esteja rodando
- Um worker precisa que o Redis esteja disponível
- Um proxy reverso precisa que os serviços backend estejam prontos
O Go Overlay resolve essas dependências automaticamente, garantindo que os serviços iniciem na ordem correta.
Configuration¶
depends_on¶
O campo depends_on especifica quais serviços devem estar em estado RUNNING antes que o serviço atual possa iniciar.
Sintaxe:
Comportamento: - O serviço permanece em estado PENDING até que todas as dependências estejam RUNNING - Se uma dependência falhar ao iniciar, o serviço dependente não iniciará - Dependências são verificadas recursivamente
wait_after¶
O campo wait_after adiciona um delay adicional após as dependências estarem prontas, antes de iniciar o serviço.
Sintaxe:
Uso comum: - Aguardar que um banco de dados esteja totalmente inicializado - Dar tempo para serviços aceitarem conexões - Aguardar warmup de caches
Formato: - Aceita valores como: "5s", "30s", "1m", "2m30s"
dependency_wait_timeout¶
Timeout global para aguardar que dependências estejam prontas.
Comportamento: - Se as dependências não estiverem prontas dentro do timeout, o serviço falha - Previne que o sistema fique travado aguardando dependências que nunca iniciarão - Aplica-se a todos os serviços
How Dependencies Affect Startup¶
Startup Sequence¶
O Go Overlay determina a ordem de inicialização baseado no grafo de dependências:
flowchart TD
A[Load Configuration] --> B[Build Dependency Graph]
B --> C[Detect Circular Dependencies]
C --> D{Circular\nDependency?}
D -->|Yes| E[Fail with Error]
D -->|No| F[Topological Sort]
F --> G[Start Services in Order]
G --> H{Service Has\nDependencies?}
H -->|Yes| I[Wait for Dependencies]
H -->|No| J[Start Immediately]
I --> K{Dependencies\nRunning?}
K -->|Yes| L[Apply wait_after]
K -->|No| M{Timeout\nExpired?}
M -->|No| K
M -->|Yes| N[Fail Service]
L --> O[Start Service]
J --> O
O --> P{More\nServices?}
P -->|Yes| G
P -->|No| Q[All Services Started] Example: Simple Dependency Chain¶
[[services]]
name = "database"
command = "/usr/bin/mysqld"
enabled = true
[[services]]
name = "cache"
command = "/usr/bin/redis-server"
enabled = true
[[services]]
name = "api"
command = "/usr/bin/api-server"
depends_on = ["database", "cache"]
wait_after = "3s"
[[services]]
name = "web"
command = "/usr/bin/web-server"
depends_on = ["api"]
wait_after = "2s"
Startup order:
databaseecacheiniciam simultaneamente (sem dependências)- Aguarda ambos atingirem estado RUNNING
- Aguarda 3 segundos adicionais (
wait_after) apiinicia- Aguarda
apiatingir estado RUNNING - Aguarda 2 segundos adicionais
webinicia
Timeline:
t=0s: database, cache start
t=5s: database RUNNING
t=6s: cache RUNNING
t=9s: api starts (3s wait_after)
t=12s: api RUNNING
t=14s: web starts (2s wait_after)
t=16s: web RUNNING
Dependency Chain Examples¶
Example 1: Web Application Stack¶
Uma stack típica com banco de dados, cache, API e frontend:
[[services]]
name = "postgres"
command = "/usr/lib/postgresql/14/bin/postgres"
args = ["-D", "/var/lib/postgresql/data"]
user = "postgres"
enabled = true
[[services]]
name = "redis"
command = "/usr/bin/redis-server"
args = ["/etc/redis/redis.conf"]
enabled = true
[[services]]
name = "migration"
command = "/app/migrate"
args = ["up"]
depends_on = ["postgres"]
wait_after = "5s"
enabled = true
[[services]]
name = "api-server"
command = "/app/api"
depends_on = ["postgres", "redis", "migration"]
wait_after = "2s"
enabled = true
[[services]]
name = "nginx"
command = "/usr/sbin/nginx"
args = ["-g", "daemon off;"]
depends_on = ["api-server"]
wait_after = "1s"
enabled = true
Dependency graph:
Startup flow:
postgreseredisstart (no dependencies)- Wait for both to be RUNNING
migrationstarts after 5s delay- Wait for
migrationto complete api-serverstarts after 2s delay- Wait for
api-serverto be RUNNING nginxstarts after 1s delay
Example 2: Microservices with Shared Dependencies¶
[[services]]
name = "rabbitmq"
command = "/usr/lib/rabbitmq/bin/rabbitmq-server"
enabled = true
[[services]]
name = "postgres"
command = "/usr/lib/postgresql/14/bin/postgres"
args = ["-D", "/var/lib/postgresql/data"]
user = "postgres"
enabled = true
[[services]]
name = "user-service"
command = "/app/user-service"
depends_on = ["postgres", "rabbitmq"]
wait_after = "3s"
enabled = true
[[services]]
name = "order-service"
command = "/app/order-service"
depends_on = ["postgres", "rabbitmq"]
wait_after = "3s"
enabled = true
[[services]]
name = "notification-service"
command = "/app/notification-service"
depends_on = ["rabbitmq"]
wait_after = "2s"
enabled = true
[[services]]
name = "api-gateway"
command = "/app/gateway"
depends_on = ["user-service", "order-service"]
wait_after = "2s"
enabled = true
Dependency graph:
┌──> user-service ──┐
postgres ──┬────────┤ ├──> api-gateway
│ └──> order-service ─┘
│
rabbitmq ──┼────────────> notification-service
│
└────────────> (shared by multiple services)
Example 3: Complex Multi-Tier Application¶
# Data Layer
[[services]]
name = "mysql"
command = "/usr/bin/mysqld"
enabled = true
required = true
[[services]]
name = "redis"
command = "/usr/bin/redis-server"
enabled = true
required = true
# Application Layer
[[services]]
name = "backend-api"
command = "/app/backend"
depends_on = ["mysql", "redis"]
wait_after = "5s"
enabled = true
required = true
[[services]]
name = "worker-queue"
command = "/app/worker"
depends_on = ["mysql", "redis"]
wait_after = "5s"
enabled = true
[[services]]
name = "scheduler"
command = "/app/scheduler"
depends_on = ["mysql", "redis"]
wait_after = "5s"
enabled = true
# Presentation Layer
[[services]]
name = "frontend-server"
command = "/app/frontend"
depends_on = ["backend-api"]
wait_after = "3s"
enabled = true
# Proxy Layer
[[services]]
name = "nginx"
command = "/usr/sbin/nginx"
args = ["-g", "daemon off;"]
depends_on = ["frontend-server", "backend-api"]
wait_after = "2s"
enabled = true
Circular Dependency Detection¶
O Go Overlay detecta automaticamente dependências circulares e falha com erro claro.
What is a Circular Dependency?¶
Uma dependência circular ocorre quando dois ou mais serviços dependem um do outro, direta ou indiretamente:
Direct circular dependency:
[[services]]
name = "service-a"
depends_on = ["service-b"]
[[services]]
name = "service-b"
depends_on = ["service-a"] # ❌ Circular!
Indirect circular dependency:
[[services]]
name = "service-a"
depends_on = ["service-b"]
[[services]]
name = "service-b"
depends_on = ["service-c"]
[[services]]
name = "service-c"
depends_on = ["service-a"] # ❌ Circular!
Detection Behavior¶
Quando uma dependência circular é detectada:
- Go Overlay falha ao iniciar
- Erro claro é exibido nos logs
- O ciclo é identificado
Example error:
ERROR: Circular dependency detected: service-a -> service-b -> service-c -> service-a
Cannot start services with circular dependencies
How to Fix¶
Redesenhe suas dependências para remover o ciclo:
Before (circular):
After (acyclic):
Ou use um serviço intermediário:
Best Practices¶
1. Keep Dependency Chains Short¶
Evite cadeias muito longas de dependências:
2. Use wait_after Appropriately¶
Adicione delays apenas quando necessário:
[[services]]
name = "database"
command = "/usr/bin/mysqld"
# Sem wait_after - inicia imediatamente
[[services]]
name = "app"
command = "/usr/bin/app"
depends_on = ["database"]
wait_after = "5s" # Database precisa de tempo para aceitar conexões
3. Set Reasonable Timeouts¶
4. Make Critical Services Required¶
[[services]]
name = "database"
command = "/usr/bin/mysqld"
required = true # Sistema falha se database não iniciar
5. Test Dependency Order¶
Teste a ordem de inicialização:
# Observe logs de startup
docker logs -f <container-id>
# Verifique ordem de inicialização
docker logs <container-id> | grep "Starting service"
6. Document Dependencies¶
Comente suas configurações:
# API depends on database and cache being ready
[[services]]
name = "api"
command = "/app/api"
depends_on = ["database", "redis"]
wait_after = "3s" # Allow time for connection pools to initialize
Troubleshooting¶
Service Never Starts (Stuck in PENDING)¶
Sintomas: - Serviço permanece em PENDING indefinidamente - Logs mostram "Waiting for dependencies"
Causas possíveis: - Dependência não está iniciando - Dependência está falhando - Timeout muito curto
Soluções:
# Verifique status de todas as dependências
go-overlay status
# Verifique logs para erros
docker logs <container-id>
# Aumente timeout se necessário
[timeouts]
dependency_wait_timeout = "180s"
Dependency Timeout Expired¶
Sintomas: - Erro: "Dependency wait timeout expired" - Serviço falha ao iniciar
Soluções:
- Verifique se dependências estão configuradas corretamente
- Aumente
dependency_wait_timeout - Verifique se dependências estão habilitadas (
enabled = true)
Services Start in Wrong Order¶
Sintomas: - Serviços iniciam antes de suas dependências - Erros de conexão nos logs
Soluções:
- Verifique configuração
depends_on - Adicione
wait_afterse necessário - Verifique se não há typos nos nomes dos serviços
Circular Dependency Error¶
Sintomas: - Erro ao iniciar: "Circular dependency detected" - Sistema não inicia
Soluções:
- Revise o grafo de dependências
- Remova dependências desnecessárias
- Redesenhe arquitetura se necessário
Advanced Patterns¶
Optional Dependencies¶
Se um serviço pode funcionar sem uma dependência, não use depends_on. Em vez disso, implemente retry logic no serviço:
// Retry connection to optional service
for i := 0; i < 10; i++ {
conn, err := connectToService()
if err == nil {
break
}
time.Sleep(2 * time.Second)
}
Conditional Startup¶
Use enabled = false para desabilitar serviços opcionais:
[[services]]
name = "debug-service"
command = "/app/debug"
enabled = false # Habilite apenas em desenvolvimento
Health Check Dependencies¶
Implemente health checks nos serviços para garantir que estão realmente prontos:
// Health check endpoint
http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
if isReady() {
w.WriteHeader(http.StatusOK)
} else {
w.WriteHeader(http.StatusServiceUnavailable)
}
})
Related Topics¶
- Service Lifecycle - Como dependências afetam estados
- Services Configuration - Configuração detalhada de serviços
- Timeouts Configuration - Configuração de timeouts