Design clássico · Sênior
Sincronização de arquivos (Google Drive/Dropbox)
Projeto de entrevista clássico. Pontos centrais: entrega de blobs e vídeo e edição colaborativa.
Resposta curta
Projeto de entrevista clássico. Pontos centrais: entrega de blobs e vídeo e edição colaborativa. Um serviço de arquivos na nuvem, no estilo Dropbox ou Google Drive, guarda os arquivos de cada pessoa e os deixa acessíveis de qualquer dispositivo. O ponto de partida é o levantamento de requisitos.
01 · CONTEXTO E REQUISITOS
O que um serviço de arquivos precisa fazer
Um serviço de arquivos na nuvem, no estilo Dropbox ou Google Drive, guarda os arquivos de cada pessoa e os deixa acessíveis de qualquer dispositivo. O ponto de partida é o levantamento de requisitos. Os requisitos funcionais centrais são enviar um arquivo, baixá-lo, compartilhá-lo com outras pessoas e manter os arquivos sincronizados entre os dispositivos. Editar arquivos e vê-los sem baixar ficam fora do escopo desta análise.
Entre os requisitos não funcionais, três mandam no desenho: alta disponibilidade, com disponibilidade priorizada sobre consistência; suporte a arquivos muito grandes, de até 50 GB na fonte; e envio, download e sincronização o mais rápidos possível. Também é preciso poder recuperar arquivos perdidos ou corrompidos.
Três entidades bastam: o arquivo em si (os bytes), os metadados do arquivo (nome, tamanho, tipo, quem enviou) e o usuário. Separar bytes de metadados guia o resto do desenho, porque cada um pede um tipo de armazenamento diferente.
02 · ENVIO E DOWNLOAD
Tirar os bytes do caminho do backend
A primeira ideia é enviar o arquivo ao servidor da aplicação e gravá-lo no disco. Serve para uma aplicação pequena, mas não escala e não é confiável: o armazenamento cresce junto com os arquivos e, se o servidor cai, perde-se o acesso a todos eles. O armazenamento de objetos resolve os dois problemas, pois guarda um número virtualmente ilimitado de arquivos e cuida da escala sozinho.
Passar o arquivo pelo backend até o armazenamento de objetos ainda envia os bytes duas vezes. A saída é o cliente enviar direto, usando uma URL pré-assinada, que dá permissão para gravar em um local específico. O serviço de arquivos vira um plano de controle: gera a URL, registra os metadados e atualiza o estado quando o armazenamento avisa que o envio terminou. Fica um caso a tratar: arquivo enviado sem os metadados salvos, e o inverso.
O cliente pede ao serviço de arquivos uma URL assinada para enviar. O serviço registra os metadados do arquivo com o envio ainda em andamento.
O cliente envia os bytes direto ao armazenamento de objetos, sem passar pelo backend.
O armazenamento avisa o serviço de que o envio terminou, e o serviço marca o arquivo como enviado nos metadados.
No download, o cliente pede a URL ao serviço, que confere nos metadados se a pessoa tem acesso e devolve uma URL assinada da CDN.
Na primeira requisição a CDN busca o arquivo no armazenamento; nas seguintes serve a cópia mais próxima de quem baixa.
No download vale a mesma lógica: baixar do armazenamento até o backend e depois ao cliente são duas transferências, lentas e caras. O cliente deve baixar direto. Para quem está longe da região do armazenamento, uma CDN guarda cópias dos arquivos em servidores espalhados pelo mundo e serve a partir do mais próximo.
| Abordagem | Como funciona | O que ainda falta |
|---|---|---|
| Servidor único | Os bytes ficam no sistema de arquivos local do servidor e os metadados vão para o banco de dados | Não escala e perde o acesso aos arquivos se o servidor cai |
| Pelo backend até o armazenamento de objetos | Escala e é confiável | Os bytes são enviados duas vezes |
| URL pré-assinada direto ao armazenamento | O cliente envia direto e o backend só coordena | Para quem está longe da região, o download ainda é lento sem CDN |
03 · METADADOS E COMPARTILHAMENTO
Quem pode ver o quê
Os metadados são pouco estruturados, têm poucas relações e a consulta principal é por usuário, o que combina com um banco NoSQL. A fonte lembra que um banco SQL funcionaria igualmente bem, então a escolha importa menos do que parece.
Para compartilhar, o primeiro impulso é pôr no metadado de cada arquivo a lista de quem tem acesso. Listar os arquivos de um usuário é fácil, mas listar os compartilhados com ele é lento, porque exige olhar a lista de cada arquivo. Um índice inverso em cache acelera a consulta, mas obriga a manter duas cópias da mesma informação em sincronia.
A alternativa é normalizar: uma tabela de compartilhamentos que liga usuário a arquivo, com o usuário como chave de partição e o arquivo como chave de ordenação. Os arquivos compartilhados com uma pessoa saem de uma única consulta, e não há cópia para sincronizar. O custo é uma consulta um pouco menos eficiente que a busca direta.
| Critério | Lista nos metadados do arquivo | Tabela de compartilhamentos |
|---|---|---|
| Listar o que foi compartilhado comigo | Lento: exige olhar a lista de cada arquivo | Uma consulta pelo usuário |
| Consistência | O índice inverso em cache precisa ficar em sincronia | Sem segunda cópia para sincronizar |
04 · SINCRONIZAÇÃO
Dois sentidos e uma fonte da verdade
Cada dispositivo guarda uma cópia local e o servidor guarda a cópia remota, então há dois sentidos de sincronização. O servidor remoto é a fonte da verdade, e por isso precisa ficar consistente o quanto antes para os outros dispositivos saberem o que puxar. No sentido local para remoto, um agente no cliente monitora a pasta por eventos do sistema de arquivos, enfileira o que mudou e envia pela API de envio.
No sentido remoto para local, o polling é simples, mas lento para detectar mudanças e gasta banda quando nada mudou. Conexões abertas por WebSocket ou SSE dão tempo real, mas podem cair e perder mensagens. A solução híbrida usa uma conexão por dispositivo, não por arquivo, para empurrar avisos, e consulta periodicamente as mudanças desde o último timestamp como rede de segurança.
O agente do dispositivo A percebe que um arquivo da pasta local mudou e enfileira o envio.
O dispositivo A envia o conteúdo ao armazenamento e comunica a mudança ao serviço de arquivos.
O serviço registra a mudança nos metadados: qual arquivo, que tipo de mudança e os dados atualizados.
O serviço avisa o dispositivo B pela conexão que ele mantém aberta.
O dispositivo B consulta o que mudou e baixa o conteúdo novo. Se o aviso se perdesse, a consulta periódica pegaria a mudança mesmo assim.
Se dois usuários editam o mesmo arquivo, a fonte resolve o conflito com última escrita vence: a edição mais recente é a que fica. Sobre versões, a fonte observa que normalmente não se sobrescreve o único arquivo: acrescenta-se um novo e atualiza-se um ponteiro de versão.
05 · ARQUIVOS GRANDES
Pedaços, impressão digital e retomada
Enviar um arquivo grande em uma única requisição esbarra em limites. Com um arquivo de 50 GB e uma conexão de 100 Mbps, são cerca de 4000 segundos, mais de uma hora sem resposta. O API Gateway usado no desenho aceita só 10 MB por requisição. Além disso, uma queda de rede obriga a recomeçar do zero, e a pessoa não vê o progresso.
A solução é o chunking: dividir o arquivo em pedaços e enviá-los um a um ou em paralelo. A divisão tem de ser feita no cliente; dividir no servidor anula o objetivo, pois o arquivo inteiro já teria sido enviado. Os pedaços típicos têm de 5 a 10 MB. Para retomar, o estado de cada pedaço fica no registro de metadados do arquivo.
Resta decidir quem atualiza esse estado. Deixar o cliente informar por conta própria é arriscado, porque o servidor passa a confiar em algo que o cliente pode falsear. Melhor é o cliente informar o ETag de cada pedaço e o backend verificar com a API ListParts do armazenamento antes de marcar o pedaço como enviado: confiar, mas verificar.
Para saber se um envio já foi tentado, o nome do arquivo não serve. Usa-se uma impressão digital (fingerprint) calculada sobre o conteúdo, do arquivo inteiro e de cada pedaço. Ela permite detectar duplicatas e retomar um envio em andamento. Como identifica o conteúdo e não o registro, o fileId continua sendo um identificador único à parte.
O cliente divide o arquivo em pedaços e calcula a impressão digital de cada um e a do arquivo inteiro.
O cliente pergunta ao serviço se já existe um envio com essa impressão. Se existir e estiver em andamento, retoma a partir do estado dos pedaços.
Se não existe, o serviço inicia o envio em partes no armazenamento, gera uma URL para cada pedaço e grava os metadados com o envio em andamento.
O cliente envia os pedaços direto ao armazenamento, em paralelo, e informa ao serviço o ETag de cada um.
O serviço confere as partes com o armazenamento antes de marcar cada pedaço como enviado nos metadados.
Com todos os pedaços marcados, o serviço pede ao armazenamento que monte o objeto final e só depois marca o arquivo como enviado.
O download não precisa de pedaços: depois da montagem o armazenamento tem um único objeto, e o cliente baixa por uma URL normal. Para arquivos muito grandes, requisições Range permitem baixar faixas em paralelo ou retomar um download interrompido.
06 · VELOCIDADE E SEGURANÇA
Menos bytes, bytes protegidos
Com pedaços, a sincronização só precisa enviar os que mudaram. Mas com pedaços de tamanho fixo, inserir um único byte perto do início desloca todas as fronteiras seguintes e muda a impressão digital de cada pedaço depois da edição, o que torna o delta sync quase inútil. O chunking por conteúdo resolve: as fronteiras vêm de um hash rolante sobre o conteúdo, e uma edição pequena afeta só os pedaços ao redor.
| Critério | Pedaços de tamanho fixo | Pedaços definidos pelo conteúdo |
|---|---|---|
| Edição perto do início do arquivo | Desloca as fronteiras de todos os pedaços seguintes | Afeta só os pedaços ao redor da edição |
| Delta sync | Quase inútil depois de uma inserção | Reaproveita os pedaços inalterados |
A compressão, feita no cliente, só compensa se o ganho na transferência superar o custo de comprimir e descomprimir. Em mídia já comprimida o ganho é muito pequeno; em texto costuma ser maior. Quando também há criptografia, comprima antes, porque a criptografia introduz aleatoriedade e atrapalha a compressão.
Na segurança, os dados vão por HTTPS em trânsito e criptografados em repouso no armazenamento. O controle de acesso vem da tabela de compartilhamentos. Para o caso de alguém repassar um link de download, as URLs assinadas valem só por pouco tempo, na fonte cerca de cinco minutos. Mas são bearer tokens: quem tem uma URL válida baixa o arquivo. A expiração curta limita a exposição sem eliminá-la, e restrições extras, como vincular a URL a um IP, endurecem o acesso.
Perguntas frequentes
O que é Sincronização de arquivos (Google Drive/Dropbox)?
Projeto de entrevista clássico. Pontos centrais: entrega de blobs e vídeo e edição colaborativa.
O que o design de Sincronização de arquivos (Google Drive/Dropbox) cobre?
Ele está organizado nestas seções: O que um serviço de arquivos precisa fazer, Tirar os bytes do caminho do backend, Quem pode ver o quê, Dois sentidos e uma fonte da verdade, Pedaços, impressão digital e retomada e Menos bytes, bytes protegidos.
Quanto tempo leva Sincronização de arquivos (Google Drive/Dropbox) e qual é o nível?
Leva cerca de 16 minutos para ler e tem nível Sênior.
Agora projete você mesmo
Rode este caso como uma entrevista na escola de System Design: você toma as decisões, o tutor as questiona e você vê a nota do seu desenho.