Anthillo /blog

Trzy bazy, jedna prawda: jak przydzielić VLAN, żeby nikt się nie zderzył

Anthillo Team

Inżynier z zespołu operacyjnego klika „przydziel VLAN” dla nowej usługi klienckiej, a system w tle musi w ułamku sekundy odpowiedzieć na pytanie, które w naszej architekturze nie ma jednego autorytatywnego adresata: czy ten numer jest naprawdę wolny? Nie ma jednej bazy, która by to wiedziała. Są trzy — każda utrzymywana przez inny zespół, żadna nie jest transakcyjnie spójna z pozostałymi — i zanim system odpowie „tak, możesz go użyć”, musi zapytać wszystkie naraz.

Trzy rejestry, zero wspólnej transakcji

System, który rozwijamy, ewidencjonuje adresację IP i zasoby sieciowe (VLAN-y, serwisy, identyfikatory węzłów) w metropolitalnej sieci dostępowej dużego operatora telekomunikacyjnego. Przy przydziale zasobu dla usługi — internetu stacjonarnego, telewizji czy sieci mobilnej — nie wystarczy sprawdzić własnej bazy. Ten sam VLAN musi być wolny jednocześnie w trzech miejscach: w naszym rejestrze, w systemie inwentaryzacyjnym operatora oraz na kontrolerze sieciowym dostawcy sprzętu, który zarządza fizyczną konfiguracją urządzeń. Każdy z tych systemów ma własnego właściciela, własny cykl aktualizacji i własne opóźnienie synchronizacji — od kilku sekund do kilku godzin.

To nie jest sytuacja, którą da się rozwiązać jedną transakcją bazodanową. Systemy należą do różnych zespołów, część integracji to odczyt przez link do bazy, część to wywołania SOAP/REST do kontrolera sprzętu. Żaden z nas nie ma uprawnień, żeby zablokować cudzą bazę na czas przydziału. Zamiast architektonicznego rozwiązania problemu spójności — event sourcing czy change data capture z jednym centralnym strumieniem prawdy — dostaliśmy w spadku kaskadę weryfikatorów i cache z TTL. Działa to od lat na produkcji obsługującej realnych abonentów, więc zanim ktokolwiek zaproponuje „przepiszmy to na event sourcing”, warto zrozumieć, dlaczego akurat to podejście przetrwało.

Weryfikacja równoległa i rezerwacja w Redisie

Sam przydział VLAN-u to wieloetapowy algorytm z regułami biznesowymi zależnymi od typu usługi i segmentu sieci — inne zakresy dla internetu stacjonarnego, inne dla telewizji, inne dla poszczególnych operatorów sieci mobilnej korzystających z infrastruktury. Kiedy algorytm wytypuje kandydata, trzy niezależne komponenty odpytują trzy źródła równolegle, a wynik jest sumą logiczną — jeśli którykolwiek system zgłasza zajętość, kandydat odpada.

VlanAvailabilityCheck.java
public VlanAllocationResult checkAvailability(int vlanCandidate, ServiceContext ctx) {
    // Trzy niezależne źródła sprawdzane równolegle — żadne z nich
    // nie jest transakcyjnie spójne z pozostałymi.
    var localRegistry = CompletableFuture.supplyAsync(
            () -> localRegistryVerifier.isOccupied(vlanCandidate, ctx));
    var operatorInventory = CompletableFuture.supplyAsync(
            () -> inventoryVerifier.isOccupied(vlanCandidate, ctx));
    var networkController = CompletableFuture.supplyAsync(
            () -> controllerVerifier.isOccupied(vlanCandidate, ctx));

    CompletableFuture.allOf(localRegistry, operatorInventory, networkController)
            .orTimeout(3, TimeUnit.SECONDS)
            .join();

    boolean occupied = localRegistry.join() || operatorInventory.join()
            || networkController.join();

    if (occupied) {
        return VlanAllocationResult.rejected(vlanCandidate);
    }

    // Krótkotrwała rezerwacja w Redisie — chroni przed dwoma
    // operatorami wypełniającymi formularz w tej samej chwili.
    boolean reserved = redisTemplate.opsForValue()
            .setIfAbsent("vlan:reservation:" + vlanCandidate,
                    ctx.sessionId(), Duration.ofMinutes(5));

    return reserved
            ? VlanAllocationResult.reserved(vlanCandidate)
            : VlanAllocationResult.rejected(vlanCandidate);
}

Redis rozwiązuje inny problem niż weryfikatory: nie sprawdza, czy VLAN jest zajęty w sieci, tylko czy ktoś inny właśnie go rezerwuje w tej chwili. Bez tego dwóch operatorów wypełniających formularz w tym samym momencie mogłoby dostać ten sam „wolny” numer — żadna z trzech baz jeszcze by o niczym nie wiedziała, bo commit następuje dopiero na końcu procesu. TTL rezerwacji jest krótki, bo to zabezpieczenie na czas wypełniania formularza, nie mechanizm długoterminowej blokady.

Migracja bazy to migracja całego ekosystemu wokół niej

Podobny brak łatwych skrótów widać było przy migracji z Oracle na PostgreSQL. Zależność od zewnętrznie utrzymywanej instancji Oracle ograniczała nam kontrolę nad wydajnością zapytań, między innymi wyszukiwania po adresach IP. Zanim jednak dało się przełączyć produkcję, trzeba było domknąć dług funkcjonalny: dwie funkcje zostawione wcześniej w starszej wersji systemu — edycja profili VLAN i edycja styków dla sieci mobilnej — musiały trafić do nowej wersji, bo inaczej ktoś musiałby ręcznie grzebać w produkcyjnej bazie. To świadomie zaakceptowane ryzyko na czas przejściowy, ograniczone i policzone z góry.

Sam moment przełączenia wymagał skoordynowanego wyłączenia trzech niezależnych usług na jednej maszynie, w konkretnej kolejności, oraz przygotowanej listy transformacji SQL — od zamiany funkcji agregujących, przez usunięcie specyficznych dla Oracle konstrukcji, po restart sekwencji PostgreSQL do wartości maksymalnej z uwzględnieniem tabel, gdzie triggery odpalają się nie tylko przy wstawianiu rekordów, ale i przy ich modyfikacji. Migracja żywej bazy stanu sieci telekomunikacyjnej okazała się migracją całego ekosystemu wokół niej, a nie samej bazy danych.

Kiedy nie naprawiasz algorytmu, tylko go monitorujesz

Nie każdy znaleziony błąd trzeba od razu naprawiać. Algorytm liczący identyfikator DSLAM-a wyprowadzał go z numeru VLAN-u przypisanego do usługi wideo za pomocą prostego przekształcenia modulo. Dla części zakresów VLAN-ów prowadziło to do rozjazdu między dwoma niezależnie utrzymywanymi identyfikatorami tego samego urządzenia. Rozważaliśmy zmianę wzoru, ale ostatecznie zdecydowaliśmy się na coś innego: algorytm zostaje nietknięty, a rozjazd wykrywa osobne zapytanie monitorujące, które porównuje wartość zapisaną z wartością wyliczoną i zgłasza niezgodność.

Powód jest prosty — algorytm działa od lat na produkcyjnej sieci obsługującej rzeczywistych abonentów, a zmiana wzoru przydziału ID niesie ryzyko trudniejsze do przewidzenia niż koszt sporadycznego, wykrywalnego dryfu danych. Błąd algorytmu, o którym wiemy i który monitorujemy, jest tańszy niż błąd migracji danych, którego nie przewidzieliśmy.

Wniosek. Kiedy nie masz jednego źródła prawdy, nie udajesz, że je masz — projektujesz system, który zakłada rozjazd między źródłami jako stan normalny, a nie wyjątkowy, i inwestujesz w wykrywanie niespójności zamiast w iluzję jednej transakcji.

Żadna z tych trzech historii nie kończy się „przepisaliśmy to porządnie”. Kaskada weryfikatorów, rezerwacja w Redisie, ostrożna migracja bazy i monitoring zamiast naprawy — to nie brak ambicji, tylko rozpoznanie, że w infrastrukturze obsługującej miliony realnych połączeń pragmatyzm bywa bardziej inżynierski niż elegancja.