La cinquième et dernière source de migration de notre déploiement Tier-3 a été livrée aujourd'hui. Collez un Dub.co API token, filtrez éventuellement par slug de projet, et cliquez sur Démarrer. Trois à cinq minutes plus tard, chaque lien se trouve sur votre domaine Elido avec le slug préservé et la structure de dossier Dub aplatie en tags Elido.
Cet article est le compte-rendu technique — ce qui est spécifique à Dub, pourquoi leur API est la plus propre des cinq fournisseurs que nous supportons, et ce qui motive un passage de Dub à Elido.
Pourquoi cette migration existe#
Dub.co est l'équivalent open-source le plus proche d'Elido en termes de fonctionnalités. Produit solide, API REST propre, tableau de bord moderne. Le chemin de migration ici est destiné aux équipes qui choisissent Elido pour l'une des trois raisons suivantes :
- Résidence dans l'UE. Le plan de données d'Elido est ancré dans l'UE — Hetzner FRA + ASH POPs, OVH SGP pour APAC, et tous les événements de clic sont envoyés par défaut dans ClickHouse dans la région UE. Dub Cloud est ancré aux États-Unis ; la posture GDPR/Schrems-II est le compromis.
- Empreinte Edge POP. Trois POPs régionaux avec un p95 < 15ms cache HIT constituent un objectif de latence différent de celui du chemin uniquement Cloudflare-Workers de Dub. Les charges de travail sensibles à la latence (mobile-first, ad-attribution) ressentent la différence.
- Profondeur de l'analytique. Analytique des clics basée sur ClickHouse avec rétention par événement, transfert de conversion vers GA4/Meta CAPI, et relecture historique complète. L'analytique de Dub est propre mais agrégée via PostgreSQL ; le plafond de profondeur est différent.
Si aucune de ces raisons ne s'applique, Dub est un excellent produit. La migration est là pour les équipes auxquelles cela s'applique.
Pourquoi l'API de Dub est la plus propre#
Nous avons maintenant construit des workers pour cinq API de fournisseurs différents. Classement par facilité d'intégration :
- Dub.co — token bearer, erreurs conformes à JSON-RFC, pagination
?page=+?limit=100, chaque champ documenté avec des exemples de charges utiles. - Short.io — propre, booléen
HasMoreexplicite, mais le partitionnement par domaine nécessite un travail sur l'UX. - Bitly — l'URL
pagination.nextest conforme aux standards ; la documentation API est exhaustive. - TinyURL — Pro/Bulk uniquement, le reste n'est pas supporté ; la documentation est rare.
- Rebrandly — le curseur
?last=<id>est correct mais la limite de 25 par page rend tout lent.
L'avantage de Dub : leur documentation inclut des exemples curl, leurs réponses d'erreur incluent à la fois un code machine et un message humain, et leur pagination est du type classique où ?page=2&limit=100 fonctionne exactement comme vous l'imagineriez.
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 ne renvoie pas de drapeau HasMore ; nous l'inférons à partir d'une page courte. C'est le modèle de pagination REST standard et il fonctionne parfaitement — une page plus courte que la limite signifie que nous avons terminé.
Les dossiers aplatis en tags#
Dub utilise à la fois des dossiers et des tags comme primitives d'organisation. Elido n'a que des tags. La migration aplatit donc les dossiers Dub dans le sac 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")
Un lien Dub dans le dossier campaigns/q3-launch avec les tags paid et linkedin est importé avec les tags paid, linkedin, campaigns, q3-launch et imported:dub. La sémantique de filtrage dans Elido gère les mêmes modèles de récupération que l'interface des dossiers de Dub — tag-égal, tag-contient, multi-tag-AND. Nous ne réinventons pas la hiérarchie des dossiers côté serveur ; l'utilisateur obtient une liste plate de tags et les primitives de filtrage.
Aurions-nous pu ajouter des dossiers à Elido ? Oui. Nous avons choisi uniquement les tags lorsque le modèle de données d'Elido a été lancé en Phase 1 ; les dossiers ont du sens pour les modèles mentaux de système de fichiers de bureau et moins pour les opérations de masse sur les liens courts. Migrer les utilisateurs de Dub vers les tags Elido est le bon côté de ce compromis.
Filtrage de projet#
Dub utilise des « workspaces » (dans sa nouvelle interface) et les appelait historiquement des « projets ». L'API accepte un paramètre workspaceId pour filtrer ; le lanceur l'expose sous forme de champ texte optionnel. Collez le slug du workspace depuis votre URL Dub, ou laissez vide pour récupérer chaque lien que le token peut voir.
Cela reflète le filtre de workspace Rebrandly et le champ de domaine Short.io. Trois de nos cinq fournisseurs de migration ont un concept de partitionnement par compte ; nous l'exposons de manière cohérente sous forme de saisie texte optionnelle plutôt que comme un menu déroulant, car l'utilisateur typique a au plus deux workspaces et l'endpoint de listing de l'API ajoute une latence qui ne justifie pas le polissage.
Ce que nous ne migrons pas#
Les règles de géo-ciblage et de ciblage d'appareil de Dub. Ce sont des fonctionnalités puissantes de Dub, mais la forme de la règle ne correspond pas au 1:1 avec les règles de smart-link Elido. Le slug est importé ; reconstruisez les règles en utilisant la syntaxe d'expression d'Elido, qui est plus permissive mais a une forme mentale différente.
L'historique par clic. Limite universelle sur les cinq sources de migration. Les données par clic de Dub se trouvent derrière leur endpoint d'analytique, qui est lié au plan tarifaire ; les nouveaux clics arrivent dans l'analytique Elido à partir du basculement.
Le style QR. Le QR Elido par défaut est régénéré ; les designs personnalisés doivent être ré-appliqués. Le tag imported:dub est la poignée de réaffectation en masse.
Les ACLs et la configuration des rôles du workspace Dub. Ré-attribuez l'accès dans Elido en utilisant SCIM/SSO ou des invitations de membre de workspace ; le modèle de rôle diffère suffisamment entre les deux produits pour qu'une correspondance automatisée puisse entraîner une élévation silencieuse des privilèges.
Dub auto-hébergé#
Dub est open-source et les instances Dub auto-hébergées sont courantes. La migration utilise la même API REST que celle exposée par le produit Dub Cloud, donc pointer vers un Dub auto-hébergé signifie remplacer DUB_API_BASE. Nous n'avons pas exposé cela comme un paramètre en libre-service dans la v1 car la gestion opérationnelle est non triviale — différentes versions de Dub exposent des formes de réponse subtilement différentes, et nous ne voulons pas livrer un lanceur qui affiche silencieusement des erreurs 500 sur un déploiement Dub v0.7 alors que la v0.9 est la cible testée.
Pour les migrations Dub auto-hébergées, envoyez un e-mail à [email protected] avec votre version de Dub et nous effectuerons une migration concierge unique. Une fois que nous aurons vu suffisamment de versions dans la nature, le remplacement deviendra un paramètre de tableau de bord en libre-service.
Gestion des tokens#
Même sémantique à usage unique que pour les quatre autres fournisseurs de migration :
bgCtx := context.WithoutCancel(r.Context())
go h.dub.Run(bgCtx, job.ID, imports.DubJobOptions{
Token: req.Token,
WorkspaceID: req.WorkspaceID,
})
source_token_id reste NULL. Le token vit dans la mémoire du processus api-core pour l'exécution du worker, et est supprimé à la fin. Pas de persistance — il s'agit d'une migration à usage unique, pas d'un appel récurrent au fournisseur.
context.WithoutCancel (Go 1.21+) maintient le worker en vie après la requête HTTP. Même modèle que pour tous les autres fournisseurs de migration de ce déploiement.
Résolution des conflits et contrat du worker#
Identique à tous les autres fournisseurs. Le suffixe parcourt mylink-2, mylink-3, …, jusqu'à 50 candidats ; « skip » laisse le lien Elido existant intact ; « fail » abandonne au premier conflit. Le contrat du worker — MaxLinksPerImport=50_000, ImportRunBudget=30*time.Minute — est partagé entre les cinq.
La recherche est une lecture indexée par ligne sur (domain_id, slug). Suffixes déterministes, erreurs plus conviviales que de chercher des violations d'unicité dans pgx.
Ce que nous avons mesuré par rapport à Bitly#
Dub et Bitly migrent à peu près au même débit — 100 liens par page, ~5 insertions/sec soutenues. Une source de 5 000 liens se termine en 4 à 7 minutes pour les deux fournisseurs. La différence visible pour l'utilisateur est l'expérience post-importation : les liens importés de Dub arrivent avec des fils d'Ariane structurés dossier-comme-tag ; les liens importés de Bitly arrivent juste avec le tag imported:bitly et tous les tags Bitly en texte libre.
Reprise et problème de déploiement#
Même compromis que pour les quatre premières migrations. Le worker est en-processus ; un déploiement en cours d'importation tue la goroutine. Le cron de nettoyage des blocages de 5 minutes bascule toute ligne running sans progression en 30 minutes vers failed. La ré-exécution est idempotente selon les stratégies « suffix » et « skip ».
Pour les comptes de plus de 10 000 liens, la reprise mériterait d'être implémentée — nous enregistrerions le curseur de page Dub dans import_jobs.source_filter et reprendrions à partir de la dernière page terminée. Les cinq fournisseurs de migration partagent la même conception en-processus ; lorsque nous livrerons la reprise, les cinq en bénéficieront.
La suite du déploiement Tier-3#
Le Tier-3 est terminé. Cinq fournisseurs de migration, une table import_jobs partagée, un contrat de worker partagé, une interface utilisateur de polling de tableau de bord partagée, cinq landing pages SEO, cinq articles de blog techniques.
Ce qui est en file d'attente après le Tier-3 :
- Reprise pour les comptes de plus de 10 000 liens. Checkpointing de curseur par fournisseur.
- Fallback d'export CSV pour les utilisateurs dont les plans ont des tokens révoqués. Actuellement uniquement via concierge.
- Fondation des service_tokens Tier-2 — tokens de fournisseur à usage récurrent pour Mailchimp, Brevo, Klaviyo. Le chemin de migration a validé le modèle JSONB
source_filter; le Tier-2 nécessite des tokens chiffrés persistants, ce qui est le territoire de ADR-0036.
Si vous regardiez Elido du coin de l'œil depuis un workspace Dub, l'histoire de la migration est maintenant documentée. Essayez-la — du token au dernier lien importé en moins de cinq minutes pour les comptes typiques.
Sur le même sujet sur le blog#
- Shipping the Bitly migration: a worker, a token, a 30-minute budget
- Shipping the Rebrandly migration: 25-per-page pagination
- Shipping the Short.io migration: per-domain pagination at 150/page
- Shipping the TinyURL migration: Pro/Bulk REST, no free-tier path
- Short links as Terraform: the engineering cornerstone