1分で読了エンジニアリング

TinyURL移行インポーターの仕組みと既知の欠陥

ElidoのTinyURL移行インポーターの仕組み: トークンの受け取り、エイリアスの取り込み、競合ルール、上限、TinyURLにないエンドポイントを呼ぶ既知のバグ。

Marius Voß
DevRel · edge infra
パイプライン図: TinyURLのAPIトークンがElidoのインポートワーカーに渡り、ワーカーがsuffix、skip、failの競合ルールのもとでlinksテーブルにリンクを書き込む

TinyURL移行インポーターは、APIトークン、ターゲットのElidoドメイン、競合戦略を受け取り、TinyURLのリンクを1つのバックグラウンドジョブとしてElidoにコピーします。重大な既知の欠陥が1つあります。api.tinyurl.comに対してGET /aliasesを呼ぶのですが、そのパスはTinyURLの公開APIにありません。本記事では、インポーターが何をするか、何を検証済みで何を未検証かを説明します。

TinyURLからの移行を計画する方法(何をリダイレクトできるか、CSVのマッピング、切り替えのチェックリスト)を知りたい場合は、TinyURLからの移行をお読みください。こちらはエンジニアリング側の話です。

既知のバグ

インポーターのページ取得は、次のリクエストを組み立てます。

GET https://api.tinyurl.com/aliases?page=1&per_page=100
Authorization: Bearer <token>

そして、{"data": [{alias, url, title, tags}], "meta": {"total", "has_more"}}が返ってくることを想定しています。

TinyURLのOpenAPIスペック(2026-10-02時点でのアクセス)には、/aliasesというパスの定義がありません。一覧はGET /urls/{type}で、typeはavailableまたはarchived、任意のfrom、to、search(alias:またはtag:の接頭辞)フィルターがあります。スペックにはpageやper_pageのパラメータも、レート制限の定義もありません。エイリアスの操作は/alias/{domain}/{alias}の下にあります。

つまり、私たちのコードにあるページネーションの契約は、TinyURLから取ったものではありませんでした。単体テストは、その形に合わせて手書きしたレスポンスを返して通りますが、それが証明するのはパーサーが私たちの想定と合っていることだけで、TinyURLについては何も証明しません。実際のTinyURLアカウントに対してインポーターを動かしたことはありません。有料プランのトークンが必要だからです。それを行うまでは、これを前提に移行を計画しないでください。修正は社内のバックログで追跡しています。/urls/availableに切り替え(/urls/archivedはフラグの背後に置く)、from/toのウィンドウでページを進め、防御的にパースし、実際に取得したレスポンスに対してテストします。実際のレスポンスがリストなのか単一のオブジェクトなのかは、スペックでは不明確です。

ワーカーがページを受け取った後の動作

デモワークスペースのElidoダッシュボードの連携ページ。ここから移行元を開始する

合成データを使ったDEMOワークスペースの連携ページ。TinyURLの移行はここから開始します。

HTTP呼び出しより後は、すべてエンドポイントの問題とは無関係です。この部分はコードから言えます。

  • 開始。 token、target_domain_id、任意のconflict_strategyを付けてPOSTします。ハンドラーは空のトークン、ドメインの欠落、suffix、skip、fail以外の戦略を拒否し、ジョブとともに202を返して、リクエストから切り離したバックグラウンドのgoroutineでワーカーを実行します。
  • リンクごと。 URLまたはエイリアスのない行は、理由付きで失敗として数えます。それ以外は、エイリアスがスラッグになり、遷移先とタイトルがコピーされ(タイトルは200文字に切り詰め)、タグはimported:tinyurlを末尾に付けてコピーされます。
  • 進捗。 カウンターは50リンクごとにジョブ行へ書き込まれます。ジョブごとに記録されるエラー行は最大1,000件です。
  • 認証とレートのエラー。 401または403は「ベアラートークンを確認してください」というメッセージでジョブを失敗とし、429はレート制限として失敗とします。再試行もバックオフもなく、ジョブは止まります。

競合ルール

デモワークスペースのElidoのリンク一覧。短縮リンク、遷移先、タグ、30日間のクリック数、ステータスが並ぶ

DEMOワークスペースのリンク一覧。インポートされたリンクにはimported:tinyurlタグが付くため、まとまりを絞り込んでレビューできます。

各挿入の前に、ワーカーは(domain, slug)に対してインデックス付きの検索を1回行います。

  • suffix(デフォルト)はalias-2、alias-3とalias-50まで試し、すべて使用済みなら、その行を失敗にします。
  • skipは既存のElidoのリンクには手を付けず、元の行を記録します。
  • failは最初の競合でジョブを失敗とします。

TinyURLはこれをエイリアスと呼び、Elidoはスラッグと呼びます。同じもので、ホストの後に続く文字です。ターゲットのドメインが空なら、エイリアスはそのまま引き継がれます。

上限

すべての移行ベンダーで使われる共通の定数が1組あります。1回の実行で5万リンク、30分の予算、50リンクごとの進捗、1,000件のエラー記録です。

知っておく価値のある挙動が2つあります。5万件の上限に達すると、ループは単に止まってジョブは完了します。失敗とはならず、誰かに連絡するよう促すこともありません。最終的な件数を元の合計と比べてください。30分の予算が尽きると、ジョブはそれまでにインポートした件数とともに失敗としてマークされます。

ワーカーはapi-coreの内部にあります。実行中のデプロイやクラッシュで終了し、5分ごとに動く掃除処理が、30分間進捗のないジョブをfailedに切り替えます。保存されたカーソルはないため、ジョブは最初から再開します。再実行はsuffixでもskipでも安全ですが、suffixでは、最初の実行がすでに書き込んだリンクの-2のコピーが作られるので、再実行にはskipをおすすめします。

トークンの扱い

トークンはリクエストボディからgoroutineへ渡るだけで、他のどこにも行きません。ジョブ行にトークンは記録されず、何も保存されないため、保存時の暗号化するものもありません。リクエストボディは4 KBの上限付きで読み取られます。

コードがやらないことは次のとおりです。開始前にトークンを検証せず、プランも確認しません。不正なトークンは、フォームのエラーとしてではなく、最初のリクエスト後に失敗したジョブとして現れます。この記事の以前のバージョンでは、ドメインのエンドポイントに対する事前チェックの手順を説明していました。その手順はコードに存在せず、TinyURLのスペックにもそのようなパスはありません。

移行されないもの

  • クリック履歴。 インポーターはリンクのフィールドだけを読み取ります。新しいクリックは、リンクがElidoで有効になった時点から数えられます。
  • QRのスタイル、テンプレート、ブランドドメインの設定。 読み取りません。ブランドのホスト名は別の手順で、Elidoのドメインとして追加してDNSを変更します。これは短縮リンクのカスタムドメインで解説しています。
  • アカウントなしのリンク。 どのトークンでも一覧にできません。

インポーターはドメインを作成せず、TinyURLのdomainフィールドも扱わず、独自のCSVアップロードもありません。ファイルベースの移行には、一括インポートフォームか、Googleスプレッドシートからの一括インポートの手順を使ってください。

テストと回避策

実用的なセクションが2つ続きます。インポーターをテストする方法と、それを使わずに移行する方法です。

インポートを安全に確認する方法

エンドポイントが修正されたとき、あるいはそれ以前に自分でインポーターをテストする場合は、小さく行ってください。使い捨てのワークスペースを作り、空のドメインを追加し、まずfail戦略でジョブを実行します。ドメインが空なら競合はないので、失敗は衝突ではなく、パースか認証の問題です。次に3つの数字を比較します。TinyURL上の元の件数、ジョブが報告するtotal、そしてインポート済み、スキップ、失敗のカウンターの合計です。ワーカーは想定するレスポンスからmeta.totalを取るため、totalと実際の件数の不一致は、レスポンスの形がパーサーの想定と異なる最初の兆候です。

その後、リンク一覧をimported:tinyurlで絞り込み、ランダムに10件のリンクを開きます。遷移先、スラッグ、タイトルをTinyURLと照らして確認します。その後の再実行にはskipに切り替えてください。

インポーターなしでTinyURLからエクスポートする方法

文書化されている一覧の呼び出しは、ベアラートークンを付けたGET /urls/availableと、アーカイブ済みリンク用のGET /urls/archivedです。アカウントが大きい場合は、スペックにページのパラメータがないため、fromとtoの日時で結果を絞り込みます。他の何かをする前に出力をファイルに書き出し、destination, slug, titleに絞り込み、一括フォームで読み込みます。このマッピングの詳細はハウツーガイドにあります。実際のレスポンスの形も未検証なので、スクリプト化する前に、レスポンスを1件、手で確認してください。

今後の予定

順序は、エンドポイントを修正し、実際に取得したレスポンスをテストのフィクスチャとして追加し、その後ハウツーガイドと移行のランディングページのすべての数値を再確認することです。ページネーションと実際のリクエストごとの制限は、スペックがどちらも示していないため、実際のアカウントに対して測り直す必要があります。それ以前にTinyURLから移る必要がある場合は、ElidoとTinyURLの比較とTinyURLの代替ツールで選択肢を比べ、CSVの経路を使ってください。

関連記事

よくある質問

ElidoのTinyURLインポーターは現在動きますか?

未検証として扱ってください。ワーカーはapi.tinyurl.comに対してGET /aliases?page=N&per_page=100を要求します。TinyURLのOpenAPIスペック(2026-10-02時点でのアクセス)には/aliasesというパスがなく、リンクの一覧はGET /urls/{type}で行います。出荷済みの単体テストは、私たちが想定したレスポンスの形を確認するだけです。修正はバックログにあり、それが入るまでは、CSVまたは一括貼り付けが確実な方法です。

インポーターは各TinyURLリンクから何をコピーしますか?

遷移先URL、エイリアス(スラッグとして使用)、タイトル、タグに加え、作成するすべてのリンクにimported:tinyurlタグを付けます。クリック履歴、QRのスタイル、その他は一切コピーしません。

エイリアスがElidoにすでに存在する場合はどうなりますか?

ジョブごとに戦略を選びます。suffixはalias-2、alias-3と、alias-50まで試し、skipは既存のリンクをそのままにして行を記録し、failは最初の競合でジョブを失敗とします。デフォルトはsuffixです。

TinyURLのトークンは保存されますか?

いいえ。開始リクエストがトークンをバックグラウンドのgoroutineへ運び、ジョブ行にはトークンへの参照を保存しません。トークンは実行の間だけプロセスのメモリ上に存在します。

1回のインポートで扱えるリンク数に上限はありますか?

はい。実行は5万リンクまたは30分のどちらか早い方で止まります。リンク数の上限に達すると、最初の5万件を処理した状態でジョブは完了として終わるため、件数を元のデータと照らして確認してください。

無料のTinyURLアカウントから移行できますか?

TinyURLはリンクの一覧取得をAPIトークンに紐づけており、サインインせずに作成したリンクはどのアカウントにも属さないため、一覧にできるものがありません。遷移先のリストを自分で作り直し、一括インポートを使ってください。

Elidoを試す

URLを貼り付けて短縮リンクを取得

登録不要。リンクは30日間有効。永久に保存するには登録してください。

Free、登録不要 · 1日あたり2件

Elidoを試す

EUホスティングのURL短縮サービス。カスタムドメイン、詳細な分析、オープンAPI付き。無料プラン - クレジットカード不要。

タグ
tinyurl migration
url shortener
go worker
data migration
engineering
tier 3 integrations

続きを読む