Koleo.pl - rozkład jazdy i bilety na pociągi

Projekt co prawda ma już trochę miesięcy, ale kilka osób z forum (@basial, @gpks, @essepl) przyłożyło swoją rekę do powstania, więc myślę, że warto się pochwalić.

KOLEO to nowoczesny rozkład jazdy pociągów z cenami biletów, gdzie można kupić bilety na pociągi różnych przewoźników (w tym w jednej trasakcji).

Ogólna metryczka techniczna:
Rails 4.2, Ember, Go, PostgreSQL, Redis, Sinatra, Ansible

Parę słów o ciekawszych częściach:

Importy danych

Zbieramy (legalnie) importy 12 przewoźników, z 6 różnych źródeł, począwszy od monstrualnych SOAPów (do debugowania pomaga niezastąpione https://www.soapui.org) poprzez różne XMLe, na płaskich CSV pobieranych przez FTP kończąc. Wszystkie dane normalizujemy (nadajemy im wspólne id, poprawiamy nazwy) i ładujemy do naszej bazy danych. Praktycznie cały czas w tle pobieramy co najmniej jeden najnowszy rozkład.

Wyszukiwarka

Stworzyliśmy własną wyszukiwarkę w GO (nawet nie próbowaliśmy tego robić w Rubym), sprawdziło się to doskonale. Jedno wyszukanie obecnie to średnio 60ms. Go dobrze działa jako nowoczesny zamiennik C, czyli: cała obsługa http, json jest banalna i wbudowana w język, działa wystarczająco szybko, a użycie goroutines znacząco przyśpiesza wczytywanie danych. Do tego szybko można się wdrożyć, a deploy to wgranie jednego pliku na serwer. Polecam! :slight_smile:

Frontend

Frontend jest napisany w Emberze. Nasz stosunek do Embera to love&hate. Ember jest świetny jak się dostosujemy do konwencji, szybko działa po wczytaniu strony i pozwala nam robić dobry UI. Super funkcjami jest to, że działa przycisk wstecz i URL działają jak przy statycznych stronach. Minusy są przede wszystkim dwa. 1. Ostra krzywa uczenia się - sporo czasu zajmuje wdrożenie się w Embera zanim zostanie się w nim produktywnym. 2. Czas ładowania strony - o ile po załadowaniu strony wszystko działa szybko, to sam start jest bardzo wolny. Szczególnie boli to na Androidzie (strona jest oczywiście responsywna), gdzie zanim cokolwiek przydatnego użytkownikowi się pojawi mija kilka (a czasem nawet kilkanaście) sekund. :expressionless: Ten drugi problem rozwiązujemy/staramy się ominąć przez natywną aplikację na Androida.

Moduł cenowy

Tutaj jest pies pogrzebany. Myślałem, że to będzie jedna z najprostszych części projektu (liczymy kilometry i mnożymy przez zniżkę), a okazało się warunki i zasady różnych ofert/promocji są przedziwne. Czy wiecie, że oferta Junior Senior w Kolejach Dolnośląskich obowiązuje od 15 stycznia do 28 lutego oraz od 1 czerwca do 30 września i jest skierowana do dorosłych powyżej 50 lat (senior) podróżujących z osobami, które nie ukończyły 18 lat (junior). Ja też nie wiedziałem, a takich ofert każdy przewoźnik ma kilkanaście, lub kilkadziesiąt. Bierzemy połączenie, pasażerów (ich wiek i zniżki) i dla nich wyliczamy najkorzystniejszą ofertę. Cała ta logika jest w PORO, większość ofert możemy dodać tworząc odpowiednie rekordy w bazie.

Połączenia z systemami przewoźników

Tutaj mamy trochę mikroserwisów, które łączą się z różnymi systemami, by raportować sprzedaż, generować kody 2D (czasem sami je generujemy), wymieniać klucze itp. Dodatkowo zrobiliśmy aplikacje do kontroli biletów, bo jeszcze nie wszyscy konduktorzy wyposażeni są w laser w oczach ;).

To tyle z technicznych informacji. Uprzedzam pierwsze pytanie: Nie sprzedajemy jeszcze biletów na Intercity, bo młyny kolejowe mielą wolno. Ale mnóstwo ludzi jeździ innymi przewoźnikami: PR, ŁKA, Arriva, KMŁ i sobie KOLEO chwalą (opinie na stronie pochodzą od prawdziwych ludzi). :slight_smile:

12 Likes

Cześć - mam pytanie - czemu akurat zdecydowaliście się na Go (a nie np. Elixir) i dlaczego napisaliście własną wyszukiwarkę, a nie skorzystaliście z jakiegoś gotowego rozwiązania? :slight_smile:

Co do pierwszego pytania, to po prostu Go jest językiem, w którym od jakiegoś czasu coś chciałem zrobić i wydawał się interesujący. Fakt, że jest to język kompilowany i bardzo dobrze wypadał w benchmarkach przypieczętował decyzję.

Czemu nie skorzystaliśmy z gotowego rozwiązania? Trzy powody:

  1. Przede wszystkim wyszukiwarka to core naszego biznesu, więc dobrze mieć własną technologię, żeby mieć nad nią pełną kontrolę. Gdy chcemy wprowadzić ustawianie czasu na przesiadkę, to możemy to sprawnie zrobić, ponieważ kod jest nasz i go znamy. Gdy chcemy wybrać jakim kryteriom nadać jakie wagi, to też możemy to zrobić.
  2. Nie ma zbyt dużo dobrych wyszukiwarek połączeń ze sztywnym rozkładem (kolejowe/autobusowe itp). Zwykłe wyszukiwarki tras działają na innej zasadzie, bo nie ma tam ograniczeń, że odjazdy są tylko o konkretnych godzinach. Co więcej dokumentacja do tych, które znaleźliśmy też pozostawia sporo do życzenia i wdrożenie nie wyglądało na wyjmij z pudełka i używaj.
  3. Algorytmy do wyszukiwania są cool, a nie codziennie ma się możliwość ich zaimplementowania. :wink:
2 Likes

Mały update po dwóch latach. :slight_smile: Cały czas działamy, rośniemy i kilka dni temu sprzedaliśmy milionowy bilet :slight_smile:

Odnośnie przewoźników to dołączyły do nas Koleje Dolnośląskie i SKM w Trójmieście, cały czas nie ma niestety Intercity (tak wiem :expressionless: ), ale wbrew pozorom kolejami regionalnymi jeździ naprawdę sporo ludzi, zwłaszcza na biletach okresowych, które też w międzyczasie dodaliśmy, więc na brak użytkowników nie narzekamy.

Z ciekawszych rzeczy, to zrobiliśmy pierwszy w Polsce graficzny wybór miejsca w pociągu jak w kinie. Możliwy jest w weekendowych pociągach Łódź Warszawa. Sam mechanizm to mikroserwis w GO. Jest to kolejny raz po wyszukiwarce gdzie użyliśmy tego języka i jesteśmy bardzo zadowoleni. Ostatni commit był w kwietniu zeszłego roku, od tego czasu nic nie ruszaliśmy, a wszystko działa solidnie. :slight_smile:

Oczywiście cały czas Ruby to nasz główny język, obecnie mamy 85kLOC w Rubym, z czego 43kLOC to testy. Wszystko działa jako monolit - kilka lat temu się zastanawialiśmy, czy nie pójść w mikroserwisy, ale z perspektywy czasu widzimy, że pozostanie z monolitem było dobrą decyzją. Jedna większa aplikacja jest łatwiejsza do utrzymania przez niewielki zespół, niż chmara mniejszych. W tej chwili dobijamy do 8 tysięcy requestów na minutę, Ruby i Railsy oczywiście nie były tutaj nigdy wąskim gardłem. Największy ruch odnotowujemy zawsze przy corocznej zmianie rozkładu na początku grudnia. Rośnie on zazwyczaj wtedy dwukrotnie, ale i z tym sobie radzimy bez problemów.

Mamy kilka planów na ten rok, ale pochwalimy się dopiero jak je zrealizujemy.

5 Likes

+1000000 za użycie Go :slight_smile: