Drugie źródło migracji w naszym wdrożeniu Tier-3 zostało dzisiaj uruchomione. Wklej klucz API Rebrandly, opcjonalnie przefiltruj według obszaru roboczego, kliknij Start. Sześć do dziesięciu minut później każdy slashtag znajduje się w Twojej domenie Elido, z zachowanym slugiem, o ile nie wystąpiła kolizja. Migracja z Bitly, która pojawiła się dwa tygodnie temu, stworzyła fundamenty; Rebrandly to drugi dostawca, który z nich korzysta.
Ten wpis to opracowanie techniczne - co jest specyficzne dla Rebrandly, co zachowaliśmy identycznie jak w przypadku workera Bitly oraz gdzie API Rebrandly wymusiło inną strukturę.
Co jest wspólne z Bitly
Cała funkcjonalność od początku miała opierać się na jednej tabeli i jednym kontrakcie workera. Oba założenia się sprawdziły.
CREATE TABLE import_jobs (
id BIGSERIAL PRIMARY KEY,
workspace_id BIGINT NOT NULL,
source_vendor TEXT NOT NULL,
target_domain_id BIGINT NOT NULL,
status TEXT NOT NULL DEFAULT 'queued',
conflict_strategy TEXT NOT NULL DEFAULT 'suffix',
source_filter JSONB NOT NULL DEFAULT '{}'::jsonb,
-- counters + error_log + timestamps elided
);
source_vendor zmienia się na rebrandly. source_filter zawiera {workspace_id: "..."}, gdy użytkownik filtruje; {}, gdy chce uzyskać każdy link widoczny dla danego klucza. Wszystko inne - 30-minutowy budżet, limit 50 tys. linków, strategia konfliktów typu suffix/skip/fail, tag imported:rebrandly - jest identyczne jak w przypadku ścieżki Bitly.
Launcher w panelu (apps/web/src/app/dashboard/integrations/[id]/rebrandly-migration-launcher.tsx) jest strukturalnie kopią launchera Bitly z usuniętą listą rozwijaną grup - Rebrandly posiada obszary robocze (workspaces), a nie grupy, więc udostępniamy je jako opcjonalny filtr tekstowy, zamiast wypełnianej listy rozwijanej, ponieważ endpoint Workspaces jest nieautoryzowany i stronicowany, a typowy użytkownik ma ich co najwyżej dwa.
W czym API Rebrandly się różni
Trzy rzeczy:
Rozmiar strony. Rebrandly ogranicza pojedynczą stronę do 25 linków. Bitly do 100. Zatem konto z 5000 linków, którego migracja zajmuje 4–8 minut w Bitly, zajmuje 6–10 minut w Rebrandly. Wąskim gardłem jest dostawca, a nie worker.
Stronicowanie. Rebrandly używa parametru zapytania last, który przyjmuje identyfikator ostatniego elementu z poprzedniej strony. Bitly zwraca URL pagination.next. Oba są w stylu kursorów; ten z Rebrandly jest po prostu nieco bardziej gadatliwy. Cała pętla ma sześć linii:
last := ""
for {
page, err := w.fetchPage(ctx, opts.Token, opts.WorkspaceID, last)
if err != nil { /* mark failed */ return }
if len(page) == 0 { break }
for _, link := range page {
// ... resolve slug, insert, update counters ...
}
last = page[len(page)-1].ID
}
Ufamy kursorowi. Jeśli Rebrandly zwróci ten sam identyfikator last dwa razy, zapętlimy się; 30-minutowy budżet ogranicza szkody.
Zakres obszarów roboczych. Klucz API Rebrandly widzi każdy link w każdym obszarze roboczym, do którego należy użytkownik. Jeśli posiadasz konto agencji z pięcioma obszarami roboczymi klientów, prawie na pewno chcesz importować je pojedynczo. Launcher udostępnia to jako opcjonalne pole tekstowe - wklej identyfikator obszaru roboczego z paska adresu URL Rebrandly lub pozostaw puste, aby zaimportować "wszystko, co widzi klucz".
Czego nie migrujemy
Historia kliknięć. Dane o kliknięciach w Rebrandly są dostępne tylko w planie Premium i są prezentowane jako liczniki zagregowane na link, a nie jako zdarzenia pojedynczych kliknięć. Informujemy o tym ograniczeniu w każdym miejscu widocznym dla użytkownika - na stronie przepisu w panelu, na stronie docelowej /migrate-from/rebrandly, w interfejsie postępu importu oraz w sekcji FAQ. Nowe kliknięcia trafiają do analityki Elido od momentu przełączenia.
Szablony UTM w Rebrandly. Są one funkcją czasu prezentacji w Rebrandly i nie mają przejrzystego API do eksportu. Odbuduj je jako reguły kampanii Elido campaign rules - tag imported:rebrandly służy do masowego przypisywania.
Stylizacja kodów QR. Domyślny kod QR Elido jest generowany dla każdego zaimportowanego linku; niestandardowe projekty należy zastosować ponownie. Większość użytkowników używa masowego filtrowania po tagach, aby po fakcie przypisać domyślną nakładkę CTA Elido lub kampanię.
Obsługa tokenów
Identyczna jak w Bitly. Token nigdy nie trafia na dysk:
bgCtx := context.WithoutCancel(r.Context())
go h.rebrandly.Run(bgCtx, job.ID, imports.RebrandlyJobOptions{
Token: req.Token,
WorkspaceID: req.WorkspaceID,
})
source_token_id pozostaje NULL. Tabela service_tokens jest przeznaczona dla integracji Tier-2 z wklejanymi tokenami (Mailchimp, Brevo, Klaviyo), gdzie cykliczne użycie uzasadnia trwałe przechowywanie. W przypadku jednorazowych migracji, przechowywanie wyłącznie w pamięci jest właściwym kompromisem operacyjnym - użytkownik wkleja token raz, worker wykonuje zadanie, token znika.
context.WithoutCancel (Go 1.21+) zachowuje wartości kontekstu - logger, identyfikatory śledzenia, czas zakończenia - ale usuwa sygnał anulowania, dzięki czemu worker działa dłużej niż żądanie HTTP, które go uruchomiło. To ten sam wzorzec, co w przypadku workera Bitly, i ten sam wzorzec, którego użyje każdy przyszły dostawca migracji.
Rozwiązywanie konfliktów
Trzy strategie, identyczne jak w Bitly. Użytkownik wybiera je podczas uruchamiania zadania:
- suffix (domyślnie): przechodzenie przez
mylink-2,mylink-3, … do 50 kandydatów. Po przekroczeniu 50 traktujemy to jako problem strukturalny i zgłaszamy błąd. - skip: pozostaw istniejący link Elido bez zmian, zaloguj wiersz źródłowy, policz jako pominięty.
- fail: przerwij całe zadanie przy pierwszym konflikcie. Dla ścisłej semantyki 1:1.
Wyszukiwanie sluga to jeden odczyt indeksowany na wiersz:
func (w *RebrandlyWorker) 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
}
Płacimy dodatkowym odczytem na wiersz, ale otrzymujemy deterministyczne przechodzenie przez sufiksy i bardziej przyjazny komunikat o błędzie. Alternatywa - szukanie naruszenia unikalności w pgx i parsowanie nazwy ograniczenia z ciągu błędu - jest gorszym rozwiązaniem.
Co jest mierzalne
Te same ustrukturyzowane logi zap co w Bitly. Obszar roboczy, domena docelowa, strategia konfliktów, opcjonalny filtr obszaru roboczego. Zdarzenia cyklu życia zadania - start, zakończenie, wykrywanie zawieszonych zadań (stuck-sweep) - już istnieją, a panel sterowania odpytuje endpoint co dwie sekundy.
Nie tworzymy jeszcze wykresów metryk zadań migracyjnych na produkcji. Kohorta Bitly dała nam pierwszą bazę danych o rzeczywistym ruchu; dane z Rebrandly powinny być bezpośrednio porównywalne, ponieważ worker jest mechanicznie identyczny, a różnice dotyczą jedynie kształtu stronicowania dostawcy. Pierwszy kandydat do alertu: liczba zawieszonych zadań (stuck-sweep) > 0 w dowolnym oknie jednogodzinnym - oznacza to, że worker umarł, a interfejs użytkownika utknął w stanie running.
Wznawialność i problem wdrażania
Ten sam kompromis co w Bitly. Worker działa wewnątrz procesu; wdrożenie w trakcie importu zabija gorutynę. Akceptujemy to w wersji v1, ponieważ:
- Większość zadań kończy się w mniej niż dziesięć minut. Wdrożenia są rzadkie w godzinach intensywnego importowania.
- Pole
import_jobs.last_progress_atoraz 5-minutowy cron wykrywający zawieszone zadania zmieniają status każdego wierszarunningbez postępu w ciągu ostatnich 30 minut nafailedz jasnym powodem. - Ponowne uruchomienie jest idempotentne w strategiach suffix i skip - już zaimportowane linki są wykrywane w drugim przebiegu i rozwiązywane zgodnie ze strategią.
W przypadku kont z ponad 10 000 linków, wznawialność staje się opłacalna - zapisujemy kursor last z Rebrandly w import_jobs.source_filter i wznawiamy pracę tam, gdzie ostatnie uruchomienie zostało przerwane. To kolejna iteracja; pozostałe cztery źródła migracji skorzystają z tej samej zmiany, gdy ją wdrożymy.
Co dalej
Te same fundamenty, jeszcze trzech dostawców do wdrożenia w tej samej tabeli import_jobs.
- Short.io -
GET /links?limit=150&domain_id=…. Stronicowanie na domenę; prosimy użytkownika o wybranie domeny źródłowej zamiast obszaru roboczego. - Dub.co -
GET /api/links?projectSlug=…&limit=100. Folders + tags zachowane; to najczystsza implementacja z czterech. - TinyURL - Pro/Bulk REST API. Darmowy TinyURL nie posiada API i nigdy nie posiadał; ta ścieżka pozostaje ręczna.
Każdy z nich trafia za ten sam interfejs odpytywania w panelu sterowania i ten sam wzorzec tagu imported:<vendor>. Worker specyficzny dla dostawcy pozostaje w services/api-core/internal/imports/<vendor>.go.
Jeśli wstrzymywałeś się z porównaniem Rebrandly, ponieważ ścieżka migracji nie była udokumentowana, teraz już jest. Wypróbuj to - od klucza API do ostatniego zaimportowanego linku w mniej niż dziesięć minut w przypadku typowych kont.
Powiązane wpisy na blogu
Wypróbuj Elido
Wklej URL, otrzymaj krótki link
Bez rejestracji. Link działa 30 dni. Zarejestruj się, aby zachować go na zawsze.
Za darmo, bez rejestracji · 2 dziennie