Elido
8 min de lecturaIngeniería

Lanzamiento de la migración de Dub.co: las carpetas se aplanan en etiquetas

Cómo construimos las importaciones de Dub.co con un clic para Elido: la API más limpia de las cinco, el aplanamiento de carpetas a etiquetas y por qué este cambio es para equipos que se preocupan por la residencia en la UE.

Marius Voß
DevRel · edge infra
Diagrama de la tubería: API REST de Dub.co a la izquierda fluyendo a través del trabajador de importación de Elido hacia la tabla de enlaces, con un panel lateral que enumera las garantías numéricas (límite de 50k, presupuesto de 30 min, 100 por página, las carpetas se aplanan a etiquetas)

La quinta y última fuente de migración en nuestro despliegue de Nivel 3 se lanzó hoy. Pega un Dub.co API token, filtra opcionalmente por slug de proyecto, haz clic en Iniciar. De tres a cinco minutos después, cada enlace está en tu dominio de Elido con el slug preservado y la estructura de carpetas de Dub aplanada en etiquetas de Elido.

Esta publicación es el informe de ingeniería: qué es específico de Dub, por qué su API es la más limpia de los cinco proveedores que admitimos y qué motiva un cambio de Dub a Elido.

¿Por qué existe esta migración?#

Dub.co es el equivalente de código abierto más cercano a Elido en superficie de características. Producto sólido, API REST limpia, panel de control moderno. La ruta de migración aquí es para equipos que eligen Elido por una de estas tres razones:

  • Residencia en la UE. El plano de datos de Elido está anclado en la UE: Hetzner FRA + ASH POPs, OVH SGP para APAC, todos los eventos de clic aterrizan en ClickHouse de la región de la UE de forma predeterminada. Dub Cloud está anclado en EE. UU.; la postura de RGPD/Schrems-II es el compromiso.
  • Huella de POP en el borde. Tres POPs regionales con p95 < 15ms cache HIT es un objetivo de latencia diferente al camino exclusivo de Cloudflare-Workers de Dub. Las cargas de trabajo sensibles a la latencia (mobile-first, ad-attribution) sienten la diferencia.
  • Profundidad de analíticas. Analíticas de clics respaldadas por ClickHouse con retención por evento, reenvío de conversiones a GA4/Meta CAPI y reproducción histórica completa. Las analíticas de Dub son limpias pero agregadas en PostgreSQL; el límite de profundidad es diferente.

Si ninguna de esas razones aplica, Dub es un buen producto. La migración está aquí para los equipos a los que sí les aplica.

Por qué la API de Dub es la más limpia#

Hemos construido trabajadores para las API de cinco proveedores. Ranking por facilidad de integración:

  1. Dub.co — token bearer, errores compatibles con JSON-RFC, paginación ?page= + ?limit=100, cada campo documentado con cargas útiles de ejemplo.
  2. Short.io — limpia, booleano HasMore explícito, pero la partición por dominio necesita trabajo de UX.
  3. Bitly — la URL pagination.next cumple con los estándares; la referencia de API adjunta es exhaustiva.
  4. TinyURL — Solo Pro/Bulk, el resto no es compatible; la documentación es escasa.
  5. Rebrandly — el cursor ?last=<id> está bien, pero el límite de 25 por página hace que todo se sienta lento.

La ventaja de Dub: sus documentos incluyen ejemplos de curl, sus respuestas de error incluyen tanto un código de máquina como un mensaje humano, y su paginación es del tipo aburrido donde ?page=2&limit=100 funciona exactamente de la manera que esperarías.

const dubPageSize = 100

page := 1
for {
    resp, err := w.fetchPage(ctx, opts.Token, opts.WorkspaceID, page)
    if err != nil { /* mark failed */ return }
    if len(resp) == 0 { break }
    for _, link := range resp { /* import */ }
    if len(resp) < dubPageSize { break }
    page++
}

Dub no devuelve un flag HasMore; lo inferimos de una página corta. Este es el patrón de paginación REST estándar y funciona bien: una página más corta que el límite significa que hemos terminado.

Las carpetas se aplanan en etiquetas#

Dub tiene tanto carpetas como etiquetas como primitivas de organización. Elido solo tiene etiquetas. Por lo tanto, la migración aplana las carpetas de Dub en el saco de etiquetas:

tags := make([]string, 0, len(link.Tags)+2)
for _, t := range link.Tags {
    tags = append(tags, t.Name)
}
if link.Folder != nil && link.Folder.Name != "" {
    // Dub folders can nest; flatten the full path.
    for _, segment := range strings.Split(link.Folder.Name, "/") {
        seg := strings.TrimSpace(segment)
        if seg != "" {
            tags = append(tags, seg)
        }
    }
}
tags = append(tags, "imported:dub")

Un enlace de Dub en la carpeta campaigns/q3-launch con etiquetas paid y linkedin se importa con las etiquetas paid, linkedin, campaigns, q3-launch y imported:dub. La semántica de filtro en Elido maneja los mismos patrones de recuperación que la interfaz de usuario de carpetas de Dub: etiqueta-igual, etiqueta-contiene, etiqueta-múltiple-AND. No estamos reinventando la jerarquía de carpetas en el lado del servidor; el usuario obtiene una lista plana de etiquetas y las primitivas de filtro.

¿Podríamos haber añadido carpetas a Elido en su lugar? Sí. Elegimos solo etiquetas cuando se lanzó el modelo de datos de Elido en la Fase 1; las carpetas tienen sentido para modelos mentales de sistemas de archivos de escritorio y menos sentido para operaciones masivas de enlaces cortos. Migrar usuarios de Dub a etiquetas de Elido es el lado correcto de ese compromiso.

Filtrado de proyectos#

Dub utiliza "espacios de trabajo" (workspaces, en su interfaz más nueva) y los llamaba históricamente "proyectos". La API acepta un parámetro workspaceId para filtrar; el lanzador lo expone como un campo de texto opcional. Pega el slug del espacio de trabajo desde tu URL de Dub, o déjalo en blanco para capturar cada enlace que el token pueda ver.

Esto refleja el filtro de espacio de trabajo de Rebrandly y el campo de dominio de Short.io. Tres de nuestros cinco proveedores de migración tienen un concepto de partición por cuenta; lo exponemos de forma consistente como una entrada de texto opcional en lugar de un menú desplegable poblado, porque el usuario típico tiene como máximo dos espacios de trabajo y el endpoint de listado de API añade una latencia que no merece la pena el pulido.

Lo que no migramos#

Reglas de geo-orientación y orientación por dispositivo de Dub. Son una característica potente de Dub, pero la forma de la regla no se asigna 1:1 a las reglas de enlace inteligente de Elido. El slug se importa; reconstruye las reglas usando la sintaxis de expresión de Elido, que es más permisiva pero tiene una forma mental diferente.

Historial por clic. Límite universal en todas las cinco fuentes de migración. Los datos por clic de Dub se encuentran detrás de su endpoint de analíticas, que está vinculado al nivel del plan; los nuevos clics aterrizan en las analíticas de Elido desde la transición.

Estilo de QR. El QR predeterminado de Elido se regenera; los diseños personalizados deben volver a aplicarse. La etiqueta imported:dub es el manejador de reasignación masiva.

ACLs del espacio de trabajo de Dub y configuración de roles. Vuelve a otorgar acceso en Elido usando SCIM/SSO o invitaciones a miembros del espacio de trabajo; el modelo de roles difiere lo suficiente entre los dos productos como para que la asignación automatizada se manifieste como una escalada de privilegios silenciosa.

Dub auto-alojado (Self-hosted)#

Dub es de código abierto y las instancias de Dub auto-alojadas son comunes. La migración utiliza la misma API REST que expone el producto Dub Cloud, por lo que apuntar a un Dub auto-alojado significa sobrescribir DUB_API_BASE. No expusimos eso como una configuración de autoservicio en la v1 porque el mantenimiento operativo no es trivial: diferentes versiones de Dub exponen formas de respuesta sutilmente diferentes, y no queremos lanzar un lanzador que dé 500 silenciosamente en una implementación de Dub v0.7 cuando v0.9 es el objetivo probado.

Para migraciones de Dub auto-alojado, envía un correo electrónico a [email protected] con tu versión de Dub y ejecutaremos una migración asistida única. Una vez que hayamos visto suficientes versiones en la naturaleza, la sobrescritura se convertirá en una configuración de panel de control de autoservicio.

Manejo de tokens#

La misma semántica de una sola vez que los otros cuatro proveedores de migración:

bgCtx := context.WithoutCancel(r.Context())
go h.dub.Run(bgCtx, job.ID, imports.DubJobOptions{
    Token:       req.Token,
    WorkspaceID: req.WorkspaceID,
})

source_token_id permanece NULL. El token vive en la memoria del proceso api-core para la ejecución del trabajador, y se elimina al finalizar. Sin persistencia: esta es una migración de una sola vez, no una llamada recurrente al proveedor.

context.WithoutCancel (Go 1.21+) mantiene vivo al trabajador después de la solicitud HTTP. El mismo patrón que cualquier otro proveedor de migración en este despliegue.

Resolución de conflictos y el contrato del trabajador#

Idéntico a cualquier otro proveedor. Los recorridos de sufijo prueban mylink-2, mylink-3, …, hasta 50 candidatos; saltar deja el enlace de Elido existente solo; fallar aborta en el primer conflicto. El contrato del trabajador — MaxLinksPerImport=50_000, ImportRunBudget=30*time.Minute — se comparte entre los cinco.

La búsqueda es una lectura indexada por fila en (domain_id, slug). Recorridos de sufijo deterministas, errores más amigables que buscar violaciones de unicidad en pgx.

Lo que medimos contra Bitly#

Tanto Dub como Bitly migran aproximadamente al mismo rendimiento: 100 enlaces por página, ~5 inserciones/seg sostenidas. Una fuente de 5.000 enlaces termina en 4–7 minutos para ambos proveedores. La diferencia visible para el usuario es la experiencia posterior a la importación: los enlaces importados de Dub llegan con rutas estructuradas de carpeta-como-etiqueta; los enlaces importados de Bitly llegan solo con la etiqueta imported:bitly y cualquier etiqueta de forma libre de Bitly.

Reanudabilidad y el problema del despliegue#

El mismo compromiso que las primeras cuatro migraciones. El trabajador está en proceso; un despliegue a mitad de la importación mata a la goroutine. El cron de limpieza de bloqueos de 5 minutos cambia cualquier fila running sin progreso en 30 minutos a failed. La re-ejecución es idempotente bajo estrategias de sufijo y salto.

Para cuentas superiores a 10.000 enlaces, la reanudabilidad valdría la pena: registraríamos el cursor de page de Dub en import_jobs.source_filter y reanudaríamos desde la última página completada. Los cinco proveedores de migración comparten el mismo diseño en proceso; cuando lancemos la reanudabilidad, los cinco se beneficiarán.

¿Qué sigue para el despliegue de Nivel 3?#

El Nivel 3 ha terminado. Cinco proveedores de migración, una tabla compartida import_jobs, un contrato de trabajador compartido, una interfaz de usuario de sondeo de panel de control compartida, cinco páginas de destino SEO, cinco publicaciones de blog de ingeniería.

Lo que está en cola detrás del Nivel 3:

  • Reanudabilidad para cuentas de más de 10.000 enlaces. Control de puntos de verificación de cursor por proveedor.
  • Respaldo de exportación CSV para usuarios en planes con tokens revocados. Actualmente solo bajo asistencia.
  • Base de service_tokens de Nivel 2: tokens de proveedor de uso recurrente para Mailchimp, Brevo, Klaviyo. La ruta de migración validó el patrón JSONB source_filter; el Nivel 2 necesita tokens cifrados persistentes, lo cual es territorio de ADR-0036.

Si has estado mirando a Elido desde un espacio de trabajo de Dub, la historia de la migración ya está documentada. Pruébalo: del token al último enlace importado en menos de cinco minutos para cuentas típicas.

Relacionado en el blog#

Prueba Elido

Acortador de URL alojado en la UE: dominios personalizados, análisis profundo y API abierta. Plan gratuito — sin tarjeta de crédito.

Etiquetas
dub.co migration
url shortener
go worker
data migration
engineering
tier 3 integrations

Seguir leyendo