Elido
6 хв читанняІнженерія

Випуск міграції з TinyURL: Pro/Bulk REST, без шляху для безкоштовного рівня

Як ми створили імпорт TinyURL Pro/Bulk в один клік для Elido — чому публічний TinyURL не має API, термінологія alias-vs-slug та обмеження, яке ми впровадили навмисно.

Marius Voß
DevRel · edge infra
Діаграма конвеєра: TinyURL Pro/Bulk API зліва, що проходить через воркер імпорту Elido в таблицю посилань, з бічною панеллю, де перелічено чисельні гарантії (ліміт 50 тис., бюджет 30 хв., 100 на сторінку, лише плани Pro/Bulk)

Сьогодні ми випустили четверте джерело міграції в межах нашого розгортання Tier-3. Вставте токен TinyURL Pro або Bulk API, виберіть цільовий домен Elido і натисніть «Почати». Через чотири-сім хвилин кожний alias TinyURL опиниться на вашому домені Elido, причому alias буде збережено, якщо не виникне колізії.

Цей пост — інженерний звіт: що є специфічним для TinyURL, навмисне обмеження, яке ми впровадили, і чому «міграція з безкоштовного рівня TinyURL» — це те, що ми не можемо створити.

Проблема безкоштовного рівня#

Публічний TinyURL не має API і ніколи не мав. Класичний tinyurl.com/<slug>, який ви створюєте без облікового запису, — це переспрямування за принципом «створив і забув»: користувач створює його через форму на головній сторінці, отримує slug, і цей slug більше ніколи не з'являється в жодній панелі керування. Немає списку для кожного користувача, тому що немає прив'язки до користувача.

Це загальновідомий факт, але його варто висвітлити на цільовій сторінці /migrate-from/tinyurl, оскільки пошуковий запит «migrate from TinyURL» не розрізняє Pro і безкоштовний рівні. Ми впровадили:

  • Чітке попередження «Лише для Pro/Bulk» у головному блоці цільової сторінки.
  • Пункт FAQ, який спрямовує користувачів безкоштовного рівня до форми /docs/guides/bulk-create для масового скорочення за списком пунктів призначення.
  • Крок перевірки токена в лаунчері, який швидко видає помилку «цей токен не належить до плану Pro або Bulk», замість того, щоб дозволяти процесу тихо видавати 401 під час пагінації.

Обґрунтування: кожне інше джерело міграції, яке ми випускаємо, має «шлях успіху» для кожного користувача, який його шукає. TinyURL — це виняток: користувачам безкоштовного рівня потрібна інша ментальна модель, і ми повинні сформувати це очікування, перш ніж вони щось вставлять.

Структура Pro/Bulk REST API#

TinyURL Pro API простий: bearer-токен, JSON-відповіді, 100 aliases на сторінку. Пагінація використовує параметр page у рядку запиту, який починається з 1; відповідь містить data.aliases (масив посилань) і meta.has_more (сигнал продовження).

const tinyurlPageSize = 100

page := 1
for {
    resp, err := w.fetchPage(ctx, opts.Token, page)
    if err != nil { /* mark failed */ return }
    if len(resp.Data.Aliases) == 0 { break }
    for _, alias := range resp.Data.Aliases { /* import */ }
    if !resp.Meta.HasMore { break }
    page++
}

Кожний alias містить url (довгий пункт призначення), alias (користувацький slug або автоматично згенерований короткий код), description (необов'язкове поле TinyURL, яке ми зберігаємо як заголовок посилання Elido) і domain (TinyURL дозволяє брендовані домени в планах Bulk).

Термінологія — alias проти slug#

TinyURL називає їх «aliases». Ми називаємо їх «slugs». Це одне й те саме — послідовність символів після хоста в URL-адресі переспрямування. Міграція зберігає alias 1:1, якщо на цільовому домені Elido немає колізії; якщо є колізія, застосовується стандартна стратегія вирішення конфліктів: суфікс/пропуск/помилка.

Ми розглядали можливість перейменування «slug» на «alias» у лаунчері, щоб відповідати термінології постачальника джерела, але відмовилися від цього через міркування узгодженості. Кожна інша поверхня Elido — список посилань, API, SDK, панель керування — використовує «slug». Внесення термінологічної асиметрії в один лаунчер зробило б досвід після імпорту заплутаним.

Лаунчер розміщує однорядкову мітку «TinyURL називає це aliases» над радіокнопкою стратегії вирішення конфліктів, тому користувачі, які шукають «alias» на сторінці рецепта, знаходять потрібний елемент керування, не читаючи кожного слова.

Брендовані домени та передача DNS#

Плани TinyURL Bulk підтримують брендовані домени — ваше власне ім'я хоста, що маршрутизується через інфраструктуру TinyURL. Коли ви мігруєте на Elido, slug імпортується чисто, а сторона DNS потребує лише зміни одного CNAME.

Цікавий випадок: «Я маю брендований домен на TinyURL Bulk і хочу зберегти те саме ім'я хоста після міграції». Ми обробляємо це так само, як і міграцію Short.io:

  • Міграція завершується. Імпортовані посилання за замовчуванням знаходяться на s.elido.me/<alias> (або на вашому існуючому власному домені Elido).
  • Ви додаєте брендований домен TinyURL як власний домен Elido через /docs/guides/custom-domains.
  • Ви вказуєте CNAME на Elido. On-demand TLS у Caddy випускає сертифікат під час першого запиту; domain-manager є єдиним джерелом істини для списку дозволених, тому неавторизовані імена хостів відхиляються.
  • Поверхня TinyURL перестає відповідати за це ім'я хоста; керування переходить до Elido.

Ви можете паралельно підтримувати обидві поверхні, доки не закінчиться ваша підписка TinyURL, а потім перемикання полягає лише в тому, щоб дозволити імені хоста TinyURL закінчити термін дії. Ніякої терміновості, ніякого ризику в день перемикання.

Що ми не мігруємо#

Історію кліків. Аналітика TinyURL Pro/Bulk — це окремі кінцеві точки звітів, які не структуровані для експорту. План Bulk надає кількість кліків на посилання в панелі керування, але не через API, зручний для міграції; нові кліки потрапляють в аналітику Elido з моменту перемикання.

Стилізацію QR-кодів і шаблони UTM рівня Bulk. Та сама історія, що й з кожним іншим джерелом міграції — slug імпортується, а навколишній рівень представлення переробляється в Elido. Тег imported:tinyurl для масового подальшого відстеження через campaigns.

Посилання безкоштовного рівня TinyURL. Як обговорювалося вище, публічний TinyURL не має API. Рішенням є форма масового створення, а не завдання міграції.

Обробка токена#

Та сама семантика одноразового виконання, як у Bitly, Rebrandly та Short.io:

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

source_token_id залишається NULL. Токен живе в пам'яті процесу api-core під час роботи воркера і видаляється після завершення. Жодної персистентності, жодного рядка service_tokens, жодного конвертного шифрування ADR-0036 — це для інтеграцій Tier-2, де користувач хоче регулярних викликів постачальника.

Крок перевірки токена на початку завдання звертається до /account/domains endpoint TinyURL — дешевий виклик, повертає список доменів, які бачить токен. Якщо він видає 401, ми швидко видаємо помилку «токен недійсний або не належить до плану Pro/Bulk», замість того, щоб дозволяти користувачеві чекати дві хвилини на 401 під час пагінації та менш корисне повідомлення про помилку.

Вирішення конфліктів#

Ідентично до кожного іншого постачальника міграції — суфікс перебирає myalias-2, myalias-3, … при колізії; пропуск залишає існуюче посилання Elido в спокої і логує вихідний рядок; помилка перериває процес при першому конфлікті.

func (w *TinyURLWorker) resolveSlug(ctx context.Context, domainID int64, desired, strategy string) (string, error) {
    if _, err := w.links.GetByDomainSlug(ctx, domainID, desired); err != nil {
        if errors.Is(err, pgx.ErrNoRows) { return desired, nil }
        return "", fmt.Errorf("slug lookup: %w", err)
    }
    // suffix/skip/fail branching identical to bitly.go
}

Пошук — це одне індексоване читання на рядок. Ми платимо додатковим читанням, але отримуємо детерміновані перебори суфіксів і більш зрозумілі повідомлення про помилки, ніж відловлювання порушень унікальності.

Контракт воркера#

MaxLinksPerImport=50_000, ImportRunBudget=30*time.Minute, progressEvery=50, errorLogCap=1_000. Спільні для всіх п'яти постачальників міграції. Ці константи — це контракт, на який розраховує інтерфейс опитування панелі керування.

Акаунт TinyURL Pro на 2 000 aliases звертається до API 20 разів і завершується за 3–5 хвилин. Акаунт Bulk на 20 000 aliases виконує 200 кругових запитів і завершується за 15–20 хвилин. Понад 50 000 aliases — воркер видає критичну помилку з інструкцією написати на [email protected] для міграції частинами; шлях міграції частинами у v1 доступний лише через консьєрж-підтримку.

Можливість відновлення та проблема розгортання#

Та сама компромісна ситуація, що й у перших трьох міграціях. Воркер працює в процесі; розгортання під час імпорту вбиває горутину. Cron-завдання для очищення «завислих» завдань переводить будь-який рядок running, у якому немає прогресу протягом 30 хвилин, у статус failed. Повторний запуск є ідемпотентним за умови використання суфікса та пропуску.

Для акаунтів, де понад 10 000 aliases, можливість відновлення була б доречною — ми записували б курсор page TinyURL у import_jobs.source_filter і відновлювали роботу з останньої завершеної сторінки. Чотири інші постачальники міграції отримають перевагу від тієї самої зміни, як тільки ми її впровадимо; дизайн спільний.

Резервний варіант CSV#

Для користувачів планів Bulk, які мають експортований CSV і більше не мають активного API-токена, ми запускаємо одноразові CSV-завдання з поштової скриньки — пишіть на [email protected]. Ми не впровадили форму самостійного завантаження CSV, тому що шлях REST покриває типовий випадок, а шлях CSV потребує маніпуляцій зі схемою для кожного акаунта, що краще робити вручну, ніж за допомогою тендітного загального парсера.

Що далі#

Ще один постачальник на черзі:

  • Dub.coGET /api/links?projectSlug=…&limit=100. Папки зводяться до тегів. Найчистіший API з п'яти.

Після Dub розгортання Tier-3 буде завершено. П'ять лендінгів міграції, п'ять інженерних блог-постів, один спільний каркас воркера, один спільний інтерфейс опитування панелі керування.

Якщо ви чекали, бо міграція з TinyURL була недокументованою, тепер вона задокументована. Спробуйте — від Pro/Bulk токена до останнього імпортованого посилання менш ніж за сім хвилин для типових акаунтів.

Схоже в блозі#

Спробуйте Elido

URL-скорочувач із хостингом у ЄС: власні домени, глибока аналітика, відкритий API. Безкоштовний тариф — без кредитної картки.

Теги
tinyurl migration
url shortener
go worker
data migration
engineering
tier 3 integrations

Читати далі