П'яте і останнє джерело міграції в нашому розгортанні Tier-3 сьогодні запущено. Вставте Dub.co API токен, за бажанням відфільтруйте за слагом проєкту, натисніть Start. Через три-п'ять хвилин кожне посилання з'явиться на вашому домені Elido зі збереженим слагом, а структура папок Dub перетвориться на теги Elido.
Цей пост - інженерний звіт: що особливого в Dub, чому їхній API - найчистіший з п'яти вендорів, які ми підтримуємо, і що мотивує перехід з Dub на Elido.
Чому існує ця міграція
Dub.co є найближчим open-source еквівалентом Elido за функціональністю. Сильний продукт, чистий REST API, сучасна панель управління. Цей шлях міграції призначений для команд, які обирають Elido з однієї з трьох причин:
- Проживання в ЄС. План даних Elido базується в ЄС - регіон ЄС + US East POPs, Азійсько-Тихоокеанський регіон для APAC, всі події кліків за замовчуванням потрапляють у наше аналітичне сховище в регіоні ЄС. Dub Cloud базується в США; позиція GDPR/Schrems-II є компромісом.
- Розташування Edge POP. Три регіональні POP з p95 < 15мс кеш HIT - це інший цільовий показник затримки, ніж шлях Dub лише через Cloudflare-Workers. Робочі навантаження, чутливі до затримки (mobile-first, ad-attribution), відчувають різницю.
- Глибина аналітики. наше аналітичне сховище-базована аналітика кліків із утриманням на подію, перенаправленням конверсій у GA4/Meta CAPI та повним історичним відтворенням. Аналітика Dub чиста, але агрегована на PostgreSQL; межа глибини інша.
Якщо нічого з цього не стосується, Dub - чудовий продукт. Міграція призначена для команд, до яких це стосується.
Чому API Dub - найчистіший
Ми вже створили воркери для п'яти API вендорів. Рейтинг за простотою інтеграції:
- Dub.co - bearer токен, помилки, сумісні з JSON-RFC, пагінація
?page=+?limit=100, кожне поле задокументовано прикладами корисного навантаження. - Short.io - чисто, явний логічний тип
HasMore, але партиціонування за доменами потребує роботи над UI/UX. - Bitly - URL
pagination.nextвідповідає стандартам; супровідна API документація є вичерпною. - TinyURL - тільки Pro/Bulk, решта не підтримується; документація мізерна.
- Rebrandly - курсор
?last=<id>нормальний, але ліміт 25 на сторінку робить усе повільним.
Перевага Dub: їхня документація включає приклади curl, відповіді на помилки містять і машинний код, і людське повідомлення, а їхня пагінація - той самий нудний тип, де ?page=2&limit=100 працює саме так, як ви очікуєте.
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 не повертає прапорець HasMore; ми робимо висновок про це за короткою сторінкою. Це стандартний шаблон пагінації REST, і він добре працює - сторінка, коротша за ліміт, означає, що ми закінчили.
Папки стають тегами
У Dub є і папки, і теги як примітиви організації. У Elido є тільки теги. Тому міграція перетворює папки Dub на набір тегів:
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")
Посилання Dub у папці campaigns/q3-launch із тегами paid та linkedin імпортується з тегами paid, linkedin, campaigns, q3-launch та imported:dub. Семантика фільтрів в Elido обробляє ті самі патерни отримання, що й UI папок Dub - tag-equals, tag-contains, multi-tag-AND. Ми не винаходимо ієрархію папок на стороні сервера; користувач отримує плоский список тегів і примітиви фільтрів.
Чи могли ми замість цього додати папки в Elido? Так. Ми вибрали тільки теги, коли модель даних Elido була випущена у Фазі 1; папки мають сенс для ментальних моделей файлової системи робочого столу і менше сенсу для операцій із великою кількістю коротких посилань. Міграція користувачів Dub на теги Elido - це правильний бік цього компромісу.
Фільтрація проєктів
Dub використовує "робочі простори" (у своєму новішому UI), а історично називав їх "проєктами". API приймає параметр workspaceId для фільтрації; лаунчер відображає його як необов'язкове текстове поле. Вставте слаг робочого простору з вашої URL-адреси Dub або залиште порожнім, щоб отримати всі посилання, які бачить токен.
Це відображає фільтр робочого простору Rebrandly та поле домену Short.io. Три з п'яти наших вендорів міграції мають концепцію партиціонування для кожного облікового запису; ми послідовно відображаємо це як необов'язкове текстове введення, а не як заповнений випадаючий список, оскільки типовий користувач має щонайбільше два робочі простори, а ендпоінт списку API додає затримку, яка не варта цієї поліровки.
Чого ми не мігруємо
Правила Dub щодо геотаргетингу та таргетингу на пристрої. Це потужна функція Dub, але форма правил не відображається 1:1 на smart-link правила Elido. Слаг імпортується; перебудуйте правила, використовуючи синтаксис виразів Elido, який є більш дозвільним, але має іншу ментальну форму.
Історія по кліках. Універсальний ліміт для всіх п'яти джерел міграції. Дані Dub по кліках знаходяться за їхнім ендпоінтом аналітики, який обмежений планом тарифу; нові кліки потрапляють в аналітику Elido після переходу.
Стилізація QR. QR-код Elido за замовчуванням генерується заново; індивідуальний дизайн потрібно застосувати повторно. Тег imported:dub є дескриптором для масового перепризначення.
ACL робочого простору Dub та конфігурація ролей. Надайте доступ знову в Elido, використовуючи SCIM/SSO або запрошення членів робочого простору; модель ролей між двома продуктами відрізняється настільки, що автоматизоване відображення проявилося б як приховане підвищення привілеїв.
Self-hosted Dub
Dub - це open-source, і самохостингові інстанси Dub є звичними. Міграція використовує той самий REST API, який відкриває продукт Dub Cloud, тому вказування на self-hosted Dub означає перезапис DUB_API_BASE. Ми не виставили це як self-serve налаштування у v1, оскільки операційний хвіст не є тривіальним - різні версії Dub відкривають трохи різні форми відповіді, і ми не хочемо випускати лаунчер, який мовчки видає 500-ту помилку на розгортанні Dub v0.7, коли цільовою протестованою версією є v0.9.
Для міграцій self-hosted Dub надішліть листа на [email protected] з вашою версією Dub, і ми проведемо одноразову консьєрж-міграцію. Як тільки ми побачимо достатньо версій у "дикій природі", перезапис стане self-serve налаштуванням панелі управління.
Обробка токенів
Та сама семантика одноразового виконання, як і у чотирьох інших вендорів міграції:
bgCtx := context.WithoutCancel(r.Context())
go h.dub.Run(bgCtx, job.ID, imports.DubJobOptions{
Token: req.Token,
WorkspaceID: req.WorkspaceID,
})
source_token_id залишається NULL. Токен живе в пам'яті процесу наш API під час роботи воркера, видаляється після завершення. Без персистентності - це одноразова міграція, а не повторюваний запит до вендора.
context.WithoutCancel (Go 1.21+) підтримує життя воркера після запиту HTTP. Той самий патерн, що й у кожного іншого вендора міграції в цьому розгортанні.
Вирішення конфліктів та контракт воркера
Ідентично до кожного іншого вендора. Перебір суфіксів mylink-2, mylink-3, …, до 50 кандидатів; пропуск залишає існуюче посилання Elido в спокої; помилка перериває роботу при першому конфлікті. Контракт воркера - MaxLinksPerImport=50_000, ImportRunBudget=30*time.Minute - є спільним для всіх п'яти.
Пошук - це одне індексоване читання на рядок за (domain_id, slug). Детермінований перебір суфіксів, зручніші помилки, ніж виловлювання порушень унікальності в pgx.
Що ми вимірювали щодо Bitly
І Dub, і Bitly мігрують приблизно з тією ж пропускною здатністю - 100 посилань на сторінку, ~5 вставок/сек стабільно. Джерело з 5,000 посиланнями завершується за 4–7 хвилин для обох вендорів. Різниця, видима користувачеві - це досвід після імпорту: посилання, імпортовані з Dub, приходять зі структурованими «хлібними крихтами» папка-як-тег; посилання, імпортовані з Bitly, приходять тільки з тегом imported:bitly та будь-якими довільними тегами Bitly.
Відновлюваність та проблема розгортання
Той самий компроміс, що й у перших чотирьох міграціях. Воркер працює в процесі; розгортання під час імпорту вбиває горутину. 5-хвилинний крон stuck-sweep переводить будь-який рядок running без прогресу за 30 хвилин у стан failed. Повторний запуск є ідемпотентним при стратегіях суфіксів та пропуску.
Для облікових записів з понад 10,000 посилань відновлюваність була б корисною - ми записували б курсор page Dub у import_jobs.source_filter і відновлювали б з останньої завершеної сторінки. Усі п'ять вендорів міграції поділяють той самий дизайн in-process; коли ми випустимо відновлюваність, усі п'ять отримають користь.
Що далі для розгортання Tier-3
Tier-3 готово. П'ять вендорів міграції, одна спільна таблиця import_jobs, один спільний контракт воркера, один спільний UI опитування панелі управління, п'ять SEO сторінок, п'ять інженерних постів у блозі.
Що в черзі після Tier-3:
- Відновлюваність для облікових записів понад 10,000 посилань. Чекпоінтинг курсору для кожного вендора.
- Резервний варіант CSV-експорту для користувачів на планах із відкликаними токенами. Зараз доступно тільки через консьєрж-сервіс.
- Фундамент Tier-2 service_tokens - токени вендорів для багаторазового використання для Mailchimp, Brevo, Klaviyo. Шлях міграції підтвердив патерн JSONB
source_filter; Tier-2 потребує персистентних зашифрованих токенів, що є окремим напрямом роботи із секретами в стані спокою.
Якщо ви придивлялися до Elido з робочого простору Dub, історія міграції тепер задокументована. Спробуйте - від токена до останнього імпортованого посилання менш ніж за п'ять хвилин для типових облікових записів.
Пов'язане в блозі
- 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
Спробуйте Elido
Вставте URL - отримайте коротке посилання
Без реєстрації. Посилання живе 30 днів. Зареєструйтесь, щоб зберегти назавжди.
Безкоштовно, без реєстрації · 2 на день