A quinta e última fonte de migração em nosso lançamento Tier-3 foi liberada hoje. Cole um Dub.co API token, filtre opcionalmente por slug de projeto, clique em Iniciar. Três a cinco minutos depois, cada link está em seu domínio Elido com o slug preservado e a estrutura de pastas do Dub transformada em tags do Elido.
Este post é o relato de engenharia — o que é específico do Dub, por que a API deles é a mais limpa dos cinco fornecedores que suportamos, e o que motiva uma migração do Dub para o Elido.
Por que esta migração existe#
Dub.co é o equivalente de código aberto mais próximo do Elido em superfície de recursos. Produto forte, API REST limpa, dashboard moderno. O caminho de migração aqui é para equipes que escolhem o Elido por um de três motivos:
- Residência na UE. O plano de dados do Elido é ancorado na UE — POPs Hetzner FRA + ASH, OVH SGP para APAC, todos os eventos de clique caem no ClickHouse da região da UE por padrão. O Dub Cloud é ancorado nos EUA; a postura de GDPR/Schrems-II é o trade-off.
- Pegada de POPs de borda. Três POPs regionais com p95 < 15ms cache HIT são um alvo de latência diferente do caminho exclusivo de Cloudflare-Workers do Dub. Workloads sensíveis à latência (mobile-first, ad-attribution) sentem a diferença.
- Profundidade de analytics. Analytics de cliques baseados em ClickHouse com retenção por evento, encaminhamento de conversão para GA4/Meta CAPI e replay histórico completo. Os analytics do Dub são limpos, mas agregados via PostgreSQL; o teto de profundidade é diferente.
Se nada disso se aplica, Dub é um bom produto. A migração está aqui para as equipes às quais isso se aplica.
Por que a API do Dub é a mais limpa#
Nós construímos workers para cinco APIs de fornecedores. Ranking por facilidade de integração:
- Dub.co — bearer token, erros compatíveis com JSON-RFC, paginação
?page=+?limit=100, cada campo documentado com exemplos de payload. - Short.io — limpo, booleano
HasMoreexplícito, mas o particionamento por domínio precisa de trabalho na UX. - Bitly — URL
pagination.nextcompatível com padrões; a referência da API completa é minuciosa. - TinyURL — Apenas Pro/Bulk, o resto não é suportado; a documentação é escassa.
- Rebrandly — cursor
?last=<id>é ok, mas o limite de 25 por página faz tudo parecer lento.
O diferencial do Dub: a documentação deles inclui exemplos em curl, as respostas de erro incluem tanto um código de máquina quanto uma mensagem humana, e a paginação é do tipo comum onde ?page=2&limit=100 funciona exatamente da maneira que você imagina.
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++
}
O Dub não retorna uma flag HasMore; inferimos isso a partir de uma página curta. Este é o padrão de paginação REST padrão e funciona bem — uma página menor que o limite significa que terminamos.
Pastas transformadas em tags#
O Dub tem pastas e tags como primitivas de organização. O Elido tem apenas tags. Portanto, a migração transforma as pastas do Dub no conjunto de tags:
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")
Um link do Dub na pasta campaigns/q3-launch com tags paid e linkedin é importado com as tags paid, linkedin, campaigns, q3-launch e imported:dub. A semântica de filtro no Elido lida com os mesmos padrões de recuperação que a UI de pastas do Dub — tag-equals, tag-contains, multi-tag-AND. Não estamos reinventando a hierarquia de pastas no lado do servidor; o usuário obtém uma lista plana de tags e as primitivas de filtro.
Poderíamos ter adicionado pastas ao Elido? Sim. Escolhemos apenas tags quando o modelo de dados do Elido foi lançado na Fase 1; pastas fazem sentido para modelos mentais de sistema de arquivos de desktop e menos sentido para operações de links curtos em massa. Migrar usuários do Dub para tags do Elido é o lado correto dessa troca.
Filtragem de projeto#
O Dub usa "workspaces" (em sua UI mais recente) e, historicamente, os chamava de "projects". A API aceita um parâmetro workspaceId para filtrar; o lançador o expõe como um campo de texto opcional. Cole o slug do workspace da sua URL do Dub, ou deixe em branco para capturar todos os links que o token pode ver.
Isso espelha o filtro de workspace do Rebrandly e o campo de domínio do Short.io. Três dos nossos cinco fornecedores de migração têm um conceito de particionamento por conta; expomos isso consistentemente como uma entrada de texto opcional em vez de um dropdown preenchido, porque o usuário típico tem no máximo dois workspaces e o endpoint de listagem da API adiciona uma latência que não compensa o polimento.
O que não migramos#
Regras de geo-targeting e device-targeting do Dub. Eles são um recurso poderoso do Dub, mas o formato da regra não mapeia 1:1 para as regras de smart-link do Elido. O slug é importado; reconstrua as regras usando a sintaxe de expressão do Elido, que é mais permissiva, mas assume uma forma mental diferente.
Histórico por clique. Limite universal em todas as cinco fontes de migração. Os dados por clique do Dub ficam atrás do endpoint de analytics deles, que é vinculado ao nível do plano; novos cliques caem nos analytics do Elido a partir da migração.
Estilização de QR. O QR padrão do Elido é regenerado; designs personalizados precisam ser reaplicados. A tag imported:dub é o identificador para reatribuição em massa.
ACLs de workspace e configuração de papéis do Dub. Reconceda acesso no Elido usando SCIM/SSO ou convites de membros do workspace; o modelo de papel difere o suficiente entre os dois produtos que um mapeamento automatizado apareceria como um aumento de privilégio silencioso.
Dub self-hosted#
O Dub é de código aberto e instâncias do Dub self-hosted são comuns. A migração usa a mesma API REST que o produto Dub Cloud expõe, então apontar para um Dub self-hosted significa sobrescrever DUB_API_BASE. Não expusemos isso como uma configuração de autoatendimento na v1 porque a cauda operacional não é trivial — diferentes versões do Dub expõem formas de resposta sutilmente diferentes, e não queremos lançar um lançador que dê erro 500 silenciosamente em um deploy do Dub v0.7 quando a v0.9 é o alvo testado.
Para migrações de Dub self-hosted, envie um e-mail para [email protected] com sua versão do Dub e faremos uma migração assistida única. Assim que virmos versões suficientes no mercado, a sobrescrita se tornará uma configuração de dashboard de autoatendimento.
Manuseio de tokens#
Mesma semântica de execução única que os outros quatro fornecedores de migração:
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. O token vive na memória do processo api-core durante a execução do worker, descartado na conclusão. Sem persistência — esta é uma migração de execução única, não uma chamada recorrente ao fornecedor.
context.WithoutCancel (Go 1.21+) mantém o worker vivo após a requisição HTTP. O mesmo padrão de todos os outros fornecedores de migração neste lançamento.
Resolução de conflitos e o contrato do worker#
Idêntico a todos os outros fornecedores. O sufixo percorre mylink-2, mylink-3, …, até 50 candidatos; skip deixa o link existente do Elido sozinho; fail interrompe no primeiro conflito. Contrato do worker — MaxLinksPerImport=50_000, ImportRunBudget=30*time.Minute — é compartilhado entre todos os cinco.
A busca é uma leitura indexada por linha em (domain_id, slug). Percursos de sufixo determinísticos, erros mais amigáveis do que pescar violações de unicidade no pgx.
O que medimos em comparação com o Bitly#
Tanto o Dub quanto o Bitly migram com praticamente o mesmo throughput — 100 links por página, ~5 inserções/seg sustentadas. Uma fonte de 5.000 links termina em 4–7 minutos para ambos os fornecedores. A diferença visível para o usuário é a experiência pós-importação: links importados do Dub chegam com caminhos estruturados de pasta-como-tag; links importados do Bitly chegam apenas com a tag imported:bitly e quaisquer tags de forma livre do Bitly.
Resumibilidade e o problema de deploy#
Mesma troca das quatro primeiras migrações. O worker está dentro do processo; um deploy no meio da importação mata a goroutine. O cron de limpeza de travados de 5 minutos altera qualquer linha running sem progresso em 30 minutos para failed. Reexecutar é idempotente sob as estratégias de sufixo e skip.
Para contas acima de 10.000 links, a resumibilidade valeria a pena — registraríamos o cursor de page do Dub em import_jobs.source_filter e retomaríamos da última página concluída. Todos os cinco fornecedores de migração compartilham o mesmo design dentro do processo; quando lançarmos a resumibilidade, todos os cinco se beneficiarão.
O que vem a seguir para o lançamento Tier-3#
Tier-3 está concluído. Cinco fornecedores de migração, uma tabela import_jobs compartilhada, um contrato de worker compartilhado, uma UI de polling de dashboard compartilhada, cinco landings de SEO, cinco posts de blog de engenharia.
O que está na fila após o Tier-3:
- Resumibilidade para contas acima de 10.000 links. Checagem de checkpoint de cursor por fornecedor.
- Fallback para exportação CSV para usuários em planos com tokens revogados. Atualmente apenas via concierge.
- Fundação para service_tokens do Tier-2 — tokens de fornecedor de uso recorrente para Mailchimp, Brevo, Klaviyo. O caminho de migração validou o padrão JSONB
source_filter; o Tier-2 precisa de tokens criptografados persistentes, o que é território do ADR-0036.
Se você tem olhado de lado para o Elido a partir de um workspace do Dub, a história da migração está documentada agora. Experimente — do token ao último link importado em menos de cinco minutos para contas típicas.
Relacionado no blog#
- Lançando a migração do Bitly: um worker, um token, um orçamento de 30 minutos
- Lançando a migração do Rebrandly: paginação de 25 por página
- Lançando a migração do Short.io: paginação por domínio a 150/página
- Lançando a migração do TinyURL: REST Pro/Bulk, sem caminho de plano gratuito
- Links curtos como Terraform: a pedra angular da engenharia