Documentation
Feedback
Guides
VTEX IO Apps

VTEX IO Apps
Cache não é compartilhado entre pods (disco é `emptyDir`, efêmero e por-pod)
vtex.rewriter
Version: 1.70.1
Latest version: 1.70.1

TL;DR

Em VTEX IO, o cache em memória (LRU do node-vtex-api) e o cache em disco (DiskCache/LRUDiskCache, ex.: /cache, /usr/local/data) são por-pod e efêmeros. Não existe cache de disco compartilhado entre pods — nem entre pods co-localizados no mesmo nó do Kubernetes. Os diretórios graváveis do app são montados como volumes emptyDir, que o Kubernetes cria vazios por pod e descarta quando o pod morre.

Consequência para o rewriter: para ter cache cross-pod (o que precisamos para tirar carga do License Manager em rollout), a única opção suportada é um store compartilhado — VBase. Ver docs/binding-cache-vbase-proposal.md.

A hipótese investigada

Surgiu a dúvida: "se dois pods do mesmo app caem no mesmo nó, eles não compartilhariam o disco (/cache)? Aí um LRUDiskCache já daria cache cross-pod de graça."

A resposta é não. Abaixo, a trilha de evidências.

Evidências (código das plataformas)

1. Quais diretórios o app pode escrever

service-runtime-node (via service-runtime-base) define os diretórios graváveis do processo do app e faz chown para o usuário do app:


_10
// vendor/github.com/vtex/service-runtime-base/runner/run_child_app.go
_10
// appWritableDirs are volume mounts that the app process may write to.
_10
// The service-runtime-base is created as root (UID/GID 0); we chown
_10
// them to app uid:gid (1000:1000) so the child can use /tmp, /cache, /run.
_10
var appWritableDirs = []string{"/tmp", "/cache", "/run"}

E o diretório de dados do app:


_10
// vtex/app-operator/controllers/deployment/container/container.go
_10
envvar.Plain("VTEX_DATA_DIR", "/usr/local/data"),

2. Como esses diretórios viram volumes

O app-operator declara os volumes do deployment. /usr/local/data, /cache, /tmp e /run são todos DataSpec:


_10
// vtex/app-operator/controllers/deployment/volume/volume_mounts.go
_10
volumeSpecs := []Spec{
_10
&DataSpec{ Name: "volume-data", Path: "/usr/local/data" },
_10
&DataSpec{ Name: "volume-cache", Path: "/cache" },
_10
&DataSpec{ Name: "volume-tmp", Path: "/tmp" },
_10
&DataSpec{ Name: "volume-run", Path: "/run" },
_10
}

3. O ponto-chave: DataSpec vira emptyDir

O DataSpec.volume() monta um corev1.Volume só com Name, sem hostPath, sem PersistentVolumeClaim, sem nfs:


_10
// vtex/app-operator/controllers/deployment/volume/data_spec.go
_10
func (spec *DataSpec) volume() corev1.Volume {
_10
return corev1.Volume{
_10
Name: spec.Name,
_10
}
_10
}

No Kubernetes, um Volume sem source explícito default para emptyDir{}. E emptyDir, por definição:

  • é criado vazio quando o pod é agendado no nó;
  • é exclusivo daquele pod (não é compartilhado com outros pods, mesmo no mesmo nó);
  • é apagado quando o pod é removido do nó.

Conclusão

  • Memória (LRU) → por-pod, efêmero. Cada réplica tem seu próprio heap.
  • Disco (/cache, /usr/local/data via DiskCache/LRUDiskCache) → por-pod, efêmero. É emptyDir, não hostPath/PVC → não há disco compartilhado no nó.
  • Logo, nenhuma camada local dá cache cross-pod. Num rollout, cada pod novo sobe com cache frio e vai à origem (ex.: License Manager) sozinho.

Nota: a plataforma suporta disco compartilhado (PVC ReadWriteMany) em casos específicos — ex.: o build cache do builder-hub. Mas isso é configuração dedicada daquele serviço; os pods de app comuns (como o rewriter) usam emptyDir.

Implicação para o rewriter

Para reduzir as chamadas ao License Manager entre pods (e domar os 429 MaxRequestsByInstance durante rollout), precisamos de um store compartilhado entre pods. A escolha suportada e barata é o VBase (escopado por conta/workspace), usado como camada intermediária, com uma LRU em processo por cima para o hot path. Detalhes em docs/binding-cache-vbase-proposal.md.

See also
Vtex.rewriter
VTEX IO Apps
VTEX App Store
VTEX IO Apps
Was this helpful?