Pasjonuję się IT, ekonomią i muzyką. Moim ulubionym językiem jest Java i w niej pracuję na codzień.
Typowy produkcyjny klaster jest zbudowany z co najmniej jednego ReplicaSetu. To oznacza, że posiadamy co najmniej jeden węzeł Primary (realizujący zapisy i odczyty) i kilka Secondary (tylko do odczytu). Domyślne ustawienia konfiguracji sterowników kierują ruch wyłącznie do Primary, rezygnując z używania pozostałych hostów. To oznacza, że obciążony jest tylko jeden node, a reszta pozostaje bierna - zasoby pod względem load-balancingu są marnowane. W przypadku awarii Mastera tracimy też możliwość odczytów, które mogłyby być nadal realizowane przez Secondary.
Zatem w jaki sposób można wymusić kierowanie ruchu do innych hostów niż Primary? Z pomocą przychodzą nam preferencje odczytu - tzw. ReadPreference
. Posiada on różne tryby, pozwalające na wybranie algorytmu poszukiwania hosta, do którego wyślemy query.
Ustawienia ReadPreference
można zdefiniować w kilku miejscach:
MongoClient mongoClient = MongoClients.create(MongoClientSettings.builder()
.hosts(listOfHostsUrls)
.readPreference(ReadPreference.secondary())
.build());
MongoDatabase database = mongoClient.getDatabase("someDatabase")
.withReadPreference(ReadPreference.secondary());
MongoCollection<Document> collection = database.getCollection("videos")
.withReadPreference(ReadPreference.secondary());
Podane ustawienia mogą zmieniać działanie usługi, jeśli na przykład posiada na klastrze kilka różnych baz danych z różnymi kolekcjami. Może być tak, że nie dla wszystkich kolekcji można sobie pozwolić na odczyt z Secondary - np. gdy potrzebujemy wyższy poziom consistency.
Domyślny dla wielu driverów. Kieruje ruch wyłącznie do hosta typu Primary, co może być znaczącą barierą w skalowaniu usługi pod kątem odczytów.
W tej preferencji w głównej mierze odczytujemy z hosta Primary, ale w przypadku gdy będzie niedostępny to odczyty zostaną przekierowana do innych replik typu Secondary.
Wszystkie odczyty są kierowane do Secondary.
W większości przypadków odczytujemy z Secondary, ale przy awarii wszystkich hostów ruch może zostać przekierowany do Primary.
Driver selekcjonuje zdrowe hosty z odpowiednio krótkim czasem odpowiedzi, tak by odpowiedziały maksymalnie szybko - np w ramach jednego datacenter. Następnie losowo wybierany jest jeden host, który zrealizuje żądanie. Nie jest uwzględniane czy node jest Primary czy Secondary.
W przypadku usługi typu heavy read z eventual consistency najbardziej optymalne będzie odczytywanie z preferencjami: nearest
, secondaryPreferred
lub secondary
. Wybór konkretnej preferencji zależałby w tym przypadku od ilości zapisów oraz posiadanych hostów i zasobów. Jeśli usługa bardzo dużo zapisuje i Primary jest już dostatecznie obciążony, można rozważyć unikanie dociążania Primary poprzez preferencję secondary
lub secondaryPreferred
.
Ostateczny wybór między pozostałymi dwoma typami odczytów może rozstrzygnąć to - czy bardziej zależy nam na stabilności odczytów czy zapisów. SecondaryPreferred dostarcza n+1 hostów, natomiast samo secondary tylko n (bo nie uwzględnia Primary).
W sytuacji niespodziewanie większego ruchu warto mieć więcej dostępnych hostów niż mniej, ale analizując z drugiej strony - ten jeden dodatkowy host to Primary, jeśli zostanie obciążony to doprowadzi to również do niestabilności zapisów. Jeśli usługa nie posiada mechanizmów ponowień takich jak klienci z ponowieniami lub message queue, to dane mogą zostać niezapisane, co skłaniałoby do użycia preferencji secondary - wtedy awaria dotyczyłaby tylko odczytów.
Po wdrożeniu nowego typu preferencji warto mieć wgląd w metryki:
Jak widać, każde takie ustawienie trzeba samodzielnie przeanalizować, uwzględniając swoje własne wymagania względem stabilności. Wybranie konkretnej preferencji odczytu może mieć dalekosiężne skutki, dlatego przed wyborem zawsze warto się dłużej zastanowić i przeanalizować słabe punkty konkretnego rozwiązania. Polecam również zapoznać się z oficjalną dokumentacją.