Confluence/Jira/FishEye a problem z zapytaniami do MySQL-a

Niedawno miałem „przyjemność” instalowania kilku aplikacji napisanych w Javie na serwerach PCLab.pl. Były to: Jira (z GreenHopper), Confluence i FishEye. Sama instalacja w systemie to nie był duży problem (może poza FishEye), ale właściwa instalacja aplikacji już nie poszła tak gładko. Zawsze gdy coś idzie sprawnie, na pewnym etapie musi pojawić się problem :) Wszelkie informacje tu zawarte pokazuję na przykładzie Debiana 5.x.

Problem

No i problem się pojawił. Jira zainstalowała się bez problemu, za to Confluence odmawiało instalacji na etapie tworzenia struktury bazy danych (używam MySQL-a). FishEye domyślnie instaluje się ze swoją bazą lokalną, którą można zmigrować do MySQL-a już po samej instalacji.

Podczas tworzenia struktury w bazie danych pojawiał się komunikat błędu:

org.springframework.jdbc.UncategorizedSQLException: Hibernate
operation: Could not save object; uncategorized SQLException
for SQL []; SQL state [HY000]; error code [1598]; Binary logging
not possible. Message: Transaction level 'READ-COMMITTED' in
InnoDB is not safe for binlog mode 'STATEMENT'; nested exception
is java.sql.SQLException: Binary logging not possible. Message:
Transaction level 'READ-COMMITTED' in InnoDB is not safe for
binlog mode 'STATEMENT' at org.springframework.jdbc.support.
AbstractFallbackSQLExceptionTranslator.translate
(AbstractFallbackSQLExceptionTranslator.java:83) caused by:
java.sql.SQLException: Binary logging not possible. Message:
Transaction level 'READ-COMMITTED' in InnoDB is not safe for
binlog mode 'STATEMENT' at
com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1072)

I najważniejszy jego fragment:

SQL state [HY000]; error code [1598]; Binary logging not
possible. Message: Transaction level 'READ-COMMITTED' in InnoDB
is not safe for binlog mode 'STATEMENT'

W przypadku FishEye, podczas migracji do MySQL-a, też występował pewien problem. Opisałem to poniżej.

Przyczyna

Zaprzągłem do pracy Google, ale z miernym skutkiem. Dowiedziałem się jednak kilku rzeczy, lecz rozwiązania problemu (które by mnie satysfakcjonowało) nie znalazłem. Zabrałem się zatem za próbę eliminacji problemu samodzielnie. Tutaj chciałbym jeszcze raz zaznaczyć, że środowisko Java jest dla mnie obcym środowiskiem. Nie pisałem w Javie prawie nic, tym bardziej nie znałem struktury URL-a połączenia do bazy danych (jdbc), stąd mogłem popełnić jakieś błędy (i mogą się one znajdować w poniższym opisie).

Problem wynika z kilku czynników, które muszą zaistnieć jednocześnie:

  1. Crowd – w tym momencie nie mogę się już doszukać od której wersji (ale problem występuje także u niektórych osób podczas migracji). Chodzi o fakt wykorzystywania innego poziomu izolacji tranzakcji dla zapytań na MySQL-u, teraz jest to ‘READ-COMMITTED’.
  2. MySQL – włączone logowanie do binloga (czyli używanie replikacji), domyślna konfiguracja replikacji, wersja 5.1.11 lub niższa, albo 5.1.29 lub wyższa.

Problem wynika z faktu, że Confluence wymaga izolacji transakcji na poziomie ‘READ-COMMITTED’, a MySQL domyślnie używa binlog_format = ‘STATEMENT’ – zapytania w takim poziomie izolacji nie mogą być bezpiecznie logowane przy takim ustawieniu MySQL-a, więc ten zwraca błąd (mogło by dojść do „rozjechania” się repliki z masterem, a następnie do zatrzymania replikacji).

Problem nie występuje dla MySQL-a w wersji 5.1.12 do 2.1.28, ponieważ te wersje zawierały inne domyślne ustawienie binloga: binlog_format = ‘MIXED’.

Możliwe ustawienia zmiennej binlog_format to:

You can select the binary logging format explicitly by starting
the MySQL server with --binlog-format=type. The supported values
for type are:
- STATEMENT causes logging to be statement based.
- ROW causes logging to be row based.
- MIXED causes logging to use mixed format.

Ustawienie ‘MIXED’ powoduje, że serwer będzie sobie dobierał automatycznie wg. potrzeb ‘ROW’ lub ‘STATEMENT’. Dzięki temu przy powyższych wersjach MySQL-a problem nie występował (czytałem o problemach po upgradzie wersji MySQL-a, które z tego właśnie wynikały).

Rozwiązanie

Na stronach pomocy Jiry czy Confluence można znaleźć informację mówiącą o konieczności przestawienia w MySQL-u (w pliku konfiguracyjnym MySQL-a) sposobu logowania na ‘ROW’.

Dla mnie to było nie do przyjęcia:

  • Po pierwsze zmiana taka powoduje dużą zmianę w sposobie logowania zapytań w binlogu. Tych zapytań jest zazwyczaj o wiele więcej, replikują się dłużej (mniej wydajnie).
  • Po drugie w zupełności wystarczy zmiana na “MIXED”, wtedy serwer sam sobie dobierze odpowiedni typ w zależności od zapytań.
  • Po trzecie wymaga zmiany ustawień na każdym z MySQL-i w taki sposób aby nie zakłócić działania replikacji.
  • Po czwarte konieczność wyłączenia portali i aplikacji pracujących obecnie na tym serwerze na czas zmiany (wyłączenie to nie jedyna możliwość, ale np. przepiecie na replikę i przywracanie ponownie to już dość duży nakład czasu).

Nie wystarczy dokonać zmiany tylko na „masterze”:

Each MySQL Server can set its own and only its own binary logging format (true whether binlog_format is set with global or session scope). This means that changing the logging format on a replication master does not cause a slave to change its logging format to match. (When using STATEMENT mode, the binlog_format system variable is not replicated; when using MIXED or ROW logging mode, it is replicated but is ignored by the slave.) Changing the binary logging format on the master while replication is ongoing, or without also changing it on the slave can thus cause unexpected results, or even cause replication to fail altogether.

Pierwszą wskazówką okazała się informacja na stronie MySQL-a:

In addition to switching the logging format manually, a slave server may switch the format automatically. This happens when the server is running in either STATEMENT or MIXED format and encounters an event in the binary log that is written in ROW logging format. In that case, the slave switches to row-based replication temporarily for that event, and switches back to the previous format afterward.

Oznacza to, że przy pozostawieniu bieżącej konfiguracji MySQL-a (binlog_format = ‘STATEMENT’), można ustawić zmienną binlog_format = ‘ROW’ tylko na czas i dla bieżącej sesji (bieżącego połączenia). Takie ustawienie będzie miało wpływ tylko i wyłącznie na zapytania kierowane do bazy na tym połączeniu, a MySQL działający jako „SLAVE” prawidłowo zreplikuje dane w tym przypadku. Zmiana nie będzie miała wpływu na inne połączenia, a co za tym idzie na inne aplikacje i serwisy.

Po małych poszukiwaniach w Gogole udało mi się ustawić zmienną sesyjną w url-u połączenia do bazy.

Implementacja rozwiązania w Confluence w trakcie instalacji

Oczywiście moje rozwiązanie nie zadziałało od razu – to było by zbyt piękne :)

Zmodyfikowałem url połączenia do bazy w ten sposób:

jdbc:mysql://localhost/jira-confluence?sessionVariables=storage_engine=InnoDB,binlog_format=ROW&useUnicode=true&characterEncoding=utf8

Pogrubiłem najważniejszą część (binlog_format=ROW). Ustawienie Unicode i Encoding jest potrzebne tylko wtedy, gdy domyślnie MySQL pracuje z innym kodowaniem (jak w moim przypadku). Aby skorzystać z takiego rozwiązania, użytkownik za pomocą którego łączymy się do bazy musi mieć nadane uprawnienia „SUPER”:

Jak wspomniałem, nie zadziałało to od razu. Tu też dłuższą chwilę szukałem rozwiązania. Sytuacja była o tyle ciekawa, że MySQL zgłaszał mi (jak na powyższym obrazku), że potrzebne jest uprawnienie SUPER (czyli zmienną sesyjną ustawiał), ale podczas tworzenia struktury bazy ponownie pojawiał się opisywany problem.

Aby w końcu instalację ponowić należy:

  1. Powrócić na ekran ustawień połączenia do bazy (“Wstecz” w przeglądarce dobrze działa w tym wypadku)
  2. Zmodyfikować URL połączenia do bazy tak, jak podałem powyżej:
    jdbc:mysql://localhost/jira-confluence?sessionVariables=storage_engine=InnoDB,binlog_format=ROW&useUnicode=true&characterEncoding=utf8
  3. Nadać użytkownikowi w MySQL-u uprawnienie SUPER:
    GRANT SUPER ON *.* TO 'jira-confluence'@'localhost';
  4. Skasować już utworzone tabele w bazie
    DROP DATABASE jira-confluence;
    CREATE DATABASE jira-confluence
        CHARACTER SET = utf8
        COLLATE = utf8_general_ci;
  5. Zatrzymać i uruchomić ponownie deamona Confluence:
    /etc/init.d/confluence stop
    /etc/init.d/confluence start
  6. Ponowić zatwierdzenie ustawień bazy danych.

Tak – restart deamona Confluence pomógł w tym miejscu. Możliwe, że jest inne rozwiązanie. To (jak na jednorazową potrzebę) było wystarczające.

W przypadku Jiry instalacja bez ustawienia tej zmiennej sesyjnej przebiega bez problemu, ale ponoć nie da się np. tworzyć nowych zgłoszeń. Sam nie sprawdzałem, zmodyfikowałem URL połączenia do bazy zaraz po instalacji Confluence.

W przypadku FishEye instalacja przebiega bez problemu, ponieważ odbywa się na wbudowanej bazie danych. Podczas migracji do MySQL-a pojawia się jednak taki sam problem.

Implementacja rozwiazania podczas instalacji

Zarówno w przypadku Jiry jak i Confluence, podczas instalacji wystarczy dodać zmienną sesyjną ustawiającą binlog_format (tak jak powyżej):

jdbc:mysql://localhost/jira-confluence?sessionVariables=storage_engine=InnoDB,binlog_format=ROW&useUnicode=true&characterEncoding=utf8

W przypadku FishEye, jak wspominałem, domyślnie instalacja przeprowadzana jest na lokalnej bazie, którą można zmigrować do MySQLa już po instalacji. Tutaj zasada jest taka sama – dodanie zmiennych sesyjnych w url-u:

Próbowałem umieścić zmienne sesyjne w polu poniżej URL-a połączenia do bazy, ale z jakiegoś powodu nie dawało to rezultatu. Jedynie z ustawieniami w URl-u poszło bez problemu.

Implementacja rozwiązania podczas upgrade-u

Przed upgradem aplikacji,  należy zatrzymać deamona, a następnie wyedytować pliki konfiguracyjne, odpowiednio:

Jira:

/var/atlassian/application-data/jira/dbconfig.xml

Confluence:

/var/atlassian/application-data/confluence/confluence.cfg.xml

FishEye:

/var/atlassian/application-data/fisheye/config.xml

(ścieżki oczywiście mogą być inne, jeśli są inne niż domyślne, w przypadku FishEye konfiguracji można dokonać z poziomu panelu administracyjnego)

Krótkie podsumowanie

Trochę szkoda, że takie rozwiązanie nie pojawiło się nigdzie w oficjalnej pomocy. Dziwne za to, że nikt nie opisał takiego rozwiązania jakie ja zastosowałem (albo ja zwyczajnie nie znalazłem), a działa znakomicie. Mam nadzieję, że ktoś kto będzie miał podobny problem znajdzie ten opis i zaoszczędzi wiele czasu.

 

 

Ten wpis został opublikowany w kategorii PCLab.pl, Programowanie, Serwery, WWW i oznaczony tagami , , , , , , , , , . Dodaj zakładkę do bezpośredniego odnośnika.

Dodaj komentarz

Musisz się zalogować (także Facebook, Google+, Twitter), aby móc dodać komentarz.