Diseña un servicio de almacenamiento de archivos como S3 o Dropbox — recorre la arquitectura
Detrás de "sube un archivo" hay un servicio de metadatos, un pipeline de fragmentación, deduplicación por contenido y una estrategia de replicación que debe sobrevivir a la pérdida de un centro de datos completo.
APRENDE HACIENDOPon a prueba tu intuición3 pasos
Diseña un servicio de almacenamiento de archivos como S3 o Dropbox — recorre la arquitectura
Usa los ejemplos y la explicación de la lección para comprobar tu idea.
Reflexión personal. No se envía a ningún servicio de IA.#Separa "qué es un archivo" de "dónde viven sus bytes"
La decisión de diseño más importante en esta pregunta es dividir el sistema en dos planos que escalan de forma completamente distinta: un plano de metadatos (el árbol de archivos/carpetas, nombres, permisos, historial de versiones — pequeño, con forma relacional, necesita consistencia fuerte) y un plano de datos (los bytes en sí — enorme, inmutable una vez escrito, necesita escalar a exabytes y no le importan las relaciones). Mezclarlos — guardar los bytes del archivo en la misma base de datos que rastrea la estructura de carpetas — es el error que hunde la mayoría de los intentos de esta pregunta en los primeros cinco minutos.
Requisitos que vale la pena enunciar antes de seguir diseñando:
- Los archivos pueden ser enormes (varios GB) y las subidas deben ser reanudables.
- Muchos archivos (o partes de archivos) están duplicados entre usuarios — la deduplicación debe ser automática y basada en contenido, no en el nombre del archivo.
- La durabilidad es la métrica principal — "once nueves" no es un número de marketing, refleja una estrategia real de replicación/codificación de borrado.
- Los clientes de sincronización necesitan una vista consistente de "qué cambió desde la última vez que miré", y las ediciones concurrentes en conflicto necesitan una resolución definida (no pérdida silenciosa de datos).
#Arquitectura de alto nivel
#Fragmentación (chunking): por qué se corta un archivo antes de guardarlo
Guardar un archivo completo como un solo blob encarece dos cosas: las subidas reanudables (reintentar el archivo completo de varios GB después de una conexión caída) y la deduplicación (dos archivos que difieren en un solo byte insertado se ven 100% distintos si se comparan por hash del archivo completo). La solución es dividir cada archivo en fragmentos — típicamente de unos pocos MB cada uno — y guardar, hashear y deduplicar cada fragmento por separado.
Dos estrategias de fragmentación:
- Fragmentación de tamaño fijo — la más simple, pero un solo byte insertado cerca del inicio del archivo desplaza todos los límites de fragmento posteriores, así que nada después de la edición deduplica contra la versión anterior.
- Fragmentación definida por contenido (CDC) — un hash deslizante (el fingerprinting de Rabin es la elección clásica) recorre el flujo de bytes y coloca un límite de fragmento donde el hash coincide con un patrón (por ejemplo, sus N bits menos significativos son cero), así que los límites los define el contenido, no la posición. Insertar un byte en cualquier parte solo cambia el/los fragmento(s) que tocan ese byte — todo lo demás sigue deduplicando.
CDC cuesta más CPU por subida, pero es lo que hace que la deduplicación realmente rinda entre versiones de un archivo (piensa en sincronizar una hoja de cálculo grande que ajustas a diario) y entre archivos distintos que comparten regiones grandes en común (piensa en las imágenes de máquina virtual de dos personas, construidas desde la misma base).
#Deduplicación y el camino de escritura
Cada fragmento se direcciona por contenido: se hashea (SHA-256 es el estándar) y ese hash se convierte en su identidad. Antes de escribir un fragmento en el almacén de blobs, se busca en un índice de fragmentos (hash a contador de referencias y ubicación de almacenamiento). Si ya existe, simplemente se incrementa el contador de referencias y listo — no se escribe ni un byte. Si es nuevo, se escribe en el almacén de blobs y se inserta en el índice con contador 1. El registro de metadatos de un archivo se convierte, en esencia, en una lista ordenada de hashes de fragmentos — reconstruir el archivo es solo concatenar los fragmentos en ese orden.
Esto tiene un efecto secundario importante: los fragmentos son compartidos e inmutables, así que borrar un archivo nunca borra un fragmento directamente — decrementa contadores de referencia, y el espacio de un fragmento solo se libera cuando su contador llega a cero (normalmente mediante un barrido asíncrono de recolección de basura, no de forma síncrona al borrar).
#Flujo de subida y descarga
Subida: el cliente pide al servicio de metadatos una sesión de subida; el archivo se divide en fragmentos del lado del cliente (o en un servicio de ingesta en el borde); cada fragmento se hashea y el cliente pregunta primero "¿ya tienes este hash?" — los fragmentos que el servidor ya tiene se omiten por completo, ahorrando ancho de banda, no solo almacenamiento. Los fragmentos restantes se suben (en paralelo, con reintento por fragmento, que es justo lo que hace reanudables las subidas de varios GB — una conexión caída solo cuesta el fragmento en tránsito, no el archivo entero). Una vez que todos los fragmentos están confirmados, el cliente confirma la nueva versión del archivo enviando la lista ordenada de hashes de fragmentos al servicio de metadatos.
Descarga: el servicio de metadatos devuelve la lista de fragmentos y sus ubicaciones; el cliente (o un borde de CDN delante del almacén de blobs) obtiene los fragmentos en paralelo y los reensambla en orden. Los fragmentos de acceso frecuente son excelentes candidatos para caché de CDN precisamente porque son inmutables y direccionados por contenido — una clave de caché nunca puede volverse obsoleta.
#Replicación y durabilidad
Tres estrategias físicas, en orden creciente de eficiencia de almacenamiento y decreciente de simplicidad:
| Estrategia | Sobrecarga de almacenamiento | Modelo de durabilidad | Notas |
|---|---|---|---|
| Replicación triple síncrona | 3x | Sobrevive 2 fallas simultáneas | Simple, lecturas/escrituras rápidas, costosa |
| Codificación de borrado (ej. 10 fragmentos de datos + 4 de paridad) | ~1.4x | Sobrevive cualquier combinación de 4 de 14 fragmentos caídos | Más barata a gran escala, cuesta CPU codificar/decodificar, peor en objetos pequeños |
| Replicación asíncrona entre regiones | Copia completa o parcial adicional | Sobrevive la caída de una región entera | Normalmente consistencia eventual |
La mayoría de los sistemas en producción combinan estas estrategias: codificación de borrado dentro de una región para durabilidad eficiente en costo contra fallas de disco/nodo, más replicación asíncrona entre regiones para recuperación ante desastres si una región completa se cae.
#Consistencia: fuerte donde es barata, eventual donde es obligatoria
Los metadatos (el árbol de archivos, permisos, "¿existe este archivo?") deben ser fuertemente consistentes — un renombrado o borrado necesita ser visible de inmediato, o los clientes de sincronización divergen. El almacén de metadatos es comparativamente pequeño, así que esta consistencia es asequible (una base de datos relacional bien particionada, o un almacén clave-valor fuertemente consistente, la maneja sin problema). Los datos de blob replicados entre regiones, en cambio, suelen ser de consistencia eventual — esperar una escritura síncrona a tres continentes en cada subida haría el producto inutilizable.
El caso límite más filoso es la edición concurrente desde varios dispositivos sin conexión: dos dispositivos que editan el mismo archivo sin contacto de red confirmarán, cada uno, una nueva versión que apunta a listas de fragmentos distintas. El sistema no puede fusionar en silencio un contenido de bytes que no entiende, así que la resolución estándar es un vector de versión o last-writer-wins con una copia en conflicto — se conservan ambas versiones, se muestran ambas al usuario (de ahí vienen justo los nombres de archivo "copia en conflicto" en productos tipo Dropbox), y es la persona quien lo resuelve.