Tema representativo de entrevista

Entrevista de backend: ¿Cómo migrarías identificadores UUIDv4 a UUIDv7 sin tiempo de inactividad?

BackendDifícil
Equipo editorial de Offer.ccPublicado Actualizado

Pregunta

Un servicio multirregión utiliza claves primarias UUIDv4 aleatorias y sufre costos de localidad de índice y paginación. Diseña una migración gradual a uuidv7() de PostgreSQL 18 sin bloquear escrituras ni romper clientes. Explica la estructura de las claves, las dobles escrituras, el backfill, las claves foráneas, el ordenamiento, la replicación, la validación y el rollback.

Planteamiento y alcance

El servicio tiene una tabla orders grande, muchas tablas hijas, URLs públicas, payloads de eventos y réplicas. PostgreSQL 18 proporciona un generador uuidv7() nativo para UUIDs ordenados por marca de tiempo. Mueve los nuevos registros hacia UUIDv7 mientras preservas el contrato existente de UUIDv4 y evitas una reescritura de la tabla o un bloqueo prolongado.

Esta es una pregunta de backend porque la habilidad fundamental es una migración de esquema e identidad en línea a través de APIs, almacenamiento y consumidores asíncronos.

Qué evalúan los entrevistadores

Primero, ¿puedes distinguir el formato del identificador de la verdad cronológica? UUIDv7 integra un orden temporal, pero no es una secuencia de confirmación global estricta y no debe reemplazar una marca de tiempo de negocio explícita.

Segundo, ¿puedes preservar la identidad pública? Las URLs existentes, cachés, claves de idempotencia, eventos y claves foráneas deben continuar resolviéndose durante la migración.

Tercero, ¿puedes diseñar fases de doble lectura y doble escritura con una fuente de verdad inequívoca?

Cuarto, ¿puedes realizar el backfill de forma segura a través de réplicas y regiones sin crear amplificación de escritura o retraso en la replicación?

Quinto, ¿puedes validar la localidad y la latencia en lugar de asumir que UUIDv7 es automáticamente más rápido para todas las cargas de trabajo?

Preguntas para aclarar primero

  • ¿El UUID está expuesto externamente, se utiliza como clave primaria o ambos?
  • ¿Pueden los clientes aceptar un nuevo identificador o el UUID antiguo debe seguir siendo canónico?
  • ¿Cuántas tablas hijas, índices, eventos y documentos de búsqueda hacen referencia a la clave?
  • ¿Qué versiones de PostgreSQL y extensiones existen en cada escritor y réplica?
  • ¿Cuáles son la tasa de escritura, el SLO de retraso de replicación y la fecha límite para el rollback?
  • ¿Se necesita el ordenamiento para paginación, visualización de auditoría o solo para la localidad del índice?

Estructura de respuesta en 30 segundos

“Mantendría UUIDv4 como el identificador público estable, agregaría una columna sustituta o de mapeo para UUIDv7 y desplegaría dobles escrituras antes de cualquier backfill. Las lecturas aceptan cualquiera de las dos claves mientras el mapeo se completa; las referencias hijas y los eventos siguen siendo compatibles. Haría el backfill en rangos acotados de claves primarias con monitoreo de retraso y bloqueos, luego cambiaría los índices internos y las rutas de consulta después de las verificaciones de paridad. UUIDv7 ayuda a la localidad y a los escaneos orientados al tiempo, pero las marcas de tiempo permanecen explícitas. El rollback es una reversión mediante feature flags hasta que todos los consumidores estén validados.”

Respuesta paso a paso

Paso 1: Elegir la estructura de compatibilidad

No cambies silenciosamente el significado de un UUID existente. Mantén order_id_v4 como el contrato externo y agrega order_id_v7 más una restricción de unicidad, o introduce una clave interna separada con un mapeo duradero. Decide qué clave utilizan las tablas hijas y los eventos durante cada fase.

sql
ALTER TABLE orders ADD COLUMN order_id_v7 uuid;
CREATE UNIQUE INDEX CONCURRENTLY orders_order_id_v7_uq
  ON orders(order_id_v7) WHERE order_id_v7 IS NOT NULL;

Usa uuidv7() únicamente en versiones de PostgreSQL que lo proporcionen; de lo contrario, despliega un generador equivalente de manera consistente antes de habilitar la ruta.

Paso 2: Desplegar primero las escrituras

Las nuevas escrituras generan ambos identificadores en una sola transacción y registran el mapeo. Las escrituras existentes que no pueden poblar la nueva columna siguen siendo válidas. Haz que los reintentos sean idempotentes para que un comando repetido no pueda crear dos mapeos.

Paso 3: Agregar resolución de doble lectura

La búsqueda de la API acepta cualquiera de los identificadores, resuelve a un orden canónico y emite el ID heredado en las respuestas antiguas. Los nuevos endpoints pueden exponer UUIDv7 bajo contratos versionados. Las claves de caché deben incluir la identidad canónica resuelta para que ambas formas no diverjan.

Paso 4: Realizar el backfill en lotes acotados

Rellena las filas utilizando un cursor estable, transacciones pequeñas y una pausa cuando el retraso de replicación, las esperas de bloqueo o la latencia de escritura superen un límite. Indexa la nueva columna una vez que existan suficientes datos, utilizando operaciones concurrentes donde sea compatible. No actualices las tablas hijas hasta que el mapeo principal sea duradero.

Paso 5: Migrar referencias y eventos

Mantén compatibles las claves foráneas hijas y los esquemas de eventos durante la superposición. Agrega campos v7 como opcionales, publica ambos valores y actualiza los consumidores antes de hacer obligatorio v7. Reconcilia mapeos faltantes y mapeos duplicados antes de cambiar las restricciones.

Paso 6: Cambiar las rutas de acceso interno

Después de las verificaciones de paridad, enruta los joins internos y la paginación a UUIDv7 donde la localidad o los escaneos orientados al tiempo importen. Mantén un created_at explícito para el ordenamiento de negocio y utiliza un criterio de desempate; el ordenamiento de UUIDv7 es aproximado a nivel de aplicación.

Paso 7: Validar y observar

Compara el tamaño del índice, las divisiones de página (page splits), el comportamiento del caché, la latencia de inserción, la latencia de escaneo de rangos, el retraso de replicación y las tasas de error contra una carga de trabajo equivalente. Verifica que las búsquedas por v4 y v7 devuelvan la misma fila y que los consumidores de eventos sigan siendo idempotentes.

Paso 8: Retirar solo después de una ventana de rollback

Conserva el mapeo, los índices antiguos y la ruta de doble lectura hasta que cada cliente, herramienta de reproducción, exportación y réplica haya superado la ventana de migración. Elimina las restricciones antiguas en despliegues separados con un plan de rollback explícito.

Respuesta modelo

“No reescribiría el contrato de UUID público in situ. Agregaría una columna UUIDv7 y un mapeo único, desplegaría dobles escrituras, luego dobles lecturas que resuelvan cualquiera de las formas a la misma orden. Realizaría el backfill en transacciones acotadas mientras monitoreo bloqueos, retraso de replicación y latencia de escritura. Las referencias hijas y los eventos llevan ambos IDs hasta que cada consumidor entienda v7. Después de las mediciones de paridad y localidad, cambiaría los joins internos y la paginación manteniendo created_at como el campo de ordenamiento de negocio. El feature flag puede revertir lecturas y escrituras hasta que la ruta antigua sea retirada.”

Errores comunes

  • Reemplazar UUIDs públicos de inmediato → los enlaces y eventos se rompen → preserva un contrato canónico y mapeo.
  • Asumir que UUIDv7 es un orden temporal estricto → la paginación omite o reordena registros → utiliza marcas de tiempo explícitas y criterios de desempate.
  • Realizar el backfill en una transacción gigante → los bloqueos y el retraso de replicación se disparan → utiliza lotes acotados.
  • Agregar una clave foránea antes de que existan los mapeos → las escrituras fallan durante la superposición → migra el padre, los hijos y luego las restricciones.
  • Generar IDs en versiones mixtas no soportadas → la semántica diverge → fija versiones o utiliza un generador consistente.
  • Ignorar los reintentos → aparecen mapeos duplicados → haz que las dobles escrituras sean idempotentes.
  • Eliminar v4 demasiado pronto → las exportaciones y reproducciones antiguas fallan → espera a que pase la ventana de rollback.

Preguntas de seguimiento

Pregunta de seguimiento 1: ¿Es UUIDv7 un reemplazo para created_at?

No. Puede mejorar la localidad y proporcionar bits orientados a marcas de tiempo, pero el ordenamiento de negocio necesita una marca de tiempo explícita y un criterio de desempate determinista.

Pregunta de seguimiento 2: ¿Pueden v4 y v7 compartir una columna UUID?

Sí, el tipo UUID puede almacenar ambos formatos, pero los metadatos de migración, los contratos externos y la semántica de ordenamiento aún necesitan un plan explícito.

Pregunta de seguimiento 3: ¿Por qué habilitar la doble lectura antes de cambiar las escrituras?

Permite que los registros antiguos y nuevos se resuelvan de manera consistente mientras el backfill y el despliegue para los consumidores están incompletos.

Pregunta de seguimiento 4: ¿Cómo regulas el ritmo del backfill?

Usa transacciones acotadas y pausa ante umbrales de retraso de replicación, esperas de bloqueo, CPU o latencia de escritura; reanuda desde un cursor duradero.

Pregunta de seguimiento 5: ¿Qué deben contener los eventos?

Durante la superposición, publica ambos IDs o una referencia de mapeo estable, versiona el esquema y actualiza los consumidores antes de hacer que v7 sea obligatorio.

Pregunta de seguimiento 6: ¿Cuándo se puede eliminar la columna antigua?

Solo después de que los clientes, exportaciones, reproducciones, réplicas y verificaciones de rollback hayan superado la ventana acordada; elimina restricciones e índices en despliegues reversibles separados.

Fuentes públicas

Preguntas relacionadas