
Software Architect
Pós-graduado em arquitetura de software e soluções. Conecto profundidade técnica com resultados de negócio para entregar produtos que as pessoas realmente usam. Também mentoro desenvolvedores e criadores em programas ao vivo, podcasts e iniciativas de comunidade focadas em tecnologia inclusiva.
Continúa explorando temas similares

Um guia de arquitetura em nível de produção para construir um sistema estilo Instagram com feed personalizado, stories, reels, mensagens, notificações, ranking em tempo real e operação global.

Um guia completo de system design para cache distribuído com Redis e Memcached, cobrindo cache-aside, read-through, write-through, invalidação, TTL, eviction, sharding, replicação, hot keys, preven...

Um guia completo de system design para construir uma plataforma de compartilhamento de corridas lidando com milhões de viagens simultâneas, matching geoespacial em tempo real, precificação dinâmica e predição de ETA em escala global.
Una lista de 47 puntos para encontrar errores, riesgos de seguridad y problemas de rendimiento antes del lanzamiento.
Templates probados en producción, usados por desarrolladores. Ahorra semanas de setup en tu próximo proyecto.

Resumo: Um guia completo de system design para construir uma plataforma estilo Dropbox ou Google Drive com upload resumível, chunking, deduplicação, metadados, cursores de sincronização, compartilhamento, antivírus, cotas, retenção, tiering e durabilidade multi-região.
Um produto de armazenamento de arquivos parece simples até um notebook perder Wi-Fi em 97% do upload de um arquivo de 4 GB. Aí os problemas reais aparecem. O sistema precisa continuar do último byte aceito. Ele precisa evitar guardar o mesmo chunk um milhão de vezes.
Ele precisa sincronizar um rename feito no celular para três laptops sem baixar o arquivo inteiro. Ele precisa permitir que um usuário compartilhe uma pasta com um time enquanto outro revoga um link público. Ele precisa escanear malware, gerar previews, aplicar cotas, preservar versões e sobreviver a falhas de região. Este guia projeta esse sistema do zero.
O produto alvo é uma plataforma estilo Dropbox ou Google Drive, não apenas um object store bruto. Usuários pensam em arquivos, pastas, dispositivos, versões, links, permissões e sync offline. O backend pensa em linhas de metadados, chunks imutáveis, logs append-only, manifests, ACLs e jobs assíncronos. A arquitetura fica clara quando separamos esses dois mundos.
Comece definindo o produto. Não estamos projetando S3. Estamos projetando um workspace de arquivos para usuários. O sistema deve suportar web, mobile e desktop. Sync desktop faz parte do escopo porque ele gera boa parte das decisões difíceis.
Funcionalidades principais:
| Requisito | Meta | Motivo |
|---|---|---|
| Disponibilidade de upload | 99,99% para sessão e commit | Usuários confiam arquivos importantes |
| Latência de metadados | p95 < 100 ms, p99 < 300 ms | Navegação em pastas precisa parecer instantânea |
| Commit de arquivo pequeno | p95 < 500 ms após bytes aceitos | Fotos e documentos dominam contagem |
| Upload de arquivo grande | resumível para 100 GB+ | Falhas de rede são normais |
| Frescor de sync | p95 < 5 segundos para devices online | Sync desktop precisa parecer vivo |
| Durabilidade | alvo lógico de 11 noves | Perda de dados é existencial |
| Consistência | forte para metadados; eventual para thumbnails e scans | Usuário espera árvore correta |
| Segurança | criptografia em trânsito e repouso | Conteúdo é sensível |
| Controle de abuso | scan antes de compartilhamento amplo | Links públicos criam risco de distribuição |
| Eficiência de custo | tiering frio e compactação | Custo de storage acumula para sempre |
Pergunte antes de desenhar caixas:
Os números abaixo são premissas direcionais. Eles não são afirmações sobre uma empresa específica.
Usuários registrados: 500 milhões
Usuários ativos mensais: 120 milhões
Usuários ativos diários: 40 milhões
Usuários pagos / enterprise: 15 milhões
Dispositivos ativos médios por DAU: 2,2
Arquivos enviados por dia: 600 milhões
Bytes lógicos de upload por dia: 25 PB
Tamanho médio por contagem: 8 MB
Arquivos acima de 1 GB: 0,5% dos uploads
Chunk mediano: 8 MB
Economia por deduplicação: 25% dos bytes lógicos
Operações de metadados por DAU por dia: 120
Multiplicador de pico: 5x
Janela de lixeira: 30 dias
Retenção de versão: 30 dias grátis, 180+ dias pago
report.pdf para board-report.pdf, a próxima listagem da pasta não deve mostrar os dois nomes como atuais. Use consistência eventual para artefatos derivados. Thumbnails, índices full-text, vereditos de malware, tiering e analytics podem atrasar.
Use logs append-only para sync. Cada mutação de metadados escreve um evento durável. Dispositivos avançam cursores nesse log. Esse é o eixo de um sync confiável.Estimativas de envelope evitam arquitetura vaga. O objetivo não é precisão. O objetivo é revelar gargalos cedo.
Bytes lógicos de upload/dia = 25 PB
Bytes/s médios = 25 PB / 86.400 ~= 289 GB/s
Multiplicador de pico = 5x
Ingress no pico ~= 1,45 TB/s
Após 25% de economia por dedup:
Bytes físicos novos/dia ~= 18,75 PB
Writes físicos médios ~= 217 GB/s
Writes físicos no pico ~= 1,08 TB/s
Ingress é grande, mas gerenciável quando uploads vão direto para gateways regionais e object storage. Ele não é gerenciável se servidores de aplicação proxyarem cada byte pelo serviço de metadados.
DAU = 40 milhões
Operações de metadados por DAU por dia = 120
Operações de metadados/dia = 4,8 bilhões
QPS médio de metadados = 55.555
QPS de pico ~= 277.777
Sessões de upload/dia = 600 milhões
Sessões de upload/s médias ~= 6.944
Sessões de upload/s no pico ~= 34.722
Writes de chunk/dia com chunk médio de 8 MB:
25 PB / 8 MB ~= 3,1 bilhões de tentativas/dia
QPS médio de chunk write ~= 36.000
QPS de pico de chunk write ~= 180.000
QPS de chunk write sozinho não basta. O tamanho do payload domina. O serviço de metadados deve ver apenas hashes, tamanhos, offsets e referências.
Usuários ativos totais = 500 milhões
Arquivos médios por usuário ativo = 2.000
Registros lógicos de arquivo = 1 trilhão
Linha média de metadados = 700 bytes
Metadados brutos = 700 TB
Multiplicador de índices e histórico = 4x
Footprint de metadados ~= 2,8 PB
Change log:
Mudanças de metadados/dia = 4,8 bilhões
Evento médio = 700 bytes
Change log bruto/dia ~= 3,36 TB
Com replicação e índices ~= 15 TB/dia
Metadados viram um grande problema de banco distribuído. Mesmo assim, seguem muito menores que blob storage.
Bytes lógicos/dia = 25 PB
Bytes físicos novos/dia após dedup = 18,75 PB
Overhead de retenção/versionamento = 1,35x
Overhead efetivo de replicação/erasure coding = 1,5x
Crescimento físico diário ~= 18,75 PB * 1,35 * 1,5
Crescimento backend diário ~= 38 PB/dia
Por isso lifecycle policy, compactação, cold tiering e fronteiras de dedup importam. Economias percentuais pequenas viram dinheiro grande.
Arquivos enviados/dia = 600 milhões
Arquivos elegíveis a preview = 45%
Arquivos com artefatos derivados/dia = 270 milhões
Jobs médios por arquivo = 2,5
Jobs derivados/dia ~= 675 milhões
Jobs/s médios ~= 7.812
Jobs/s no pico ~= 39.000
Preview deve ser assíncrono. Não coloque isso no caminho crítico do commit.
DAU = 40 milhões
Dispositivos ativos médios = 2,2
Dispositivos online = 88 milhões
Colaboradores médios por arquivo alterado = 1,8
Um write pode produzir:
- um evento do owner
- um evento da pasta pai
- várias visões compartilhadas
- várias notificações de device
A arquitetura tem quatro planos:
Separe APIs de metadados de transferência massiva de bytes. APIs de metadados respondem:
O caminho crítico deve ser curto:
O caminho de sync:
Bytes de arquivo devem ser imutáveis após commit. Uma versão aponta para um manifest. Um manifest aponta para chunks. Chunks são endereçados por hash de conteúdo.
O caminho /team/design/logo.png é metadado. Os bytes não são o caminho. Um rename deve atualizar metadados e emitir evento. Ele não deve copiar blobs.
Sync precisa de histórico. Um device não pergunta: "qual é o estado atual da pasta?" Ele pergunta: "o que mudou depois do cursor 928173?" Essa diferença importa. Estado atual serve para navegação. Histórico de mudanças serve para sincronização.
Clientes desktop fazem retry agressivo. Clientes mobile repetem depois de background wakeups. Browsers repetem depois de troca de rede. Todo endpoint de escrita deve aceitar idempotency keys ou operation IDs determinísticos.
Usuário pode enviar arquivo rapidamente. O sistema pode depois:
Os exemplos usam REST por clareza. Internamente, o mesmo design funciona com gRPC.
| Recurso | Propósito |
|---|---|
/upload-sessions | Criar sessões de upload resumível |
/upload-sessions/{id}/chunks | Enviar bytes de chunk |
/upload-sessions/{id}/status | Consultar offset e ranges aceitos |
/upload-sessions/{id}/commit | Commitar manifest em versão de arquivo |
/files/{id} | Ler e mudar metadados |
/files/{id}/content | Baixar bytes |
/folders/{id}/children | Listar entradas da pasta |
/changes | Listar mudanças após cursor |
/shares | Conceder acesso a usuário ou grupo |
/links | Criar e gerenciar links públicos |
/devices/{id}/cursor | Guardar checkpoints de sync |
POST /v1/upload-sessions HTTP/1.1
Authorization: Bearer <token>
Idempotency-Key: 87fb7c9a-1b3c-4a9b
Content-Type: application/json
{"parent_id":"fld_9mS7","name":"resultado-trimestral.pdf","size_bytes":73400320,"content_type":"application/pdf","client_modified_at":"2026-02-11T10:21:30Z","mode":"add","base_revision_id":null,"chunking":{"strategy":"fixed","chunk_size_bytes":8388608,"hash":"sha256"}}
201 Created
Content-Type: application/json
{"upload_session_id":"ups_01HR8Q7TVX2M","expires_at":"2026-02-12T10:21:30Z","accepted_offset":0,"recommended_chunk_size_bytes":8388608,"max_parallel_chunks":4}
Notas de design:
Idempotency-Key evita sessões duplicadas após retry.parent_id evita ambiguidade de path.base_revision_id permite detecção de conflito em overwrite.expires_at limita limpeza de sessões abandonadas.recommended_chunk_size_bytes deixa o serviço ajustar memória e throughput.PUT /v1/upload-sessions/ups_01HR8Q7TVX2M/chunks/0 HTTP/1.1
Authorization: Bearer <token>
Content-Type: application/octet-stream
Content-Length: 8388608
Content-Range: bytes 0-8388607/73400320
X-Chunk-SHA256: 6e7f...
202 Accepted
Content-Type: application/json
{"chunk_index":0,"start":0,"end":8388607,"accepted":true,"content_hash":"sha256:6e7f...","dedup_hit":true,"accepted_offset":8388608}
Para upload sequencial, use accepted_offset. Para chunks paralelos, acompanhe índices e monte o manifest no commit. Os dois modelos são válidos. Uploads sequenciais são mais simples.
Chunks paralelos melhoram throughput em arquivos grandes.
GET /v1/upload-sessions/ups_01HR8Q7TVX2M/status HTTP/1.1
Authorization: Bearer <token>
{"upload_session_id":"ups_01HR8Q7TVX2M","state":"open","accepted_ranges":[{"start":0,"end":33554431},{"start":50331648,"end":58720255}],"missing_ranges":[{"start":33554432,"end":50331647},{"start":58720256,"end":73400319}],"expires_at":"2026-02-12T10:21:30Z"}
Esse endpoint é essencial depois de restart do app mobile. Ele permite continuar sem confiar na memória local.
POST /v1/upload-sessions/ups_01HR8Q7TVX2M/commit HTTP/1.1
Authorization: Bearer <token>
Idempotency-Key: commit-87fb7c9a
Content-Type: application/json
{"parent_id":"fld_9mS7","name":"resultado-trimestral.pdf","mode":"add","client_operation_id":"op_desktop_0001982","chunks":[{"index":0,"offset":0,"size_bytes":8388608,"hash":"sha256:6e7f..."}],"file_hash":"sha256:b91a..."}
{"file_id":"fil_2bK9","revision_id":"rev_0042","path":"/financeiro/resultado-trimestral.pdf","size_bytes":73400320,"sync_cursor":"cur_99881271","scan_state":"pending","preview_state":"queued"}
Commit é a transação de metadados. Upload de chunk só prepara conteúdo. Commit torna o arquivo visível.
GET /v1/files/fil_2bK9/content HTTP/1.1
Authorization: Bearer <token>
Range: bytes=0-1048575
206 Partial Content
Content-Type: application/pdf
Content-Range: bytes 0-1048575/73400320
ETag: "rev_0042"
Cache-Control: private, max-age=3600
O download gateway resolve a revision para um manifest. Depois mapeia ranges do arquivo para reads de chunks.
GET /v1/folders/fld_9mS7/children?page_size=200&page_token=abc HTTP/1.1
Authorization: Bearer <token>
{"entries":[{"id":"fil_2bK9","type":"file","name":"resultado-trimestral.pdf","revision_id":"rev_0042","size_bytes":73400320,"modified_at":"2026-02-11T10:24:12Z","scan_state":"clean"}],"next_page_token":"def","folder_revision":18821}
Listagem de pasta deve ser paginada. Pastas compartilhadas grandes podem conter milhões de entradas.
GET /v1/changes?cursor=cur_99880000&limit=500 HTTP/1.1
Authorization: Bearer <token>
X-Device-Id: dev_macbook_123
{"changes":[{"change_id":"chg_99880001","type":"file_added","file_id":"fil_2bK9","parent_id":"fld_9mS7","revision_id":"rev_0042","name":"resultado-trimestral.pdf","content_hash":"sha256:b91a...","occurred_at":"2026-02-11T10:24:12Z"}],"next_cursor":"cur_99880500","has_more":true}
O cliente de sync só guarda next_cursor depois de aplicar o batch localmente. Isso dá entrega at-least-once com aplicação idempotente.
POST /v1/shares HTTP/1.1
Authorization: Bearer <token>
Content-Type: application/json
{"resource_id":"fld_9mS7","resource_type":"folder","principal":{"type":"user","id":"usr_712"},"role":"editor","message":"Pasta de orçamento para revisão"}
{"share_id":"shr_18Y","resource_id":"fld_9mS7","effective_role":"editor","created_at":"2026-02-11T11:01:00Z"}
POST /v1/links HTTP/1.1
Authorization: Bearer <token>
Content-Type: application/json
{"resource_id":"fil_2bK9","role":"viewer","expires_at":"2026-03-01T00:00:00Z","password_required":true,"allow_download":true}
{"link_id":"lnk_9Ab","url":"https://files.example.com/s/lnk_9Ab","status":"active"}
O modelo separa namespace, conteúdo, permissões, sessões e sync.
file_nodes| Campo | Tipo | Observações |
|---|---|---|
file_id | string | Identificador lógico estável |
owner_id | string | Usuário ou workspace |
parent_id | string | ID da pasta pai |
type | enum | file ou folder |
name | string | Nome exibido |
current_revision_id | string | Revisão atual |
folder_revision | int64 | Versão monotônica da pasta |
deleted_at | timestamp | Soft delete |
created_at | timestamp | Tempo do servidor |
updated_at | timestamp | Tempo do servidor |
created_by_device_id | string | Auditoria/debug |
namespace_shard | int | Dica de roteamento |
| Índices importantes: | ||
| Índice | Consulta | |
| --- | --- | |
(owner_id, parent_id, name) | unicidade e lookup de pasta | |
(parent_id, updated_at) | listagem de pasta | |
(owner_id, deleted_at) | listagem de lixeira | |
(file_id) | lookup direto | |
| Não use path como chave primária. Paths mudam. IDs permanecem. |
file_revisions| Campo | Tipo | Observações |
|---|---|---|
revision_id | string | ID imutável da revisão |
file_id | string | ID lógico do arquivo |
manifest_id | string | Manifest de conteúdo |
size_bytes | int64 | Tamanho lógico |
file_hash | string | Hash do arquivo inteiro |
content_type | string | MIME type |
client_modified_at | timestamp | Vindo do device |
server_created_at | timestamp | Tempo de commit |
created_by_user_id | string | Ator |
created_by_device_id | string | Device |
scan_state | enum | pending, clean, blocked |
preview_state | enum | none, queued, ready, failed |
| Revisions são imutáveis. Rollback torna uma revision antiga a atual. Ele não muta a revision antiga. |
manifests| Campo | Tipo | Observações |
|---|---|---|
manifest_id | string | ID estável |
file_hash | string | Hash do conteúdo completo |
chunking_strategy | string | fixed ou content_defined |
chunk_count | int | Número de chunks |
total_size_bytes | int64 | Tamanho do arquivo |
created_at | timestamp | Criação do manifest |
manifest_chunks| Campo | Tipo | Observações |
|---|---|---|
manifest_id | string | Manifest pai |
chunk_index | int | Posição ordenada |
chunk_hash | string | Endereço por conteúdo |
offset | int64 | Byte inicial |
size_bytes | int | Tamanho do chunk |
compression | string | Opcional |
encryption_key_id | string | Referência de chave |
chunks| Campo | Tipo | Observações |
|---|---|---|
chunk_hash | string | Chave primária |
size_bytes | int | Tamanho armazenado |
storage_uri | string | Ponteiro no object store |
ref_count | int64 | Referências lógicas |
durability_state | enum | local, replicated, erasure_coded |
hotness_score | float | Dica de tiering |
first_seen_at | timestamp | Dedup/debug |
last_read_at | timestamp | Tiering |
region_set | array | Regiões com o chunk |
| Não confie apenas em reference count. Use mark-and-sweep ou reconciliação por log porque contadores podem desviar em falhas. |
upload_sessions| Campo | Tipo | Observações |
|---|---|---|
upload_session_id | string | ID da sessão |
owner_id | string | Usuário/workspace |
parent_id | string | Pasta alvo |
name | string | Nome alvo |
expected_size_bytes | int64 | Tamanho declarado |
state | enum | open, committing, committed, expired, aborted |
accepted_ranges | json | Ranges aceitos |
idempotency_key | string | Segurança contra retry |
expires_at | timestamp | Limpeza |
created_at | timestamp | Tempo do servidor |
change_events| Campo | Tipo | Observações |
|---|---|---|
change_id | int64/string | Monotônico dentro do shard |
namespace_id | string | Namespace do usuário/workspace |
resource_id | string | ID de arquivo/pasta |
event_type | enum | added, updated, deleted, moved, shared |
revision_id | string | Revisão de conteúdo se relevante |
parent_id | string | Pasta pai |
actor_user_id | string | Ator |
actor_device_id | string | Device |
occurred_at | timestamp | Tempo do servidor |
payload | json | Payload compacto |
| O change log pode ser particionado fisicamente por namespace. Dentro de um namespace, use ordenação monotônica. Ordenação global é desnecessária e cara. |
Upload resumível não é opcional. A documentação do Google Drive descreve uploads resumíveis como úteis para arquivos grandes e redes instáveis. O Google Cloud Storage também expõe uploads resumíveis e discute chunk size com trade-off entre memória e velocidade. O protocolo tus formaliza ideia parecida com offsets e requests PATCH.
Neste design, usamos o princípio sem copiar exatamente uma API externa.
O design mais simples aceita bytes no offset atual. Se o cliente envia offset 64 MB enquanto o servidor só aceitou 56 MB, retorne conflito e o offset atual. Isso se aproxima do modelo de offset do tus.
Benefícios:
Para uploads grandes em desktop, permita chunks fora de ordem. Cada chunk tem índice, offset, tamanho e hash. O commit verifica que todos os ranges foram cobertos exatamente uma vez.
Upload paralelo exige validação mais forte:
[0, size) sem buracos.Sessões expiram. Chunks staged podem ser retidos por pouco tempo após expiração porque outra sessão pode deduplicar o mesmo conteúdo. Limpeza recomendada:
| Objeto | TTL |
|---|---|
| Sessão aberta | 24 horas |
| Metadados de sessão expirada | 7 dias |
| Chunk staged sem referência | 7-30 dias |
| Registro de idempotência de commit | 7 dias |
Algoritmo do cliente:
Chunking afeta dedup, resume de upload e performance de download.
Chunking fixo é simples. Exemplo: dividir cada arquivo em chunks de 8 MB.
bytes do arquivo:
[0..8MB) [8MB..16MB) [16MB..24MB) ...
Benefícios:
Chunking definido por conteúdo escolhe fronteiras via rolling hash. Ele é comum em backup e sistemas com forte dedup. Se bytes são inseridos no início, várias fronteiras posteriores continuam estáveis.
Benefícios:
Use chunks fixos primeiro. Adicione chunking definido por conteúdo depois para sync desktop avançado. Divisão prática:
| Tamanho do arquivo | Estratégia |
|---|---|
| < 8 MB | chunk único |
| 8 MB - 1 GB | chunks fixos de 8 MB |
| > 1 GB | chunks fixos de 16 MB ou adaptativo |
| workloads de backup | chunks definidos por conteúdo |
Use hashes criptográficos para endereçar chunks. SHA-256 é um default comum. Guarde hash do arquivo completo como metadado. Guarde hashes de chunks no manifest.
Finalidades do hash:
Deduplicação economiza storage e banda. Ela também cria riscos de privacidade e abuso.
| Nível | Descrição | Prós | Riscos |
|---|---|---|---|
| sem dedup | armazena cada upload isolado | privacidade simples | custo alto |
| por usuário | dedup dentro da conta | baixo risco | economia limitada |
| por tenant | dedup em workspace/empresa | bom trade-off enterprise | isolamento necessário |
| global | dedup entre todos os usuários | economia máxima | risco de side-channel |
| Em storage consumer, cuidado com dedup global. Se um usuário descobre que um hash já existe, pode inferir que alguém enviou conteúdo conhecido. |
Faça dedup server-side depois que os bytes chegam. Não permita que clientes consultem livremente se um hash existe globalmente. Fluxo:
Reference count ajuda, mas não basta. Falhas criam divergências:
GC deve ser conservador. Processo recomendado:
Download é mais que GET object. O usuário pede um arquivo. A camada de storage tem chunks. O gateway mapeia ranges do arquivo para ranges de chunks.
Tamanho do arquivo: 20 MB
Tamanho do chunk: 8 MB
Chunk 0: bytes 0 - 8.388.607
Chunk 1: bytes 8.388.608 - 16.777.215
Chunk 2: bytes 16.777.216 - 20.971.519
Cliente pede:
Range: bytes=10.000.000-12.000.000
Gateway lê:
Chunk 1 offset 1.611.392 length 2.000.001
Cacheie manifests agressivamente. Cacheie metadados com TTL curto. Cacheie previews públicos em CDN. Não cacheie bytes privados em edge compartilhado sem tokens curtos e vinculados a acesso. Cache keys úteis:
| Dado | Cache key |
|---|---|
| Metadados atuais | file_id + revision_id |
| Manifest | manifest_id |
| Chunk | chunk_hash |
| Preview | revision_id + preview_type + size |
| Auth de link público | link_id + policy_version |
Para arquivos grandes, suporte:
Sync é o coração do produto. Também é onde designs ingênuos quebram. O sistema precisa reconciliar três fluxos:
Clientes desktop precisam de banco local. Não infira tudo por timestamp do filesystem. Campos locais:
| Campo | Propósito |
|---|---|
local_path | path local atual |
file_id | ID no servidor |
revision_id | última revision sincronizada |
content_hash | último hash sincronizado |
mtime | modified time local |
size_bytes | tamanho local |
sync_state | clean, uploading, downloading, conflict |
last_error | problema visível |
cursor | último cursor aplicado |
Push notifications devem ser dicas. Elas não carregam a verdade completa. Fluxo:
/changes.O change log é o contrato de sync.
Particione por namespace. Um namespace pode ser:
Não exija um ID global monotônico. Isso cria coordenação desnecessária.
Um cursor significa: "O device observou e aplicou todas as mudanças até este ponto nesta visão de namespace." Cursor pode ser guardado no cliente, no servidor ou em ambos. Cursor server-side ajuda:
Change logs não podem crescer para sempre. Opções:
cursor_expired.| Tipo | Significado | Ação do cliente |
|---|---|---|
file_added | novo arquivo visível | criar/baixar |
file_updated | nova revision | baixar ou marcar online-only |
file_deleted | lixeira ou remoção | deletar ou tombstone local |
file_moved | parent/name mudou | mover path local |
folder_added | nova pasta | criar pasta local |
acl_changed | permissão mudou | atualizar acesso |
link_changed | policy de link mudou | atualizar UI |
Sync desktop depende de watchers da plataforma:
| Sinal local | Mutação provável |
|---|---|
| novo inode/path | adicionar arquivo |
| hash mudou | atualizar arquivo |
| path mudou mesmo file id | move/rename |
| path ausente | delete |
| diretório criado | adicionar pasta |
| permissão negada | erro de sync |
| Detecção de rename é difícil. Muitos filesystems reportam rename como delete mais create. Use inode/file IDs locais quando disponíveis. Fallback para hash e heurísticas de tempo. |
Quando um laptop está offline:
Conflitos acontecem quando dois atores editam o mesmo arquivo lógico a partir da mesma base revision.
Todo update inclui uma base revision.
{"file_id":"fil_2bK9","base_revision_id":"rev_0042","new_manifest_id":"man_778","client_operation_id":"op_laptop_555"}
Se a revision atual ainda é rev_0042, commit normal. Se a revision atual virou rev_0043, o servidor retorna conflito.
Para arquivos binários:
Exemplo:
proposal.docx
proposal (copia em conflito de Anderson 2026-02-11).docx
Compartilhamento transforma storage pessoal em namespace colaborativo. Também complica cada read, write, sync e notificação.
Use ACLs com roles:
| Role | Capacidades |
|---|---|
| owner | tudo, transferir propriedade, deletar permanentemente |
| editor | ler, enviar, atualizar, mover dentro do escopo |
| commenter | ler, comentar se existir comentário |
| viewer | ler e preview |
| uploader | adicionar arquivos, sem ler existentes |
Pastas normalmente herdam permissões para descendentes. Evite copiar linhas de ACL para cada filho no share. Em vez disso:
Cache keys precisam de policy version.
key = principal_id + resource_id + acl_version
value = effective_role, expires_at
Quando ACL muda, incremente acl_version. Entradas antigas naturalmente dão miss.
Uma mudança em pasta compartilhada deve aparecer para cada colaborador. Não duplique fisicamente metadados por usuário. Use mounts virtuais:
| Conceito | Significado |
|---|---|
| shared root | folder node real |
| mount point | onde colaborador vê |
| namespace view | árvore visível do usuário |
| effective ACL | permissões pelo mount |
Links públicos não são apenas ACL entries. Eles são capacidades bearer. Qualquer pessoa com URL pode acessar o recurso, salvo controles extras.
| Campo | Propósito |
|---|---|
link_id | identificador opaco |
resource_id | arquivo ou pasta |
role | viewer ou editor se permitido |
expires_at | revogação automática |
password_hash | senha opcional |
allow_download | modo preview-only |
max_downloads | controle de abuso |
created_by | auditoria |
revoked_at | revogação |
policy_version | invalidação de cache |
Preview generation é workflow assíncrono. Ele não deve bloquear commit de upload.
Preview workers parseiam arquivos não confiáveis. Rode-os em sandboxes com:
| Tipo fonte | Artefatos derivados |
|---|---|
| imagem | thumbnails small, medium, large |
| thumbnails de páginas, extração de texto | |
| vídeo | poster frame, metadados curtos |
| áudio | waveform, duração |
| office docs | PDF preview se suportado |
| archive | lista de arquivos se policy permitir |
O arquivo pode ficar visível antes do preview. Estados de UI:
Storage vira sistema de distribuição quando sharing existe. Malware scanning é obrigatório para links públicos e pastas compartilhadas.
| Estado | Acesso do owner | Acesso compartilhado | Link público |
|---|---|---|---|
| pending | permitido ou limitado | limitado | bloqueado |
| clean | permitido | permitido | permitido |
| restricted | permitido com aviso | bloqueado ou aviso | bloqueado |
| blocked | quarentena para owner | bloqueado | bloqueado |
| A política exata depende do produto e de requisitos legais. |
Use filas com prioridade:
Usuários esperam recuperação. Empresas esperam policy. Times de storage esperam custo limitado.
Cada update cria uma revision imutável. O file node aponta para a revision atual. Revisions antigas seguem recuperáveis até expirar a retenção.
Delete normalmente deve significar soft delete. Soft delete:
| Plano | Lixeira | Histórico de versão | Legal hold |
|---|---|---|---|
| free | 30 dias | 30 dias | não |
| pro | 180 dias | 180 dias | não |
| business | configurável | configurável | sim |
| enterprise regulado | definido por policy | definido por policy | sim |
Quota é mais difícil com dedup. Storage físico e storage lógico visível são diferentes. Usuários devem ser cobrados por uso lógico. A plataforma otimiza uso físico internamente.
| Caso | Política |
|---|---|
| upload abandonado | liberar reserva após TTL |
| dedup hit | ainda contar bytes lógicos do owner |
| upload em pasta compartilhada | contar do owner ou workspace |
| nova versão | contar conforme plano |
| lixeira | contar até delete permanente ou quota separada |
Use contadores fortemente consistentes no commit. Evite decrementar uso antes da retenção expirar. Mantenha:
A maioria dos arquivos esfria. O sistema deve manter chunks recentes e frequentes em storage hot, depois mover dados frios para storage mais barato.
| Sinal | Significado |
|---|---|
last_read_at | acesso recente |
download_count_30d | popularidade |
owner_plan | plano pago pode exigir restore mais rápido |
shared_link_active | provável acesso |
legal_hold | manter durável, talvez frio |
file_type | vídeos e archives podem ser grandes |
region_policy | restrição de residência |
Reads do cold tier podem ser mais lentos. Produto user-facing precisa de policy:
O design precisa sobreviver a falhas de disco, máquina, rack, zona e região. A arquitetura Magic Pocket do Dropbox descreve publicamente blocos imutáveis, índice por hash, replicação entre zonas e erasure coding posterior para eficiência. Esse é o modelo mental correto.
Para metadados:
| Estado | Significado | Tratamento visível |
|---|---|---|
staged | upload aceito, não commitado | não visível |
local_durable | armazenado em zona/cell local | commit pode aguardar ou seguir por policy |
multi_zone | replicado entre zonas | seguro para visibilidade normal |
multi_region | replicado entre regiões | alta durabilidade |
erasure_coded | layout frio compacto | custo eficiente |
Replicação é simples, mas cara. Erasure coding reduz overhead para chunks frios imutáveis. Trade-off:
Objetivos de recuperação:
| Componente | RPO | RTO |
|---|---|---|
| metadata service | segundos a minutos | minutos |
| chunks commitados | segundos a minutos | minutos a horas |
| thumbnails | regenerável | horas |
| search index | reconstruível | horas |
| analytics | horas | horas |
| Nem todo dado precisa do mesmo alvo de recuperação. Não gaste orçamento premium de durabilidade em artefatos reconstruíveis. |
Confiabilidade em file storage é nunca perder dado confirmado e nunca corromper namespace.
| Falha | Comportamento esperado |
|---|---|
| cliente perde rede durante upload | continuar do range aceito |
| upload gateway cai após gravar chunk | status revela estado aceito após reconciliação |
| commit de metadados dá timeout | retry idempotente retorna arquivo commitado ou erro seguro |
| read no object store falha | retry em réplica alternativa |
| notificação de change cai | device faz polling em /changes |
| watcher desktop perde evento | scan local periódico detecta drift |
| backlog de scanner cresce | sharing espera, upload continua commitado |
| preview worker cai | job faz retry ou marca failed |
| região falha | roteamento para réplica com regras de stale-safety |
Use operation IDs para:
idempotency_key | chave de retry |
| actor_id | evita replay entre usuários |
| request_hash | detecta reuso com payload diferente |
| response_body | retorna mesmo resultado no retry |
| expires_at | storage limitado |Verifique checksums em:
Scrub em background lê chunks e verifica hashes. Se aparecer corrupção:
Quando o sistema sobrecarrega:
File storage é sensível por padrão. Usuários enviam impostos, contratos, fotos, código-fonte e documentos médicos.
Use:
Todo read de conteúdo precisa de autorização. Não assuma que signed chunk URL basta se ele pode sobreviver a mudanças de permissão. Download tokens curtos devem incluir:
Use:
Dedup global pode vazar informação se exposto como oracle. Evite APIs como:
Hash X já existe?
Aceite bytes e trate dedup como otimização interna.
Audite:
Observabilidade deve mapear jornadas do usuário. Não monitore só CPU e tamanho de fila. Monitore se usuários conseguem fazer upload, download, navegação, sync, share e recovery.
| Jornada | SLI | Meta |
|---|---|---|
| criar sessão de upload | taxa de sucesso | 99,99% |
| enviar chunk | sucesso por bytes aceitos | 99,95% |
| commitar upload | sucesso e latência | 99,99%, p99 < 1s |
| listar pasta | latência | p99 < 300ms |
| primeiro byte de download | latência | p99 < 500ms |
| frescor de sync | tempo do commit até device ver mudança | p95 < 5s |
| revogar link público | tempo até negar | p99 < 30s |
| backlog de scan | tempo até veredito | p95 < 5 min |
Meça:
Use logs estruturados com:
Trace através de:
| Alerta | Página? | Motivo |
|---|---|---|
| burn de error budget no upload commit | sim | caminho de dados visível |
| p99 do metadata DB acima da meta | sim | impacto em browse/sync |
| lag de replicação acima do limite | sim | risco de durabilidade |
| backlog de preview alto | não, ticket | artefato derivado |
| backlog de scan para links públicos alto | sim | segurança e sharing |
| dedup ratio cai | ticket | regressão de custo |
Diga claramente: "Estou projetando um sistema user-facing de armazenamento e sync de arquivos, não apenas um object store." Isso mostra que você entende metadados, sync e sharing.
Use:
Mencione:
Esse é sinal de senioridade. Upload de chunk prepara bytes. Commit cria revision e evento.
Não diga apenas "usa hash e dedup global". Explique risco de privacidade. Diga que dedup pode ser interno e escopado por tenant.
A API central de sync é:
GET /changes?cursor=...
Notificações são dicas. Cursores são a verdade.
Mencione base revision checks. Mencione cópias em conflito. Mencione que overwrite silencioso é inaceitável.
| Tema | Resposta simples | Resposta senior |
|---|---|---|
| upload | enviar arquivo ao servidor | sessões resumíveis com chunk e commit |
| storage | salvar no S3 | chunks imutáveis e manifests |
| sync | WebSocket updates | invalidação mais pull por cursor |
| sharing | tabela de ACL | herança, mounts e invalidação de cache |
| dedup | usar hash | escopo de dedup e sem oracle de existência |
| delete | deletar linha | lixeira, versões, legal hold e GC |
Servidores de metadados não devem trafegar 25 PB/dia. Use upload/download gateways e object storage.
Paths mudam. Use IDs estáveis. Paths são views sobre metadados.
Preview generation é caro e perigoso. Rode assíncrono em sandboxes.
Notificações caem. Use-as como dicas. Devices devem puxar mudanças por cursor.
Retries são normais. Sem idempotência, usuários recebem arquivos duplicados e quota inconsistente.
Existência de hash pode vazar informação. Escopo dedup ou esconda isso atrás de verificação server-side.
Deletes precisam de lixeira, retenção de versão, legal hold e janelas seguras de GC. Delete físico imediato quebra recovery e corre contra referências.
Filesystem watchers perdem eventos. Clientes precisam de scans periódicos e estado local.
| Componente | Responsabilidade |
|---|---|
| API Gateway | auth, roteamento, rate limit, APIs de metadados |
| Upload Gateway | ingress de bytes resumível |
| Download Gateway | range reads e signed downloads |
| Metadata Service | namespace, file nodes, revisions |
| Manifest Service | revision para lista de chunks |
| Chunk Service | verificação de hash e storage |
| Content Index | dedup e localização de chunks |
| Change Log Service | eventos append-only de sync |
| Device Cursor Service | progresso de sync |
| Permission Service | ACL e avaliação de shares |
| Quota Service | uso lógico e reservas |
| Preview Workers | thumbnails e artefatos |
| Malware Scanner | veredito por revision |
| Tiering Workers | placement hot/cold |
| Garbage Collector | deleção segura de chunks sem referência |
POST /v1/upload-sessions
PUT /v1/upload-sessions/{id}/chunks/{index}
GET /v1/upload-sessions/{id}/status
POST /v1/upload-sessions/{id}/commit
GET /v1/files/{id}/content
GET /v1/folders/{id}/children
GET /v1/changes?cursor=...
POST /v1/shares
POST /v1/links
| Decisão | Default |
|---|---|
| chunk size | 8 MB para arquivos normais |
| hash de chunk | SHA-256 |
| modelo de upload | sessões resumíveis |
| identidade de arquivo | file_id estável |
| identidade de conteúdo | chunk hash imutável |
| modelo de sync | pull por cursor com push como dica |
| conflito | base revision check |
| delete | soft delete com retenção |
| escopo de dedup | interno, ciente de tenant |
| previews | workers assíncronos em sandbox |
| scanning | assíncrono, bloqueia sharing arriscado |
| durabilidade | multi-zone depois multi-region |
| cold storage | tiering após queda de acesso |
A resposta correta mais simples: