Elido
6 мин чтенияИнженерия

Релиз миграции с Short.io: постраничная навигация по доменам по 150 записей

Как мы реализовали импорт с Short.io для Elido в один клик — модель постраничной навигации по доменам, правило для деактивированных приватных ссылок и самый быстрый из пяти наших источников миграции.

Marius Voß
DevRel · edge infra
Диаграмма конвейера: REST API Short.io слева передает данные через воркер импорта Elido в таблицу ссылок, с боковой панелью, перечисляющей числовые гарантии воркера (лимит 50 тыс., бюджет времени 30 мин., 150 записей на страницу, токен только в памяти)

Сегодня мы выпустили третий источник миграции в рамках нашего релиза Tier-3. Вставьте API-ключ Short.io, выберите исходный домен Short.io (например, example.short.gy), выберите целевой домен Elido и нажмите «Старт». Через три-шесть минут все ссылки окажутся на вашем домене Elido с сохраненными слагами.

В этом посте — технический обзор: особенности Short.io, что нас удивило в их REST API и почему мы решили использовать задания для каждого домена, а не пакетную обработку по всему аккаунту.

Подоменная, а не поаккаунтная обработка#

Модель данных Short.io имеет одну особенность, которая определила весь UX запуска импорта: ссылки организованы по доменам, а эндпоинт /links поддерживает постраничную навигацию по доменам. Не существует вызова «дай мне все ссылки по всем доменам в этом аккаунте».

Мы рассмотрели несколько вариантов проектирования:

  • A. Перебор каждого домена на стороне сервера, предоставление одного задания пользователю. Быстрее с точки зрения количества кликов; сложнее отображать прогресс и выбор стратегии разрешения конфликтов для каждого домена.
  • B. Одно задание Elido на исходный домен. Медленнее в кликах (пользователь запускает N заданий для N доменов), но у каждого задания есть четкий контракт: один исходный домен → один целевой домен → одна стратегия разрешения конфликтов.
  • C. Список всех доменов, позволяющий пользователю выбрать несколько, постановка N заданий в очередь на стороне сервера.

Мы реализовали вариант B, а C оставили для следующей итерации плана релиза. Интерфейс запуска требует ввести имя хоста исходного домена в текстовое поле (без выпадающего списка — список /domains у Short.io недорогой в вызове, но добавляет лишний round-trip, а пользователь всегда знает свой домен). Одно задание на домен, ставится в очередь из панели управления по одному.

Преимущество размера страницы#

Short.io по умолчанию отдает 150 ссылок за вызов — это самый высокий показатель среди пяти наших источников миграции. Сравните:

  • Bitly: 100 на страницу
  • Rebrandly: 25 на страницу
  • TinyURL: 100 на страницу (Pro/Bulk)
  • Dub.co: 100 на страницу
  • Short.io: 150 на страницу

Для домена Short.io с 5000 ссылок требуется 34 round-trip запроса. Для аккаунта Rebrandly с 5000 ссылок — 200. Воркер проводит большую часть времени в ожидании HTTP-ответов, поэтому это важно — Short.io эмпирически является самым быстрым источником миграции, который мы поддерживаем.

const shortioPageSize = 150

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

HasMore — это логическое значение, которое Short.io возвращает явно: не нужно парсить курсоры или гнаться за последним ID. Их API — одно из самых хорошо спроектированных среди пяти поддерживаемых нами вендоров.

Приватные ссылки — что мы делаем#

В Short.io есть флаг «приватности» для каждой ссылки. Мы импортируем приватные ссылки как ссылки Elido с параметром is_active=false, чтобы слаг не разрешался на edge-серверах. Пользователь активирует их выборочно из панели управления после проверки импорта.

Логика такова: если ссылка в Short.io была приватной в источнике, пользователь не хотел, чтобы она разрешалась публично. Импорт с is_active=true сделал бы доступными URL, которые были намеренно закрыты. Импорт с is_active=false оставляет слаг зарезервированным, но недоступным, пока пользователь не решит иначе — это строго безопаснее альтернативного варианта.

isActive := !link.Private
linkID, err := w.links.InsertImported(ctx, sqldb.InsertImportedLinkParams{
    WorkspaceID:    job.WorkspaceID,
    DomainID:       job.TargetDomainID,
    Slug:           slug,
    DestinationURL: link.OriginalURL,
    Title:          truncate(link.Title, 250),
    Tags:           append(link.Tags, "imported:shortio"),
    IsActive:       isActive,
    CreatedByUserID: createdByUserID,
})

Это небольшое отличие от Bitly (нет аналогичного флага) и Rebrandly (нет аналогичного флага). Стоит упомянуть об этом в рецепте после импорта, чтобы пользователь понимал, почему некоторые импортированные ссылки не работают сразу.

Что мы не мигрируем#

Конфигурации A/B-тестирования Short.io не имеют чистого экспорта — это встроенный в приложение конструктор, который не отдает детерминированную JSON-структуру через REST API. Пересоздайте их как правила smart-link в Elido после импорта; синтаксис там более выразительный, но ментальная модель та же.

История кликов — универсальное ограничение для любого источника миграции. Данные о кликах в Short.io находятся в их аналитическом экспорте, который доступен только на тарифном плане Team (данные на 2026-05-22) и представляет собой агрегированные счетчики, а не события по кликам. Новые клики начинают попадать в аналитику Elido с момента переключения.

Дизайны QR-кодов и пресеты UTM для ссылок — та же история, что с Bitly и Rebrandly. Отмечены тегом imported:shortio, готовы к массовой обработке через кампании в Elido.

Передача домена#

Интересный сценарий использования Short.io: «Я использую брендированный домен на Short.io и хочу перенести его на Elido, не меняя URL». Миграция корректно переносит ссылки, а DNS — это просто изменение CNAME.

Мы задокументировали последовательность передачи домена на лендинге /migrate-from/shortio — оставьте оба сервиса работающими параллельно, пока ваша подписка Short.io не закончится, а затем направьте DNS на Elido. Нет необходимости отключать Short.io в день завершения импорта.

Пользовательские домены в Elido используют Caddy on-demand TLS с domain-manager в качестве источника разрешенных доменов, поэтому переключение — это изменение CNAME плюс вызов API для верификации домена. Никаких «танцев» с сертификатами на стороне пользователя.

Разрешение конфликтов и контракт воркера#

Идентично Bitly и Rebrandly — перебор суффиксов mylink-2, mylink-3, … при коллизиях; пропуск (skip) оставляет существующую ссылку Elido без изменений и логирует исходную строку; прерывание (fail) останавливает работу при первом конфликте. Поиск выполняется одним индексированным чтением на строку.

Контракт воркера — MaxLinksPerImport=50_000, ImportRunBudget=30*time.Minute, progressEvery=50, errorLogCap=1_000 — общий для всех пяти вендоров. Эти константы выполняют основную часть работы, и это не конфигурационные параметры. Это контракт, на который полагается UI опроса панели управления.

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

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

source_token_id остается NULL. Тот же подход «одного выстрела», что и с Bitly и Rebrandly — пользователь вставляет токен один раз, воркер запускается, токен удаляется из памяти по завершении. Мы не сохраняем его, так как ценность сохранения (повторное использование) не применима к миграциям.

context.WithoutCancel сохраняет воркер активным после завершения HTTP-запроса, который его запустил. Тот же паттерн, что и у других вендоров миграции в этом релизе.

Сравнение с путем экспорта через CSV#

Short.io предоставляет экспорт в CSV на планах Team. Мы выбрали REST вместо CSV, потому что:

  • REST структурно сохраняет теги Short.io. CSV преобразует их в строку, разделенную запятыми, что требует последующего парсинга.
  • REST открывает доступ к флагу private. CSV не всегда включает его.
  • REST дает нам детерминированный прогресс (ссылок просмотрено / ссылок осталось). CSV — это однократная загрузка файла без сигналов о прогрессе в процессе выполнения.
  • REST не зависит от тарифного плана — каждый план Short.io открывает /links. Экспорт в CSV доступен только на Team.

Путь через CSV остается в запасе для пользователей с устаревшими аккаунтами Short.io, чей API-токен был отозван, но у которых сохранился CSV-файл от последнего экспорта.

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

Те же компромиссы, что и в первых двух миграциях. Воркер выполняется в процессе; деплой в середине импорта убивает горутину. Поле import_jobs.last_progress_at плюс крон, который раз в 5 минут ищет «зависшие» процессы, помечает любую строку со статусом running без прогресса за последние 30 минут как failed. Повторный запуск идемпотентен благодаря логике суффиксов и пропуска.

Для аккаунтов с числом ссылок более 10 000 по нескольким доменам Short.io помогает именно подоменная архитектура заданий — каждый домен независимо ограничен 30-минутным бюджетом, поэтому деплой во время обработки третьего домена не приводит к потере работы по первым двум.

Что дальше#

Осталось добавить еще два вендора:

  • Dub.coGET /api/links?projectSlug=…&limit=100. Папки преобразуются в теги. Самый чистый API из пяти.
  • TinyURL — Pro/Bulk REST API по 100 на страницу. У бесплатного TinyURL API нет и никогда не было; это остается ручным путем.

После Dub и TinyURL релиз Tier-3 будет завершен. Пять лендингов миграции (/migrate-from/bitly, /migrate-from/rebrandly, /migrate-from/shortio, /migrate-from/dub, /migrate-from/tinyurl) и пять инженерных постов охватывают все поисковые запросы вендоров, на которые может выйти пользователь в поиске альтернатив Bitly.

Если вы откладывали сравнение с Short.io из-за отсутствия документации по миграции, теперь она есть. Попробуйте — API-ключ + домен до последней импортированной ссылки менее чем за шесть минут для типичных аккаунтов.

Похожие статьи в блоге#

Попробуйте Elido

URL-сокращатель с хостингом в ЕС: собственные домены, глубокая аналитика, открытый API. Бесплатный тариф — без банковской карты.

Теги
short.io migration
url shortener
go worker
data migration
engineering
tier 3 integrations

Читать дальше