Etapa 03 — Object storage do XLS recebido (Garage)¶
Status: Aprovado · Responsável: Gustavo Madruga · Atualizado em: 2026-06-15
Estende a recepção da etapa 01: onde o binário do
.xlsxé gravado e lido.
1. Contexto e escopo¶
O binário do .xlsx recebido era guardado em BYTEA no Postgres, o que infla o
banco com o volume de uploads. O objetivo é mover o binário para object storage
S3-compatível (Garage) sem perder a rede de segurança durante a transição.
Dentro do escopo: seam de storage; 3 modos de operação; chave content-addressed; backfill no startup; tratamento de falha de I/O no upload.
Fora do escopo: CDN/cache de leitura; versionamento de objeto.
2. Componentes e modelo de dados¶
flowchart TB
rec[receber upload] --> coord[ArmazenamentoArquivoService]
coord -->|seam ArquivoXlsStorage| s3[S3ArquivoXlsStorage<br/>AWS SDK v2]
s3 --> garage[(Garage bucket: vantroba)]
coord -.->|modo PSQL / PSQL_GARAGE| bytea[(xls_processamento.arquivo_bytes)]
Único ponto que conhece o modo: service/ArmazenamentoArquivoService. As
referências do objeto vivem em xls_processamento (V8): objeto_bucket
(VARCHAR(63)), objeto_chave (VARCHAR(512)), objeto_tamanho_bytes (BIGINT); o
binário no Postgres fica em arquivo_bytes (BYTEA, V1). Chave content-addressed
{aaaa}/{mm}/{checksum_sha256}.xlsx, particionada pela data de referência do
upload (cliente AWS SDK v2 com forcePathStyle(true) — obrigatório para o Garage).
3. Fluxos e estados¶
app.storage.modo / env S3_FILE_STORAGE (sempre em MAIÚSCULAS; enum
ArmazenamentoModo):
| Modo | Grava | Lê (processamento) | Backfill no startup |
|---|---|---|---|
PSQL |
só Postgres (arquivo_bytes) |
Postgres | no-op |
PSQL_GARAGE (default) |
Postgres e Garage | Garage | copia legados p/ Garage, mantém arquivo_bytes |
GARAGE |
só Garage | Garage | migra legados e zera arquivo_bytes |
- Leitura com fallback cruzado: lê da fonte preferida do modo e recorre ao outro backend se vazia — cobre registros em transição de modo.
- Falha de I/O no upload →
StorageIndisponivelException→ HTTP 503 (sem linha persistida; o cliente reenvia). - Backfill (
StorageBackfillService,@Order(-100), roda antes do recovery): idempotente, isola falha por linha, re-enfileiraPENDENTEmigrado. - A coluna
arquivo_bytesnão é dropada — o "cleanup" do modoGARAGEé zerar em runtime (reversível: voltar paraPSQL_GARAGEre-popula no backfill).
4. Configuração¶
| Env | Property | Default |
|---|---|---|
S3_FILE_STORAGE |
app.storage.modo |
PSQL_GARAGE |
GARAGE_ENDPOINT |
garage.endpoint |
http://localhost:3900 |
GARAGE_REGION |
garage.region |
garage |
GARAGE_ACCESS_KEY / GARAGE_SECRET_KEY |
garage.access-key/garage.secret-key |
dev keys |
GARAGE_BUCKET |
garage.bucket |
vantroba |
GARAGE_CRIAR_BUCKET |
garage.criar-bucket |
false |
Prefixo migrado
s3.*→garage.*na adoção do xadm-comum-storage 0.2.0 (oS3Configda lib passou a@ConfigurationProperties("garage")). Envs antigasS3_*seguem via fallback${GARAGE_X:${S3_X:default}}.S3_FILE_STORAGE/app.storage.modoinalterado.
Em testes que não exercitam storage, fixar app.storage.modo=PSQL evita depender
do Garage. Setup do cluster Garage (init manual): operacao/deploy.
5. Decisões¶
| Nº | Decisão |
|---|---|
| 0008 | Object storage Garage com 3 modos |
6. Riscos¶
- Garage indisponível no modo
GARAGE→ uploads recusados (503) até voltar; o modoPSQL_GARAGEmitiga durante a transição. - Migração irreversível por engano → mitigado:
arquivo_bytesnão é dropada; o modoGARAGEsó zera em runtime.