Jeśli mamy API restowe, to jedną z możliwości utrzymaniu ładu i skladu przy wielu klientach jest wersjowanie API. Dzięki temu jeśli mamy 10 klientów, to możemy np. usunąc jakies pole z JSONa w nowszej wersji nie obawiając się że wykrzaczą się dotychczasowi klienci. Ale jak to jest w przypadku korzystania z narzędzi typu RabbitMQ albo Apache Kafka? W większych projektach jak jakiś amazon takich klientów jest troszkę, co zrobić gdy chcemy modyfikować format wiadomości?
- Rejestracja:ponad 4 lata
- Ostatnio:około 2 lata
- Lokalizacja:Warszawa
- Postów:1092

- Rejestracja:ponad 8 lat
- Ostatnio:około 5 godzin
- Lokalizacja:U krasnoludów - pod górą
- Postów:4707
Nie modyfikujesz. Dodajesz nowe typy wiadomości. Hardkorowo to już mieć w nazwach "eventów" zaszyte wersje. UserMadePooV1.
Możesz nawet dodawać nowe topics
(ale tego raczej nigdy nie robiłem).
Po jakimś czasie można usunąć oczywiście - jak już wiadomo, że nie będzie obsługi starego formatu.

- Rejestracja:około 9 lat
- Ostatnio:ponad 2 lata
- Lokalizacja:UK
- Postów:2235
W naprawdę dużych systemach takich jak wymieniony przez Ciebie Amazon to faktycznie sprawa jest bardziej skomplikowana, i tam rzeczywiście należy albo unikać do maksimum "breaking changes" albo jak wspomniał Jarek dodawać nowe wersje w postaci całkowicie nowych typów.
W innych systemach można pokusić się o zwykle wersjonowanie, i liczyć się z tym że jak np. usunie się jakieś pole z wiadomości to klienci którzy się nie zaktualizowali będą dostawać domyślne wartości (bo raczej tak będzie skonfigurowana deserializacja).

- Rejestracja:prawie 7 lat
- Ostatnio:około 2 godziny
- Lokalizacja:Kraków
- Postów:1999
Robić zmiany kompatybilne wstecz / wprzód, jeśli tylko się da
Takie "niełamiące" zmiany to może być np.:
- usunięcie atrybutu, który dotąd był opcjonalny
- dodanie opcjonalnego atrybutu
A jeśli musisz zrobić zmianę łamiącą kompatybilność i jako tako jesteś w stanie zapanować nad klientami (czyli nie na zasadzie - masz 100 klientów z czego 30 na pewno znajdzie ważny powód, by nigdy nie zupgradować swojej wersji klienta), to w miarę możliwości rozbić na etapy, np. chcąc usunąć/zastąpić atrybut, który był obowiązkowy ale właściwie na wyrost:
- robisz ten niechciany atrybut jako opcjonalny (w nowej wersji/nowym typie - jak wolisz) i wypuszczasz klienta, który sobie bez niego poradzi, oznaczasz to jako deprecated czy coś i rozgłaszasz nowinę
- dajesz temu trochę pożyć, żeby się otoczenie oswoiło i przeniosło (w końcu, kiedyś)
- kiedyś będziesz mógł to bezpiecznie usunąć. Chyba. Oby. Nadal będą kombinacje producera / consumera które są ze sobą niekompatybilne, ale przynajmniej będą
jakieś
kompatybilne i istnieje jakakolwiek gładka ścieżka.
Nie modyfikujesz. Dodajesz nowe typy wiadomości.
Nie będzie to nadal powodowało problemów, jeżeli stary odbiorca nie przewiduje
/ nie obsługuje
(nawet przez zignorowanie) nowych i nieznanych typów wiadomości? Jeśli w tej samej kolejce / topiku zacznie pojawiać się coś o nieznanym typie / schemacie to IMO tak czy owak może to łamać kompatybilność i stanowić breaking change
. Jak ta nieznaność
nie jest nijak obsłużona, to w sumie chyba mała różnica czy zmienił się typ, czy pojawił się nowy?
Tak czy siak moim zdaniem ważniejsze od konkretnego sposobu realizacji jest projektowanie z myślą o tym, by ewentualne zmiany były jak najmniej bolesne. Jeśli schemat jest sztywny i klient sztywno się go trzyma, to co do zasady ciężko będzie zrobić zmianę bez wysadzania czegoś w powietrze i/lub bez spędzenia kilku sprintów czekając, aż wszyscy łaskawie przyjmą do wiadomości zmianę.
Może trochę pomóc wpisanie do kodeksu rycerskiego, że w kwestii formatu wiadomości ostateczne słowo ma nadawca ;)
- Rejestracja:ponad 4 lata
- Ostatnio:około 2 lata
- Lokalizacja:Warszawa
- Postów:1092
Nie będzie to nadal powodowało problemów, jeżeli stary odbiorca nie przewiduje / nie obsługuje (nawet przez zignorowanie) nowych i nieznanych typów wiadomości?
No ale o to chodzi.
Jest np. topic1 z serwisu A , na który są podpięte serwisy X, Y, Z. Serwis A chce coś zmydyfikowac, więc powstaje topic2, X i Y zostają na topic1 a Z ma topic2

- Rejestracja:prawie 7 lat
- Ostatnio:około 2 godziny
- Lokalizacja:Kraków
- Postów:1999
Aleksander32 napisał(a):
No ale o to chodzi.
Jest np. topic1 z serwisu A , na który są podpięte serwisy X, Y, Z. Serwis A chce coś zmydyfikowac, więc powstaje topic2, X i Y zostają na topic1 a Z ma topic2
Słowa Jarka:
Możesz nawet dodawać nowe topics (ale tego raczej nigdy nie robiłem).
Zatem rozumiem, że idea dodawania nowych typów jest ortogonalna do dodawania nowych topików ;) nie mówiąc o tym, że w systemach rozproszonych mniej znaczy więcej, im mniej "odnóg" w postaci wysyłania eventów, requestów itd. tym lepiej bo mniej rzeczy może się popsuć.
Nie mówiąc o tym, że sam fakt dodania / zaprzestania używania topików
sam w sobie mógłby stanowić breaking change. Szczególnie, jeśli zaczniesz rozważać sytuacje, gdy w tych topicach persystujesz sobie eventy przez nieograniczony czas i potencjalnie możesz do nich kiedyś wrócić - ale wtedy to już jakikolwiek breaking change
, nawet rozbity na etapy, będzie dużym problemem gdy np. baaardzo nowy consumer zassie przedpotopowy event...
Ba, jak popuścić wodze fantazji jest jeszcze gorzej - co, jeśli oba topiki (stary i nowy) są spartycjonowane, kolejność eventów zachowana jest w obrębie partycji, a wprowadzona zmiana dotknie sposobu partycjonowania bo dotknie jakoś klucza? Może się okazać, że w zależności od topiku consumerzy mogą dostawać te same eventy w zamienionej kolejności - to już chyba lepiej walczyć z wieloma wersjami pod jednym topikiem ;)

dostawać te same eventy w zamienionej kolejności
w zasadzie to prawie zawsze trzeba się przygotowwywać na absurdalne kolejności eventów. Np. najpierw dostajesz anulowanie zamówienia
, a potem zamówienie
- dość często to normalka.

- Rejestracja:ponad 8 lat
- Ostatnio:około 5 godzin
- Lokalizacja:U krasnoludów - pod górą
- Postów:4707
Nie będzie to nadal powodowało problemów, jeżeli stary odbiorca nie przewiduje / nie obsługuje (nawet przez zignorowanie) nowych i nieznanych typów wiadomości? Jeśli w tej samej kolejce / topiku zacznie pojawiać się coś o nieznanym typie / schemacie to IMO tak czy owak może to łamać kompatybilność i stanowić breaking change. Jak ta nieznaność nie jest nijak obsłużona, to w sumie chyba mała różnica czy zmienił się typ, czy pojawił się nowy?
Tak będą problemy - stąd te nowe topics
. Nie pracuje w żadnych amazonach więc u mnie tego typu problemy sa raczej proste:
a ) release - puty wszystkie instancje nie zostaną zupdatowane - (i tylko w dziwnych przypadkach to nie jest pare minut )- wtedy wystarczy zrobić najpierw release obsługujacy nowe wersje eventów, a jak już wszystko się zupdatuje to wrzucam taki, który też nowe eventy faktycznie wrzuca
b) inne serwisy, ale to są negocjowalne pojedyncze przypadki - można zawsze plan ułożyć (stare wersje eventów do starej kolejki/topiku, nowe do nowej)

- Rejestracja:prawie 18 lat
- Ostatnio:17 dni
@Aleksander32: Nie napiszę nic odkrywczego, większość już padła w tym temacie.
Przede wszystkim trzeba zadbać, żeby dało się wprowadzać zmiany. Czyli jeżeli są jakieś nowe pola w strukturze, to muszą one zostać zachowane, nawet jeżeli obecny kod ich nie rozumie. Czyli parsowanie jsona na jakieś nasze DTO na 99% nie przejdzie, bo w naszej klasie nie będzie zmiennych na nowe pola, więc potem je stracimy. Nieznane pola dobrze jest sygnalizować w aplikacji czy logach, ale nie wolno ich tracić.
Po drugie, przy wprowadzaniu zmian robimy okres przejściowy, gdy kod rozpoznaje obie wersje wiadomości (starą i nową). Może to robić przez osobny topic, przez osobny typ, albo po prostu ifologią, podejść jest wiele, każde ma wady i zalety. Ważne jest, żeby w okresie przejściowym przemigrować wszystkie dane, czyli jeżeli trzeba, to idziemy do bazy danych i robimy update na każdym rekordzie, żeby był w nowym formacie czy co tam jest potrzebne. Możemy też robić to na bieżąco, ale wtedy rzadko dotykane rekordy prawdopodobnie zostaną w starym formacie na bardzo długo. Tu dobrze jest stosować podejście z krokowym wdrażaniem aplikacji, mamy setkę maszyn, to wrzucamy nową wersję aplikacji tylko na jedną maszynę i patrzymy, czy wszystko działa. Trzymamy to na produkcji przez chwilę i dopiero potem lecimy z podmianą dalej.
Po trzecie, hashmapa w evencie jest bardzo przydatna. Robimy zwykły słownik ze stringa na stringa i tam możemy wrzucać wszystkie rzeczy „na chwilę” lub w przypadku nieprzewidzianych sytuacji.
Dalej dochodzą mniejsze sztuczki, na przykład aliasy w enumach mapowane na tę samą wartość, jakiś sidecar tłumaczący wiadomości, scentralizowanie tworzenia wiadomości do jakiegoś serwisu, aby ten zajął się dbaniem o strukturę i tym podobne. Zależy od skali, konkretnego zastosowania i czasu.

- Rejestracja:około 17 lat
- Ostatnio:około godziny
- Postów:1873
Poczytaj o AVRO schema


writer's schema
i reader's schema
https://avro.apache.org/docs/current/spec.html#Schema+Resolution

- Rejestracja:ponad 11 lat
- Ostatnio:prawie 3 lata
@Charles_Ray: Dokładnie.
Sposobów na to jest wiele i raczej nie ma złotego graala, czasami poświęcasz latency, czasami flexibility, czasami coś innego. Warto zobaczyć jak to jest rozwiązane w popularnych protokołach - np. Protobuff, Avro, Thrift. Ogólnie jest to dość problematyczne, szczególnie jeżeli musisz zapewnić możliwość odtwarzania starych wiadomości (czasami nawet trzeba mieć sposób na powiązanie wersji kodu z wersją message z danego momentu).
Alternatywnie, w zależności od systemu, może da się wprowadzić 'negocjowanie' schema.
Oczywiście można stosować podejścia rodem z RESTa ale uciekałbym się do nich tylko w ostateczności.
scibi_92