Inżynier liniowy Mufeez Amjad opublikował szczegółowy opis tego, jak firma przerobiła swój proces ciągłej integracji po tym, jak agenci kodujący AI zamienili CI w najgorsze wąskie gardło — skracając czas oczekiwania na żądanie ściągnięcia z ponad sześciu minut do nieco ponad pięciu, podczas gdy liczba zestawów testów wzrosła prawie czterokrotnie od początku roku.
Post, opublikowany 21 września na blogu inżynierskim firmy Linear, rozpoczął się, jak to często bywa, od zwięzłego wstępu z góry. Na początku tego roku Amjad otworzył Linear i odkrył, że Tuomas, dyrektor ds. technicznych firmy, zlecił mu zadanie zatytułowane „Koszty CI są wysokie” i poprosił go, aby przy okazji przyspieszył CI. Więcej kontekstu tej historii znajdziesz w naszych bieżących reportażach z branży AI.
Gdy agenci wyprzedzają weryfikację
Problem ma charakter strukturalny, a nie przypadkowy. „Agenci znacznie przyspieszyli wysyłkę kodu” – napisał Amjad, „ale sprawdzanie poprawności tych zmian nie przebiega w tym samym tempie”. Każde żądanie ściągnięcia musi jeszcze przejść przez CI, więc w miarę przyspieszania rozwoju CI staje się wąskim punktem – podnosząc koszty infrastruktury i sprawiając, że programiści i ich agenci muszą dłużej czekać na informacje zwrotne.
Liniowo zoptymalizowany pod kątem dwóch wskaźników: tego, jak długo PR czeka na CI i ile czasu zajmuje biegacz. Wyniki po miesiącach pracy: pomimo niemal czterokrotnego wzrostu liczby zestawów testów od stycznia, czas oczekiwania na żądanie ściągnięcia spadł z ponad sześciu minut do nieco ponad pięciu, a czas wykonywania testu na test skrócono mniej więcej o połowę.
Prace można było ogólnie podzielić na cztery kategorie: ulepszona infrastruktura i narzędzia, optymalizacja zadań, które łączą się z innymi zadaniami, ograniczenie liczby wielokrotnych konfiguracji oraz zwiększenie wydajności wykonywania testów. Baza kodu Linear to przede wszystkim TypeScript, ale wiele optymalizacji ma zastosowanie w różnych językach i łańcuchach narzędzi.
Szybsze maszyny i natywny kompilator
Niektóre z najwcześniejszych osiągnięć nie wymagały prawie żadnej optymalizacji samej CI. Przeniesienie obciążeń z GitHub Actions do zewnętrznych modułów uruchamiających z szybszymi procesorami, wydajniejszą pamięcią masową i lepszą infrastrukturą pamięci podręcznej opłaciło się natychmiast: w podobnym porównaniu dwóch dni po obu stronach przełącznika zadania działały średnio o 34% szybciej, a niektóre obciążenia, takie jak `tsc`, spadły o 52%.
Modernizacja zestawu narzędzi zwiększyła zwycięstwo. Przejście na `tsgo`, natywny kompilator TypeScriptu, zmniejszyło tygodniową medianę kontroli typu o 73% — wystarczająco dużo, aby całkowicie wyeliminować wąskie gardło podczas sprawdzania typu.
Linting bez wykresu typów
Linting był kolejnym wczesnym celem. Kilka niestandardowych reguł ESLint oprogramowania Linear zależało od informacji o typie TypeScriptu, co zmuszało każde uruchomienie lint do zbudowania pełnego wykresu typu przed ich oceną — co sprawiało, że linting był jednym z najbardziej obciążających pamięć zadań CI.
Zespół przepisał zasady, aby używać analizy statycznej na abstrakcyjnym drzewie składni, identyfikując konstrukcje podobne do funkcji i wzorce ochronne bez żadnych informacji o typie. To pozwoliło ESLint całkowicie zrezygnować z TypeScriptu, redukując czas lintowania API o 68% i czas lintowania pełnego repozytorium o 55%, przy znacznym spadku zużycia pamięci. Ułatwiło to również późniejszą migrację do Oxlint, co jeszcze bardziej zmniejszyło liczbę minut pracy CI spędzanych na lintingu.
Zmniejszanie ścieżki krytycznej
Dzięki szybszym indywidualnym kontrolom Linear pomniejszył i potraktował CI jako system. To zwróciło uwagę na małe zadania, które przeważają nad wszystkim innym — wykrywanie zmian ścieżki i sprawdzanie pamięci podręcznej wyników testów, które są bramkowane na poziomie zadania, co oznacza, że żaden z ośmiu fragmentów testowych interfejsu API nie może się rozpocząć, dopóki nie zostaną zakończone.
Poprawki były szczegółowe, ale addytywne. Ograniczenie głębokości pobierania zajęło najwolniejszej bramce z 94 sekund do 20. Całkowite usunięcie realizacji transakcji z zadań, które nigdy nie potrzebowały działającego drzewa, skróciło te zadania z 27 sekund do 7. Rzadkie, pozbawione plamy pobieranie z ograniczoną historią pozwoliło zaoszczędzić kolejne 11 nieparzystych sekund na zdarzenia wypychania i kolejki scalania. Łącznie mediana czasu trwania zadania wykrywania zmian spadła z 26 sekund do 8, p90 z 31 do 12, a najwolniejszy czas trwania zadania z 138 do 37 sekund.
Niezawodność realizacji transakcji również wymagała pracy: ponieważ zewnętrzne programy uruchamiające znajdują się poza siecią GitHub i polegają na bezpośrednim łączu IP, sporadyczna degradacja łącza czasami powoduje zatrzymanie pobierania. Liniowy zastąpił `akcję/wyewidencjonowanie` własną akcją złożoną, która ponawia próbę z wycofywaniem, ustawia `GIT_HTTP_LOW_SPEED_LIMIT` i `GIT_HTTP_LOW_SPEED_TIME`, dzięki czemu zablokowane połączenie zostaje przerwane po około 30 sekundach zamiast się zawieszać, i wykorzystuje pamięć podręczną wyewidencjonowania, która utrzymuje trwałe lustro git na dysku przyklejonym.
Jedna subtelna poprawka usunęła zmarnowany czas w kolejce scalania: znaczniki pamięci podręcznej były zapisywane w ramach ostatecznej kontroli przed połączeniem, więc PR mógł czekać w kolejce nawet po przejściu testów. Przeniesienie tego zapisu do zadania niebramkowego skróciło ścieżkę scalania o 42 sekundy dla każdego żądania ściągnięcia interfejsu API i wpisu w kolejce scalania. Łącznie te zmiany skróciły wymagane kontrole PR API w przypadku chybień w pamięci podręcznej o około minutę, jednocześnie ograniczając liczbę startów biegaczy.
Mniej straconych sekund na zadanie
Ostatnia kategoria zaatakowała koszt konfiguracji powtarzający się w przypadku każdego zadania. Każdy fragment testowy API spędzał od 7 do 8 sekund na instalowaniu tego samego klienta Postgres z apt przy każdym uruchomieniu; przeniesienie go do małego obrazu podstawowego CI obok węzła oznaczało, że fragmenty mogły być gotowe do uruchomienia. Zespół dodał później do obrazu nagłówki kompilacji natywnej, gdy odkrył, że pobieranie ich podczas instalacji mogło czasami się zawieszać.
Dlaczego to odbiło się echem
Post wywołał zdenerwowanie programistów: trafił na pierwszą stronę Hacker News, zbierając około 250 punktów i około 280 komentarzy w ciągu jednego dnia. Reakcję można łatwo wyjaśnić — doświadczenie firmy Linear wymienia koszty rozwoju wspomaganego sztuczną inteligencją, z którymi większość organizacji inżynieryjnych dopiero teraz musi się zmierzyć. Agenci generujący żądania ściągnięcia w ciągu kilku minut nadal czekają na potokach walidacyjnych zaprojektowanych z myślą o ludzkim rytmie, a każda minuta tego oczekiwania jest mnożona przez każdego agenta pracującego równolegle.
Lekcja z przeróbki Lineara jest taka, że wąskie gardło można przesuwać, ale tylko poprzez nieestetyczną kumulację wielu drobnych poprawek — szybszych programów uruchamiających, tańszych kontroli typów, reguł lint na poziomie składni, ograniczonych pobrań, odpornych kasowania, gotowych obrazów — a nie jakiejkolwiek pojedynczej srebrnej kuli.
---
Wyprzedź sztuczną inteligencjęOtrzymuj najnowsze wiadomości, analizy i przełomowe informacje dotyczące sztucznej inteligencji — wszystko w jednym miejscu.
Przeczytaj więcej aktualności o sztucznej inteligencji →