Enunciado y escenarios aplicables
Diseña un servicio de almacenamiento de archivos en la nube y sincronización multidispositivo similar a Dropbox o Google Drive. Cuenta con 50 millones de usuarios registrados y 5 millones de usuarios activos diarios. Cada usuario almacena 10 GB de datos lógicos en promedio. El sistema recibe 100 millones de nuevas versiones de archivo por día, con 4 MB de contenido nuevo o modificado por versión. El tráfico pico es cinco veces el promedio diario. Un archivo puede llegar a pesar hasta 50 GB, los dispositivos en línea deben observar creaciones, actualizaciones, movimientos y eliminaciones en un lapso de 5 segundos, y el servicio de metadatos tiene como objetivo una disponibilidad del 99,99%.
La escala, la latencia, la disponibilidad y el tamaño de fragmento objetivo de 4 MiB son supuestos de entrevista, no compromisos públicos de rendimiento de ningún producto de almacenamiento. El alcance incluye subida, descarga, transferencia reanudable, recuperación de versiones, sincronización multidispositivo, edición sin conexión, copias en conflicto, uso compartido simple y eliminación. La edición colaborativa a nivel de caracteres, la fusión semántica de documentos de Office, un sistema completo de autorización empresarial y las escrituras activo-activo entre regiones quedan fuera de alcance.
Los enunciados públicos de diseño de sistemas en 2026 todavía usan Dropbox, Google Drive o un servicio genérico de sincronización de archivos para indagar sobre la fragmentación de archivos grandes, la sincronización delta, las versiones, las operaciones sin conexión y los conflictos. La documentación oficial de almacenamiento de objetos también respalda el valor en ingeniería de las subidas en partes múltiples (multipart): las partes pueden subirse en paralelo y una parte fallida puede reintentarse de forma independiente, mientras que las partes abandonadas requieren una cancelación (abort) o una limpieza por ciclo de vida. La entrevista se enfoca en separar la transferencia de contenido del estado del espacio de nombres (namespace) y cerrar el ciclo de corrección en torno a la recuperación y la eliminación, no en memorizar la arquitectura interna de una sola empresa.
Qué evalúa el entrevistador
Primero, ¿puede el candidato separar el contenido del archivo de los metadatos? Las secuencias grandes de bytes pertenecen a un almacenamiento de objetos inmutable, económico y duradero. Los nombres, las relaciones padre-hijo, las versiones actuales, los marcadores de eliminación y los cursores de sincronización requieren escrituras condicionales y cambios ordenados. Poner un archivo de 50 GB en una base de datos relacional, o usar claves de objetos como el modelo completo de directorios, vuelve frágiles las actualizaciones, los movimientos, las confirmaciones (commits) de transacciones y la recuperación.
Segundo, ¿existe un único punto de publicación explícito para una subida? Un cliente puede subir fragmentos faltantes en paralelo, pero la versión se vuelve visible solo después de que el servidor verifica cada fragmento y actualiza atómicamente currentVersionId con el registro de cambios. De lo contrario, los metadatos podrían hacer referencia a contenido faltante, o los bytes subidos podrían no llegar a ser visibles para el usuario.
Tercero, ¿puede el protocolo de sincronización sobrevivir a notificaciones perdidas, duplicadas y desordenadas? Un mensaje push es solo una pista de que algo cambió. Un dispositivo debe usar un cursor duradero para obtener (pull) cambios fidedignos. Los dispositivos que estuvieron sin conexión durante días, los clientes reinstalados y los cursores vencidos necesitan una vía de recuperación. Una conexión WebSocket no es la fuente de la verdad.
Cuarto, ¿son honestas las semánticas de concurrencia? Cuando dos dispositivos sin conexión modifican el mismo archivo binario común a partir de la misma versión antigua, un servicio genérico no puede fusionarlos de manera confiable. Un commit condicional fallido debería preservar ambos resultados y crear una copia en conflicto en lugar de aplicar silenciosamente la regla de "el último escritor gana" (last-writer-wins). La documentación de ayuda pública de Dropbox explica igualmente que las ediciones simultáneas o sin conexión pueden generar una copia en conflicto que los usuarios deben fusionar.
Finalmente, ¿conecta la respuesta la capacidad, el costo y la recuperación de espacio? El candidato debe distinguir la capacidad lógica de la capacidad física deduplicada, estimar los commits de versiones y el rendimiento en bytes, y explicar los fragmentos huérfanos, las versiones retenidas, las lápidas (tombstones) de eliminación, las cuotas, los namespaces calientes y por qué la recolección de basura no puede depender de un único recuento de referencias instantáneo.
Preguntas de aclaración antes de responder
- ¿Qué contenido se sincroniza? Archivos y directorios comunes; sin colaboración en vivo a nivel de caracteres.
- ¿Qué consistencia se requiere? El dispositivo que sube obtiene el comportamiento de lectura de sus propias escrituras (read-your-write); otros dispositivos en línea convergen en un plazo de 5 segundos.
- ¿Cuánto tiempo se retiene el historial incremental? Supongamos 30 días; los cursores más antiguos requieren una instantánea (snapshot) del namespace antes de reanudar los deltas.
- ¿Cómo se manejan las ediciones concurrentes? Solo un commit de un
baseVersionIddado se convierte en actual; el resultado posterior se retiene como una copia en conflicto. - ¿Puede la eliminación recuperar bytes inmediatamente? No. Escribe un tombstone y retenlo durante 30 días para sincronización sin conexión y recuperación.
- ¿La deduplicación es global entre usuarios? Por defecto, se limita la deduplicación al ámbito de la cuenta o inquilino (tenant) para reducir filtraciones sobre la existencia de contenido y el acoplamiento de cifrado.
- ¿Qué tipo de uso compartido está dentro del alcance? Enlaces de solo lectura para un archivo o directorio; los permisos de organización complejos son un tema de seguimiento.
- ¿Cómo se manejan el cifrado y los archivos maliciosos? Cifrado en tránsito y en reposo, URL firmadas de corta duración y análisis asíncrono de malware; el cifrado de extremo a extremo está fuera de alcance.
- ¿Cómo aceptan escrituras las regiones? Cada namespace tiene una región de escritura principal (home region); los objetos pueden replicarse entre regiones y los metadatos tienen replicación asíncrona para recuperación ante desastres.
- ¿Se garantiza un acierto de subida instantánea? No se asume una proporción de deduplicación fija. Se cobra la cuota lógica por el tamaño visible para el usuario y se miden los ahorros físicos por separado.
Marco de respuesta en 30 segundos
"Almacenaría fragmentos inmutables de aproximadamente 4 MiB en almacenamiento de objetos y mantendría directorios, versiones actuales, manifiestos, tombstones y cambios ordenados en metadatos fuertemente consistentes. Los clientes suben solo los fragmentos faltantes y luego realizan un commit atómico con baseVersionId y una clave de idempotencia. Los dispositivos conservan un cursor y usan las notificaciones únicamente para activar /changes?cursor=, de modo que una notificación perdida no pierde datos. Un commit concurrente sin conexión preserva una copia en conflicto. Con 100 millones de versiones por día, el pico de cinco veces es de unos 6.000 commits/s, mientras que 400 TB de ingreso diario alcanzan un pico cercano a 25 GB/s. Las eliminaciones retienen tombstones y los fragmentos se reclaman solo después de un período de gracia y la reconciliación de manifiestos".
Análisis detallado paso a paso
Comencemos con cinco invariantes:
- Una versión publicada hace referencia únicamente a fragmentos que existen y superaron las comprobaciones de integridad.
- Un commit reemplaza a
currentVersionIdsolo cuandobaseVersionIdaún es igual a la versión actual. - Los números de secuencia de cambios aumentan estrictamente dentro de un namespace, y los cursores de los dispositivos solo avanzan.
- Una eliminación escribe un tombstone; los datos a los que aún se pueda hacer referencia no se eliminan físicamente durante la ventana de sincronización y recuperación.
- La recolección de basura elimina un fragmento solo después de que un período de gracia y la reconciliación de manifiestos continúen mostrando cero referencias.
Paso uno: separar el cliente, el plano de metadatos y el plano de contenido.
El cliente tiene un observador de archivos (file watcher), índice local, diario de operaciones duradero, fragmentador (chunker) y motor de sincronización. Tras una caída, recupera qué fragmentos se subieron y qué commit es incierto en lugar de volver a escanear y subir todos los archivos. El servidor tiene un API gateway, comprobaciones de autenticación y cuota, un servicio de metadatos, coordinador de subidas, almacenamiento de objetos, registro de cambios de namespace, servicio de notificaciones, CDN de descargas, escáner de malware y recolector de basura.
Los metadatos se enrutan por namespaceId. Un disco personal es un namespace, y una carpeta compartida puede convertirse en otro, manteniendo la autorización, los cambios ordenados y el aislamiento de puntos calientes (hotspots) dentro de un único límite. Inicialmente, cada namespace tiene una región de escritura principal con replicación sincrónica de metadatos a través de zonas de disponibilidad. Las descargas pueden usar una CDN cercana o una réplica de objetos. Esto evita que dos líderes regionales acepten concurrentemente actualizaciones de directorio conflictivas.
Paso dos: definir el modelo de datos.
FileEntry(id, namespaceId, parentId, name, type, currentVersionId, deletedAt)
FileVersion(id, fileId, baseVersionId, size, manifestHash, createdBy, createdAt)
VersionChunk(versionId, ordinal, chunkHash, size)
UploadSession(id, fileId, baseVersionId, state, expiresAt, idempotencyKey)
Change(namespaceId, seq, entityId, operation, versionId, createdAt)El árbol de directorios utiliza valores estables de fileId y parentId, por lo que mover o renombrar cambia los metadatos sin copiar el contenido. Un FileVersion es inmutable, y las filas ordenadas de VersionChunk forman su manifiesto. manifestHash valida el manifiesto, pero no reemplaza la suma de verificación (checksum) de cada fragmento. UploadSession almacena el estado de la sesión y su clave de idempotencia. Change.seq es monótono dentro de un namespace, y una eliminación es otra operación registrada.
Aplica la unicidad del nombre con una restricción condicional en (namespaceId, parentId, normalizedName). El producto debe definir la normalización de mayúsculas y minúsculas de forma explícita; de lo contrario, los clientes de Windows, macOS y Linux pueden discrepar sobre si dos nombres entran en conflicto.
Paso tres: diseñar la subida en partes múltiples reanudable.
POST /files/{fileId}/upload-sessions
{ baseVersionId, size, chunks[], idempotencyKey }
-> { uploadSessionId, missingChunks[], signedUrls[] }
PUT {signedChunkUrl}
Content-Checksum: ...
POST /upload-sessions/{uploadSessionId}/commit
{ manifestHash, idempotencyKey }
-> { fileVersionId, changeSeq }
GET /files/{fileId}/download-manifest
-> { fileVersionId, chunks[], signedUrls[] }El cliente comienza con fragmentos de aproximadamente 4 MiB y calcula un hash criptográfico fuerte. El servidor busca fragmentos existentes solo dentro de la cuenta o el inquilino y emite URL firmadas de corta duración, delimitadas por objeto y operación, para los fragmentos faltantes. El cliente sube con paralelismo acotado y reintenta solo las partes fallidas. La documentación oficial de multipartes de AWS y Alibaba Cloud utiliza un modelo de sesión con initiate, upload-parts y complete. También señalan que las partes sin finalizar continúan ocupando almacenamiento, por lo que las sesiones necesitan expiración y las subidas abandonadas por mucho tiempo necesitan una cancelación explícita.
Los fragmentos de tamaño fijo son simples y se paralelizan bien para la mayoría de los archivos. Si la carga de trabajo inserta frecuentemente unos pocos bytes cerca del inicio, todos los límites fijos posteriores se desplazan y los hashes cambian. La fragmentación definida por contenido (content-defined chunking) puede recuperar más datos no modificados a costa de CPU y complejidad de implementación. Comienza con fragmentos fijos y actualiza solo cuando los patrones de edición observados lo justifiquen.
Paso cuatro: hacer de la confirmación de versión el único límite de publicación.
El endpoint de commit primero busca un resultado existente por idempotencyKey, y luego valida los tamaños de fragmentos, los hashes, la autorización y la cuota. En una transacción de metadatos, este:
- Bloquea o lee condicionalmente
FileEntry.currentVersionId. - Verifica que aún sea igual al
baseVersionIdde la solicitud. - Escribe el
FileVersioninmutable y el manifiesto deVersionChunk. - Actualiza
currentVersionId. - Agrega el siguiente
Change.seqy un registro transaccional en outbox.
Los bytes de los objetos se completan antes de la transacción de metadatos, por lo que la transacción no puede publicar una referencia a un fragmento faltante. Un fallo en la notificación tras el commit no afecta la corrección: el outbox reintenta y los dispositivos aún pueden consultar por cursor. Si la respuesta del commit se pierde, reintentar con la misma clave de idempotencia devuelve el fileVersionId original en lugar de crear otra versión lógica o cobrar la cuota dos veces.
Paso cinco: sincronizar dispositivos con cursores.
GET /changes?namespaceId={id}&cursor={lastSeq}&limit=1000
-> { changes[], nextCursor, hasMore }Un dispositivo almacena lastSeq y su índice de archivos local en la misma transacción duradera. Tras una notificación de "el namespace pudo haber cambiado", extrae datos hasta hasMore=false, aplica creaciones, actualizaciones, movimientos y tombstones en orden, y solo entonces confirma el nuevo cursor. seq hace que la reejecución sea idempotente. Las notificaciones desordenadas no afectan el resultado de la extracción. Si se pierden todas las notificaciones, los despertares en primer plano y las comprobaciones periódicas aún descubren una secuencia más nueva.
Si el cursor es más antiguo que la ventana de retención de 30 días, el servicio devuelve cursor_expired con una ubicación para una instantánea consistente del namespace. El cliente carga la instantánea, reconcilia las operaciones locales no confirmadas y reanuda desde la marca de agua (watermark) de la instantánea. No debe limitarse a borrar todos los archivos locales. Las notificaciones son una optimización de latencia; el registro del cursor es el protocolo de sincronización.
Paso seis: gestionar conflictos, eliminación y recuperación de versiones.
Los dispositivos A y B editan ambos la versión 10 sin conexión. A confirma la versión 11 con baseVersionId=10. El commit condicional posterior de B falla. El servicio retiene el contenido subido por B, crea una nueva entrada como "nombre (copia en conflicto de B)" o una versión en conflicto, y añade un cambio en el namespace. Los archivos binarios comunes no se fusionan automáticamente en silencio. La fusión específica de texto o documentos es una capacidad de producto independiente.
Una eliminación actualiza deletedAt y añade un tombstone. Los dispositivos en línea mueven la entrada a la papelera, y un dispositivo sin conexión puede enterarse de la eliminación cuando se vuelva a conectar. Las versiones históricas y las referencias a fragmentos siguen siendo válidas durante la ventana de recuperación de 30 días. Después, el recolector calcula el conjunto de fragmentos referenciados por los manifiestos retenidos, marca los candidatos no referenciados, espera un período de gracia y vuelve a reconciliar antes de eliminar. Los reintentos, eventos retrasados y tareas de reparación pueden hacer que un recuento de referencias instantáneo sea incorrecto, por lo que no puede ser la única autoridad para una eliminación irreversible.
Paso siete: estimar la capacidad y aislar puntos calientes.
50 million × 10 GB = 500 PB de almacenamiento lógico. La replicación, el historial y la deduplicación cambian la capacidad física, pero el enunciado no da proporciones, por lo que un número físico preciso sería inventado. El ingreso lógico diario es de 100 million × 4 MB = 400 TB, con un promedio de aproximadamente 4,6 GB/s y un pico de alrededor de 25 GB/s. Los commits de versiones promedian 100,000,000 / 86,400 ≈ 1,157/s y se redondean a unos 6.000/s en el pico de cinco veces.
Si cada cambio da una pista a tres dispositivos en línea en promedio, las notificaciones pueden alcanzar un pico cercano a 18.000/s. Las notificaciones pueden fusionarse en "este namespace cambió" en lugar de ser mensajes confiables por archivo. Distribuye los metadatos en fragmentos mediante hash (hash-shard) por namespaceId, manteniendo las escrituras ordenadas para un namespace en un único shard primario. Un espacio compartido muy grande puede sobrecalentarse. Limita la tasa de operaciones masivas de directorio, fusiona notificaciones y sub-fragmenta registros de archivos por fileId solo con evidencia concreta, manteniendo al mismo tiempo un generador de secuencias de namespace separado.
Paso ocho: cerrar los ciclos de fallos, seguridad y validación.
- Subida interrumpida: consulta la sesión y sube solo las partes faltantes; el procesamiento del ciclo de vida expira las sesiones abandonadas.
- Partes subidas pero commit fallido: las partes son huérfanas temporales y la reconciliación de manifiestos las reclama tras un período de gracia.
- Commit exitoso pero notificación fallida: el outbox transaccional reintenta, y las extracciones por cursor se recuperan de todos modos.
- Commit exitoso pero respuesta perdida: la misma clave de idempotencia devuelve el resultado original.
- Escrituras concurrentes sin conexión: la condición
baseVersionIdfalla y el servicio preserva una copia en conflicto. - Fragmento corrupto: la subida y la descarga verifican un hash fuerte, y el contenido no válido no puede ingresar a un manifiesto publicado.
- Cursor vencido: carga una instantánea y luego reanuda desde su marca de agua.
- La región principal no puede escribir: detén las escrituras o realiza un failover bajo un procedimiento explícito de RPO/RTO; nunca permitas dos escritores principales.
Las URL firmadas deben ser de corta duración y estar vinculadas a una cuenta, objeto, tamaño y operación. El servidor vuelve a comprobar la autorización y la cuota en el commit. La subida instantánea global entre usuarios crea un canal lateral sobre la existencia de contenido y acopla el cifrado y los derechos de eliminación por usuario, por lo que la deduplicación está limitada al inquilino por defecto. Las métricas clave incluyen el p99 de subida y confirmación, el retraso de sincronización, cursores vencidos, copias en conflicto, bytes de fragmentos huérfanos, aciertos de deduplicación, diferencias entre candidatos de GC y eliminación real, namespaces calientes, fallos de checksum y éxito en la recuperación.
La validación abarca transferencias reanudables de archivos de 50 GB, caídas tras cada etapa de subida, commits duplicados, notificaciones perdidas y desordenadas, ediciones concurrentes sin conexión, un dispositivo antiguo que regresa tras una eliminación, corrupción de checksums, failover del shard primario, cursores vencidos, límites de cuota y protección de GC contra eliminaciones falsas. La aserción de extremo a extremo más importante es que cada fileVersionId visible se descargue con contenido verificado y completo, y que una versión dentro de la ventana de recuperación nunca sea reclamada.
Respuesta de muestra de alta calidad
"En primer lugar, separaría el plano de contenido del plano de metadatos. Los archivos se dividen en fragmentos inmutables de aproximadamente 4 MiB en almacenamiento de objetos. Los directorios, los valores estables de fileId, las versiones actuales, los manifiestos de versiones, los tombstones y los números de secuencia del namespace residen en una capa de metadatos que admite escrituras condicionales. Los movimientos y cambios de nombre actualizan los metadatos sin sobrescribir el historial.
Durante la subida, el cliente genera hashes de los fragmentos y crea una sesión con baseVersionId y una clave de idempotencia. El servidor devuelve URL firmadas de corta duración solo para los fragmentos faltantes dentro del ámbito del inquilino. Después de que todos los fragmentos se suben y verifican, una transacción de metadatos confirma que la versión actual no cambió, escribe la nueva versión y el manifiesto, actualiza currentVersionId y añade Change.seq más un registro en el outbox. El contenido se completa antes de la publicación de metadatos, por lo que una versión visible nunca apunta a bytes faltantes. Una respuesta perdida se recupera con la misma clave de idempotencia.
La sincronización utiliza un cursor duradero. Una notificación solo indica que un namespace pudo haber cambiado. Los dispositivos llaman a /changes?cursor=, aplican los cambios en orden y luego avanzan el cursor. Por lo tanto, las notificaciones perdidas, duplicadas y desordenadas no provocan pérdida de datos. Un cursor más antiguo que la ventana de retención de 30 días se reconstruye a partir de una instantánea consistente y se reanuda desde su marca de agua. Cuando dos dispositivos editan la misma versión sin conexión, el commit posterior preserva una copia en conflicto en lugar de sobrescribir la versión más nueva.
La capacidad es de 500 PB de almacenamiento lógico. El contenido nuevo o modificado es de 400 TB por día, con un promedio de unos 4,6 GB/s y un pico cercano a 25 GB/s. Cien millones de versiones promedian aproximadamente 1.157 commits por segundo y alcanzan un pico de cerca de 6.000/s. Los metadatos se particionan por namespace con una región de escritura principal, mientras que las descargas escalan a través de CDN y réplicas regionales de objetos.
Una eliminación escribe un tombstone retenido durante 30 días. Cuando el historial expira, la recolección de basura marca los candidatos a partir de todos los manifiestos retenidos, espera un período de gracia y vuelve a reconciliar. Nunca elimina únicamente porque un recuento de referencias haya llegado a cero. Inyectaría fallos en cada límite de subida y commit, y luego verificaría notificaciones perdidas, conflictos sin conexión, cursores vencidos, corrupción de checksums, failover primario y seguridad del GC, asegurando continuamente que cada versión visible se descargue por completo".
Errores comunes
- Almacenar bytes de archivos en la base de datos de metadatos → los objetos grandes sobrecargan la replicación, las copias de seguridad y las transacciones → almacena referencias en metadatos y contenido inmutable en almacenamiento de objetos.
- Publicar un archivo después de subir la primera parte → otros dispositivos pueden leer una versión incompleta → haz commit atómico de los metadatos solo después de que cada parte esté verificada.
- Tratar las notificaciones de WebSocket como la verdad de la sincronización → un mensaje perdido o un período sin conexión pierde cambios permanentemente → usa las notificaciones solo para activar extracciones autorizadas por cursor.
- Hacer commit sin
baseVersionId→ un dispositivo sin conexión sobrescribe silenciosamente una versión más nueva → usa commit condicional y preserva una copia en conflicto en caso de fallo. - Generar una nueva sesión y clave de idempotencia en cada reintento → se multiplican las versiones duplicadas, los cobros de cuota y los huérfanos → usa una clave estable para recuperar el resultado original.
- Configurar por defecto la subida instantánea global entre usuarios → se filtra la existencia de contenido y se acoplan el cifrado y la eliminación → limita la deduplicación a una cuenta o inquilino.
- Eliminar fragmentos inmediatamente después de una eliminación del usuario → la sincronización sin conexión, la recuperación o las transacciones demoradas harán referencia a bytes faltantes → usa tombstones, retención, un período de gracia y reconciliación.
- Asumir una proporción fija de deduplicación para la capacidad física → una carga de trabajo desconocida produce una falsa precisión → reporta 500 PB lógicos y calibra los ahorros físicos a partir de mediciones.
- Enviar de forma confiable cada cambio de archivo a cada dispositivo mediante push → el costo de notificación y el estado de reintentos explotan → fusiona las pistas de namespace y permite que los clientes extraigan deltas.
- Comenzar con escrituras de metadatos activo-activo entre regiones → los conflictos de nombres, movimientos y versión actual se vuelven difíciles de converger → mantén un único escritor principal por namespace primero.
Preguntas de seguimiento y respuestas
Pregunta de seguimiento 1: ¿Cómo eliges entre fragmentos de tamaño fijo y definidos por contenido?
Los fragmentos cercanos a 4 MiB son simples y predecibles, y funcionan bien para adiciones (appends), sobrescrituras localizadas y la mayoría de los archivos multimedia. Si los usuarios insertan con frecuencia contenido cerca del principio, los límites fijos se desplazan y todos los hashes posteriores cambian. La fragmentación definida por contenido puede redescubrir contenido sin cambios, pero consume más CPU y requiere un algoritmo de límites estable y versionado. Conviene lanzar con fragmentos fijos, medir el porcentaje de bytes reutilizables después de las ediciones y habilitar la fragmentación definida por contenido para archivos grandes seleccionados solo cuando los ahorros observados justifiquen la complejidad.
Pregunta de seguimiento 2: ¿Cómo preservan los movimientos de directorio el orden de sincronización?
Un movimiento actualiza parentId y añade una secuencia de namespace en una transacción de metadatos. Los clientes lo aplican mediante seq. Un movimiento no reescribe las rutas para cada descendiente porque las rutas se derivan de la cadena de padres. Un movimiento entre distintos namespaces no puede pretender ser una única actualización de metadatos local; modélalo como un flujo de trabajo reintentable de copia y eliminación, y muestra un estado de "en progreso" al usuario.
Pregunta de seguimiento 3: ¿Cómo coexisten el cifrado de extremo a extremo y la subida instantánea?
Con el cifrado de extremo a extremo en el lado del cliente, el servidor generalmente solo ve texto cifrado. Diferentes claves por usuario hacen que el mismo texto sin formato produzca diferentes textos cifrados, lo que elimina la mayor parte de la deduplicación entre usuarios. El cifrado convergente introduce ataques de confirmación de contenido y riesgos en la gestión de claves. El producto debe decidir: un modo de alta privacidad acepta una menor deduplicación, mientras que un modo con claves administradas por el inquilino puede deduplicar dentro del inquilino. No se puede prometer una subida instantánea global incondicional y una estricta confidencialidad de extremo a extremo al mismo tiempo.
Pregunta de seguimiento 4: ¿Qué sucede si un directorio compartido enorme se vuelve un punto caliente?
Primero, fusiona las notificaciones, limita la tasa de operaciones masivas, almacena en caché páginas de directorios de solo lectura y mide si el cuello de botella es la asignación de secuencias, la unicidad de nombres o el listado. Los metadatos de archivos pueden sub-fragmentarse por fileId, pero el namespace aún necesita una marca de agua ordenada. Internamente, asigna rangos de secuencias por lotes o utiliza un registro particionado exponiendo al mismo tiempo un cursor externo estable. No descartes semánticas de ordenamiento recuperables simplemente para alegar escalabilidad horizontal.
Pregunta de seguimiento 5: ¿Cómo evita la recolección de basura las eliminaciones falsas?
La GC no confía únicamente en los recuentos de referencias en vivo. Construye un conjunto activo a partir de cada manifiesto que aún esté dentro del período de retención, marca los fragmentos fuera de ese conjunto como candidatos, espera un tiempo superior al retraso máximo de transacción, replicación y recuperación, y reconcilia con los manifiestos actuales antes de eliminar. Las eliminaciones son idempotentes y auditadas; los manifiestos faltantes o las discrepancias retrasan la recolección. Las partes incompletas de subidas multipartes también necesitan expiración de sesión independiente y procesamiento de cancelación.
Pregunta de seguimiento 6: ¿Cómo proporcionas recuperación ante desastres entre regiones?
Normalmente, cada namespace tiene una región principal que acepta escrituras de metadatos. Otras regiones replican asincrónicamente su registro y sus objetos. La conmutación por error (failover) primero aísla (fences) al escritor antiguo, promueve una réplica de recuperación bajo una nueva época (epoch) y modifica el enrutamiento. El RPO depende del retraso de replicación y el RTO depende de la detección y la promoción. Un RPO de cero requiere un cuórum sincrónico entre regiones y una mayor latencia de escritura. Un primario antiguo recuperado debe validar la época de liderazgo del namespace antes de aceptar escrituras.
Pregunta de seguimiento 7: ¿Cómo demuestras que la sincronización nunca pierde datos?
Construye un modelo de estados que genere secuencias de subida, confirmación, movimiento, eliminación, conflicto y reintento. Afirma que el estado de un cliente tras aplicar la secuencia N es igual a la instantánea del servidor en N. Las pruebas de extremo a extremo descartan, duplican y desordenan notificaciones aleatoriamente sin corromper el registro de cambios; los dispositivos deben seguir convergiendo a través de extracciones por cursor. Inyecta caídas tras aplicar un cambio pero antes de guardar el cursor, y tras guardar el contenido local pero antes de completar el proceso. La reejecución debe seguir siendo idempotente y cada dispositivo debe alcanzar en última instancia el mismo grafo de versiones.