W artykule opowiem, jak podeszliśmy do kwestii odporności PostgreSQL, dlaczego stało się to dla nas ważne i co ostatecznie osiągnęliśmy.
Mamy wysoko obciążoną usługę: 2,5 miliona użytkowników na całym świecie, ponad 50 tysięcy aktywnych użytkowników każdego dnia. Serwery znajdują się w Amazone w jednym regionie Irlandii: w działaniu jest ciągle ponad 100 różnych serwerów, z czego prawie 50 – z bazami danych.
Cały backend to duża monolityczna aplikacja stateful napisana w Javie, która utrzymuje stałe połączenie websocket z klientem. Kiedy kilku użytkowników pracuje jednocześnie na jednej tablicy, wszyscy widzą zmiany w czasie rzeczywistym, ponieważ każdą zmianę zapisujemy w bazie. Mamy około 10 tysięcy zapytań na sekundę do naszych baz. W szczytowym obciążeniu w Redis wysyłamy od 80 do 100 tysięcy zapytań na sekundę.

Dlaczego przeszliśmy z Redis na PostgreSQL
Początkowo nasza usługa działała z Redis, magazynem kluczy-wartości, który przechowuje wszystkie dane w pamięci operacyjnej. serwera.
Zalety Redis:
- Wysoka prędkość odpowiedzi, ponieważ wszystko jest przechowywane w pamięci;
- Wygoda tworzenia kopii zapasowych i replikacji.
Wady Redis dla nas:
- Brak prawdziwych transakcji. Próbowaliśmy je naśladować na poziomie naszej aplikacji. Niestety, nie zawsze działało to dobrze i wymagało napisania bardzo złożonego kodu.
- Objętość danych jest ograniczona przez ilość pamięci. Przy wzroście ilości danych pamięć będzie rosła, a w końcu napotkamy na właściwości wybranego instancji, co w AWS wymaga zatrzymania naszej usługi, aby zmienić typ instancji.
- Należy nieustannie utrzymywać poziom niskiego opóźnienia, ponieważ mamy bardzo dużą liczbę zapytań. Optymalny dla nas poziom opóźnienia to 17-20 ms. Przy poziomie 30-40 ms otrzymujemy długie odpowiedzi na zapytania naszej aplikacji oraz degradację usługi. Niestety, wydarzyło się to we wrześniu 2018 roku, kiedy jedna z instancji z Redis z jakiegoś powodu uzyskała opóźnienie dwukrotnie większe od normalnego. Aby rozwiązać problem, zatrzymaliśmy usługę w połowie dnia roboczego na nieplanową konserwację i wymieniliśmy problemową instancję Redis.
- Łatwo można uzyskać niespójność danych nawet przy niewielkich błędach w kodzie, a następnie spędzić dużo czasu na pisaniu kodu, aby naprawić te dane.
Zidentyfikowaliśmy wady i zrozumieliśmy, że musimy przenieść się na coś bardziej wygodnego, z normalnymi transakcjami i mniejszą zależnością od opóźnień. Przeprowadziliśmy badania, przeanalizowaliśmy wiele opcji i wybraliśmy PostgreSQL.
Migracja do nowej bazy danych trwa już 1,5 roku i przenieśliśmy tylko niewielką część danych, dlatego pracujemy jednocześnie z Redis i PostgreSQL. Więcej na temat etapów migracji i przełączania danych między bazami opisano w artykułach mojego kolegi..
Gdy tylko zaczynaliśmy migrację, nasza aplikacja działała bezpośrednio z bazą danych i łączyła się z głównym Redis i PostgreSQL. Klaster PostgreSQL składał się z głównego i repliki z asynchroniczną replikacją. Tak wyglądała schemat pracy z bazami:

Wdrożenie PgBouncer
Podczas gdy migrowaliśmy, produkt również się rozwijał: zwiększała się liczba użytkowników i liczba serwerów pracujących z PostgreSQL, i zaczęło brakować nam połączeń. PostgreSQL dla każdego połączenia tworzy osobny proces i zużywa zasoby. Możliwość zwiększenia liczby połączeń istnieje do pewnego momentu, w przeciwnym razie istnieje ryzyko nieoptymalnej pracy bazy danych. Idealnym rozwiązaniem w takiej sytuacji byłby wybór menedżera połączeń, który stanie przed bazą.
Mieliśmy dwa warianty dla menedżera połączeń: Pgpool i PgBouncer. Jednak pierwszy nie obsługuje trybu transakcyjnego pracy z bazą, dlatego wybraliśmy PgBouncer.
Skonfigurowaliśmy następujący schemat działania: nasza aplikacja łączy się z jednym PgBouncer, za którym znajdują się główne bazy PostgreSQL, a za każdą główną - jedna replika z asynchroniczną replikacją.

W tym czasie nie mogliśmy przechowywać całej objętości danych w PostgreSQL i dla nas liczyła się szybkość pracy z bazą, dlatego zaczęliśmy szardować PostgreSQL na poziomie aplikacji. Opisana powyżej schemata jest stosunkowo wygodna do tego: przy dodawaniu nowego sharda w PostgreSQL wystarczy zaktualizować konfigurację PgBouncer, a aplikacja może natychmiast pracować z nowym shardem.
Niezawodność PgBouncer
Ten schemat działał do momentu, gdy jedyny instancja PgBouncer się zawiesił. Jesteśmy w AWS, gdzie wszystkie instancje działają na sprzęcie, który okresowo zawodzi. W takich przypadkach instancja po prostu przenosi się na nowy sprzęt i działa z powrotem. Tak stało się również z PgBouncer, jednak stał się on niedostępny. Efektem tej awarii była niedostępność naszej usługi przez 25 minut. AWS zaleca stosowanie redundancji po stronie użytkownika w takich sytuacjach, co nie zostało wtedy przez nas wdrożone.
Po tym zdarzeniu poważnie zaczęliśmy myśleć o odporności PgBouncer i klastrów PostgreSQL, ponieważ podobna sytuacja mogła się powtórzyć z dowolną instancją w naszym koncie AWS.
Schemat odporności PgBouncer zbudowaliśmy w następujący sposób: wszystkie serwery aplikacji kierują swoje zapytania do Network Load Balancer, za którym znajdują się dwa PgBouncer. Każdy z PgBouncer obserwuje te same główne instancje PostgreSQL każdego shardu. W przypadku powtórzenia sytuacji z awarią instancji AWS, cały ruch jest przekierowywany przez inny PgBouncer. Odporność Network Load Balancer zapewnia AWS.
Taki schemat umożliwia bezproblemowe dodawanie nowych serwerów PgBouncer.

Tworzenie odpornego klastra PostgreSQL
Rozważając to zadanie, braliśmy pod uwagę różne opcje: własne rozwiązanie failover, repmgr, AWS RDS, Patroni.
Własne skrypty
Mogą monitorować działanie głównej instancji i, w przypadku jej awarii, promować replikę do roli głównej oraz aktualizować konfigurację PgBouncer.
Zalety tego podejścia polegają na maksymalnej prostocie, ponieważ sami piszecie skrypty i dokładnie rozumiecie, jak one działają.
Wady:
- Główna instancja mogła nie umrzeć, lecz mogło dojść do awarii sieci. Failover, nie wiedząc o tym, promuje replikę do roli głównej, a stara główna instancja nadal działa. W rezultacie mamy dwa serwery pełniące rolę głównej i nie będziemy wiedzieć, na którym z nich znajdują się najbardziej aktualne dane. Taką sytuację nazywa się split-brain.
- Pozostaliśmy bez repliki. W naszej konfiguracji mamy główną instancję i jedną replikę, po przełączeniu replika staje się główną, a my nie mamy więcej replik, więc musimy ręcznie dodać nową replikę.
- Potrzebny jest dodatkowy monitoring pracy failover, a mamy 12 shardów PostgreSQL, co oznacza, że musimy monitorować 12 klastrów. Przy zwiększaniu liczby shardów nie możemy zapomnieć o aktualizacji failover.
Samodzielne rozwiązanie failover wydaje się skomplikowane i wymaga nietrywialnego wsparcia. W przypadku jednego klastra PostgreSQL będzie to najprostsza opcja, ale nie scala się, więc nie będzie odpowiednia dla nas.
Repmgr
Menadżer replikacji dla klastrów PostgreSQL, który potrafi zarządzać działaniem klastra PostgreSQL. Nie ma automatycznego failover „z pudełka”, więc aby to działało, trzeba napisać własną „owijkę” nad gotowym rozwiązaniem. Dlatego może być to nawet bardziej skomplikowane niż z samodzielnie napisanymi skryptami, a więc Repmgr nawet nie próbowaliśmy.
AWS RDS
Obsługuje wszystko, co jest nam potrzebne, potrafi robić kopie zapasowe i obsługuje pulę połączeń. Ma automatyczne przełączanie: po śmierci głównego węzła replika staje się nowym głównym, a AWS zmienia rekord DNS na nowego głównego, przy czym repliki mogą znajdować się w różnych AZ.
Do minusów można zaliczyć brak drobnych ustawień. Jako przykład drobnych ustawień: na naszych instancjach obowiązują ograniczenia dla połączeń TCP, czego niestety nie można zrobić w RDS:
net.ipv4.tcp_keepalive_time=10
net.ipv4.tcp_keepalive_intvl=1
net.ipv4.tcp_keepalive_probes=5
net.ipv4.tcp_retries2=3
Ponadto cena AWS RDS jest prawie dwa razy droższa od standardowej ceny instancji, co było głównym powodem rezygnacji z tego rozwiązania.
Patroni
To jest szablon w Pythonie do zarządzania PostgreSQL z dobrą dokumentacją, automatycznym failover i kodem źródłowym na GitHubie.
Zalety Patroni:
- Każdy parametr konfiguracji jest dokładnie opisany, jasno jak to działa;
- Automatyczny failover działa z pudełka;
- Napisany w Pythonie, a ponieważ sami dużo piszemy w Pythonie, łatwiej będzie nam zajmować się problemami i być może nawet pomóc w rozwoju projektu;
- Całkowicie zarządza PostgreSQL, pozwala na zmianę konfiguracji na wszystkich węzłach klastra, a jeśli nowa konfiguracja wymaga ponownego uruchomienia klastra, można to zrobić ponownie za pomocą Patroni.
Wady:
- Z dokumentacji nie jest jasne, jak prawidłowo pracować z PgBouncer. Chociaż trudno to nazwać wadą, ponieważ zadaniem Patroni jest zarządzanie PostgreSQL, a jak będą odbywać się połączenia z Patroni — to już nasz problem;
- Mało przykładów wdrożenia Patroni przy dużych objętościach, przy tym wiele przykładów wdrożenia od zera.
W rezultacie do stworzenia odpornego na awarie klastra wybraliśmy właśnie Patroni.
Proces wdrażania Patroni
Przed wprowadzeniem Patroni mieliśmy 12 shardów PostgreSQL w konfiguracji z jednym masterem i jedną repliką z asynchroniczną replikacją. Serwery aplikacji łączyły się z bazami danych poprzez Network Load Balancer, za którym stały dwa instancje z PgBouncer, a za nimi znajdowały się wszystkie serwery PostgreSQL.

Aby wdrożyć Patroni, musieliśmy wybrać rozproszone magazynowanie konfiguracji klastra. Patroni działa z rozproszonymi systemami magazynowania konfiguracji, takimi jak etcd, Zookeeper, Consul. Mamy właśnie w produkcji pełnoprawny klaster Consul, który współpracuje z Vault, a nie używamy go w żaden inny sposób. To doskonała okazja, aby zacząć używać Consul zgodnie z przeznaczeniem.
Jak działa Patroni z Consul
Mamy klaster Consul, który składa się z trzech węzłów oraz klaster Patroni, który składa się z lidera i repliki (w Patroni master nazywany jest liderem klastra, a slave'y – replikami). Każda instancja klastra Patroni nieustannie przesyła do Consul informacje o stanie klastra. Dlatego z Consul można zawsze uzyskać aktualną konfigurację klastra Patroni i to, kto jest obecnie liderem.

Aby połączyć Patroni z Consul, wystarczy zapoznać się z oficjalną dokumentacją, w której napisane jest, że należy wskazać host w formacie http lub https, w zależności od tego, jak pracujemy z Consul, oraz opcjonalnie schemat połączenia:
host: host:port dla punktu końcowego Consul, w formacie: http(s)://host:port
scheme: (opcjonalnie) http lub https, domyślnie httpWygląda prosto, ale tutaj zaczynają się problemy. Z Consul pracujemy za pomocą zabezpieczonego połączenia przez https, a nasza konfiguracja połączenia będzie wyglądać następująco:
consul:
host: https://server.production.consul:8080
verify: true
cacert: {{ consul_cacert }}
cert: {{ consul_cert }}
key: {{ consul_key }}Ale to nie działa. Przy uruchamianiu Patroni nie może połączyć się z Consul, ponieważ wciąż próbuje połączyć się za pośrednictwem http.
Zrozumienie problemu ułatwił kod źródłowy Patroni. Dobrze, że jest napisany w Pythonie. Okazuje się, że parametr host w żaden sposób nie jest analizowany, a protokół musi być wskazany w schemacie. Oto jak wygląda działający blok konfiguracji do pracy z Consul u nas:
consul:
host: server.production.consul:8080
scheme: https
verify: true
cacert: {{ consul_cacert }}
cert: {{ consul_cert }}
key: {{ consul_key }}Consul-template
Więc wybraliśmy magazyn konfiguracyjny. Teraz musimy zrozumieć, jak PgBouncer będzie przełączał swoją konfigurację w momencie zmiany lidera w klastrze Patroni. W dokumentacji na ten temat nie ma odpowiedzi, ponieważ zasadniczo nie opisano współpracy z PgBouncer.
W poszukiwaniu rozwiązania znaleźliśmy artykuł (tytuł niestety nie pamiętam), w którym napisano, że Consul-template bardzo pomógł w połączeniu PgBouncer i Patroni. To zachęciło nas do zbadania działania Consul-template.
Okazało się, że Consul-template nieustannie monitoruje konfigurację klastra PostgreSQL w Consul. W momencie zmiany lidera aktualizuje konfigurację PgBouncer i wysyła polecenie do jej przeładowania.

Wielką zaletą template jest to, że jest przechowywany w postaci kodu, dzięki czemu w przypadku dodania nowego shardu wystarczy wykonać nowy commit i zaktualizować template w trybie automatycznym, zachowując zasady Infrastructure as code.
Nowa architektura z Patroni
W rezultacie uzyskaliśmy taką schemę pracy:

Wszystkie serwery aplikacji kierują zapytania do balancera → za nim stoją dwa instancje PgBouncer → na każdej instancji działa Consul-template, który monitoruje stan każdego klastra Patroni i dba o aktualność konfiguracji PgBouncer, który kieruje zapytania do aktualnego lidera każdego klastra.
Ręczne testowanie
Przed wdrożeniem na produkcję uruchomiliśmy tę schemę w małym środowisku testowym i sprawdziliśmy działanie automatycznego przełączania. Otwieraliśmy tablicę, przesuwaliśmy naklejkę i w tym momencie „zabijaliśmy” lidera klastra. W AWS wystarczy wyłączyć instancję przez konsolę.

Naklejka wracała w ciągu 10-20 sekund, a następnie zaczynała ponownie normalnie się przesuwać. Oznacza to, że klaster Patroni zadziałał prawidłowo: zmienił lidera, przekazał informacje do Consul, a Consul-template natychmiast przejął te informacje, zaktualizował konfigurację PgBouncer i wysłał polecenie do przeładowania.
Jak przetrwać przy wysokim obciążeniu i zachować minimalny czas przestoju?
Wszystko działa świetnie! Ale pojawiają się nowe pytania: Jak to zadziała przy wysokim obciążeniu? Jak szybko i bezpiecznie wdrożyć wszystko na produkcję?
Odpowiedź na pierwsze pytanie daje nam środowisko testowe, w którym przeprowadzamy testy obciążeniowe. Jest ono całkowicie identyczne z produkcyjnym pod względem architektury i ma wygenerowane dane testowe, których objętość jest mniej więcej równa objętości danych produkcyjnych. Postanawiamy po prostu "zabić" jednego z masterów PostgreSQL podczas testu i zobaczyć, co się stanie. Ale przed tym ważne jest, aby sprawdzić automatyczne wypuszczanie, ponieważ w tym środowisku mamy kilka shardów PostgreSQL, więc zyskamy doskonałe testowanie skryptów konfiguracyjnych przed produkcją.
Oba zadania wydają się ambitne, ale mamy PostgreSQL 9.6. Może od razu zaktualizujemy do 11.2?
Zdecydowaliśmy się to zrobić w dwóch etapach: najpierw zaktualizować wersję do 11.2, a potem uruchomić Patroni.
Aktualizacja PostgreSQL
Aby szybko zaktualizować wersję PostgreSQL, należy użyć opcji -k, w której tworzone są twarde linki na dysku i nie ma potrzeby kopiowania Twoich danych. Na bazach o wielkości 300-400 GB aktualizacja zajmuje 1 sekundę.
Mamy wiele shardów, dlatego aktualizacja musi być przeprowadzona automatycznie. W tym celu napisaliśmy playbook Ansible, który wykonuje cały proces aktualizacji za nas:
/usr/lib/postgresql/11/bin/pg_upgrade
<b>--link </b>
--old-datadir='' --new-datadir=''
--old-bindir='' --new-bindir=''
--old-options=' -c config_file='
--new-options=' -c config_file='Ważne jest, aby przed rozpoczęciem aktualizacji wykonać ją z parametrem —check, aby upewnić się w możliwościach aktualizacji. Nasz skrypt również dokonuje wymiany plików konfiguracyjnych na czas aktualizacji. Skrypt zakończył się po 30 sekundach, co jest doskonałym wynikiem.
Uruchomienie Patroni
Aby rozwiązać drugi problem, wystarczy spojrzeć na konfigurację Patroni. W oficjalnym repozytorium znajduje się przykład konfiguracji z initdb, który odpowiada za inicjalizację nowej bazy podczas pierwszego uruchomienia Patroni. Ale ponieważ mamy już gotową bazę, po prostu usunęliśmy ten segment z konfiguracji.
Kiedy zaczęliśmy instalować Patroni na gotowym klastrze PostgreSQL i uruchamiać go, napotkaliśmy nowy problem: oba serwery uruchamiały się jako liderzy. Patroni nic nie wie o wczesnym stanie klastra i próbuje uruchomić oba serwery jako dwa osobne klastry o tej samej nazwie. Aby rozwiązać ten problem, należy usunąć katalog danych na slave:
rm -rf /var/lib/postgresql/Należy to zrobić tylko na slave!
Podczas podłączania czystej repliki, Patroni wykonuje kopię zapasową bazy lidera i przywraca ją na replikę, a następnie synchronizuje aktualny stan na podstawie logów wal.
Kolejnym wyzwaniem, z którym się zmierzyliśmy, jest to, że wszystkie klastry PostgreSQL domyślnie nazywają się main. Kiedy każdy klaster nic nie wie o innym — to w porządku. Ale gdy chcesz używać Patroni, wszystkie klastry muszą mieć unikalną nazwę. Rozwiązaniem jest zmiana nazwy klastra w konfiguracji PostgreSQL.
Test obciążeniowy
Przeprowadziliśmy test, który imituje pracę użytkowników na tablicach. Gdy obciążenie osiągnęło naszą średnią dzienną wartość, powtórzyliśmy dokładnie ten sam test, wyłączając jedną instancję z liderem PostgreSQL. Automatyczny failover zadziałał zgodnie z naszymi oczekiwaniami: Patroni zmienił lidera, Consul-template zaktualizował konfigurację PgBouncer i wysłał polecenie do reload. Na naszych wykresach w Grafanie było widać opóźnienia trwające od 20 do 30 sekund oraz niewielką liczbę błędów związanych z połączeniem z bazą. To normalna sytuacja, takie wartości są dopuszczalne dla naszego failover i są zdecydowanie lepsze niż przestoje serwisu.
Wdrożenie Patroni w produkcji
Ostatecznie otrzymaliśmy następujący plan:
- Wdrożenie Consul-template na serwery PgBouncer i uruchomienie;
- Aktualizacja PostgreSQL do wersji 11.2;
- Zmiana nazwy klastra;
- Uruchomienie klastra Patroni.
Nasza schemat pozwala na realizację pierwszego punktu praktycznie w dowolnym momencie, możemy kolejno wyłączać każdy PgBouncer z pracy i przeprowadzać na nim wdrożenie oraz uruchomienie consul-template. Tak właśnie zrobiliśmy.
Do szybkiego wdrożenia użyliśmy Ansible, ponieważ wszystkie playbooki przetestowaliśmy już w środowisku testowym, a czas wykonania pełnego scenariusza wynosił od 1,5 do 2 minut dla każdego shardu. Mogliśmy wprowadzać zmiany po kolei na każdym sharde bez zatrzymywania naszego serwisu, ale musielibyśmy na kilka minut wyłączyć każdy PostgreSQL. W takiej sytuacji użytkownicy, którzy mają dane na tym shardzie, nie mogliby normalnie pracować w tym czasie, co jest dla nas nieakceptowalne.
Wyjściem z tej sytuacji był planowy maintenance, który odbywa się co 3 miesiące. To okno na prace konserwacyjne, kiedy całkowicie wyłączamy nasz serwis i aktualizujemy instancje baz danych. Do kolejnego okna pozostał tydzień, postanowiliśmy więc po prostu poczekać i dodatkowo się przygotować. W czasie oczekiwania dodatkowo się zabezpieczyliśmy: dla każdego shardu PostgreSQL uruchomiliśmy zapasową replikę na wypadek awarii, aby zachować najnowsze dane, oraz dodaliśmy nową instancję dla każdego shardu, która ma stać się nową repliką w klastrze Patroni, aby nie wykonywać polecenia do usunięcia danych. Wszystko to pomogło maksymalnie zredukować ryzyko błędu.

Uruchomiliśmy nasz serwis ponownie, wszystko działało jak należy, użytkownicy kontynuowali pracę, ale na wykresach zauważyliśmy nienaturalnie wysokie obciążenie serwerów Consul.

Dlaczego nie zauważyliśmy tego w środowisku testowym? Problem ten doskonale ilustruje, że należy przestrzegać zasady Infrastructure as code i rozwijać całą infrastrukturę, zaczynając od środowisk testowych, a kończąc na produkcji. W przeciwnym razie bardzo łatwo napotkać taki problem, jaki mieliśmy. Co się stało? Consul najpierw pojawił się w produkcji, a potem w środowiskach testowych, w rezultacie w środowiskach testowych wersja Consul była wyższa niż w produkcji. Właśnie w jednej z wersji rozwiązano wyciek CPU podczas pracy z consul-template. Dlatego po prostu zaktualizowaliśmy Consul, w ten sposób rozwiązując problem.
Restartuj klaster Patroni
Jednak napotkaliśmy nowy problem, o którym nawet nie mieliśmy pojęcia. Podczas aktualizacji Consul po prostu usuwamy węzeł Consul z klastra za pomocą polecenia consul leave → Patroni łączy się z innym serwerem Consul → wszystko działa. Ale kiedy dotarliśmy do ostatniej instancji klastra Consul i wysłaliśmy mu polecenie consul leave, wszystkie klastry Patroni po prostu się zrestartowały, a w logach zobaczyliśmy następujący błąd:
BŁĄD: get_cluster
Ślad stosu (ostatnie wywołanie ostatnie):
...
RetryFailedError: 'Przekroczono termin ponownego próby'
BŁĄD: Błąd w komunikacji z DCS
<b>LOG: system bazy danych został zamknięty</b>Klaster Patroni nie mógł uzyskać informacji o swoim klastrze i zrestartował się.
Aby znaleźć rozwiązanie, skontaktowaliśmy się z autorami Patroni poprzez zgłoszenie na Githubie. Zaproponowali poprawki naszych plików konfiguracyjnych:
consul:
consul.checks: []
bootstrap:
dcs:
retry_timeout: 8Udało nam się powtórzyć problem w środowisku testowym i przetestować te parametry, ale niestety nie zadziałały.
Problem nadal nie jest rozwiązany. Planujemy spróbować następujących opcji rozwiązania:
- Użyć agenta Consul na każdym instancie klastra Patroni;
- Naprawić problem w kodzie.
Rozumiemy miejsce wystąpienia błędu: prawdopodobnie problem leży w użyciu domyślnego timeoutu, który nie jest nadpisywany przez plik konfiguracyjny. Po usunięciu ostatniego serwera Consul z klastra, cały klaster Consul zawiesza się na dłużej niż sekundę, przez co Patroni nie może uzyskać stanu klastra i całkowicie restartuje cały klaster.
Na szczęście nie napotkaliśmy więcej żadnych błędów.
Podsumowanie użycia Patroni
Po pomyślnym uruchomieniu Patroni dodaliśmy dodatkową replikę w każdym klastrze. Teraz w każdym klastrze występuje coś w rodzaju kworum: jeden lider i dwie repliki, dla zabezpieczenia na wypadek split-brain podczas przełączania.

Na produkcji Patroni działa od ponad trzech miesięcy. W tym czasie zdążył nam pomóc. Niedawno w AWS lider jednego z klastrów przestał działać, automatyczny failover zadziałał i użytkownicy mogli kontynuować pracę. Patroni wykonał swoje główne zadanie.
Małe podsumowanie użycia Patroni:
- Wygoda zmiany konfiguracji. Wystarczy zmienić konfigurację na jednym instancie, a ona zostanie przekazana na cały klaster. Jeśli do zastosowania nowej konfiguracji potrzebny jest restart, Patroni o tym poinformuje. Patroni może zrestartować cały klaster za pomocą jednej komendy, co również jest bardzo wygodne.
- Automatyczny failover działa i już zdążył nam pomóc.
- Aktualizacja PostgreSQL bez przestojów w aplikacji. Najpierw należy zaktualizować repliki do nowej wersji, następnie zmienić lidera w klastrze Patroni i zaktualizować starego lidera. Przy tym przeprowadzane są niezbędne testy automatycznego failover.
Źródło: habr.com
